Почему мой Git Submodule HEAD отсоединен от мастера?

163

Я использую подмодули Git. После получения изменений с сервера моя голова субмодуля много раз отсоединялась от основной ветки.

Почему это происходит?

Я всегда должен делать:

git branch
git checkout master

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

om471987
источник
1
Вы читали этот ответ? stackoverflow.com/questions/1777854/…
Джонни Z
@bitoiu Я посмотрел на поддерево и Google Repo. У меня пока нет идеального решения :(
om471987
1
мой опыт работы с gitsubmodules, в среде CI ужасен, может быть, у некоторых других людей есть лучший опыт.
bitoiu
@JohnnyZ Спасибо. Я понял, что подмодуль указывает на коммит, а не на верхушку дерева. Но почему оторвался от филиала. Если у вас есть одна ветка, она не должна быть присоединена к ней по умолчанию
om471987
3
Не спешите отклонять подмодули только потому, что слышали, что они плохие. Они являются плохим решением, если вы хотите непрерывной интеграции, но они почти идеальное решение, если вы хотите встроить код из внешнего проекта, и вы явно управляете всеми извлечениями. Часто это лучший метод, если вы интегрируетесь с не раздвоенным модулем, который не контролируется вашей организацией. Проблема в том, что они являются заманчивым решением во всех других ситуациях, когда они не очень хорошо работают. Лучший совет - прочитать о том, как они работают, и оценить ваш сценарий.
Сара Г

Ответы:

176

РЕДАКТИРОВАТЬ:

См @Simba Ответ для правильного решения

submodule.<name>.updateэто то, что вы хотите изменить, см. документы - по умолчаниюcheckout
submodule.<name>.branch укажите удаленную ветвь для отслеживания - по умолчаниюmaster


СТАРЫЙ ОТВЕТ:

Лично я ненавижу здесь ответы, которые ссылаются на внешние ссылки, которые могут перестать работать со временем, и проверяю мой ответ здесь (если вопрос не повторяется) - я обращаюсь к вопросу, который охватывает тему между строк другой темы, но в целом равен: «Я не отвечая, прочитайте документацию. "

Итак, вернемся к вопросу: почему это происходит?

Ситуация, которую вы описали

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

Это распространенный случай, когда кто-то не использует подмодули слишком часто или только начинает работу с подмодулями . Я полагаю, что я правильно заявляю, что мы все были там в какой-то момент, когда отсоединяется ГОЛОВА нашего подмодуля .

  • Причина: Ваш подмодуль не отслеживает правильную ветвь (мастер по умолчанию).
    Решение: убедитесь, что ваш подмодуль отслеживает правильную ветку
$ cd <submodule-path>
# if the master branch already exists locally:
# (From git docs - branch)
# -u <upstream>
# --set-upstream-to=<upstream>
#    Set up <branchname>'s tracking information so <upstream>
#    is considered <branchname>'s upstream branch.
#    If no <branchname> is specified, then it defaults to the current branch.
$ git branch -u <origin>/<branch> <branch>
# else:
$ git checkout -b <branch> --track <origin>/<branch>
  • Причина. Ваш родительский репозиторий не настроен для отслеживания ветви подмодулей.
    Решение: Сделайте так, чтобы ваш подмодуль отслеживал его удаленную ветвь, добавляя новые подмодули с помощью следующих двух команд.
    • Сначала вы говорите Git, чтобы отслеживать ваш пульт <branch>.
    • вы говорите git выполнить rebase или объединить вместо checkout
    • вы говорите git обновить ваш подмодуль с пульта.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ git config -f .gitmodules submodule.<submodule-path>.update rebase
    $ git submodule update --remote
  • Если вы не добавили существующий субмодуль, как это, вы можете легко это исправить:
    • Во-первых, вы хотите убедиться, что в вашем подмодуле отмечена ветка, которую вы хотите отслеживать.
    $ cd <submodule-path>
    $ git checkout <branch>
    $ cd <parent-repo-path>
    # <submodule-path> is here path releative to parent repo root
    # without starting path separator
    $ git config -f .gitmodules submodule.<submodule-path>.branch <branch>
    $ git config -f .gitmodules submodule.<submodule-path>.update <rebase|merge>

В общих случаях вы уже исправили свою DETACHED HEAD, так как она была связана с одной из проблем конфигурации выше.

фиксация DETACHED HEAD, когда .update = checkout

$ cd <submodule-path> # and make modification to your submodule
$ git add .
$ git commit -m"Your modification" # Let's say you forgot to push it to remote.
$ cd <parent-repo-path>
$ git status # you will get
Your branch is up-to-date with '<origin>/<branch>'.
Changes not staged for commit:
    modified:   path/to/submodule (new commits)
# As normally you would commit new commit hash to your parent repo
$ git add -A
$ git commit -m"Updated submodule"
$ git push <origin> <branch>.
$ git status
Your branch is up-to-date with '<origin>/<branch>'.
nothing to commit, working directory clean
# If you now update your submodule
$ git submodule update --remote
Submodule path 'path/to/submodule': checked out 'commit-hash'
$ git status # will show again that (submodule has new commits)
$ cd <submodule-path>
$ git status
HEAD detached at <hash>
# as you see you are DETACHED and you are lucky if you found out now
# since at this point you just asked git to update your submodule
# from remote master which is 1 commit behind your local branch
# since you did not push you submodule chage commit to remote. 
# Here you can fix it simply by. (in submodules path)
$ git checkout <branch>
$ git push <origin>/<branch>
# which will fix the states for both submodule and parent since 
# you told already parent repo which is the submodules commit hash 
# to track so you don't see it anymore as untracked.

Но если вам удалось внести некоторые изменения локально уже для субмодуля и зафиксировать, перенести их в удаленный режим, а затем, когда вы выполнили «git checkout», Git уведомит вас:

$ git checkout <branch>
Warning: you are leaving 1 commit behind, not connected to any of your branches:
If you want to keep it by creating a new branch, this may be a good time to do so with:

Рекомендуемая опция для создания временной ветки может быть хорошей, и тогда вы можете просто объединить эти ветви и т. Д. Однако я лично использовал бы только git cherry-pick <hash>в этом случае.

$ git cherry-pick <hash> # hash which git showed you related to DETACHED HEAD
# if you get 'error: could not apply...' run mergetool and fix conflicts
$ git mergetool
$ git status # since your modifications are staged just remove untracked junk files
$ rm -rf <untracked junk file(s)>
$ git commit # without arguments
# which should open for you commit message from DETACHED HEAD
# just save it or modify the message.
$ git push <origin> <branch>
$ cd <parent-repo-path>
$ git add -A # or just the unstaged submodule
$ git commit -m"Updated <submodule>"
$ git push <origin> <branch>

Хотя есть еще несколько случаев, когда вы можете перевести свои подмодули в состояние DETACHED HEAD, я надеюсь, что теперь вы немного больше понимаете, как отлаживать ваш конкретный случай.

mkungla
источник
2
HEAD detached - это поведение по умолчанию git submodule update --remote. Пожалуйста, посмотрите на ответ Симбы, я думаю, что это должен быть правильный ответ.
magomar
78

Добавление branchопции в .gitmoduleэто не имеет отношение к обособленному поведению подмодулей на всех. Старый ответ от @mkungla неверен или устарел.

От git submodule --help, HEAD отсоединен - ​​это поведение по умолчаниюgit submodule update --remote .

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

--удаленный

Вместо того, чтобы использовать записанный SHA-1 суперпроекта для обновления субмодуля, используйте статус ветви удаленного отслеживания субмодуля. В качестве удаленного используется ответвление remote ( branch.<name>.remote), по умолчанию используется значениеorigin . Удаленная ветвь использовала по умолчаниюmaster .

Зачем

Так почему же HEAD отсоединяется после update? Это вызвано поведением обновления модуля по умолчанию:checkout .

--проверять, выписываться

Оформить коммит, записанный в суперпроекте на отдельном HEAD в субмодуле. Это поведение по умолчанию , основное использование этого параметра - переопределение, submodule.$name.updateесли задано значение, отличное от checkout.

Чтобы объяснить это странное поведение обновления, нам нужно понять, как работают подмодули?

Цитата из раздела «Подмодули» в книге Pro Git

Хотя sbmodule DbConnector- это подкаталог в вашем рабочем каталоге, Git видит его как подмодуль и не отслеживает его содержимое, когда вы не в этом каталоге. Вместо этого Git видит это как определенный коммит из этого хранилища .

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

Как

Если вы хотите автоматически объединить подмодуль с удаленной веткой, используйте --mergeили --rebase.

--merge

Эта опция действительна только для команды обновления . Объединить фиксацию, записанную в суперпроекте, с текущей веткой субмодуля. Если указан этот параметр, заголовок подмодуля не будет отсоединен .

--rebase

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

Все, что вам нужно сделать, это,

git submodule update --remote --merge
# or
git submodule update --remote --rebase

Рекомендуемый псевдоним:

git config alias.supdate 'submodule update --remote --merge'

# do submodule update with
git supdate

Там также возможность сделать --mergeили --rebaseкак поведение по умолчанию git submodule update, установив submodule.$name.updateв mergeили rebase.

Вот пример того, как настроить стандартное поведение обновления субмодуля в .gitmodule.

[submodule "bash/plugins/dircolors-solarized"]
    path = bash/plugins/dircolors-solarized
    url = https://github.com/seebi/dircolors-solarized.git
    update = merge # <-- this is what you need to add

Или настройте его в командной строке,

# replace $name with a real submodule name
git config -f .gitmodules submodule.$name.update merge

Ссылки

Simba
источник
6
Я использую git submodule update --remote --merge, и он сносит подмодуль в отдельном состоянии. Также пытался --rebaseс тем же результатом.
Джо Строут
8
@JoeStrout Если ваш подмодуль уже отключен, исправьте отключенное состояние, прежде чем выполнять обновление с помощью вышеуказанных команд. cdв субмодуль, извлеките субмодуль в определенную ветку с помощью git checkout master.
Симба
2
Или - если это слишком хлопотно для нескольких (рекурсивных) подмодулей - просто сделайте это git submodule foreach --recursive git checkout master.
Stefanct
1
Я только частично понимаю описания «как работает git». TBH Мне не очень интересно понимать, как работает git, я просто хочу его использовать. Теперь я понимаю, что я могу исправить отдельные модули с git submodule foreach --recursive git checkout master. Но как я могу предотвратить то, что git всегда отсоединяет их? Настройка параметров конфигурации для каждого подмодуля не является вариантом!
Николас
Для меня запуск git submodule update --remote --mergeне оставил подмодуль в отсоединенном состоянии HEAD, но запустился git submodule updateпосле редактирования моего .gitmoduleфайла, как вы указали, DID оставил подмодуль в отсоединенном состоянии HEAD.
bweber13
41

Я устал от того, что он всегда отключается, поэтому я просто использую сценарий оболочки, чтобы создать его для всех своих модулей. Я предполагаю, что все подмодули на master: вот скрипт:

#!/bin/bash
echo "Good Day Friend, building all submodules while checking out from MASTER branch."

git submodule update 
git submodule foreach git checkout master 
git submodule foreach git pull origin master 

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

j2emanue
источник
2
подмодуль git foreach мастер git pull origin - это то, что я искал ..
слава
просто и лаконично! Спасибо!
Жекаус
12

Проверьте мой ответ здесь: Подмодули Git: Укажите ветку / тег

Если вы хотите, вы можете добавить строку "branch = master" в ваш файл .gitmodules вручную. Прочитайте ссылку, чтобы увидеть, что я имею в виду.

РЕДАКТИРОВАТЬ: Чтобы отслеживать существующий проект субмодуля в филиале, следуйте инструкциям VonC здесь:

Подмодули Git: укажите ветку / тег

Джонни З
источник
14
Ответы должны быть встроенными; IIRC, связывающий с ответами, является ошибкой Stack Overflow.
Тони Топпер
1
@TonyTopper Даже когда просто ссылка на другой ответ SO? IIRC хмурится только на внешние ссылки, так как они могут исчезнуть, а затем ссылка не работает, и ответ, ну, в общем, бесполезен. Однако с SO-ответами такой опасности нет, они никогда не исчезнут, если SO не исчезнет (и может быть восстановлено, что бы ни случилось). Также он ответил на вопрос, так как branch = master" line into your .gitmoduleфактически является полным ответом, решил эту проблему для меня.
Меки
9

Другой способ заставить ваш подмодуль проверить ветку - это зайти в .gitmodulesфайл в корневой папке и добавить поле branchв конфигурации модуля следующим образом:

branch = <branch-name-you-want-module-to-checkout>

frontendgirl
источник
15
Для меня это не работает. Я правильно установил branch = my_wanted_branch. Но при git submodule update --remoteего запуске все равно проверяется как оторванная голова.
Андрюс
Сделайте это, затем cd sudmodule & git co thebranche & cd .., затем git submodule update --remote, и это работает!
pdm
Разве не так, что «.gitmodules» активно используется (читается) только тогда, когда суперпроект клонируется рекурсивным способом субмодулей или инициализируется субмодуль? Другими словами, в вашем собственном репозитории вы обновляете файл, в котором не всегда используются обновления конфигурации подмодулей, помещенные в «.gitmodules». В моем понимании .gitmodules - это шаблон для конфигурации, созданный во время клонирования репо или около того.
Na13-c
3

Как уже говорили другие люди, причина этого состоит в том, что родительское репо содержит только ссылку на (SHA1 of) определенный коммит в подмодуле - он ничего не знает о ветвях. Вот как это должно работать: ветвь, которая была в этом коммите, могла продвинуться (или назад), и если родительское репо ссылалось на ветку, то это может легко сломаться, когда это произойдет.

Однако, особенно если вы активно развиваетесь как в родительском репо, так и в субмодуле, detached HEADсостояние может быть запутанным и потенциально опасным. Если вы делаете коммиты в подмодуле, пока он находится в detached HEADсостоянии, они становятся висящими, и вы можете легко потерять свою работу. (Висячие коммиты обычно могут быть спасены с помощью git reflog, но в первую очередь их лучше избегать.)

Если вы похожи на меня, то большую часть времени, если в подмодуле есть ветвь, которая указывает на извлекаемый коммит, вы бы скорее проверили эту ветвь, чем находились бы в отключенном состоянии HEAD при том же коммите. Вы можете сделать это, добавив следующий псевдоним в ваш gitconfigфайл:

[alias]
    submodule-checkout-branch = "!f() { git submodule -q foreach 'branch=$(git branch --no-column --format=\"%(refname:short)\" --points-at `git rev-parse HEAD` | grep -v \"HEAD detached\" | head -1); if [[ ! -z $branch && -z `git symbolic-ref --short -q HEAD` ]]; then git checkout -q \"$branch\"; fi'; }; f"

Теперь, после выполнения, git submodule updateвам просто нужно позвонить git submodule-checkout-branch, и любой подмодуль, извлеченный при фиксации, на который указывает ветка, будет проверять эту ветку. Если у вас не часто есть несколько локальных веток, все указывающие на один и тот же коммит, то это обычно будет делать то, что вы хотите; если нет, то, по крайней мере, он гарантирует, что любые сделанные вами коммиты перейдут на реальную ветку, а не останутся висящими.

Кроме того, если вы настроили git для автоматического обновления подмодулей при оформлении заказа (используя git config --global submodule.recurse true, см. Этот ответ ), вы можете сделать хук после проверки, который автоматически вызывает этот псевдоним:

$ cat .git/hooks/post-checkout 
#!/bin/sh
git submodule-checkout-branch

Тогда вам не нужно вызывать либо git submodule updateили git submodule-checkout-branchпросто делать git checkoutобновят все подмодули в соответствующие фиксации и проверить соответствующие ветви (если они существуют).

deltacrux
источник
0

Самое простое решение:

git clone --recursive git@github.com:name/repo.git

Затем перейдите в каталог репо и:

git submodule update --init
git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
git config --global status.submoduleSummary true

Дополнительное чтение: лучшие практики Git submodules .

noccoa0
источник