4

If I update the submodule in one parent project branch, then switch to another branch in the same working tree, the submodule reference appears as if it has been modified.

For example (the hashes will be different probably and you probably have to scroll the code window down to see all of the lines):

# create soon to be submodule
mkdir /tmp/example-submodule
cd /tmp/example-submodule
git init
echo a > a
git add a
git commit -m 'm'
cp -r .git /tmp/example-submodule.git
cd /tmp/example-submodule.git
git config --bool core.bare true
cd /tmp
/bin/rm -rf /tmp/example-submodule
git clone file:///tmp/example-submodule.git

#create other project and add sub-module
mkdir /tmp/example-supermodule
cd /tmp/example-supermodule
git init
echo x > x
git add x
git commit -m 'm'
cp -r .git /tmp/example-supermodule.git
cd /tmp/example-supermodule.git
git config --bool core.bare true
cd /tmp
/bin/rm -rf /tmp/example-supermodule
git clone file:///tmp/example-supermodule.git

#update the submodule
cd /tmp/example-supermodule
git submodule add file:///tmp/example-submodule.git example-sub
git commit -a -m'm'
git submodule status
# prints: 189ea25d45618862d0bfcbf5bd995e05ce1c2e4e example-sub (heads/master)
git push

#update the submodule
cd /tmp/example-submodule
echo b > b
git add b
git commit -m 'm'
git push

#create new branch in super module and update submodule to new HEAD
cd /tmp/example-supermodule
git branch otherbranch
git checkout otherbranch
git submodule update --remote
git commit -a -m'm'
git submodule status
# prints: 26f94e59002603475a1ba731104f92e10e88cf6b example-sub (remotes/origin/HEAD)
git push origin otherbranch

#checkout another branch
git checkout master
git status
cat .git/modules/example-sub/HEAD
# prints: 26f94e59002603475a1ba731104f92e10e88cf6b

After master is checked out, the submodule's hash is different as if the update in otherbranch also affected master.

If I clone the super-project instead of switching the branch in the same working tree:

cd /tmp
git clone --recursive file:///tmp/example-supermodule.git/ other
cd /tmp/other
git submodule status

it shows the submodule commit that was added in the master branch.

How can someone switch between branches in their working tree and have the submodule's commit match what it was committed as? Or how can one recover after changing branches shows the submodule as modified when it wasn't modified in that branch?

1 Answer 1

3

I just reproduced the issue with git 2.9.0

All you have to do at the end to get back a clean master is:

git submodule update

That will reset the submodule to its master value, and .git/modules/example-sub/HEAD will have the right value

You would have the same issue when you checkout again otherbranch.
The right gitlink (special entry in the index) will be checked out.
But the .git/modules/example-sub/HEAD won't be changed right away.

In both cases, that is because checkout only modify the gitlink, not the content of the submodule.
Only a git submodule update will actually check out the content of the submodule to its recorded gitlink SHA1.

That way, if you had work in progress in the submodule, they are not wiped out by a checkout in the parent repo.


Here is my own experiment:

C:\Users\vonc\prog\git\tests>mkdir subm
C:\Users\vonc\prog\git\tests>cd subm

Let's create two repos, each with one commit:

C:\Users\vonc\prog\git\tests\subm>git init s
Initialized empty Git repository in C:/Users/vonc/prog/git/tests/subm/s/.git/

C:\Users\vonc\prog\git\tests\subm>git init p
Initialized empty Git repository in C:/Users/vonc/prog/git/tests/subm/p/.git/

C:\Users\vonc\prog\git\tests\subm>cd s
C:\Users\vonc\prog\git\tests\subm\s>git commit --allow-empty -m "first s commit"
[master (root-commit) d11bd81] first s commit

C:\Users\vonc\prog\git\tests\subm\s>cd ..\p
C:\Users\vonc\prog\git\tests\subm\p>git commit --allow-empty -m "first p commit"
[master (root-commit) 10a7044] first p commit

Let's add s as a submodule of p (the parent repo)

C:\Users\vonc\prog\git\tests\subm\p>git submodule add -- ../s
Cloning into 'C:/Users/vonc/prog/git/tests/subm/p/s'...
done.
C:\Users\vonc\prog\git\tests\subm\p>git st
On branch master
Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

        new file:   .gitmodules
        new file:   s
C:\Users\vonc\prog\git\tests\subm\p>git commit -m "add p"
[master aa33ba9] add p
 2 files changed, 4 insertions(+)
 create mode 100644 .gitmodules
 create mode 160000 s

Now let's modify the s repo, adding a new commit to its master branch

C:\Users\vonc\prog\git\tests\subm\p>cd ..\s
C:\Users\vonc\prog\git\tests\subm\s>git commit --allow-empty -m "second s commit"
[master 81e4800] second s commit
C:\Users\vonc\prog\git\tests\subm\s>gl
* 81e4800 - (HEAD -> master) second s commit (1 second ago) <VonC>
* d11bd81 - first s commit (3 minutes ago) <VonC>

