Как сделать мелкие подмодули git? - PullRequest
119 голосов
/ 27 января 2010

Возможно ли иметь неглубокие подмодули? У меня есть суперпроект с несколькими подмодулями, каждый из которых имеет длинную историю, поэтому он становится слишком большим, перетаскивая всю эту историю.

Все, что я нашел, это эта тема без ответа .

Должен ли я просто взломать git-submodule для реализации этого?

Ответы [ 9 ]

109 голосов
/ 17 июля 2013

Новое в предстоящем git1.8.4 (июль 2013) :

"git submodule update" может произвольно клонировать репозитории субмодулей.

(А git 2.10 Q3 2016 позволяет записывать это с git config -f .gitmodules submodule.<name>.shallow true.
Смотрите конец этого ответа)

См. commit 275cd184d52b5b81cb89e4ec33e540fb2ae61c1f :

Добавьте параметр --depth к командам добавления и обновления «подмодуля git», который затем передается команде клона. Это полезно, когда субмодуль (ы) огромен, и вы на самом деле не заинтересованы ни в чем, кроме последнего коммита.

Добавлены тесты и внесены некоторые изменения в отступы для соответствия остальной части тестового файла в «Обновление подмодуля может обрабатывать символические ссылки в pwd».

Подписано: Фредрик Густафссон <iveqy@iveqy.com>
Автор: Дженс Леманн <Jens.Lehmann@web.de>

Это значит, что это работает:

git submodule add --depth 1 -- repository path
git submodule update --depth -- [<path>...]

С:

--depth::

Эта опция действительна для команд add и update.
Создайте «неглубокий» клон с историей, усеченной до указанного количества ревизий.


atwyman добавляет в комментарии :

Насколько я могу судить, эта опция не может использоваться для субмодулей, которые не очень точно отслеживают master. Если вы установите глубину 1, то submodule update может быть успешным только в том случае, если требуемый коммит подмодуля является последним мастером. В противном случае вы получите "fatal: reference is not a tree" .

Это правда.
То есть до git 2.8 (март 2016). В версии 2.8 у submodule update --depth есть еще один шанс на успех, даже если SHA1 напрямую доступен с одного из головок удаленного репо.

См. commit fb43e31 (24 февраля 2016 г.) от Stefan Beller (stefanbeller) .
Помощник: Джунио С Хамано (gitster) .
(Объединено с Junio ​​C Hamano - gitster - в коммит 9671a76 , 26 февраля 2016 г.)

субмодуль: стараться получить необходимый sha1 прямым путем, выбрав sha1

При просмотре изменения, которое также обновляет подмодуль в Gerrit, обычной практикой проверки является загрузка и выборочная установка патча локально для его тестирования.
Однако при локальном тестировании «git submodule update» может не получить нужную подмодуль sha1, поскольку соответствующая фиксация в подмодуле еще не является частью истории проекта, но также является предложенным изменением.

Если $sha1 не был частью выборки по умолчанию, мы пытаемся получить $sha1 напрямую . Однако некоторые серверы не поддерживают прямую выборку по sha1, что приводит к быстрому отказу git-fetch.
Мы можем здесь потерпеть неудачу, так как все еще отсутствующий sha1 в любом случае может привести к сбою на этапе проверки, поэтому неудача здесь настолько хороша, насколько мы можем ее получить.


MVG указывает в комментариях до коммит fb43e31 (git 2.9, февраль 2016 г.)

Мне кажется, что commit fb43e31 запрашивает отсутствующий коммит по идентификатору SHA1, поэтому настройки uploadpack.allowReachableSHA1InWant и uploadpack.allowTipSHA1InWant на сервере, вероятно, будут влиять на то, будет ли это работать.
Сегодня я написал сообщение в список git , в котором указывалось, как использовать мелкие подмодули для лучшей работы в некоторых сценариях, а именно, если фиксация также является тегом.
Давайте подождем и посмотрим.

