Git submodules: Specify a branch/tag

How does git submodule add -b work?

After adding a submodule with a specific branch, a new cloned repo (after git submodule update --init ) will be at a specific commit, not the branch itself ( git status on the submodule shows "Not currently on any branch").

I can't find any information on .gitmodules or .git/config about the submodule's branch or any specific commit, so how does git figure it out?

Also, is it possible to specify a tag instead of a branch?

PS: I'm using 1.6.5.2 .


Note: Git 1.8.2 added the possibility to track branches. See some of the answers below.


It's a little confusing to get used to this, but submodules are not on a branch. They are, like you say, just a pointer to a particular commit of the submodule's repository.

This means, when someone else checks out your repository, or pulls your code, and does git submodule update, the submodule is checked out to that particular commit.

This is great for a submodule that does not change often, because then everyone on the project can have the submodule at the same commit.

If you want to move the submodule to a particular tag:

cd submodule_directory
git checkout v1.0
cd ..
git add submodule_directory
git commit -m "moved submodule to v1.0"
git push

Then, another developer who wants to have submodule_directory changed to that tag, does this

git pull
git submodule update

git pull changes which commit their submodule directory points to. git submodule update actually merges in the new code.


I'd like to add an answer here that is really just a conglomerate of other answers, but I think it may be more complete.

You know you have a git submodule when you have these two things.

1) Your .gitmodules has an entry like so:

[submodule "SubmoduleTestRepo"]
    path = SubmoduleTestRepo
    url = https://github.com/jzaccone/SubmoduleTestRepo.git

2) You have a submodule object (named SubmoduleTestRepo in this example) in your git repository. Github shows these as "submodule" objects. Or do git submodule status from a command line. Git submodule objects are special kind of git object and they hold the SHA infomation for a specific commit.

Whenever you do a git submodule update , it will populate your submodule with content from the commit. It knows where to find the commit because of the information in the .gitmodules .

Now, all the -b does is add one line in your .gitmodules file. So following the same example it would look like this:

[submodule "SubmoduleTestRepo"]
    path = SubmoduleTestRepo
    url = https://github.com/jzaccone/SubmoduleTestRepo.git
    branch = master

EDIT: only branch name is supported above, not SHA or TAG.

The submodule object is still pointing at a specific commit. The only thing that the -b option buys you is the ability to add a --remote flag to your update as per Vogella's answer:

git submodule update --remote

Instead of populating the content of the submodule to the commit pointed to by the submodule, it replaces that commit with the latest commit on the master branch, THEN it populates the submodule with that commit. This can be done in two steps by djacobs7 answer. Since you have now updated the commit the submodule object is pointing to, you have to commit the changed submodule object into your git repository.

git submodule add -b is not some magically way to keep everything up to date with a branch. It is simply adds information about a branch in the .gitmodules file and gives you the option to update the submodule object to the latest commit of a specified branch before populating it.


Note that if you have an existing submodule which isn't tracking a branch yet , then (if you have git 1.8.2+):

  • Make sure the parent repo knows that its submodule now tracks a branch:

    cd /path/to/your/parent/repo
    git config -f .gitmodules submodule.<path>.branch <branch>
    
  • Make sure your submodule is actually at the latest of that branch:

    cd path/to/your/submodule
    git checkout -b branch --track origin/branch
      # if the master branch already exist:
      git branch -u origin/master master
    
  • (with 'origin' being the name of the upstream remote repo the submodule has been cloned from.
    A git remote -v inside that submodule will display it. Usually, it is 'origin')

  • Don't forget to record the new state of your submodule in your parent repo:

    cd /path/to/your/parent/repo
    git add path/to/your/submodule
    git commit -m "Make submodule tracking a branch"
    
  • Subsequent update for that submodule will have to use the --remote option:

    # update your submodule
    # --remote will also fetch and ensure that
    # the latest commit from the branch is used
    git submodule update --remote
    
    # to avoid fetching use
    git submodule update --remote --no-fetch 
    

  • Note that with Git 2.10+ (Q3 2016), you can use ' . ' as a branch name:

    The name of the branch is recorded as submodule.<name>.branch in .gitmodules for update --remote .
    A special value of . is used to indicate that the name of the branch in the submodule should be the same name as the current branch in the current repository .


    If you want to update all your submodules following a branch:

        git submodule update --recursive --remote
    

    Note that the result, for each updated submodule, will always be a detached HEAD , as Dan Cameron note in his answer.
    To ensure the branch is actually checked out (and that won't modify the SHA1 of the special entry representing the submodule for the parent repo), he suggests:

    git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; git checkout $branch'
    

    Each submodule will still reference the same SHA1, but if you do make new commits, you will be able to push them because they will be referenced by the branch you want the submodule to track.
    After that push within a submodule, don't forget to go back to the parent repo, add, commit and push the new SHA1 for those modified submodules.

    Note the use of $toplevel , recommended in the comments by Alexander Pogrebnyak.
    $toplevel was introduced in git1.7.2 in May 2010: commit f030c96.

    it contains the absolute path of the top level directory (where .gitmodules is).

    dtmland adds in the comments:

    The foreach script will fail to checkout submodules that are not following a branch.
    However, this command gives you both:

     git submodule foreach -q --recursive 'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; [ "$branch" = "" ] && git checkout master || git checkout $branch' –
    

    The same command but easier to read:

    git submodule foreach -q --recursive 
        'branch="$(git config -f $toplevel/.gitmodules submodule.$name.branch)"; 
         [ "$branch" = "" ] && 
         git checkout master || git checkout $branch' –
    

    umläute refines dtmland's command with a simplified version in the comments:

    git submodule foreach -q --recursive 'git checkout $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
    

    multiple lines:

    git submodule foreach -q --recursive 
      'git checkout 
      $(git config -f $toplevel/.gitmodules submodule.$name.branch || echo master)'
    
    链接地址: http://www.djcxy.com/p/7844.html

    上一篇: 我如何克隆成非

    下一篇: Git子模块:指定一个分支/标签