Здесь происходит то, что ваш репозиторий субмодулей находится на коммите, отличном от хеш-идентификатора, записанного в суперпроекте.Ваш git status
, запущенный в суперпроекте, говорит вам об этом, не меняя его, и ваш git add -A
, по-видимому, тоже не изменил.
Эта последняя часть кажется неправильной.Когда я делаю что-то подобное, а затем использую git add -A
, я получаю:
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
modified: [submodule path]
Если я затем запускаю еще две команды, все возвращается назад, как я и ожидал:
$ git reset
Unstaged changes after reset:
M [submodule path]
$ git submodule update
Submodule path [path]: checked out '[hash]'
$ git status
On branch ...
nothing to commit, working tree clean
(Я подозреваю , что вы внесли некоторые изменения в подмодуль, но никогда не вносили их там.)
Что происходит, с мелкими деталями, которые позволят вам диагностировать проблему
У нас есть один репозиторий Git, называемый суперпроектом , который управляет вторым репозиторием, называемым подмодулем .Суперпроект на самом деле имеет три отдельные ручки управления, одна из которых присутствует в каждом коммите, и поэтому также находится в index (так как индекс контролирует то, что будет входить в next commit).
Одной из этих ручек управления является файл, который вы упомянули, .gitmodules
.Он сообщает суперпроекту , как клонировать подмодуль , если подмодуль еще не git clone
d.Как только субмодуль клонируется, его основная работа завершена.
Второй - это ваш .git/config
файл.Он содержит информацию, скопированную из файла .gitmodules
, которую вы можете обновить при необходимости, если файл .gitmodules
не совсем подходит для ваших собственных целей (который может отличаться от тех, кто отвечает за файл .gitmodules
),Любые настройки в .git/config
переопределяют настройки в .gitmodules
.В противном случае эти два места для установки параметров по существу эквивалентны.
Последнее является причиной возникновения проблемы.Чтобы подмодуль стал извлеченным в ваше рабочее дерево и, следовательно, был полезен для вас, Git, который контролирует суперпроект, запускает второй набор команд Git.В общем, вы можете запустить:
git submodule update --init
, чтобы получить подмодуль для проверки (хотя, если вы используете git clone --recursive
, Git сделает это за вас).
На этом этапе суперпроектGit создал почти пустой каталог с правильным путем.(Каталог содержит файл .git
с именем пути к клонированному репозиторию, либо в старые времена, либо с использованием режима обратной совместимости старого стиля, содержит сам фактический каталог .git
.) Суперпроект Git chdir
s вэтот каталог и сообщает подмодулю Git:
- run
git checkout <em>hash</em>
Как только это произошло, путь заполнен файлами, извлеченными из коммита, чей ID равен hash
, который в основном делает внешний Git (суперпроект) "готовым" к файлам.Но есть побочный эффект, поскольку подмодуль сам по себе является полным Git-репозиторием со всем, что это означает.
В частности, подпроект имеет свой собственный HEAD
.Это HEAD
теперь отсоединено , а текущая фиксация хранилища подмодуля hash
, так что это находится в индексе и рабочем дереве подмодуля, что, конечно,мы хотели: рабочим деревом подмодуля является путь в суперпроекте, куда идут все файлы подмодуля.
Но есть интересный вопрос, на который нужно ответить: Где суперпроект Git получил хеш-идентификатор? Ответ таков: он хранится в каждом снимке - ну, каждый снимок, который использует субмодуль - в суперпроекте, так же, как каждый снимок имеет полную, полную копию каждого файла.Чтобы это произошло, индекс для суперпроекта содержит специальную запись типа gitlink .
. Эта запись gitlink в индексе суперпроекта сообщает суперпроекту, какой хэш-идентификатор следует предоставлять подмодулю Git всякий раз, когдасуперпроект сообщает субмодулю Git: проверить какой-то конкретный коммит .
Если вы вручную войдете в подмодуль и git checkout
имя ветки или любой другой коммит по хеш-идентификатору, репозиторий подмодуля HEAD
изменится.Он либо присоединяется к имени ветви, либо указывает на другой коммит, все еще находящийся в режиме detached-HEAD.
В этот момент субмодуль и суперпроект не синхронизированы .Суперпроект Git пока ничего не делает.Вы контролируете, вы выбираете, какой коммит вы хотите.Вы даже можете делать новые коммиты и git push
их в какой-то апстрим.После того, как вы выполнили все действия по фиксации и git checkout
, которые хотите, и все правильно расположили, вы должны вылезти из рабочего дерева подмодуля обратно в свой суперпроект.
Теперь git status
и git diff
по умолчанию - здесь также есть тонна управляющих ручек - скажет вам, что суперпроект вызывает некоторый хеш H , но у подмодуля есть какой-то другой хеш S проверено.(Они могут или не могут также сказать вам, нужно ли самому подмодулю совершить коммит, если вы установите для этого регуляторы управления.) Если вы хотите, чтобы ваш следующий коммит суперпроекта записывал, в gitlink для этого подмодуля этот новый хеш коммита S , вы запускаете:
git add path-to-submodule
(или git add -A
должен сделать то же самое, вот почему это озадачивает).Это обновит gitlink в вашем индексе для записи хеш-идентификатора S , а не H , так что следующий коммит суперпроекта по команде git submodule update
сообщит субмодулю Git: проверить коммит S, как ваш отдельный HEAD .
Как только индекс в суперпроекте совпадает с HEAD
в фактическом извлеченном подмодуле, подмодуль не будет быть внесенным в список изменений, не подготовленных для фиксации .Если хеш в gitlink в индексе не совпадает с хешем в gitlink в HEAD
, git status
будет перечислять путь подмодуля в изменениях для фиксации .