我正在使用Git子模块。从服务器提取更改后,很多时候我的子模块头都与master分支分离。
为什么会发生?
我必须总是这样做:
git branch
git checkout master
如何确保子模块始终指向master分支?
我正在使用Git子模块。从服务器提取更改后,很多时候我的子模块头都与master分支分离。
为什么会发生?
我必须总是这样做:
git branch
git checkout master
如何确保子模块始终指向master分支?
Answers:
编辑:
submodule.<name>.update
是您要更改的内容,请参见文档 - 默认checkout
submodule.<name>.branch
指定要跟踪的远程分支- 默认master
老答案:
我个人不喜欢这里的答案,这些答案直接指向可能随着时间的推移而停止工作的外部链接,并在此处 检查我的答案(除非有重复的问题) -指向确实涵盖其他主题之间的主题的问题,但总体上等于:不回答,请阅读文档。”
回到问题:为什么会发生?
您描述的情况
从服务器提取更改后,很多时候我的子模块头都与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>'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>
<branch>
。 $ 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状态,但我希望您现在对如何调试特定情况有更多的了解。
git submodule update --remote
。请看一下Simba的答案,我认为这应该是正确的答案。
添加branch
选项中.gitmodule
的不涉及在所有子模块的分离行为。@mkungla的旧答案不正确或已过时。
从git submodule --help
,头部卸下是默认行为的git submodule update --remote
。
首先,无需指定要跟踪的分支。origin/master
是要跟踪的默认分支。
- 远程
不要使用超级项目的记录的SHA-1更新子模块,而要使用子模块的远程跟踪分支的状态。所使用的遥控器是分支机构的遥控器(
branch.<name>.remote
),默认为origin
。使用的远程分支默认为master
。
那么为什么HEAD在之后脱离update
呢?这是由默认模块更新行为checkout
引起的。
- 退房
在子模块中的分离HEAD上签出超级项目中记录的提交。这是默认行为,
submodule.$name.update
当设置为以外的值时,此选项的主要用途是覆盖checkout
。
为了解释这种奇怪的更新行为,我们需要了解子模块如何工作?
尽管sbmodule
DbConnector
是工作目录中的子目录,但是Git将其视为子模块,并且不在该目录中时不会跟踪其内容。相反,Git将其视为该存储库中的特定提交。
主仓库跟踪子模块及其在特定点的状态,即提交ID。因此,当您更新模块时,就是将提交ID更新为新的提交ID。
如果您希望子模块自动与远程分支合并,请使用--merge
或--rebase
。
- 合并
此选项仅对update命令有效。将超级项目中记录的提交合并到子模块的当前分支中。如果指定了此选项,则不会分离子模块的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
这是有关如何在中配置子模块update的默认更新行为的示例.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
git submodule --help
git submodule update --remote --merge
,它以分离状态拉下子模块。还尝试--rebase
了相同的结果。
cd
进入子模块,使用将子模块签出到特定分支git checkout master
。
git submodule foreach --recursive git checkout master
。
git submodule foreach --recursive git checkout master
。但是如何防止git总是将它们分离呢?不能为每个子模块设置配置选项!
git submodule update --remote --merge
并不会使子模块处于分离的HEAD状态,而是git submodule update
在编辑我的.gitmodule
文件后运行(如您指示的那样,DID使子模块处于分离的HEAD状态)。
我已经厌倦了它总是分离,所以我只用一个shell脚本为我所有的模块构建它。我假设所有子模块都在主模块上:这是脚本:
#!/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的说明:
branch = master" line into your .gitmodule
实际上是完整的回答,为我解决了这个问题。
使子模块签出分支的另一种方法是转到.gitmodules
根文件夹中的文件,并branch
在模块配置中添加字段,如下所示:
branch = <branch-name-you-want-module-to-checkout>
branch = my_wanted_branch
。但是运行git submodule update --remote
它仍然会退为头部。
正如其他人所说的,发生这种情况的原因是父仓库仅包含对子模块中特定提交(SHA1)的引用-它对分支一无所知。这就是它应该如何工作的:位于该提交的分支可能已经向前移动(或向后移动),并且如果父仓库已经引用了该分支,那么在发生这种情况时很容易中断。
但是,尤其是如果您同时在父仓库和子模块中进行开发时,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
然后在repo目录中执行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子模块最佳实践。