Ошибка в заголовке подмодуля Git «ссылка не дерево»


305

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

$ git submodule update
fatal: reference is not a tree: 2d7cfbd09fc96c04c4c41148d44ed7778add6b43
Unable to checkout '2d7cfbd09fc96c04c4c41148d44ed7778add6b43' in submodule path 'mysubmodule'

Я знаю, каким должен быть подмодуль HEAD, есть ли способ, которым я могу изменить это локально, не нажимая на репо, который делает есть коммит 2d7cfbd09fc96c04c4c41148d44ed7778add6b43?

Я не уверен, что мне все ясно ... вот похожая ситуация, которую я обнаружил.


11
«роковое: ссылка не является деревом» в отношении подмодулей, как правило, означает, что коммит подмодуля, который, как ожидает родительское репо, еще не был передан или испорчен каким-либо другим способом. Для нас это запутанное сообщение об ошибке было разрешено простым нажатием субмодуля, который кто-то забыл нажать.
Крис Москини

1
@ChrisMoschini - у меня только что была эта проблема, и это было мое «решение», я нажал и вытащил основной репо, но я забыл протолкнуть свой последний коммит в репо подмодуля. Спасибо!
Rotem

Может быть, вы забыли нажать последние коммиты субмодулей
Hafenkranich

Ответы:


378

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

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

Наизнанку

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

Пример:

$ git submodule update
fatal: reference is not a tree: e47c0a16d5909d8cb3db47c81896b8b885ae1556
Unable to checkout 'e47c0a16d5909d8cb3db47c81896b8b885ae1556' in submodule path 'sub'

К сожалению, кто-то сделал супер-проект коммита, который ссылается на неопубликованный коммит в субмодуле sub. Почему-то мы уже знаем, что хотим, чтобы субмодуль был в коммите5d5a3ee314476701a20f2c6ec4a53f88d651df6c . Идите туда и проверьте это непосредственно.

Оформить заказ в субмодуле

$ cd sub
$ git checkout 5d5a3ee314476701a20f2c6ec4a53f88d651df6c
Note: moving to '5d5a3ee314476701a20f2c6ec4a53f88d651df6c' which isn't a local branch
If you want to create a new branch from this checkout, you may do so
(now or later) by using -b with the checkout command again. Example:
  git checkout -b <new_branch_name>
HEAD is now at 5d5a3ee... quux
$ cd ..

Так как мы проверяем коммит, это приводит к отсоединению HEAD в подмодуле. Если вы хотите убедиться, что субмодуль использует ветку, используйтеgit checkout -b newbranch <commit> для создания и извлечения ветку при фиксации или извлечение ветки, которую вы хотите (например, ту, у которой в подсказке есть нужная фиксация).

Обновление Супер-проекта

Оформление заказа в подмодуле отражается в суперпроекте как изменение рабочего дерева. Таким образом, мы должны поэтапно изменить индекс супер-проекта и проверить результаты.

$ git add sub

Проверьте результаты

$ git submodule update
$ git diff
$ git diff --cached
diff --git c/sub i/sub
index e47c0a1..5d5a3ee 160000
--- c/sub
+++ i/sub
@@ -1 +1 @@
-Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

Обновление субмодуля было тихим, потому что субмодуль уже находится в указанной фиксации. Первый diff показывает, что индекс и рабочее дерево совпадают. Третья разница показывает, что единственное поэтапное изменение - это перемещение subподмодуля в другой коммит.

совершить

git commit

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


Снаружи

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

$ git submodule update
fatal: reference is not a tree: e47c0a16d5909d8cb3db47c81896b8b885ae1556
Unable to checkout 'e47c0a16d5909d8cb3db47c81896b8b885ae1556' in submodule path 'sub'

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

Найти Странствующий коммит Супер-проекта

$ git log --oneline -p -- sub
ce5d37c local change in sub
diff --git a/sub b/sub
index 5d5a3ee..e47c0a1 160000
--- a/sub
+++ b/sub
@@ -1 +1 @@
-Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c
+Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
bca4663 added sub
diff --git a/sub b/sub
new file mode 160000
index 0000000..5d5a3ee
--- /dev/null
+++ b/sub
@@ -0,0 +1 @@
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