Полагаю, именно поэтому fb43e31 сделал выборку для конкретного SHA1 резервным вариантом после выборки для ветви по умолчанию.
Тем не менее, в случае «--depth 1» я думаю, что было бы целесообразно прервать работу раньше: если ни один из перечисленных ссылок не соответствует запрошенному, и запрос от SHA1 не поддерживается сервером, то нет смысла получить что-нибудь, так как мы не сможем удовлетворить требование субмодуля в любом случае.


Обновление август 2016 г. (3 года спустя)

С Git 2.10 (3 квартал 2016 года) вы сможете делать

 git config -f .gitmodules submodule.<name>.shallow true

Подробнее см. * Подмодуль Git без лишнего веса .


Git 2.13 (второй квартал 2017 года) добавляет коммит 8d3047c (19 апреля 2017 года) Себастьян Шуберт (sschuberth) .
(Объединено Себастьяном Шубертом - sschuberth - в коммит 8d3047c , 20 апреля 2017 г.)

клон этого подмодуля будет выполнен как мелкий клон (с глубиной истории 1)

Однако Сиро Сантилли добавляет в комментарии (и подробности в своем ответе )

shallow = true на .gitmodules влияет только на ссылку, отслеживаемую HEAD пульта дистанционного управления при использовании --recurse-submodules, даже если на целевую фиксацию указывает ветвь, и даже если вы поставили branch = mybranch на .gitmodules а также.


В Git 2.20 (Q4 2018) улучшена поддержка подмодулей, которая была обновлена ​​для чтения из большого двоичного объекта в HEAD:.gitmodules, когда файл .gitmodules отсутствует в рабочем дереве.

См. коммит 2b1257e , коммит 76e9bdc (25 октября 2018 г.) и коммит b5c259f , коммит 23dd8f5 , коммит b2faad4 , commit 2502ffc , commit 996df4d , commit d1b13df , commit 45f5ef3 , commit bcbc780 ( 05 октября 2018 г.) Антонио Оспите (ao2) .
(Объединено с Junio ​​C Hamano - gitster - в коммит abb4824 , 13 ноября 2018 г.)

submodule: поддержка чтения .gitmodules, когда его нет в рабочем дереве

Если файл .gitmodules недоступен в рабочем дереве, попробуйте используя содержимое из индекса и из текущей ветви.
Это относится к случаю, когда файл является частью репозитория, но для некоторых причина, по которой он не проверен, например, из-за редкой проверки.

Это позволяет использовать как минимум команды git submodule. который читает файл конфигурации gitmodules без полного заполнения рабочее дерево.

Запись в .gitmodules по-прежнему требует извлечения файла, так проверьте это перед звонком config_set_in_gitmodules_file_gently.

Добавьте аналогичную проверку также в git-submodule.sh::cmd_add(), чтобы предвидеть возможный сбой команды "git submodule add", когда .gitmodules не является безопасным для записи; это не позволяет команде оставлять хранилище в ложном состоянии (например, хранилище подмодулей было клонировано, но .gitmodules не было обновлено из-за сбоя config_set_in_gitmodules_file_gently).

Более того, поскольку config_from_gitmodules() теперь получает доступ к глобальному объекту хранить, необходимо защитить все пути кода, которые вызывают функцию против одновременного доступа к глобальному хранилищу объектов.
В настоящее время это происходит только в builtin/grep.c::grep_submodules(), поэтому звоните grep_read_lock() перед вызовом кода, включающего config_from_gitmodules().

ПРИМЕЧАНИЕ: есть один редкий случай, когда эта новая функция не работает пока правильно: вложенные подмодули без .gitmodules в их рабочем дереве.

22 голосов
/ 15 августа 2016

Git 2.9.0 поддерживает субмодули мелкого клона напрямую, так что теперь вы можете просто позвонить:

git clone url://to/source/repository --recursive --shallow-submodules
15 голосов
/ 31 января 2010

