git просит зафиксировать измененный контент подмодуля

Недавно я обновил подмодули в своем репозитории конфигурации vim с помощью этой команды:

git submodule update --recursive --remote

И когда я позвонил git status, я получил это:

On branch master
Your branch is ahead of 'origin/master' by 5 commits.
  (use "git push" to publish your local commits)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
  (commit or discard the untracked or modified content in submodules)

        modified:   .vim/pack/starter-pack/start/YouCompleteMe (modified content)

no changes added to commit (use "git add" and/or "git commit -a")

Затем я проследил цепочку подмодулей с «измененным содержимым» и обнаружил, что единственной модификацией были неотслеживаемые коммиты подмодулей:

On branch master
Your branch is behind 'origin/master' by 1 commit, and can be fast-forwarded.
  (use "git pull" to update your local branch)
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   vendor/bottle (new commits)
        modified:   vendor/jedi (new commits)
        modified:   vendor/waitress (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

главные ветки этих подмодулей (бутылка, джедай, официантка) находятся за главными ветвями их удаленных источников, поэтому я полагаю, что git submodule update не просто вытащил происхождение каждого репо, но и нашел соответствующую версию, которую требует родительский репозиторий.

Почему даже git помечает эти репозитории (new commits), если это именно те коммиты, которые требуются родительским модулям? Что там происходит?


person arrowknee    schedule 01.07.2017    source источник


Ответы (1)


Здесь произошло то, что ваш суперпроект теперь несовместим. В частности, в вашем суперпроекте есть gitlinks, которые необходимо зафиксировать.

Вы должны добавить новые gitlinks (с git add, как обычно) и зафиксировать (как обычно). Затем вы можете нажать новый коммит (как обычно).

Подмодуль подразумевает суперпроект

Подмодуль — это просто репозиторий Git, который напрямую используется другим репозиторием Git. Сам подмодуль в данном случае является обычным репозиторием Git: он ничего не знает об этом другом репозитории Git. Другой репозиторий — это тот, который мы называем суперпроектом, и он действительно знает о подмодуле.

Любому репозиторию Git нужен каталог .git с некоторыми данными.

Как правило, репозиторий Git создается путем его клонирования из другого места:

git clone http://...

или что-то еще. Или вы можете запустить git init в каталоге. В любом случае вы получите каталог .git, в котором находится сам репозиторий Git. В этом .git вы обычно определяете пульт с именем origin. Это короткое имя (в частности, имя origin!), которое записывает URL-адрес, который вы указали для git clone выше. Этот URL-адрес может даже указывать на ваш собственный репозиторий, скажем, на GitHub.

(Если вы начали с чужого репозитория, а затем решили создать свой собственный на GitHub, у вас может быть даже два пульта. Обычно вы должны назвать свой собственный репозиторий origin, а другой — upstream, но насколько Сам Git обеспокоен, это просто произвольные имена.Единственная причина, по которой мы все согласны с origin, заключается в том, что это имя git clone создает для нас, когда мы впервые запускаем git clone url.)

В любом случае данные в каталоге .git включают следующее:

  • URL-адрес для origin.
  • Имена любых ветвей и идентификаторы хэшей коммитов, которые идентифицируют эти ветки.
  • Точно так же имена тегов и их коммитов.
  • текущая фиксация: что именно извлечено в репозитории? Это может быть имя ветки, и в этом случае репозиторий находится в ветке, или это может быть необработанный хэш-идентификатор фиксации, и в этом случае репозиторий находится в "отключенном" состоянии. ГОЛОВА».

Что, если суперпроект создает подмодуль?

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

Точная форма и содержание этого файла описаны в документации gitmodules. . Чтобы процитировать его немного, предположим, что он говорит:

[submodule "libfoo"]
        path = include/foo
        url = git://foo.com/git/lib.git

Это означает, что когда вы клонируете суперпроект, а затем запускаете git submodule init, ваш Git будет знать, что он должен запустить git clone git://foo.com/git/lib.git — это часть url — с клоном, идущим в каталог include/foo: путь< /em> часть.

В этой головоломке отсутствует один важный элемент. После того, как ваш Git клонирует другой Git в include/foo, какая фиксация проверяется в подмодуле?

В большинстве обычных репозиториев это не такой уж большой вопрос. Какой коммит проверяется? Я не знаю, я просто запускаю git checkout master, верно? Это дает мне последний коммит в ветке master, чего я и хочу.

Суперпроекты и подмодули так не работают. Когда я использую подмодуль из своего суперпроекта, я строю свой код суперпроекта вокруг одного конкретного коммита в подмодуле. Например, я мог зависеть конкретно от v3.4.1 чьей-то еще библиотеки, поэтому я спускался в подпроект и запускал git checkout v3.4.1, чтобы проверить этот конкретный тег.

В идеале я мог бы записать этот тег в своем суперпроекте (это было бы неплохо, и gitlinks должны разрешать это, но в настоящее время они этого не делают).1 Но тег, в Git — это просто удобочитаемое имя для одного конкретного коммита. Тег v3.4.1 может быть именем фиксации feeddadac0ffee... или чем-то подобным. Это — большой уродливый хэш-идентификатор — это то, что на самом деле входит в gitlink.

Сам gitlink хранится в каждом коммите, точно так же, как обычный файл хранится в каждом коммите. Если я делаю новый коммит в суперпроекте с новым или измененным файлом README, новая версия README попадает в репозиторий Git, а новый коммит ссылается на новый README. Каждая фиксация после этого продолжает ссылаться на новый README.

То же самое верно и для gitlink: если мой include/foo относится к хэш-идентификатору для v3.4.1 подмодуля, каждый коммит с этого момента имеет запись gitlink, которая гласит: «когда вы проверяете этот коммит, вы также должны войти в субмодуль include/foo и проверить хэш-идентификатор feeddadac0ffee...".


1Если кто-то хочет попробовать добавить это, я думаю, что есть способ сделать это, который может быть даже несколько обратно совместим: сохранить необработанный хэш-идентификатор, как обычно, за которым следует байт NUL, а затем по ссылочному имени. Старый Git, который не понимает новый тип gitlink, может напрямую использовать хэш-идентификатор, а более новый Git может обнаружить и использовать имя. Записи Gitlink в любом случае потребуют аналогичного изменения при переходе хэша от SHA-1 к тому, что Git будет использовать в будущем, поэтому сейчас самое время добавить это.


Что если владелец подмодуля сделает новый релиз?

Итак, я протестировал свой суперпроект с v3.4.1, и все работает. Большой! Но теперь тот, кто отвечает за эту include/foo библиотеку, обновил свой код и выпустил версию v3.4.2. Эта новая версия имеет некоторые новые функции, и я хотел бы использовать их.

Я, как владелец суперпроекта, должен теперь зайти в свой подмодуль и git fetch, а затем git checkout v3.4.2. (Возможно, это не feeddadac0ffee, а хэш-идентификатор deadcabbadcab005e....) Затем я должен вернуться к своему суперпроекту, внести все изменения, необходимые для использования нового подмодуля, все протестировать и зафиксировать.

Когда я делаю новую фиксацию для использования v3.4.2 подмодуля, я должен не только фиксировать мои изменения. Мне также нужно обновить мою gitlink. Поскольку я уже сделал git checkout deadcabbadcab005e — или git checkout v3.4.2, что на самом деле то же самое, — в подмодуле, все, что мне нужно сделать, это git add include/foo в моем суперпроекте. Это добавляет обновленную gitlink в мой индекс, так что когда я запускаю git commit, я записываю новую gitlink вместе с другими моими изменениями.

Это создает новую фиксацию, и теперь я могу отправить свою фиксацию, если есть какое-то другое место, где я также храню свой суперпроект (на GitHub или где-то еще).

person torek    schedule 01.07.2017
comment
Спасибо за ваш ответ и полное объяснение. Но я боялся делать коммиты в чужих репозиториях, потому что думал, что это может привести к конфликту слияния в будущем, когда я снова обновлю подмодули. Может ли произойти конфликт слияния, если я совершу это? И если это возможно, будет ли это разрешено автоматически? (Надо было сначала спросить об этом) - person arrowknee; 02.07.2017
comment
Мне совсем не ясно, делали ли вы какие-либо коммиты в каком-либо из подрепозиториев. (git submodule update --remote нет, он просто меняет выбранную фиксацию.) Однако все репозитории Git являются локальными по определению и все ваши репозитории являются вашими собственными. Сюда входят репозитории ваших подмодулей: они ваши. Возможно, вы скопировали их у кого-то другого, но теперь они ваши. Только git push, если вы это сделаете, может повлиять на чужой репозиторий (и то только если они это разрешат). - person torek; 02.07.2017