ОК, похоже, что все пошло не ce5d37cтак, поэтому мы восстановим подмодуль из его родителя ( ce5d37c~).

В качестве альтернативы, вы можете взять коммит субмодуля из текста патча ( 5d5a3ee314476701a20f2c6ec4a53f88d651df6c) и использовать вместо него вышеописанный процесс «изнутри, снаружи».

Оформить заказ в Супер-проекте

$ git checkout ce5d37c~ -- sub

Это сбрасывает запись субмодуля для subтого, что было в коммите ce5d37c~в суперпроекте.

Обновите субмодуль

$ git submodule update
Submodule path 'sub': checked out '5d5a3ee314476701a20f2c6ec4a53f88d651df6c'

Обновление субмодуля прошло нормально (это указывает на отключенную ГОЛОВКУ).

Проверьте результаты

$ git diff ce5d37c~ -- sub
$ git diff
$ git diff --cached
diff --git c/sub i/sub
index e47c0a1..5d5a3ee 160000
--- c/sub
+++ i/sub
@@ -1 +1 @@
-Subproject commit e47c0a16d5909d8cb3db47c81896b8b885ae1556
+Subproject commit 5d5a3ee314476701a20f2c6ec4a53f88d651df6c

Первый diff показывает, что subтеперь то же самое в ce5d37c~. Вторая разница показывает, что индекс и рабочее дерево совпадают. Третья разница показывает, что единственное поэтапное изменение - это перемещение subподмодуля в другой коммит.

совершить

git commit

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


В подходе "Снаружи, в", не могли бы вы объяснить, почему "похоже, что все плохо в ce5d37c?" Какие пальцы что то плохое совершают?
Гаррет Олбрайт

5
@Garrett: Предполагается, e47c0aчто это коммит, которого нет в локальном репозитории sub, но супер-проект subуказывает на этот коммит. Это могло произойти из-за того, что кто-то еще создал e47c0aв своей копии sub, обновил свой супер-проект, чтобы указать на этот коммит, и отправил супер-проект, не отправляя e47c0aв центральный / общий репозиторий для sub. Когда мы вытягиваем из центрального / общего супер-проекта мы получаем коммит , что точки subв e47c0a, но мы не можем «видеть» , что совершить. ce5d37cэто подозрительно, потому что, на основании различий, он ввел e47c0a.
Крис Джонсен

Это все еще остается довольно расплывчатым, где находится конкретный хеш subхранящегося в родительском репо, который имеет его в качестве подмодуля, и может ли он быть напрямую обработан для текущего HEAD subнапрямую, не полагаясь на более старое состояние родителя РЕПО, что не всегда может помочь.
matanster


187

попробуй это:

git submodule sync
git submodule update

2
К сожалению, не для меня, один из наших подмодулей был нацелен главным репозиторием git с помощью команды add, и теперь возникают проблемы при его отмене
Даниэль

9
У меня тоже сработало. Хотелось бы знать почему, хотя.
BenBtg

12
Оказывается, что делать это git submodule syncнеобходимо в сценариях, где URL удаленного для данного подмодуля изменился. В нашем случае мы добавили наш субмодуль из публичного репозитория, а затем изменили URL-адрес на частный форк - и попали в этот конкретный рассол.
Samscam

Например: у меня было настроено репо (A) с подмодулем, указывающим на мое репозиторий GitHub (B). Я создал ветку в репо A, потому что хотел указать B на чье-либо репозиторий github. После некоторой борьбы с этим и фиксации ветки я переключил свой репо A обратно на master и решил эту проблему с репо B. Решение @Lonre Wang устранило ее.
fbicknel

2
Предполагая, что никто ДЕЙСТВИТЕЛЬНО не облажался (в этом случае вам понадобится отличный ответ Криса Джонсена), ответ Lonre Wang должен решить проблему, ... ЕСЛИ ваши подмодули имеют собственные подмодули (и проблема находится внутри подмодуля). В этом случае вам нужно перейти в субмодуль, в котором есть субмодуль с проблемой, и выполнить приведенные выше команды. Обратите внимание, что в обновлении есть опция --recursive (git submodule update --recursive), но в синхронизации нет; вам действительно нужно вручную запустить 'git submodule sync' внутри подмодуля, который имеет проблемный суб (суб) модуль. Это была моя проблема;).
Карло Вуд