Let's create a new branch in the parent repo

C:\Users\vonc\prog\git\tests\subm\s>cd ..\p
C:\Users\vonc\prog\git\tests\subm\p>git commit --allow-empty -m "second s commit"
C:\Users\vonc\prog\git\tests\subm\p>git checkout -b branch
Switched to a new branch 'branch'

Let's update the submodule, and add it to the new branch branch:

C:\Users\vonc\prog\git\tests\subm\p>git submodule update --remote
remote: Counting objects: 1, done.
remote: Total 1 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (1/1), done.
From C:/Users/vonc/prog/git/tests/subm/s
   d11bd81..81e4800  master     -> origin/master
Submodule path 's': checked out '81e48007afc90aceca16d884d0fdc34074121732'


C:\Users\vonc\prog\git\tests\subm\p>git st
On branch branch
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)
        modified:   s (new commits)
no changes added to commit (use "git add" and/or "git commit -a")
C:\Users\vonc\prog\git\tests\subm\p>git add .
C:\Users\vonc\prog\git\tests\subm\p>git commit -m "update s to latest master"
[branch 271a4fb] update s to latest master
 1 file changed, 1 insertion(+), 1 deletion(-)

C:\Users\vonc\prog\git\tests\subm\p>cat .git\modules\s\HEAD
81e48007afc90aceca16d884d0fdc34074121732

C:\Users\vonc\prog\git\tests\subm\p>gl
* 271a4fb - (HEAD -> branch) update s to latest master (26 seconds ago) <VonC>
* f86aab2 - (master) second s commit (2 minutes ago) <VonC>
* aa33ba9 - add p (3 minutes ago) <VonC>
* 10a7044 - first p commit (4 minutes ago) <VonC>

It I checkout master, the right submodule SHA1 is checked out, but the content of the submodule is not yet changed.

C:\Users\vonc\prog\git\tests\subm\p>git checkout master
M       s
Switched to branch 'master'
C:\Users\vonc\prog\git\tests\subm\p>cat .git\modules\s\HEAD
81e48007afc90aceca16d884d0fdc34074121732
C:\Users\vonc\prog\git\tests\subm\p>git st
On branch master
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   s (new commits)

no changes added to commit (use "git add" and/or "git commit -a")

If I were to switch back to the branch branch, the status would be clean (that is because the content of the submodule s matches its recorded gitlink SHA1)

C:\Users\vonc\prog\git\tests\subm\p>git checkout branch
Switched to branch 'branch'
C:\Users\vonc\prog\git\tests\subm\p>git st
On branch branch
nothing to commit, working directory clean

But on master, I need a submodule update to reset the content of the submodule s to its SHA1:

C:\Users\vonc\prog\git\tests\subm\p>git checkout master
M       s
Switched to branch 'master'
C:\Users\vonc\prog\git\tests\subm\p>git submodule update
Submodule path 's': checked out 'd11bd8132971a18e3e7cd19984cc6ce106478087'
C:\Users\vonc\prog\git\tests\subm\p>cat .git\modules\s\HEAD
d11bd8132971a18e3e7cd19984cc6ce106478087
C:\Users\vonc\prog\git\tests\subm\p>git st
On branch master
nothing to commit, working directory clean

The same issue would appear when I checkout branch:

C:\Users\vonc\prog\git\tests\subm\p>
C:\Users\vonc\prog\git\tests\subm\p>git checkout branch
M       s
Switched to branch 'branch'
C:\Users\vonc\prog\git\tests\subm\p>git st
On branch branch
Changes not staged for commit:
  (use "git add <file>..." to update what will be committed)
  (use "git checkout -- <file>..." to discard changes in working directory)

        modified:   s (new commits)

no changes added to commit (use "git add" and/or "git commit -a")
C:\Users\vonc\prog\git\tests\subm\p>git show s
commit 271a4fb6bf79e2524f98dfbd505b2876b0c173a8
Author: VonC <[email protected]>
Date:   Sat Jul 9 06:52:57 2016 +0200

    update s to latest master

diff --git a/s b/s
index d11bd81..81e4800 160000
--- a/s
+++ b/s
@@ -1 +1 @@
-Subproject commit d11bd8132971a18e3e7cd19984cc6ce106478087
+Subproject commit 81e48007afc90aceca16d884d0fdc34074121732

Again, a git submodule update would reset the content of the submodule s to the SHA1 recorded in branch branch of the parent repo.

1
  • Thank you for the answer and the links.
    – wdkendall
    Commented Jul 9, 2016 at 10:43

Not the answer you're looking for? Browse other questions tagged or ask your own question.