How to see which commit a git submodule points at
Asked Answered
C

6

113

As far as I understand, if you add a submodule in git, then the main repo contains a pointer to a certain commit of the submodule.

Is there any way to see to which commit the main repo points at for a specific submodule, without checking out the code of the submodule?

Chiropteran answered 18/12, 2013 at 10:6 Comment(0)
M
25

Sure; there are several ways:

  1. git ls-tree <commit> <relative path to submodule>
  2. git ls-tree <commit>:<absolute path to parent of submodule>
  3. git ls-tree <commit>:./<relative path to parent of submodule>
  4. 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 below src/thirdparty, including your submodule src/thirdparty/libfoo.
  • git ls-tree -r HEAD src/thirdparty gives you everything directly below src/thirdparty, including your submodule src/thirdparty/libfoo, but will also recurse into src/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).

Milk answered 18/12, 2013 at 10:48 Comment(7)
This doesn't seem to work (anymore?). I did git ls-tree HEAD:some/path (where some/path is a submodule) and git outputs fatal: not a tree object. I'm using git 2.25.0.Sisyphean
@Sisyphean you need to use some, then. I guess my example was a little hard to read, but the idea is to list the parent of the submodule, not the submodule itself.Potboy
@Sisyphean git ls-tree -r HEAD | grep commit a little work-aroundProductive
git ls-tree master <path> worked for me with git 2.25.0Stelu
@mic, as SColvin says - i think it no longer requires the colonWhorled
The fatal: not a tree object is due to the syntax, you need to remove the colon : The correct syntax is: git ls-tree HEAD src/thirdparty Like @MzA answeredNought
@YuriNatividade the syntax with the : is correct, too; it just doesn't work if you put in the full path to the submodule. As my answer says, with this syntax you need to pass the parent of the submodule. For instance, if you have submodules below src/thirdparty, you have to pass HEAD:src/thirdparty and HEAD:src/thirdparty/foo will not work (because foo is a gitlink rather than a tree object). I may not have been aware of the colon-less syntax when writing my answer.Potboy
L
125

As the other answers explain, you can use two commands:

  • git submodule status, or
  • git ls-tree HEAD, taking only the lines where the second column is commit (if you have awk you can use git 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 (with git log or git 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 use git 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.

Lengthen answered 17/1, 2019 at 15:19 Comment(2)
instead of using awk to get the commit hash you can use git ls-tree --object-only HEADSack
At least with git >= 2.36.0 I just noticed...Sack
A
29

A more straight-forward command would be:

git submodule status

Andrade answered 25/7, 2018 at 17:30 Comment(4)
This is great, just takes long if you have several submodulesTorso
@Torso How many should I have to consider them "several"? 10? Just curious.Reproachful
@Reproachful it of course also deppends on the size of each submodule and what kin dof computger you have I guess. In my case, I have 15 submodules of arround 100MB (~7000files). And it may take up to 2 minutes.Torso
Umm, I didn't consider repo size, but it makes sense.Reproachful
M
25

Sure; there are several ways:

  1. git ls-tree <commit> <relative path to submodule>
  2. git ls-tree <commit>:<absolute path to parent of submodule>
  3. git ls-tree <commit>:./<relative path to parent of submodule>
  4. 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 below src/thirdparty, including your submodule src/thirdparty/libfoo.
  • git ls-tree -r HEAD src/thirdparty gives you everything directly below src/thirdparty, including your submodule src/thirdparty/libfoo, but will also recurse into src/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).

Milk answered 18/12, 2013 at 10:48 Comment(7)
This doesn't seem to work (anymore?). I did git ls-tree HEAD:some/path (where some/path is a submodule) and git outputs fatal: not a tree object. I'm using git 2.25.0.Sisyphean
@Sisyphean you need to use some, then. I guess my example was a little hard to read, but the idea is to list the parent of the submodule, not the submodule itself.Potboy
@Sisyphean git ls-tree -r HEAD | grep commit a little work-aroundProductive
git ls-tree master <path> worked for me with git 2.25.0Stelu
@mic, as SColvin says - i think it no longer requires the colonWhorled
The fatal: not a tree object is due to the syntax, you need to remove the colon : The correct syntax is: git ls-tree HEAD src/thirdparty Like @MzA answeredNought
@YuriNatividade the syntax with the : is correct, too; it just doesn't work if you put in the full path to the submodule. As my answer says, with this syntax you need to pass the parent of the submodule. For instance, if you have submodules below src/thirdparty, you have to pass HEAD:src/thirdparty and HEAD:src/thirdparty/foo will not work (because foo is a gitlink rather than a tree object). I may not have been aware of the colon-less syntax when writing my answer.Potboy
S
17

Pardon for lurking, but I believe the answer to the original question is likely to be:

git submodule status --cached

"git submodule status" tells you which commit is checked out "--cached" tells you which commit a "git submodule update" would checkout.

I'm not sure when --cached was added to git.

Sip answered 28/3, 2022 at 2:35 Comment(0)
D
3

The accepted answer returns a 4-column output which contains the commit that a submodule gitlink points at. This is useful to verify that the pointed-to path is indeed a submodule, for example when that input user-provided in a script. To do that, verify that the second column is equal to "commit", e.g. using awk.

However, if this check is not required, a simpler output that has only the submodule commit and nothing else by one of

git rev-parse HEAD:./<relative-path>
git rev-parse HEAD:<repository-path>

where <relative-path> is the path to the submodule relative to the shell working directory and <repository-path> is the submodule path relative to the repository root. Instead of HEAD, any branch name, commit hash or other valid revision description (see man git rev-parse) can be used.

Dairymaid answered 11/10, 2023 at 9:38 Comment(0)
L
0
git ls-tree -r <submodule-commitID-of-interest> <submodule-path> | awk '{print $3}'

Here a bash line to print submodule's repo commit IDs the last 10 submodule's commit IDs are pointing to:

git --no-pager log -10 --pretty="%H" <submodule-path> | while read i; do git ls-tree -r $I <submodule-path> | awk '{print $3}'; done
Lecherous answered 22/7, 2021 at 13:42 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.