После Ответ Райана Мне удалось придумать этот простой скрипт, который перебирает все подмодули и мелко клонирует их:

#!/bin/bash
git submodule init
for i in $(git submodule | sed -e 's/.* //'); do
    spath=$(git config -f .gitmodules --get submodule.$i.path)
    surl=$(git config -f .gitmodules --get submodule.$i.url)
    git clone --depth 1 $surl $spath
done
git submodule update
8 голосов
/ 30 января 2010

Считывая "исходный" подмодуль git, похоже, что git submodule add может обрабатывать подмодули, в которых уже есть свои репозитории. В таком случае ...

$ git clone $remote1 $repo
$ cd $repo
$ git clone --depth 5 $remotesub1 $sub1
$ git submodule add $remotesub1 $sub1
#repeat as necessary...

Вы хотите убедиться, что требуемый коммит находится в репозитории подмодулей, поэтому убедитесь, что вы установили соответствующий параметр --depth.

Редактировать: возможно, вам удастся избежать нескольких клонов субмодулей с последующим одним обновлением:

$ git clone $remote1 $repo
$ cd $repo
$ git clone --depth 5 $remotesub1 $sub1
#repeat as necessary...
$ git submodule update
5 голосов

Сводка ошибок / неожиданного / раздражающего поведения на Git 2.14.1

  1. shallow = true в .gitmodules влияет только на git clone --recurse-submodules, только если HEAD удаленного подмодуля указывает на требуемую фиксацию, даже если целевая фиксация указана веткой, и даже если вы поставили branch = mybranch на .gitmodules.

    Локальный тестовый скрипт . Такое же поведение на GitHub 2017-11, где HEAD управляется настройкой репо филиала по умолчанию:

    git clone --recurse-submodules https://github.com/cirosantilli/test-shallow-submodule-top-branch-shallow
    cd test-shallow-submodule-top-branch-shallow/mod
    git log
    # Multiple commits, not shallow.
    
  2. git clone --recurse-submodules --shallow-submodules завершается неудачей, если на коммит не ссылается ни ветка, ни тег с сообщением: error: Server does not allow request for unadvertised object.

    Локальный тестовый скрипт . Такое же поведение на GitHub:

    git clone --recurse-submodules --shallow-submodules https://github.com/cirosantilli/test-shallow-submodule-top-sha
    # error
    

    Я также спросил в списке рассылки: https://marc.info/?l=git&m=151863590026582&w=2 и получил ответ:

    Теоретически это должно быть легко. :)

    На практике не так много, к сожалению. Это потому, что клонирование будет просто получить последний кончик ветки (обычно мастер). В клоне нет механизма указать точный sha1, который требуется.

    Протокол проводной связи поддерживает запрос точных значений sha1, так что это должно быть охвачено. (Предостережение: это работает, только если оператор сервера включает uploadpack.allowReachableSHA1InWant, который github не имеет AFAICT)

    git-fetch позволяет извлекать произвольный sha1, поэтому в качестве обходного пути вы можете запустить fetch после рекурсивного клона с помощью «git submodule update», так как это будет использовать извлекает после первоначального клона.

TODO тест: allowReachableSHA1InWant.

2 голосов
/ 30 января 2010

Канонические места для ваших подмодулей удалены? Если да, то можете ли вы клонировать их один раз? Другими словами, хотите ли вы мелкие клоны только потому, что вы страдаете от потери пропускной способности частых субмодульных (ре) клонов?

Если вы хотите, чтобы мелкие клоны сохранили локальное дисковое пространство, то ответ Райана Грэма, кажется, хороший путь. Вручную клонировать репозитории, чтобы они были мелкими. Если вы считаете, что это будет полезно, адаптируйте git submodule для его поддержки. Отправьте электронное письмо на список с просьбой об этом (советы по его реализации, предложения по интерфейсу и т. Д.). По моему мнению, люди вполне поддерживают потенциальных участников, которые искренне хотят улучшить Git конструктивным образом.

