Вы можете сделать то, что в вашем вопросе о названии («вернуться к предыдущему коммиту в одном подмодуле»).Каждый субмодуль является независимым хранилищем по своему праву.Что не ясно, это то, что вы на самом деле сделали.Я подозреваю, что вы создали один репозиторий с несколькими подкаталогами и, возможно, еще два репозитория, которые живут в одном репозитории, но не являются подмодулями .
Стоит вернуться сюда и определить некоторые термины.Я действительно не в восторге от терминологии Git здесь («подмодуль» и «суперпроект» вроде неуклюжи), но я буду придерживаться их.
Очевидно, это не очень помогает, ?Итак, давайте добавим некоторые классификаторы:
A подмодуль - это Git-репозиторий, который в настоящее время используется другим Git-репозиторием, который мы называем superproject ,Для этого подмодуля Git repo существует ровно один суперпроект.
A superproject - это Git-репозиторий, который в настоящее время использует другой Git-репозиторий в качестве подмодуля.В этом суперпроекте может быть несколько подмодулей.
(Это приводит к возможности того, что некоторое хранилище Git одновременно является подмодулем и суперпроектом. Это немногокошмар, и вы должны попытаться избежать его, но это случается.)
Теперь, когда суперпроект предъявляет требования к другому репозиторию Git, который суперпроект использует в качестве подмодуля, способ Суперпроект Git делает это - по крайней мере обычно - для команды подмодуля Git войти в режим detached HEAD . Любой Git-репозиторий может находиться в этом состоянии, но большинство обычных репозиториев - нет, за исключением тех случаев, когда вы находитесь в середине длительного перебазирования или используете git checkout <em>commit-or-tag</em>
для перехода к конкретному историческому коммиту.Обычно при разработке вы находитесь на ветке типа master
или develop
, которая противоположна «отсоединенной ГОЛОВКЕ»: здесь имя HEAD
образно присоединено к названию ветви.Таким образом, git checkout master
прикрепляет ваш HEAD
к master
, а git checkout develop
присоединяет ваш HEAD
к развитию.
(HEAD
, написано прописными буквами, как это всегда, всегда - ссылается на текущий коммит в текущем Git-репозитории. Базовая реализация этого заключается в том, что каталог .git
, в котором находится репозиторий, содержит файл с именем HEAD
. Этот файл .git/HEAD
также содержит имя ветки , в этом случае вы находитесь в этой ветке, или она содержит идентификатор хеша коммита , в этом случае у вас есть отсоединенный HEAD при этом коммите. Так как Git хранит его вфайла, в Windows и MacOS возможно использовать head
в нижнем регистре, но лучше придерживаться версии с прописными буквами. Если вам нужен ярлык, который легче набирать, @
сам по себе также означает HEAD
.)
Когда вы хотите использовать обычный репозиторий, в системе, в которой вы начинаете с клонирования репозитория (а не создаете его с нуля), вы делаете это:
git clone <url> [<directory>]
например, git clone https://github.com/git/git.git
для клонированияGit репозиторий для Git через GitHub.Это создаст каталог git
, где бы вы ни находились.Если по какой-то причине вы хотите, чтобы клон был введен в /tmp/git
, вы должны использовать git clone https://github.com/git/git.git /tmp/git
.Таким образом, для создания клона Git нужны два ключевых элемента:
- URL и
- путь, по которому должен идти клон.
URL-адрес, как правило, представляет собой URL-адрес в стиле https://
или ssh://
, в котором перечислены некоторые вышестоящие хост / сервер (или облачная система, такая как GitHub) и путь к этому хосту / серверу.(Обратите внимание, что git@github.com:path/to/repo.git
- это просто сокращение для ssh://git@github.com/path/to/repo.git
. Эти два означают абсолютно одно и то же.)
Процесс добавления подмодуля в существующий репозиторий во многом такой же:
git submodule add <url> [<directory>]
1102 *url
здесь также обычно начинаются с https://
или ssh://
.<directory>
- это путь в вашего репозитория, т. Е. Место для размещения подмодуля.
Причина этого URL-пути заключается в том, что git submodule add
фактически будет работатьgit clone
для вас.Клон, который он делает, будет обычным Git-репозиторием, поскольку подмодуль является обычным Git-репозиторием.Git просто нужно знать , где мне взять клон из и , где я должен поместить его в этот репозиторий .
Другая вещь, которую сделает git submodule add
-дополнительная часть, которая делает ваш текущий Git-репозиторий в качестве суперпроекта для этого подмодуля, - это создание или обновление файла с именем .gitmodules
и добавление записи в индекс вашего суперпроекта..
Обратите внимание, что подпроект не должен знать о своем суперпроекте, и в старые добрые времена действительно ничего о нем не знал.(В современном Git каталог подпроекта .git
переносится в каталог суперпроекта .git
. .git
, который будет найден в подмодуле, заменяется файлом , который указывает подмодуль на удержание его суперпроектаarea.)
В любом случае, побочным эффектом всего этого является то, что набор коммитов в подмодуле определяется только содержимым субмодуля.Суперпроект не влияет на это!Подмодуль - это просто клон существующего URL.
Это не тот способ, которым вы пытаетесь использовать подмодули, но прежде чем мы перейдем к этому, давайте рассмотрим остальную часть нормальной работы всего этого.,У нас есть некоторый суперпроект - локальный репозиторий Git, который, возможно, является клоном некоторого репозитория origin
- где мы делаем коммиты нашего суперпроекта.В этом суперпроекте мы теперь создали файл с именем .gitmodules
, в котором указан URL-адрес и путь к другому Git-репозиторию.Допустим, путь - dir/sub
.Если мы запустим:
cd dir/sub
, мы обнаружим, что теперь мы находимся в рабочем дереве отдельного клона , который имеет своих собственных origin/master
и master
и т. Д .;но у этого клона отдельная ГОЛОВА.Выполнение git log
показывает фиксацию detached-HEAD, затем его родителя (-ей) и их родителя (-ей) и т. Д., Как будто история заканчивается при любом коммите, который мы выделили как отсоединенный HEAD.Это наш подмодуль Git-репозиторий.
Если мы cd
вернемся в исходный репозиторий:
cd - # or cd ../..
, мы вернемся в основной репозиторий.Использование обычных инструментов файловой системы показывает нам, что dir/sub
существует и является каталогом.Существует файл (или, если ваш Git старше, директория) с именем dir/sub/.git
.Если это файл, он содержит одну строку:
gitdir: ../../.git/modules/sub
При запуске git status
отображаются два добавленных файла:
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: .gitmodules
new file: dir/sub
Но при проверке индекса - чтонемного сложнее;Я буду использовать git ls-files
здесь - показывает, что dir/sub
вообще не является каталогом :
$ git ls-files --stage dir/sub
160000 50298bbf97b317f17b3e1cf9287e912fb5de886e 0 dir/sub
Записи с режимом 160000
- это то, что Git называет gitlink .
Если вы знаете, что dir/sub
- это gitlink, вы можете просмотреть его хеш-идентификатор более непосредственно, используя git rev-parse
.Синтаксис :0:dir/sub
означает «dir / sub из индекса (в нулевом интервале)»:
$ git rev-parse :0:dir/sub
50298bbf97b317f17b3e1cf9287e912fb5de886e
Они говорят нам то же самое, за исключением того, что если dir/sub
не было подмодуль, мы могли бы увидеть это в выводе git ls-files --stage
.
Так Git обычно предполагает использование подмодуля
Общая идея здесь заключается в том, что в вашем суперпроекте выиспользуйте какую-нибудь стороннюю библиотеку (скажем, Google gRPC), которую вы лично не контролируете.Вместо этого вы пишете свое программное обеспечение и заставляете его работать с одной конкретной версией этой библиотеки:
$ (cd dir/sub; git checkout v3.2.1)
Проверяя какой-то определенный тег в подмодуле, вы перемещаетесь ОТДЕЛЬНАЯ ГОЛОВКА для этого конкретного коммита.Затем вы вносите любые изменения, необходимые для вашего собственного проекта - вашего суперпроекта - чтобы он работал с v3.2.1 или любой другой версией:
$ ... make some changes ...
$ git add ... files ...
Имея сейчасобновив ваши файлы, вы теперь также обновите запись gitlink , в которой говорится, что ваш суперпроект Git должен git checkout
один конкретный коммит, который вы сейчас имеете в ваш подмодуль:
$ git add dir/sub # update the gitlink to whatever hash v3.2.1 represents
Теперь, когда вы делаете новый коммит, суперпроектный коммит продолжает перечислять другой репозиторий - со своим URL, что бы это ни было, иего путь, dir/sub
- в ваших .gitmodules
, и этот же коммит объявляет: Этот коммит работает с субмодулем, отсоединенным к.
Итак, всякий раз, когда кто-то запускает git clone
в вашем суперпроекте, а затем выполняет git checkout
из , этот конкретный суперпроектный коммит , следующий:
$ git submodule update
удостоверится, что dir/sub
имеет , что конкретный gitlink-ed коммит извлечен, как отсоединенный HEAD.Теперь ваш суперпроект и субмодуль синхронизированы, и вы можете строить.
Это не тот способ, которым вы пытаетесь использовать субмодули
В вашем случае у вас уже есть субмодульGit репозитории .Они могут иметь или не иметь подходящего верхнего хранилища.Они существуют в sub1
и sub2
.Я буду использовать, как мой пример, dir/sub
снова, хотя:
$ git submodule add ./dir/sub dir/sub
Adding existing repo at 'dir/sub' to the index
URL-адрес здесь, ./dir/sub
, совершенно бесполезен для всех остальных.(Он должен начинаться с ./
или ../
относительно текущего рабочего каталога - Git отказывается добавлять подмодуль без начального ./
.)
В этот момент происходит то же самоекак с обычным URL: Git создал или обновил ваш .gitmodules
, чтобы перечислить URL и путь:
$ cat .gitmodules
[submodule "dir/sub"]
path = dir/sub
url = ./dir/sub
и поместить хеш-идентификатор, соответствующий HEAD
подмодуля, в индекс, чтобы он служил в качествеследующая зафиксированная запись gitlink:
$ (cd dir/sub; git rev-parse HEAD)
1fdcf14961c81d03496b359389058410f0169782
$ git rev-parse :0:dir/sub
1fdcf14961c81d03496b359389058410f0169782
$ git status --short
A .gitmodules
A dir/sub
Таким образом, если вы сейчас сделаете новый коммит в этот момент, новый коммит будет иметь .gitmodules
и индексные записи, необходимые для попытки этого репозитория Git управлять -или клонировать, если он отсутствует - другой репозиторий Git в dir/sub
, основанный на URL ./dir/sub
.
Этот URL, конечно, совершенно бесполезен, если в уже нет репозитория Git вdir/sub
, но именно так мы говорим этому Git, что он является суперпроектом для другого репозитория Git в dir/sub
.Вы можете использовать Git таким образом, и если у вас уже есть другой репозиторий Git в dir/sub
, ваш суперпроект Git будет в порядке с этим и будет им командовать.Команда, которую ваш суперпроект Git выдаст подмодулю Git: Проверить этот конкретный коммит как отдельную HEAD.
Как суперпроект видит изменения подмодуля
Предположимвы заходите в подмодуль и используете git checkout
, чтобы проверить или даже создать какой-нибудь другой коммит, возможно, выполнив git checkout
какого-либо имени ветки и затем, возможно, работая в хранилище как обычно и фиксируя.Затем вы cd
возвращаетесь в суперпроект и запускаете git status
.Ваш Git скажет вам, что подмодуль изменен (обратите внимание на пробел до M
здесь):
$ git status --short
M dir/sub
Эта модификация существует, но еще не в вашем index , т. е. еще не настроен для фиксации:
$ (cd dir/sub; git rev-parse HEAD)
860be47095f79afbf94c62d0c3936a9875905e16
$ git rev-parse :0:dir/sub
1fdcf14961c81d03496b359389058410f0169782
Как видите, подмодуль отключен на 860be47095f79afbf94c62d0c3936a9875905e16
, хотя индекс говоритчто следующий коммит будет содержать директиву для использования 1fdcf14961c81d03496b359389058410f0169782
.** Это точно так же, как любой измененный файл в том же хранилище, * за исключением того, что вы используете git add
здесь, чтобы сказать Git: вставьте новый хэш-идентификатор в вместо того, чтобы сказать ему скопировать работусодержимое дерева в .
Следовательно, как только мы сделаем git add
, вывод состояния --short
сместит M
одну букву влево:
$ git add dir/sub
$ git status --short
M dir/sub
, потому чтотеперь индексная запись суперпроекта для подмодуля отличается от значения HEAD
для этого подмодуля, но соответствует фактическому подмодулю, найденному в рабочем дереве.Итак, теперь, если все готово, и мы хотим сказать нашему суперпроекту Git дать команду подмодулю Git использовать 860be47095f79afbf94c62d0c3936a9875905e16
в следующем коммите, который мы делаем, мы готовы сделать этот коммит сейчас:
$ git commit
[edit a message, etc]
Опять же, ключи здесь:
- Каждый подмодуль является собственным репозиторием Git.
- Суперпроект находит каждый подмодуль по пути и / или по
.gitmodules
, по мере необходимости,Новый клон просто суперпроекта, очевидно, еще не клонировал ни один из подмодулей, так что для этого хороши записи .gitmodules
: они предоставляют URL и путь! - Суперпроект Git может просмотреть каждый подмодуль и найти его
HEAD
: он получает суперпроекту Git фактический хэш-идентификатор и позволяет вам git add
этот хэш-идентификатор индекса суперпроекта готов для следующего коммитаВы делаете в суперпроекте. - Или, суперпроект Git может подмодульно Git дать команду
git checkout
, в качестве отдельного HEAD, одного конкретного хеш-идентификатора фиксации, который есть в суперпроекте 's index прямо сейчас.
Если вы хотите, чтобы ваша суперпроектная команда имела несколько подмодулей, вы git submodule add
все эти подмодули.Чтобы убедиться, что эти подмодули получают правильный хеш-идентификатор фиксации, извлеченный как отдельные заголовки, вы вводите подмодули, ставите их в правильные коммиты, а затем git add
подмодули всуперпроект.
В современном Git команда git submodule
имеет несколько довольно хитрых приемов для координации обновления подмодулей, используя имена веток, найденные в remote (обычно origin
) дляподмодуль.Идея в том, что если вы используете , например, с помощью Google gRPC, и хотите обновить, git submodule
может заменить несколько из вышеперечисленных шагов - cd
-ing в подмодуль, запустив git fetch
, бегущий git checkout
и cd
-ing назад - с одним шагом.Но фактический дизайн подмодулей по-прежнему «отделен HEAD, как приказано суперпроектом»: вы должны убедиться, что репозиторий Git суперпроекта записывает правильные хеш-идентификаторы подмодуля.