Могу ли я вернуться к предыдущему коммиту в одном субмодуле? - PullRequest
0 голосов
/ 02 декабря 2018

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

- root
|--rootDoc.txt
|--.git
|
|---- sub1
    |--sub1.txt
    |--.git
|---- sub2
    |--sub2.txt
    |--.git

Теперь я внес следующие изменения:

  • изменение sub1.txt
  • коммит sub1 субмодуль
  • от главного к мастеру
  • изменение sub2.txt
  • коммит sub2 субмодуль
  • от главного к мастеру

Теперь я быхотел бы вернуть sub1 -подмодуль в состояние до последних изменений в нем, но сохранить sub2 в его текущем состоянии.Если это невозможно для подмодулей, есть ли другое решение для моей проблемы или мне нужно использовать два совершенно разных репозитория?

Редактировать: Что я пробовал:

c:\dev\root\sub1>git log
commit a172db9a5f11738383d28e082db2c22d3f2d3e75 (HEAD -> master, origin/master, origin/HEAD)
Author: %me%
Date:   Sun Dec 2 20:24:59 2018 +0100

    updated sub2

commit 0becb718a4db9c73b03fa65e332f20c7715463cb
Author: %me%
Date:   Sun Dec 2 20:23:40 2018 +0100

    sub1 actual now

commit 85d68703bff1af2b95a7ee8d7926d7fd700b1da4
Author: %me%
Date:   Sun Dec 2 20:10:50 2018 +0100

    Added submodules

commit b3b67de3e54f1db7e56d516af2baaf50541f7ca2
Author: %me%
Date:   Sun Dec 2 20:05:44 2018 +0100

    initial commit

c:\dev\root\sub1>git checkout 85d68703bff1af2b95a7ee8d7926d7fd700b1da4
Note: checking out '85d68703bff1af2b95a7ee8d7926d7fd700b1da4'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by performing another checkout.

If you want to create a new branch to retain commits you create, 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 85d6870 Added submodules

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

1 Ответ

0 голосов
/ 03 декабря 2018

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


Стоит вернуться сюда и определить некоторые термины.Я действительно не в восторге от терминологии Git здесь («подмодуль» и «суперпроект» вроде неуклюжи), но я буду придерживаться их.

  • A подмодуль это Git-репозиторий.

  • A superproject - это 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 суперпроекта записывает правильные хеш-идентификаторы подмодуля.

...