How to see which commit a git submodule points at
Sure; there are several ways:
git ls-tree <commit> <relative path to submodule>
git ls-tree <commit>:<absolute path to parent of submodule>
git ls-tree <commit>:./<relative path to parent of submodule>
git ls-tree -r <commit> <relative path to parent of submodule>
The first is easiest by far for checking a single submodule.
The second/third is easiest if you want to check several submodules contained in the same directory, but you don't want to recurse into any other subdirectories that are not submodules. (The :
syntax uses the top level of the repository as its reference point by default, rather than your current working directory, so be sure to include the ./
prefix if you're way in the depths of your repo right now.)
The fourth is easiest if you have a lot of submodules, and only submodules, in a single directory.
Examples:
git ls-tree HEAD src/thirdparty/libfoo
shows the submodule and nothing else.git ls-tree HEAD:src/thirdparty
gives you everything directly belowsrc/thirdparty
, including your submodulesrc/thirdparty/libfoo
.git ls-tree -r HEAD src/thirdparty
gives you everything directly belowsrc/thirdparty
, including your submodulesrc/thirdparty/libfoo
, but will also recurse intosrc/thirdparty/docs
which is actually a regular directory in your repo.git ls-tree -r HEAD
lists absolutely everything in your repo, including submodules.
Submodules will show up as type commit
(as opposed to the usual blob
or tree
).
A more straight-forward command would be:
git submodule status
As the other answers explain, you can use two commands:
git submodule status
, orgit ls-tree HEAD
, taking only the lines where the second column iscommit
(if you have awk you can usegit ls-tree HEAD | awk '$2 == "commit"'
).
However, these commands give different results!
What's the difference?
git submodule status
always reports the current status (as the name suggests), that is, the commit that is currently checked-out. The hash that you see here is the same that you'd see by going into the submodule's directory1 and checking the latest commit (withgit log
orgit rev-parse HEAD
)git ls-tree HEAD
shows the target status, which is not necessarily the current one. If you want to update your submodules so that they correspond to the specified version, you have to usegit submodule update
.
What can cause the current and target status to differ?
The typical situation in which they differ is when you git checkout
another branch/tag/commit, or you use git pull
to update your current branch. Both these commands will cause HEAD
to be updated to the corresponding commit. Now, if this commit specifies that your submodule has to use a different version, git submodule status
will still show the old one, but the target shown by git ls-tree HEAD
will already be the new one.
Is there a simpler way of noticing that they are out of sync?
Check the output of git submodule status
. As the manual explains, if there's a +
before the hash, it means that the currently checked-out version and the target one are different.
How do I bring them back in sync?
By running git submodule update
: the new submodule will be loaded, and both commands will indicate the same commit.
Example
For example, let's say that in our repo we have a submodule called base
.
The output of git submodule status
is (notice the +
):
+059ca6c4940813aa956e8668cb0af27efa189b22 base (release-1.2)
And the output of git ls-tree HEAD
is
160000 commit fbc447ef9468def36cf4089094d6960cc51618b3 base
As we can see, the hashes are different. In fact, the +
had already informed us.
Now, if we type git submodule update
, it says:
Submodule path 'base': checked out 'fbc447ef9468def36cf4089094d6960cc51618b3'
And now all the commands we can use (git submodule status
, git ls-tree HEAD
, and git log
from inside base
) indicate fbc447ef9468def36cf4089094d6960cc51618b3
, and there's no +
in front of it in the output of git submodule status
. If we run git submodule update
again, nothing happens, as everything is already up to date, and there isn't even any output.
1: You have to be careful when you check the commit of a submodule, because it's tricky: to find the last commit that was made in submodule base
you can't use git log base
, you have to enter that directory (cd base
) and then run git log
from there. The reason is that the first command lists the commits of the "main" repository which set a new version of the submodule, and these commits are completely independent from those that were made inside the submodule.