Reputation: 5627
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?
Upvotes: 264
Views: 204576
Reputation: 1215
Since git 1.8.2 you can use -b branch_name
option when adding git submodule. Or for existing submodule you can add info about tracking branch:
git submodule set-branch -b branch_name submodule_name
For more info you can refer this article.
Upvotes: 0
Reputation: 27588
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.
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 submodule
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?
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
# 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
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
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 toorigin
. The remote branch used defaults tomaster
.
git submodule --help
Upvotes: 190
Reputation: 62519
I got tired of it always detaching so iIjust use a shell script to build it out for all my modules. I assume all submodules are on master
: here is the script:
#!/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
execute it from your parent module
Upvotes: 75
Reputation: 379
I am also still figuring out the internals of git, and have figured out this so far:
% cat .git/HEAD
ref: refs/heads/master
% cat .git/refs/heads/master
cbf01a8e629e8d884888f19ac203fa037acd901f
% cat .git/HEAD
8e2c815f83231f85f067f19ed49723fd1dc023b7
This is called a detached HEAD. The remote master is ahead of your local master. 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.
Upvotes: 0
Reputation: 155
The simplest solution is:
git clone --recursive [email protected]:name/repo.git
Then cd in the repo directory and:
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
Additional reading: Git submodules best practices.
Upvotes: 3
Reputation: 3538
EDIT:
See @Simba Answer for valid solution
submodule.<name>.update
is what you want to change, see the docs - defaultcheckout
submodule.<name>.branch
specify remote branch to be tracked - defaultmaster
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.
$ 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>
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.
Upvotes: 208
Reputation: 1316
As other people have said, the reason this happens is that the parent repo only contains a reference to (the SHA1 of) a specific commit in the submodule – it doesn't know anything about branches. This is how it should work: the branch that was at that commit may have moved forward (or backwards), and if the parent repo had referenced the branch then it could easily break when that happens.
However, especially if you are actively developing in both the parent repo and the submodule, detached HEAD
state can be confusing and potentially dangerous. If you make commits in the submodule while it's in detached HEAD
state, these become dangling and you can easily lose your work. (Dangling commits can usually be rescued using git reflog
, but it's much better to avoid them in the first place.)
If you're like me, then most of the time if there is a branch in the submodule that points to the commit being checked out, you would rather check out that branch than be in detached HEAD state at the same commit. You can do this by adding the following alias to your gitconfig
file:
[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"
Now, after doing git submodule update
you just need to call git submodule-checkout-branch
, and any submodule that is checked out at a commit which has a branch pointing to it will check out that branch. If you don't often have multiple local branches all pointing to the same commit, then this will usually do what you want; if not, then at least it will ensure that any commits you do make go onto an actual branch instead of being left dangling.
Furthermore, if you have set up git to automatically update submodules on checkout (using git config --global submodule.recurse true
, see this answer), you can make a post-checkout hook that calls this alias automatically:
$ cat .git/hooks/post-checkout
#!/bin/sh
git submodule-checkout-branch
Then you don't need to call either git submodule update
or git submodule-checkout-branch
, just doing git checkout
will update all submodules to their respective commits and check out the corresponding branches (if they exist).
Upvotes: 8
Reputation: 15449
Check out my answer here: Git submodules: Specify a branch/tag
If you want, you can add the "branch = master" line into your .gitmodules file manually. Read the link to see what I mean.
EDIT: To track an existing submodule project at a branch, follow VonC's instructions here instead:
Git submodules: Specify a branch/tag
Upvotes: 14
Reputation: 787
The other way to make your submodule to check out the branch is to go the .gitmodules
file in the root folder and add the field branch
in the module configuration as following:
branch = <branch-name-you-want-module-to-checkout>
Upvotes: 9