TL; DR
Я думаю, что вы обнаружили ошибку в Git. Чтобы обойти это, используйте --no-single-branch
или настройте ветку вручную.
Другие сведения:
Если у вас есть рекурсивные подмодули, убедитесь, что ваш Git используется недавно и используйте --recommend-shallow
для рекурсивного включения мелких подмодулей или --no-recommend-shallow
для их отключения.
Вам может необходимо сделать это в два этапа. Я покажу это как последовательность из двух шагов ниже. Я знаю, что этот код сильно изменился между Git 1.7 и текущим (2.26 или около того) Git, и я ожидаю, что двухэтапная последовательность будет работать и для большинства старых версий.
Два шага:
N=... # set your depth here, or expand it in the two commands
git submodule update --init --depth $N --no-single-branch
git submodule update --remote --depth $N
Люди Git недавно исправили различные ошибки подмодулей мелкого клона как часть добавления --recommend-shallow
с рекурсивными подмодулями, поэтому все это может работать как одна команда , На основании анализа, приведенного ниже, должно работать как одна команда в текущем Git. Однако --no-single-branch
извлекает больше объектов, чем --single-branch
.
Другой вариант может заключаться в том, чтобы разрешить режим с одной ветвью, но исправить fetch
refspe c в подмодуле. Это требует трех шагов - ну, в любом случае, трех отдельных команд Git:
branch=... # set this to the branch you want
git submodule update --init --depth $N
(cd path/to/submodule &&
git config remote.origin.fetch +refs/heads/$branch:refs/remotes/origin/$branch)
git submodule update --remote --depth $N
(Вы можете сделать это во всех подмодулях с git submodule foreach
, но не забудьте выбрать правильное имя ветви для подмодуля. )
В целом - это не указывает c на вашу ошибку - я рекомендую избегать мелких подмодулей: они, как правило, работают не очень хорошо. Если вы действительно хотите их использовать, используйте довольно большую глубину: например, 50 или 100 или более. Настройте это на основе ваших собственных репозиториев и потребностей. (Ваша текущая настройка разрешает --depth 1
, если вы решите другую проблему.)
Long: вероятно, это ошибка в Git
Обратите внимание, что приведенный ниже анализ основан на исходный код. Я на самом деле не проверял это, так что возможно я что-то пропустил. Однако все принципы здравы.
Все подмодули всегда"коммиты ша", или, может быть, коммиты "sha1" - Git используются для их вызова , но теперь называет их OID, где OID обозначает Object ID. Будущее Git, вероятно, будет использовать SHA-2. 1 Так что «OID» или «ha sh ID», если кто-то хочет избежать синдрома TLA, 2 , безусловно, является лучший срок. Итак, позвольте мне сказать это так: все подмодули используют коммиты OID / ha sh -ID.
Что я подразумеваю под "все подмодули всегда используют OIDs / ha sh ID"? Ну, это один из ключей к мелким субмодулям. Неглубокие подмодули изначально присуще agile, и сложно Git использовать их правильно во всех случаях. Это утверждение:
Подмодуль отслеживается по имени ветви, а не по номеру ша-ша.
неправильно, что важно. Независимо от того, как сильно вы пытаетесь, подмодули - или, точнее, подмодуль коммитов - отслеживаются по ha sh ID.
Теперь верно, что есть имена ветвей, участвующих в клонировании и извлечении в подмодулях. Когда вы используете --shallow
с подмодулями, это может стать очень важным, поскольку большинство серверов не допускают выборку по ha sh -ID. Глубина, которую вы выбираете, и имя отдельной ветви, поскольку --depth
подразумевает --single-branch
, поэтому должна быть достаточно глубокой, чтобы достичь commit , который выбирает суперпроект Git.
Если вы override Git отслеживаемых по га sh -ID отслеживания фиксации с подмодулями, вы можете обойти одну проблему хрупкости. Это то, что вы делаете, но у вас есть ошибка.
1 И это не будет весело. Git довольно сильно зависит от каждого коммита, имеющего уникальный OID; введение нового пространства имен OID, так что каждый Git имеет два OID, каждый из которых уникален в своем пространстве имен, означает, что commitits не будет обязательно иметь соответствующий OID. Все протоколы усложняются: любой Git, который поддерживает только старую схему, требует SHA-1 га sh для (одиночного) OID, тогда как любой Git, который использует новую схему, хотел бы SHA-2 ха sh, возможно, вместе с SHA-1 га sh, чтобы дать старым гитам. Как только у нас есть объект, мы можем использовать его для вычисления другого ха sh (ов), но если у нас есть только один из двух хэшей, он должен быть правильным.
Простой способ справиться с этим - возложить бремя вычисления "ха другого парня sh" на Git, который имеет объект, в случае объекта, существующего в хранилище, которое использует другой OID Пространство имен. Но SHA-1 Gits нельзя изменить, поэтому мы не можем использовать этот метод. Бремя должно быть на новых гитарах SHA-2.
2 Обратите внимание, что само "SHA" - это TLA: трехбуквенная аббревиатура. TLAS, что означает синдром TLA, представляет собой ETLA: расширенную трехбуквенную аббревиатуру. 11
Как суперпроект Git выбирает подмодуль Git commit?
Команда git submodule
- , в настоящее время все еще большой сценарий оболочки , но использует C языковой помощник для большей части своей работы. Хотя это сложный сценарий оболочки, суть его заключается в следующем:
(cd $path && git $command)
для выполнения действий в каждом подмодуле. $path
- это путь для подмодуля, а $command
- команда для запуска в этом подмодуле.
Здесь есть кое-что из курицы и яйца, потому что $path
изначально просто пустой каталог: фактического клона пока нет, сразу после клонирования суперпроекта. Пока не станет клоном, команда Git не будет работать! Ну, ничего, кроме самого git clone
, то есть.
Между тем, каждый коммит суперпроекта имеет два элемента:
- a
.gitmodules
файл, перечисляющий имя подмодуля и любой данные конфигурации и инструкции для клонирования, если / когда это необходимо; и - a gitlink для подмодуля (ов).
gitlink содержит директиву: this commit требует , что субмодуль S будет отмечен как commit ha sh hash-value
. В интересный момент ниже мы получаем возможность использовать или игнорировать это значение ha sh, но сейчас отметим, что каждый коммит, по сути, говорит: Мне нужен клон, а в этом клоне мне нужен один конкретный коммит, по его sh ID.
Клонирование хранилища подмодулей
Чтобы клонировать подмодуль, нам нужен его URL. Мы запустим:
git clone $url $path
или, может быть:
git clone --depth $N --no-single-branch $url $path
или подобное. URL и путь являются наиболее важными частями. Они находятся в файле .gitmodules
, но это не то, где Git хочет их: Git хочет их в файле конфигурации в репозитории Git.
Выполнение git submodule init
копирует данные из файла .gitmodules
туда, куда Git их хочет. В действительности, эта команда не делает ничего интересного. Кажется, никто не использует его, потому что git submodule update --init
сделает это за вас каждый раз. Отдельная команда init
существует, так что вы можете, как сказано в документации, "настроить ... расположение подмодулей" (настроить URL-адреса).
Запуск git submodule update
(с или без --remote
, --init
и / или --depth
) заметят, существует ли клон. Ему нужна информация, которую git submodule init
сохранит, поэтому, если вы еще не сделали git submodule init
, вам нужна опция --init
, чтобы это произошло. Если подмодуль отсутствует отсутствует - если в суперпроекте еще нет клона подмодуля - git submodule update
теперь будет запускать git clone
. На самом деле это вспомогательный модуль, который запускает git clone
; см. line 558 ff. , хотя номера строк, несомненно, изменятся в будущих Git выпусках.
Обратите внимание на эти вещи git clone
:
- Он получает аргумент
--depth
, если вы используете --depth
. - Если он действительно получает аргумент
--depth
, он устанавливает --single-branch
по умолчанию, если вы не используете --no-single-branch
. - Он создает реальное хранилище для подмодуля, но ему всегда сообщается
--no-checkout
, поэтому он никогда не выполняет начальную git checkout
какой-либо фиксации. - Он никогда не получает
-b
/ --branch
аргумент . Это удивляет меня и, возможно, неправильно, но см. clone_submodule
в submodule--helper.c
источнике .
Теперь объедините пункт 2 с пунктом 4. Клонирование с --depth
подразумевает --single-branch
, который настраивает хранилище субмодулей, чтобы иметь:
remote.origin.fetch=+refs/heads/<name>:refs/remotes/origin/<name>
в качестве его предварительно сконфигурированной установки fetch
. Но Git не предоставил здесь имя ветви , поэтому значение по умолчанию name
является рекомендованным other Git, т. Е. Git что ты клонируешь. Это не то имя, которое вы сами настроили в своем суперпроекте.
Использование --no-single-branch
в строке git submodule update --init
вынуждает сделать клон без режима --single-branch
. Это дает вам --depth
фиксацию из коммитного наконечника всех ветвей и оставляет строку fetch
, настроенную как:
remote.origin.fetch=+refs/heads/*:refs/remotes/origin/*
, так что ваш репозиторий субмодулей имеет все имена веток в это (плюс глубина-50, или как бы глубоко вы ни указали, фиксирует доступность из этих имен). Или, как я уже говорил выше, вы можете использовать git config
в подмодуле, чтобы исправить настройку remote.origin.fetch
.
Проверка правильного коммита
Как только мы есть клон, оставшаяся задача - запустить правильный git checkout
или (другая команда Git) в подмодуле. То есть из команд:
(cd $path; git $command)
у нас теперь есть путь с рабочим деревом подмодуля; все, что нам нужно, это найти идентификатор ha sh и запустить git checkout
для этого идентификатора ha sh.
идентификатор ha sh хранится в gitlink. Обычно это то, что Git будет использовать здесь. Однако при использовании --remote
сценарий git submodule
теперь будет запускать вспомогательный модуль для определения «правильного» имени ветви. То есть помощник подмодуля найдет имя, которое вы настроили, если вы его настроили, или воспользуетесь именем ветви суперпроекта, если вы этого не сделали.
Обратите внимание, что это довольно поздно: подмодуль уже клонирован, и remote.origin.fetch
уже имеет другое имя . (Если, возможно, вам не повезло: возможно, другой Git рекомендовал такое же имя, которое вы получите здесь с --remote
. Но, вероятно, нет.)
Вот интересный фрагмент кода из тех строк исходного кода, которые я связал выше:
# enter here with:
# $sm_path: set to the submodule path
# $sha1: set to the hash from the gitlink
# $just_cloned: a flag set to 1 if we just ran `git clone`
if test $just_cloned -eq 1
then
subsha1= # i.e., set this to the empty string
else
subsha1=(...find hash ID that is currently checked out...)
fi
if test -n "$remote"
then
branch=(...find the branch you want...)
... fetch_in_submodule "$sm_path" $depth ...
sha1=(...use git rev-parse to find the hash ID for origin/$branch...)
fi
if test "$subsha1" != "$sha1" || test -n "$force"; then
... do stuff to the submodule ...
... in this case, git checkout -q $sha1 ...
fi
(я пропустил некоторые несущественные фрагменты и заменил несколько разделов $(...)
описаниями того, что они делают, а не на настоящий код) .
Вся эта работа заключается в следующем:
Хранилище субмодуля обычно находится в режиме detached HEAD , с проверкой одного конкретного коммита на ха sh ID. Даже если он находится в другом режиме - на ветке или в подключенном режиме HEAD, чтобы использовать очевидную противоположность - у него все еще есть один конкретный коммит, проверенный sh ID.
(Единственное реальное исключение здесь - право после первоначального клона, когда буквально ничего не извлечено.)
В разделе кода subsha1
выясняется, какой у него sh идентификатор.
В оставшейся части кода указано, какой га sh ID следует проверить. С опцией --remote
вы говорите суперпроекту Git: полностью игнорировать настройку gitlink . Все остальные опции используют настройку gitlink, и любой из них может вызвать проблемы с --depth 1
.
Здесь выдается сообщение об ошибке
Вы используете --remote
, чтобы сообщить своему суперпроекту Git: игнорировать gitlink ha sh ID . При этом используются branch=(...)
, а затем sha1=(...)
назначения для переопределения gitlink ha sh ID.
Это sha1=
назначение буквально означает этот код:
sha1=$(sanitize_submodule_env; cd "$sm_path" &&
git rev-parse --verify "${remote_name}/${branch}") ||
die "$(eval_gettext "Unable to find current \${remote_name}/\${branch} revision in submodule path '\$sm_path'")"
и здесь вы Вы узнаете сообщение об ошибке:
Unable to find current origin/version/3.2.0-era revision in submodule path '...'
Теперь команда git fetch
должна , можно надеяться, что она получила коммит с именем имя-ветви version/3.2.0-era
. Если бы он получил этот коммит, можно было бы надеяться, что он обновил бы правильное имя удаленного слежения, в этом случае, origin/version/3.2.0-era
.
Единственная команда-кандидат git fetch
, однако, вызывается by:
fetch_in_submodule "$sm_path" $depth
Эта команда запускает git fetch
с указанным вами параметром --depth
. Это не предоставляет какие-либо названия веток! Другие fetch_in_submodule
вызовы, в частности , этот на линии 628 , предоставляют raw ha sh ID (все еще не имя ветви), но это только предоставляет аргумент --depth
, если вы его указали.
Без refspe c, такого как имя ветви, git fetch origin
выбирает только то, что сконфигурировано в remote.origin.fetch
. Это имя из other Git.
Если параметр fetch=
не извлекает имя нужной ветви - и с клоном с одной ветвью вполне вероятно, что здесь git fetch
не получит требуемый коммит, и последующее git rev-parse
для преобразования имени удаленного отслеживания origin/$branch
в идентификатор ha sh не удастся. Это ошибка, которую вы видите.
Я не буду пытаться точно сказать, где находится ошибка - и, следовательно, как ее исправить, с точки зрения установки правильной конфигурации и / или выдачи git fetch
с соответствующими аргументами - здесь, но ясно, что текущая Git установка не работает для вашего случая. В итоге, однако, что Git пытается здесь сделать, это найти правильный OID , или в этом случае не может его найти.
Найдя правильный OID - используя git rev-parse origin/version/3.2.0-era
для вашего конкретного случая - ваш суперпроект Git будет затем запускать:
(cd $path; git checkout $hash)
в подмодуле, оставляя вас с отключенной HEAD, указывающей на тот же идентификатор ha sh, который вы запросили по филиалу. имя. Когда вы решите проблему, вы будете находиться в режиме отсоединенного заголовка commit-by-OID. только способ выйти из него - это ручной: вы должны выполнить свою собственную (cd $path; git checkout branch-name)
операцию.
Если вы когда-либо не используете git submodule update --remote
- если ваша система CI создает коммит, который, как говорит хранилище суперпроекта, строит, а не зависит от имени какой-либо ветви, находящегося под чьим-либо контролем, - мелкий клон должен содержать этот коммит после git fetch
. Вот где глубина составляет fr agile: насколько глубокой должна быть N? Нет правильного ответа, поэтому вы должны установить его самостоятельно.
Если вы настраиваете origin
Git с uploadpack.allowReachableSHA1InWant
или uploadpack.allowAnySHA1InWant
, установленным на true
, то git fetch
-by-ha sh -ID может получить произвольный коммит, позволяющий --depth 1
работать, но для этого вам нужно иметь контроль над репозиторием origin
Git (и увидеть предостережения в git config
документация относительно этих настроек).