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

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

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

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

8 ответов

Решение

Новое в готовящемся выпуске git1.8.4 (июль 2013 г.):

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

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

Смотрите https://github.com/git/git/commit/275cd184d52b5b81cb89e4ec33e540fb2ae61c1f:

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

Добавлены тесты и внесены некоторые коррективы в отступы, чтобы они соответствовали остальной части тестового файла: "Обновление подмодуля может обрабатывать символические ссылки в 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 напрямую доступен с одного из головок удаленного репо.

Смотрите коммит fb43e31 (24 февраля 2016 г.) от Stefan Beller ( stefanbeller )
Помогает: Junio ​​C Hamano ( gitster )
(Объединено Юнио С Хамано - 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, в котором указывалось, как использовать мелкие подмодули, чтобы они работали лучше для некоторых сценариев, а именно, если commit также является тегом.
Давайте подождем и посмотрим.

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


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

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

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

Смотрите " Подмодуль Git без лишнего веса " для более подробной информации.


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

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

Тем не менее, Ciro Santilli 新疆改造中心 六四事件 法轮功 добавляет в комментариях (и подробности в своем ответе)

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


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

См. Коммит 2b1257e, коммит 76e9bdc (25 октября 2018 года) и коммит b5c259f, коммит 23dd8f5, коммит b2faad4, коммит 2502ffc, коммит 996df4d, коммит d1b13df, коммит 45f5ef3, коммит bcbc780 (05 окт 2018) Антонио Оспите ( ao2 )
(Объединено Юнио С Хамано - 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 в их рабочем дереве.

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

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

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

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

Резюме ошибочного / неожиданного / раздражающего поведения на 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 и получил ответ:

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

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

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

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

Тест TODO: allowReachableSHA1InWant,

Читая git-подмодуль "source", он выглядит 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

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

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

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

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

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

И, как git clone manpage говорит, не используйте --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).

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

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

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

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

Это быстро, но вы не можете зафиксировать ваши изменения в подмодуле (вы должны получить его до отмены /questions/27373395/kak-preobrazovat-melkij-klon-git-v-polnyij-klon/27373404#27373404)

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

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

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

Неглубокий клон подмодуля идеален, потому что он делает снимок определенной ревизии / набора изменений. С веб-сайта легко загрузить 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 строки после, чтобы получить значение. Этот метод может работать нестабильно и требует доработки. URL grep удаляет все оставшиеся.git ссылки типа путем сопоставления с последним / и все до ..

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

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

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

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

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

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

Подмодуль инициализации

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

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

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

Мне нужно было решение для мелкого клонирования подмодулей, когда я не могу повлиять на клонирование основного репо. На основе одного решения выше:

#!/bin/bash
git submodule init
for i in $(git submodule | sed -e 's/.* //'); do
    git submodule update --init --depth 1 -- $i
done
Другие вопросы по тегам