how to keep git submodule on-branch status after c

2019-05-31 22:41发布

问题:

I am testing the new git submodule add -b feature (after git 1.8.2), which creates sub-module that allegedly tracks a branch rather than a commit. I am using git version 1.8.4.msysgit.0. The branch-tracking feature for submodules seems to work fine in the original super-project but fails as soon as the super-project is cloned. To be more specific:

What I did is typical, and is roughly as follows,

1. create a git repo (called common): ...
2. create a main project (called main), which uses common as a library/submodule.
 mkdir main && cd main
 git init
 git submodule add -b master url_to_common.git
 git commit -m "initial commit"
 cd common
 git status

As advertised, the added submodule tracks the master branch of the submodule repo. And I got:

# On branch master
nothing to commit, working directory clean

In addition, if I do git pull or git push, I get

Already up-to-date.
Everything up-to-date

, respectively.

However, if I clone the main project in any way, the common submodule in the cloned project loses the "On branch" status. And I couldn't git pull or git push inside the common folder as in the prototype main project. Of course, I can add origin master to make pull and push work for common in the cloned project, but this seems to defeat the purpose of having a tracking submodule (submodule add -b).

The commands I used to clone and check submodules were:

cd main
git clone . ../main2 --recursive
cd ../main2/common
git status

I got:

# HEAD detached at 0259d75
nothing to commit, working directory clean

I also tried git clone . ../main3 --recurse-submodules, as well as,

git clone . ../main4.git --bare
git clone url_to_main4.git --recursive

the same thing happens to main3 and main4.

In sum, I created a super-project with a submodule tracking its master branch. The branch-tracking capability is lost as soon as I tried to clone it and work on it elsewhere. It seems to me that the only way to keep branch-tracking submodules is to compress the original main project and copy it around. Did I missing something in this case, or is copying the original project around the best I can have?

My question is, how to keep the branch-tracking capability after cloning the super-project. I am particularly interested in making main4.git work because it involves a bare clone on a remote server.

Note: specifying a branch after the branch is lost (e.g. Git submodules: Specify a branch/tag) is not what I am looking for, because the info given in git submodule add -b is still lost, and we are back to square one. We might as well delete the submodule and add it again.

回答1:

For anyone else with this problem:

As the answers have stated, your submodule tracks a commit. This isn't actually an problem, a submodule represents an external dependency at a single point in time (ie, a commit) rather than an active stream of development (a branch). You manually update this dependency at your choosing (and theoretically, are only ever on Master branch)

You only really care about your branch when making commits. Committing without being on a branch makes a detached head, and it starts being easy for your team to lose its work.

I solved this at our studio by adding a pre-commit hook to reject commits if not on a branch:

#!/bin/sh

function parse_git_branch_check {
    if [[ ${branch_name} == "* (detached from "* ]]; then
        echo "********************************************"
        echo "You need to be on a branch before committing"
        echo "********************************************"
        exit 1
    else
        echo "-- You are on branch $branch_name --"
        exit 0
    fi
}
function parse_git_branch {
  git branch --no-color 2> /dev/null | sed -e '/^[^*]/d'
}

branch_name=$(parse_git_branch)
parse_git_branch_check;

This hook is committed to the super repo, along with this BAT (yes, I know, sorry) script to install the hooks

REM - Create links for all submodules to our pre-commit hooks for preventing submissions without a branch

if EXIST .git\hooks\pre-commit (
    del .git\hooks\pre-commit
)
mklink /h .git\hooks\pre-commit pre-commit

FOR /F "tokens=*" %%i IN ('DIR .git\modules /A:D /b') do (
    if EXIST .git\modules\%%i\hooks\pre-commit (
        del .git\modules\%%i\hooks\pre-commit
    )
    mklink /h .git\modules\%%i\hooks\pre-commit pre-commit
)

New clones need to run the script, which is not perfect, but the closest to an automatic solution I could come up with.



回答2:

Git submodules are tracked via specific commit references. Not via branches. Hence even if you are at the HEAD of master, your main project will track a specific commit from common. This is why submodules are very static in reference, and this has caused confusion and problems at times :)

This blog describes this concept fairly well.



回答3:

There are different use cases for git. But for my case, the following worked for git 1.8.5.2 (and likely later). Basically, first rm the submodule completely and then add it back. Naturally, the submodules are up-to-date and on-track.

cd main
git rm common
rm -rf .git/modules/common
git submodule add -b master url_to_common.git
cd common
git status

Note, the third line rm ... is necessary because git (as of 1.8.5.2) will leave .git/modules/common in the superproject, which prevents the submodule from being added back.