Если вы в порядке с выполнением одного полного клона каждого подмодуля (плюс более поздние выборки, чтобы поддерживать их актуальность), вы можете попробовать использовать параметр --reference git submodule update (это в Git 1.6.4 и новее ) для ссылки на локальные хранилища объектов (например, сделайте --mirror клонов из канонических репозиториев подмодулей, затем используйте --reference в ваших подмодулях, чтобы указать на эти локальные клоны). Обязательно прочитайте о git clone --reference / git clone --shared перед использованием --reference. Единственная вероятная проблема со ссылками на зеркала - это если они когда-нибудь будут получать обновления без ускоренной пересылки (хотя вы можете включить повторные журналы и расширить их окна истечения срока действия, чтобы помочь сохранить любые оставленные коммиты, которые могут вызвать проблему). У вас не должно быть проблем, пока

  • вы не делаете никаких локальных коммитов субмодулей, или
  • любые коммиты, оставленные висящими из-за небыстрых форвардов, которые могут публиковать канонические репозитории, не являются предками ваших локальных коммитов субмодулей, или
  • вы усердно следите за тем, чтобы ваши локальные коммиты субмодулей были перебазированы поверх того, что может быть опубликовано в не-быстрых форвардах в канонических репозиториях субмодулей.

Если вы идете с чем-то подобным, и есть вероятность, что вы можете выполнять локальные коммиты подмодулей в ваших рабочих деревьях, вероятно, было бы неплохо создать автоматизированную систему, которая бы обеспечивала критические объекты, на которые ссылается извлеченный подмодули не остаются висящими в зеркальных хранилищах (и, если таковые имеются, копируют их в нужные им хранилища).

И, как говорится в справочной странице git clone, не используйте --reference, если вы не понимаете этих последствий.

# Full clone (mirror), done once.
git clone --mirror $sub1_url $path_to_mirrors/$sub1_name.git
git clone --mirror $sub2_url $path_to_mirrors/$sub2_name.git

# Reference the full clones any time you initialize a submodule
git clone $super_url super
cd super
git submodule update --init --reference $path_to_mirrors/$sub1_name.git $sub1_path_in_super
git submodule update --init --reference $path_to_mirrors/$sub2_name.git $sub2_path_in_super

