Как сделать мелкие подмодули git?

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

Все, что я нашел, это этот неотвеченный поток.

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

Ответ 1

Новое в готовящемся 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".

Signed-off-by: Fredrik Gustafsson <[email protected]>
Acked-by: Jens Lehmann <[email protected]>

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

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 напрямую доступен с одного из заголовков удаленного репо.

См. коммит fb43e31 (24 февраля 2016 г.) Стефаном Беллером (stefanbeller).
Помощник: Джунио С. Хамано (gitster).
(Merged by Junio C Hamano -- [TG415] -- in commit 9671a76, 26 Feb 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 года) добавляет commit 8d3047c (19 апреля 2017 года) от Себастьяна Шуберта (sschuberth).
(Merged by Sebastian Schuberth -- [TG424] -- in commit 8d3047c, 20 Apr 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).
(Merged by Junio C Hamano -- [TG433] -- in commit abb4824, 13 Nov 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 в их рабочем дереве.


Примечание: Git 2.24 (Q4 2019) исправляет возможную ошибку сегмента при клонировании мелкого субмодуля.

См. commit ddb3c85 (30 сентября 2019 г.) автора Али Утку Selenium (auselen).
(Merged by Junio C Hamano -- [TG452] -- in commit 678a9ca, 09 Oct 2019)

Ответ 2

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

git clone url://to/source/repository --recursive --shallow-submodules

Ответ 3

После Ryan answer Я смог придумать этот простой script, который итерации через все подмодули и мелкие клонирует их:

#!/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

Ответ 4

Чтение через источник git -submodule ", похоже, что 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, который требуется.

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

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

Тест TODO: allowReachableSHA1InWant.

Ответ 6

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

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

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

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

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

И, как говорится в man-странице 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).

Ответ 7

Я создал немного другую версию, потому что, когда она не работает на краю кровотечения, что не все проекты делают. Стандартные дополнения к субмодулям не работали, а не script выше. Поэтому я добавил хэш-поиск для тега 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 

Ответ 8

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

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

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

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

Это быстро, но вы не можете зафиксировать свое редактирование на субмодуле (вы должны получить его не до того, как fooobar.com/questions/6390/...)

в полном объеме:

#!/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

Ответ 9

Мелкий клон подмодуля идеален, потому что они делают снимок при определенной ревизии/наборе изменений. Легко скачать почтовый индекс с сайта, поэтому я попробовал скрипт.

#!/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 keys on path = затем просматривает 2 строки после, чтобы получить значение. Этот метод не может работать последовательно и требует доработки. Grep url удаляет все оставшиеся ссылки .git типов, сопоставляя их с последним / и любым до . ,

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

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

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

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

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

Удалить загруженный почтовый файл.

Подмодуль init

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

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

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