Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is my Git Submodule HEAD detached from master?

I am using Git submodules. After pulling changes from server, many times my submodule head gets detached from master branch.

Why does it happen?

I have to always do:

git branch git checkout master 

How can I make sure that my submodule is always pointing to master branch?

like image 862
om471987 Avatar asked Sep 12 '13 17:09

om471987


People also ask

What causes submodule detached head?

Working in submodules By default your submodule repository is in a state called 'detached HEAD'. This means that the checked-out commit -- which is the one that the super-project (core) needs -- is not associated with a local branch name.

How do you fix a detached head?

If you want to keep changes made with a detached HEAD, just create a new branch and switch to it. You can create it right after arriving at a detached HEAD or after creating one or more commits. The result is the same. The only restriction is that you should do it before returning to your normal branch.

Why is head detached git?

When you use the git checkout command to view a commit, you'll enter “detached HEAD state”. This refers to when you are viewing a commit that is not the most recent commit in a repository. Detached HEAD state is not an error nor is it a problem. When you are ready, you can navigate back to the HEAD in your repository.

Why is submodule dirty?

Submodules are now regarded as dirty if they have any modified files or untracked files, whereas previously it would only be the case if HEAD in the submodule pointed to the wrong commit.


2 Answers

EDIT:

See @Simba Answer for valid solution

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


OLD ANSWER:

Personally I hate answers here which direct to external links which may stop working over time and check my answer here (Unless question is duplicate) - directing to question which does cover subject between the lines of other subject, but overall equals: "I'm not answering, read the documentation."

So back to the question: Why does it happen?

Situation you described

After pulling changes from server, many times my submodule head gets detached from master branch.

This is a common case when one does not use submodules too often or has just started with submodules. I believe that I am correct in stating, that we all have been there at some point where our submodule's HEAD gets detached.

  • Cause: Your submodule is not tracking correct branch (default master).
    Solution: Make sure your submodule is tracking the correct branch
$ 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> 
  • Cause: Your parent repo is not configured to track submodules branch.
    Solution: Make your submodule track its remote branch by adding new submodules with the following two commands.
    • First you tell git to track your remote <branch>.
    • you tell git to perform rebase or merge instead of checkout
    • you tell git to update your submodule from remote.
    $ git submodule add -b <branch> <repository> [<submodule-path>]     $ git config -f .gitmodules submodule.<submodule-path>.update rebase     $ git submodule update --remote 
  • If you haven't added your existing submodule like this you can easily fix that:
    • First you want to make sure that your submodule has the branch checked out which you want to be tracked.
    $ 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> 

In the common cases, you already have fixed by now your DETACHED HEAD since it was related to one of the configuration issues above.

fixing DETACHED HEAD when .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. 

But if you managed to make some changes locally already for submodule and commited, pushed these to remote then when you executed 'git checkout ', Git notifies you:

$ 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: 

The recommended option to create a temporary branch can be good, and then you can just merge these branches etc. However I personally would use just git cherry-pick <hash> in this case.

$ 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> 

Although there are some more cases you can get your submodules into DETACHED HEAD state, I hope that you understand now a bit more how to debug your particular case.

like image 175
mkungla Avatar answered Oct 12 '22 00:10

mkungla


From git submodule --help, HEAD detached is the default behavior of git submodule update --remote. This has nothing to do with which branch is being tracked in a submodule.

For anyone only want a solution, jump to the 2nd part directly.

Reason

We need to understand what a submodule is.

Submodule is a way to include another project into your current project. It's not really adding these files into the commit history of your main project, but by referring a snapshot (commit) of the submodule.

Quote from Starting with Submodules section in book Pro Git

Although sbmodule DbConnector is a subdirectory in your working directory, Git sees it as a submodule and doesn’t track its contents when you’re not in that directory. Instead, Git sees it as a particular commit from that repository.

Every commit of a repo is a snapshot/state of your code at that time. The state of submodule at that time has to be deterministic too. You can't say in this commit, I include the master (or another) branch of another repo. You have to specify the state of submodule by a commit id.

Including another repos as a submodule is basically

git clone uri://another-repo path/to/submodule cd path/to/submodule git checkout <commit-id>  # git submodule system will add the reference commit id but not the files 

When anyone uses your repo with submodule, it will clone the submodule and checkout the specified commit as well.

And checking out a commit results HEAD detached. Why did my Git repo enter a detached HEAD state?

Solution

If you want the submodule merged with remote branch automatically, use --merge or --rebase.

man git-submodule

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

If your submodule has already been detached, fix the detached state before using following 2 solutions.

cd path/to/submodule # Assuming you're tracking the 'master' in the submodule git checkout master 

Solution 1: Use options in command line

# cd back to project root git submodule update --remote --merge # or git submodule update --remote --rebase 

Recommended alias:

git config alias.supdate 'submodule update --remote --merge'  # do submodule update with git supdate 

Solution 2: Add options into config file

Another solution is to change submodule update behavior in the gitmodule file by by setting submodule.$name.update to merge or rebase. It basically means you can do git submodule update --remote without passing --merge or --rebase explcitly, but read from config file automatically.

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 

Or configure it through command line,

# replace $name with a real submodule name git config -f .gitmodules submodule.$name.update merge 

Others

Adding a branch option in .gitmodule is NOT related to the detached behavior of submodules at all. The old answer from mkungla is incorrect, or obsolete.

Let's make it clear 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.

References

  • git submodule --help
  • Submodules tutorial from book Pro Git
like image 21
Simba Avatar answered Oct 12 '22 00:10

Simba