# To avoid extra packs in each of the superprojects' submodules,
#   update the mirror clones before any pull/merge in super-projects.
for p in $path_to_mirrors/*.git; do GIT_DIR="$p" git fetch; done

cd super
git pull             # merges in new versions of submodules
git submodule update # update sub refs, checkout new versions,
                     #   but no download since they reference the updated mirrors

В качестве альтернативы, вместо --reference, вы можете использовать зеркальные клоны в сочетании с функцией жесткой связи по умолчанию git clone, используя локальные зеркала в качестве источника для ваших подмодулей. В новых клонах супер-проекта выполните git submodule init, измените URL подмодуля в .git/config, чтобы указать на локальные зеркала, затем выполните git submodule update. Вам нужно будет отложить все существующие проверенные подмодули, чтобы получить жесткие ссылки. Вы бы сэкономили пропускную способность, загрузив только один раз в зеркала, а затем загрузив локально из них в свои проверенные подмодули. Жесткое связывание позволит сэкономить дисковое пространство (хотя выборки будут иметь тенденцию накапливаться и дублироваться во многих экземплярах хранилищ объектов извлеченных подмодулей; вы можете периодически отгибать извлеченные подмодули из зеркал, чтобы восстановить экономию дискового пространства, обеспечиваемую hardlinking).

1 голос
/ 06 июня 2019

Мелкий клон подмодуля идеален, потому что они делают снимок при определенной ревизии / наборе изменений. Скачать zip-файл с сайта очень просто, поэтому я попробовал скрипт.

#!/bin/bash
git submodule deinit --all -f
for value in $(git submodule | perl -pe 's/.*(\w{40})\s([^\s]+).*/\1:\2/'); do
  mysha=${value%:*}
  mysub=${value#*:}
  myurl=$(grep -A2 -Pi "path = $mysub" .gitmodules | grep -Pio '(?<=url =).*/[^.]+')
  mydir=$(dirname $mysub)
  wget $myurl/archive/$mysha.zip
  unzip $mysha.zip -d $mydir
  test -d $mysub && rm -rf $mysub
  mv $mydir/*-$mysha $mysub
  rm $mysha.zip
done
git submodule init

git submodule deinit --all -f очищает дерево подмодулей, что позволяет повторно использовать скрипт.

git submodule извлекает 40 символов sha1, за которыми следует путь, соответствующий этому в .gitmodules. Я использую perl для объединения этой информации, разделенной двоеточием, а затем использую преобразование переменных для разделения значений на mysha и mysub.

Это критические ключи, потому что нам нужен sha1 для загрузки и путь для корреляции url в .gitmodules.

С учетом типичной записи субмодуля:

[submodule "label"]
    path = localpath
    url = https://github.com/repository.git

myurl клавиш на path = затем просматривает 2 строки после, чтобы получить значение. Этот метод не может работать последовательно и требует доработки. Grep url удаляет все оставшиеся ссылки типа .git, сопоставляя их с последними / и любыми значениями вплоть до ..

mydir - это mysub минус окончательный /name, который будет указывать каталог, ведущий к имени подмодуля.

Далее идет wget с форматом загружаемого URL-адреса zip-архива. Это может измениться в будущем.

Распакуйте файл в mydir, который будет подкаталогом, указанным в пути к субмодулю. Результирующая папка будет последним элементом url - sha1.

Проверьте, существует ли подкаталог, указанный в пути к субмодулю, и удалите его, чтобы разрешить переименование извлеченной папки.

mv переименуйте извлеченную папку, содержащую наш sha1, в правильный путь к субмодулю.

Удалить загруженный zip-файл.

Подмодуль init

Это скорее подтверждение концепции WIP, а не решение. Когда это работает, результатом является неглубокий клон подмодуля с указанным набором изменений.

Если в хранилище повторно размещен субмодуль для другой фиксации, повторно запустите сценарий для обновления.

Единственный раз, когда такой скрипт будет полезен, - это локальное построение исходного проекта без совместной работы.

1 голос
/ 01 августа 2016

Ссылка на Как клонировать git-репозиторий с определенной ревизией / changeset?

Я написал простой скрипт, у которого нет проблем, если ваша ссылка на подмодуль отсутствует от мастера

git submodule foreach --recursive 'git rev-parse HEAD | xargs -I {} git fetch origin {} && git reset --hard FETCH_HEAD'

Этот оператор извлекает ссылочную версию подмодуля.

Это быстро, но вы не можете зафиксировать ваши изменения в подмодуле (вы должны извлечь их до того, как https://stackoverflow.com/a/17937889/3156509)

полностью:

#!/bin/bash
git submodule init
git submodule foreach --recursive 'git rev-parse HEAD | xargs -I {} git fetch origin {} && git reset --hard FETCH_HEAD'
git submodule update --recursive
1 голос
/ 15 декабря 2015

Я создал немного другую версию, когда она не работает на переднем крае, что не все проекты делают. Стандартные дополнения к субмодулям не работали, как и скрипт выше. Поэтому я добавил поиск хеша для тега ref, и если у него его нет, он возвращается к полному клонированию.

#!/bin/bash
git submodule init
git submodule | while read hash name junk; do
    spath=$(git config -f .gitmodules --get submodule.$name.path)
    surl=$(git config -f .gitmodules --get submodule.$name.url)
    sbr=$(git ls-remote --tags $surl | sed -r "/${hash:1}/ s|^.*tags/([^^]+).*\$|\1|p;d")
    if [ -z $sbr ]; then
        git clone $surl $spath
    else
        git clone -b $sbr --depth 1 --single-branch $surl $spath
    fi
done
git submodule update 
...