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

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

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

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

git branch
git checkout master

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

Большое спасибо

9 ответов

Решение

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

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

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

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

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

  • Причина: Ваш подмодуль не отслеживает ни одну или правильную ветку. Решение: убедитесь, что ваш подмодуль отслеживает правильную ветвь
$ 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 обновить ваш подмодуль с пульта.
    $ git submodule add -b <branch> <repository> [<submodule-path>]
    $ 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>

Однако, даже если вы настроили свой подмодуль для отслеживания правильной ветви, вы все равно можете оказаться в ситуациях, когда ваш подмодуль получает HEAD detached at <commit-hash>

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

Бежать $ git status в вашем родителе, а также в пути к субмодулю, чтобы убедиться, что все отслежено правильно и все обновлено, затем запустите $ cd <parent-repo> а также git submodule update --remote, Как видите, если вы снова запустите git status, то пока все в порядке.

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

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

Добавление branch вариант в .gitmodule недостаточно, чтобы ваши подмодули не отсоединились. То, что @mkungla сказал тебе, неверно.

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

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

--дистанционный пульт

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

Зачем

Так почему ГОЛОВА отсоединена после update? Потому что поведение по умолчанию submodule.$name.update является checkout,

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

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

Как

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

--merge

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

--rebase

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

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

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

Также есть возможность сделать --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

RTFM. git submodule --help,

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

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

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

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

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

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

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

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

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

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

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

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

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.

Начиная с git 1.8.2 вы можете использовать-b branch_nameопция при добавлении подмодуля git. Или для существующего подмодуля вы можете добавить информацию о ветке отслеживания:

      git submodule set-branch -b branch_name submodule_name

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

Я также все еще разбираюсь во внутренностях git и до сих пор понял это:

  1. HEAD - это файл в вашем каталоге.git/, который обычно выглядит примерно так:
% cat .git/HEAD
ref: refs/heads/master
  1. refs / Heads / master сам по себе является файлом, который обычно имеет хеш-значение последней фиксации:
% cat .git/refs/heads/master 
cbf01a8e629e8d884888f19ac203fa037acd901f
  1. Если вы git checkout удаленную ветку, которая опережает ваш мастер, это может привести к тому, что ваш файл HEAD будет обновлен, чтобы содержать хэш последней фиксации в удаленном мастере:
% cat .git/HEAD
8e2c815f83231f85f067f19ed49723fd1dc023b7

Это называется отделенной ГОЛОВКОЙ. Удаленный мастер опережает вашего локального мастера. Когда вы выполните команду git submodule --remote myrepo, чтобы получить последнюю фиксацию вашего подмодуля, он по умолчанию выполнит проверку, которая обновит HEAD. Поскольку ваш текущий мастер ветки отстает, HEAD, так сказать, "отсоединяется" от вашей текущей ветки.

Другие вопросы по тегам