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

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

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

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

git branch
git checkout master

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

Ответ 1

EDIT:

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

.

submodule.<name>.update is what you want to change, see the docs - default checkout
submodule.<name>.branch specify remote branch to be tracked - default master


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

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

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

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

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

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

  • Причина. Ваш подмодуль не отслеживает правильную ветвь (мастер по умолчанию).
    Решение: убедитесь, что ваш подмодуль отслеживает правильную ветвь
$ cd <submodule-path>
# if the master branch already exists locally:
# (From git docs - branch)
# -u <upstream>
# --set-upstream-to=<upstream>
#    Set up <branchname> tracking information so <upstream>
#    is considered <branchname> 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 или слияние вместо извлечения
    • вы говорите 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 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, я надеюсь, что теперь вы немного больше понимаете, как отлаживать ваш конкретный случай.

Ответ 2

Я устал от того, что он всегда отключается, поэтому я просто использую сценарий оболочки, чтобы создать его для всех моих модулей. Я предполагаю, что все подмодули на 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 

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

Ответ 3

Добавление параметра branch в .gitmodule НЕ связано с отсоединенным поведением подмодулей. Старый ответ от @mkungla неправильный или устарел.

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

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

--remote

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

Почему

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

--checkout

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

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

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

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

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

Как

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

--merge

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

--rebase

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

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

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

Ссылки

Ответ 4

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

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

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

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

Ответ 5

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

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

Ответ 6

Сделайте это, затем cd sudmodule&git co thebranche & cd.., затем git submodule update --remote и это работает!

Ответ 7

Как уже говорили другие люди, причина, по которой это происходит, заключается в том, что родительское репо содержит только ссылку на (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 обновит все подмодули до их соответствующих коммитов и проверит соответствующие ветки (если они существуют).