When I clone my git repo, one of the submodules is in a branch with a strange name, which I think means it has a "detached head" (I'm not even sure what that means).
If I checkout my main branch for the submodule, then run "git submodule update --init --recursive
" it happens again.
Does anybody know what's going on?
When you do git submodule --remote myrepo to get the latest commit of your submodule, it will by default do a checkout, which will update HEAD. Since your current branch master is behind, HEAD becomes 'detached' from your current branch, so to speak.
If you track branches in your submodules, you can update them via the --remote parameter of the git submodule update command. This pulls in new commits into the main repository and its submodules.
Cloning a Project with Submodules If you pass --recurse-submodules to the git clone command, it will automatically initialize and update each submodule in the repository, including nested submodules if any of the submodules in the repository have submodules themselves.
A submodule is always checked out as a detached HEAD (see "Why did git detach my head?"), since the index of the parent repo contains only the SHA1 as a special entry in its index, as explained by Gary Fixler's answer.
Even if you configure your submodule to follow a branch (or convert an existing submodule to follow a branch), a git submodule update --remote
would checkout the latest SHA1 of that remote branch, but the result would by default be a detached HEAD.
Only by adding --merge
or --rebase
to that command (git submodule update --remote (--merge/--rebase)
) would you get a non-detached HEAD, as seen in Simba's answer): by default, the master
branch.
If you don't update that way (git submodule update --remote (--merge/--rebase)
), then you need to go in that submodule and make a branch yourself there.
If you want to contribute to said submodule (making new commits in it), it is a good idea to create a new branch.
cd mySubmodule
git checkout -b aNewBranch
# work
git add .
git commit -m "new commits"
git push -u origin aNewBranch
# record the new submodule state in the parent repo:
cd ..
git add mySubmodule
git commit -m "new state of mySubmodule"
git push
Note for Git 2.16 (Q1 2018): "git checkout --recursive
" may overwrite and rewind the history of the branch that happens to be checked out in submodule
repositories, which might not be desirable.
Detach the HEAD
but still allow the recursive checkout to succeed in such a case.
See commit 57f22bf (28 Jul 2017), and commit 3ef2538 (24 Jul 2017) by Stefan Beller (stefanbeller
).
(Merged by Junio C Hamano -- gitster
-- in commit 0b75572, 06 Dec 2017)
recursive submodules: detach HEAD from new state
When a submodule is on a branch and in its superproject you run a recursive checkout, the branch of the submodule is updated to what the superproject checks out.
This is very unexpected in the current model of Git as e.g. 'submodule update
' always detaches the submodule HEAD.Despite having plans to have submodule HEADS not detached in the future, the current behavior is really bad as it doesn't match user expectations and it is not checking for loss of commits (only to be recovered via the reflog).
Detach the HEAD unconditionally in the submodule when updating it.
The contents of git directories are stored in simple text file manifests (i.e. directory listings) called "trees" that look like the following, where blobs are the contents of files, and trees are yet more trees like this one:
100644 blob 0c31be662540ce902cee106f86bfdeef519fc662 .gitignore
100644 blob 1d364edf530c2238e79162bf2d9f30c2af610347 .gitmodules
040000 tree fc6bc39202ec20228e9135cd426110c558b625cd foo
040000 tree 2f9fc460f3a2370ed45b39b2bcaaf9b6e746b823 bar
If bar was a submodule, instead of merely a directory, however, the tree containing it would be listed like this:
100644 blob 0c31be662540ce902cee106f86bfdeef519fc662 .gitignore
100644 blob 1d364edf530c2238e79162bf2d9f30c2af610347 .gitmodules
040000 tree fc6bc39202ec20228e9135cd426110c558b625cd foo
040000 commit 2f9fc460f3a2370ed45b39b2bcaaf9b6e746b823 bar
Note that instead of bar being a tree, it's now a commit (in a submodule). This is all git stores about a submodule at the tree/commit level, so it can't know what branch a commit was on. In fact, storing branch names can't work. They can change. Also, if you check out an old commit in your repo that needs to roll the submodule back as well, should the branch move back in the submodule? That would cast the commits after that new branch location into unreferenced territory.
The branches are for humans to use, to make sense of the DAG, and where particular lines of thought are. Git doesn't care how we refer to the commits. It needs a concrete location so you can safely move the containing repo around, and know that the submodule will always be checked out to where it was at that time. The only truth is the hash.
For users of Sourcetree - you will observe the same behavior - a clone will give you submodules in a detached state. As VonC mentions in his answer if you want to contribute to the submodule you need to perform some extra steps.
Besides creating a new branch from the point of the submodule SHA-1, in most cases you will simply want to checkout the head of the existing branch. This will be master unless the sub-module is configured to follow a specific branch.
You can either open each submodule and manually checkout their corresponding branches, or you can create a custom action to do it for you (recursively!):
Script to Run
cmd
Parameters
/c %LOCALAPPDATA%\Atlassian\SourceTree\git_local\bin\sh.exe --login -i -c "git pull; git submodule foreach -q --recursive 'toplevel=\"$(git rev-parse --show-toplevel)\"; branch=\"$(git config -f $toplevel/.gitmodules submodule.$name.branch)\"; [ \"$branch\" = \"\" ] && branch=master; git checkout $branch; git fetch; git merge FETCH_HEAD;'"""
This will check for any submodules in your project and checkout the branch that they point to (or master if they don't specify a branch). It is recursive, so any submodules that contain submodules will also be handled.
As I found out in an earlier post, the extra pair of quotes on the end are required.
Adding a branch
option in .gitmodule
is NOT related to the detached behavior of submodules at all.
From git submodule --help
, HEAD detached is the default behavior of git submodule update --remote
.
First, there's no need to specify a branch to be tracked. origin/master
is the default branch to be tracked.
--remote
Instead of using the superproject's recorded SHA-1 to update the submodule, use the status of the submodule's remote-tracking branch. The remote used is branch's remote (
branch.<name>.remote
), defaulting toorigin
. The remote branch used defaults tomaster
.
So why is HEAD detached after update
? Because the default behavior of submodule.$name.update
is checkout
.
--checkout
Checkout the commit recorded in the superproject on a detached HEAD in the submodule. This is the default behavior, the main use of this option is to override
submodule.$name.update
when set to a value other thancheckout
.
If you want the submodule merged with remote branch automatically, use --merge
or --rebase
.
--merge
This option is only valid for the update command. Merge the commit recorded in the superproject into the current branch of the submodule. If this option is given, the submodule's HEAD will not be detached.
--rebase
Rebase the current branch onto the commit recorded in the superproject. If this option is given, the submodule's HEAD will not be detached.
All you need to do is,
git submodule update --remote --merge
# or
git submodule update --remote --rebase
There's also an option to make --merge
or --rebase
as the default behavior of git submodule update
, by setting submodule.$name.update
to merge
or rebase
.
Here's an example about how to config the default update behavior of submodule update in .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
My whole answer is based on the manual. git submodule --help
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With