16

Эта ошибка может означать, что в подмодуле отсутствует коммит. То есть хранилище (A) имеет подмодуль (B). A хочет загрузить B так, чтобы он указывал на определенный коммит (в B). Если этот коммит как-то отсутствует, вы получите эту ошибку. Однажды возможная причина: ссылка на коммит была помещена в А, но фактический коммит не был перенесен из Б. Поэтому я бы начал с этого

Менее вероятно, есть проблема с разрешениями, и фиксация не может быть получена (возможно, если вы используете git + ssh).

Убедитесь, что пути подмодулей выглядят нормально в .git / config и .gitmodules.

Последнее, что нужно попробовать - внутри директории подмодуля: git reset HEAD --hard


3
Я уже объяснил, что в вопросе ... сам вопрос был, как решить это. И на это уже успешно ответили почти два года назад ... Разрешения тут не причем.
Маурисио Шеффер

1
Вы заявили об этом, вы, конечно, не объяснили это.
Даниэль Цадок

Я хочу сказать, что этот ответ не добавляет никакой ценной информации, я бы ее удалил.
Маурисио Шеффер

4
мне помог "git reset HEAD --hard" ... больше ничего не работало. Я пробовал предыдущие решения тоже, без игры в кости. Спасибо!
Вирджил

1
Каждая нить - это свой маленький мир онлайн. То, что вы говорите, представляет вас - вы не можете ожидать, что люди изучат вашу личную историю, чтобы попытаться сформулировать ваши комментарии в контексте, который дает вам уважение, которое вы желаете. Будьте добры, будьте уважительны, и вам не нужно будет просить людей понять ваши личные причуды. Если вы можете прочитать свой комментарий из нейтрального контекста, как посторонний, вы поймете мою критику.
Конюшня

10

Возможная причина

Это может произойти, когда:

  1. Подмодуль (ы) были отредактированы на месте
  2. Подмодуль (ы) совершено, который обновляет хэш подмодуля, на который указывают
  3. Подмодуль (ы) не выдвинут .

например, что-то подобное произошло:

$ cd submodule
$ emacs my_source_file  # edit some file(s)
$ git commit -am "Making some changes but will forget to push!"

В этом месте должен быть нажат субмодуль.

$ cd .. # back to parent repository
$ git commit -am "updates to parent repository"
$ git push origin master

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

Решение

Informa человек, который изменил субмодуль для push, т.е.

$ cd submodule
$ git push

6

Я получил эту ошибку, когда я сделал:

$ git submodule update --init --depth 1

но коммит в родительском проекте указывал на более ранний коммит.

Удаление папки submodule и запуск:

$ git submodule update --init

НЕ решил проблему. Я удалил репо и попробовал снова без флага глубины, и это сработало.

Эта ошибка возникает в Ubuntu 16.04 git 2.7.4, но не в Ubuntu 18.04 git 2.17, TODO находит точную фиксацию фиксации или версию.


с тех пор моя команда отказалась от подмодулей в нашем коде, слишком много хлопот, лол
Платон

1
какая была твоя альтернатива?
nuzzolilo

@nuzzolilo, который мы добавили username/repo#shaв package.json, гораздо более гибкий вариант - организовать вашу систему с помощью набора докер-контейнеров
Платон

3
Это так раздражает. --depth=1экономит столько пропускной способности, когда мне не нужна история репо. Если кто-нибудь когда-нибудь найдет или знает, почему это происходит, я бы хотел знать.
i336_

@ i336_ Хотя я не могу объяснить почему, я написал помощник cmake, который поможет решить эту проблему: github.com/LMMS/lmms/blob/… . Он использует deinitподход, который решает проблему большую часть времени. В комплекте с системой сборки конечный пользователь может просто позволить системе сборки извлечь субмодули и полностью отказаться от сломанной recursiveкоманды. Есть все еще сценарии, где это прерывается, например, субмодуль сделал принудительный толчок и полностью уничтожил коммит.
Tresf

5

