Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Git submodule is in "detached head" state after cloning and submodule update

Tags:

git

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?

like image 877
Barry Fruitman Avatar asked Dec 27 '13 04:12

Barry Fruitman


People also ask

Why is submodule head detached?

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.

Does git submodule update pull?

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.

Does git clone get 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.


4 Answers

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.

like image 81
VonC Avatar answered Oct 05 '22 01:10

VonC


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.

like image 42
Gary Fixler Avatar answered Oct 05 '22 01:10

Gary Fixler


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!):

enter image description here

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.

like image 29
dtmland Avatar answered Oct 05 '22 02:10

dtmland


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 to origin. The remote branch used defaults to master.

Why

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 than checkout.

How

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.

like image 40
Simba Avatar answered Oct 05 '22 01:10

Simba