Reference Is Not a Tree
Git is an excellent version control system. However, it does not offer much hand-holding. Linus expects you to read the documentation and understand what you are doing. How rude! One of Git’s primary features includes: making you feel less intelligent than you thought you were! It satisfies this feature quite well. This can prove especially true when dealing with submodules.
Have you ever seen something like this when working with submodules?
fatal: reference is not a tree: d001e26744dab4b92014a77a4552a535b8fddad8
(Your SHA may vary.) Reference is not a tree?
Dangling Commits
When you clone a repository (from now on: repo for short) as submodules within a super-repo, you can initialise and update any submodules recursively using
git submodule update --init --recursive
After doing so however, your submodules will not sit on a branch. Verify this using
git submodule foreach --recursive git branch
You will notice that all the submodules report “no branch.” They all sit on a ‘detached head’ corresponding to the commit for the branch you originally added as a submodule. (Following it so far?)
So here is the problem: commit any changes to the submodules and you commit to a detached head. When you push the submodule to its origin, Git reports “everything up-to-date” because it pushes the master by default. “Everything up-to-date” refers to the master and not to the detached head. This can easily catch you out, and doubly so if you use a GUI tool where you might not so easily notice the lack of ‘pushing noise’ so to speak.
Prevention Better Than Cure
What to do? When pushing commits within submodules, make sure you move the submodule to a branch, e.g.
git submodule foreach --recursive git checkout master
moves all repos to their master branch for the submodules and sub-submodules recursively. Do this before committing if you can remember.
Reattaching Heads
If you forget, you can conveniently verify that you have dangling commits by checking out the master branch within your submodule. Git warns you about ‘leaving commits behind.’ You can also verify the dangling commit using
git submodule foreach --recursive git fsck --no-reflog
which outputs things like:
dangling commit d001e26744dab4b92014a77a4552a535b8fddad8
(Of course, your SHA may vary again.)
The fix for detached heads temporarily adds a branch, merges it with master and discards the temporary branch. Use the following Git command sequence within your detached head.
git branch detached
git checkout master
git merge detached
git branch -d detached
You can use any name for the detached branch. Hereafter, you can happily push your formerly orphaned changes.