Это также может произойти, если у вас есть субмодуль, указывающий на репозиторий, который был перебазирован, и данный коммит «пропал». Хотя фиксация все еще может находиться в удаленном репозитории, она не находится в ветке. Если вы не можете создать новую ветку (например, не свой репозиторий), вы застряли с необходимостью обновить супер проект, чтобы он указывал на новый коммит. В качестве альтернативы вы можете перенести одну из своих копий подмодулей в другое место, а затем обновить супер-проект, указав вместо этого этот репозиторий.


5

Ваша ветка может быть не в курсе, простое решение, но попробуйте git fetch


2

Этот ответ предназначен для пользователей SourceTree с ограниченным опытом работы с терминалом.

Откройте проблемный субмодуль из проекта Git (супер-проект).

Получите и убедитесь, что флажок «Получить все теги» отмечен.

Rebase тянуть ваш проект Git.

Это решит проблему «ссылка не дерево» 9 из 10 раз. Это 1 раз, это не будет, это исправление терминала, как описано в верхнем ответе.


1

Ваша история субмодулей в любом случае безопасно сохраняется в подмодуле git.

Итак, почему бы просто не удалить подмодуль и добавить его снова?

В противном случае, вы пытались вручную редактировать HEADили refs/master/headв подмодуле.git


1
Это не будет работать, потому что где-то есть ссылка на 2d7cfbd09fc96c04c4c41148d44ed7778add6b43, который находится только в локальном репо где-то еще, но не опубликован
Маурисио Шеффер

1

Просто чтобы быть уверенным, попробуйте обновить ваши gitдвоичные файлы.

GitHub для Windows имеет версию, git version 1.8.4.msysgit.0которая в моем случае была проблемой. Обновление решило это.


1

В моем случае ни один из приведенных выше ответов не решает проблему, даже если они являются хорошими ответами. Поэтому я публикую свое решение (в моем случае это два клиента git, клиент A и B):

  1. перейти в каталог подмодуля:

    cd sub
    
  2. Оформить заказ мастеру:

    git checkout master
    
  3. перебазировать в код коммита, который видит оба клиента

  4. вернитесь в каталог родителей:

  5. поручить освоить

  6. перейти к другому клиенту , сделать rebaseснова.

  7. наконец-то теперь все работает нормально! Может быть, потерять пару коммитов, но это работает.

  8. К вашему сведению, не пытайтесь удалить ваш подмодуль, он останется .git/modulesтам и не сможет снова прочитать этот подмодуль, если он не является локальным реактивным.


1

Чтобы синхронизировать репозиторий git с головой субмодуля, в случае, если это действительно то, что вам нужно, я обнаружил, что удаление субмодуля и последующее его чтение позволяет избежать работы с историей. К сожалению, удаление подмодуля требует взлома, а не единственной команды git, но выполнимой.

Шаги, которые я выполнил, чтобы удалить подмодуль, вдохновлены https://gist.github.com/kyleturner/1563153 :

  1. Запустите git rm --cached
  2. Удалите соответствующие строки из файла .gitmodules.
  3. Удалите соответствующий раздел из .git / config.
  4. Удалите теперь неотслеживаемые файлы субмодулей.
  5. Удалить каталог .git / modules /

Опять же, это может быть полезно, если все, что вам нужно, это снова указать на головку подмодуля, и вы не усложнили задачу, сохранив локальную копию подмодуля в целости и сохранности. Предполагается, что у вас есть подмодуль «право» в качестве собственного репо, где бы он ни находился, и вы просто хотите вернуться к правильному включению его в качестве подмодуля.

Примечание: всегда делайте полную копию своего проекта перед тем, как приступить к подобным манипуляциям или любой команде git, кроме простого коммита или толчка. Я бы посоветовал это со всеми другими ответами, а также в качестве общего руководства Git.


1

Просто наткнулся на эту проблему, и ни одно из этих решений не помогло мне. То, что оказалось решением моей проблемы, на самом деле намного проще: обновить Git. Мой был 1.7.1, и после того, как я обновил его до 2.16.1 (последний), проблема ушла без следа! Думаю, я оставляю это здесь, надеюсь, это кому-нибудь поможет.

Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.