Reputation: 3470
due to company policies, we have very little tools in the toolbelt. I'm stuck with using Git for a number of things where another tool would do the work better, faster.
I need a specific dll in my project and the fastest way to get it was found to add a second repository as a submodule. I'm in the testing department, so I don't need the latest dll, nor the stable branch of the dll. I need a specific version of said dll, and I need fine control over it.
Luckily, I can use tags to navigate to the specific version. By git checkout <tag>
I send the submodule in detached head and I can freeze the information with a commit of the superproject. Running git status
doesn't explicitly tell me where I am with the submodule.
How can I explicitly get the information regarding tag the submodule is aligned within the detached head from the superproject? Ideally, I would like to have this information stored in plain text, for instance in the .gitsubmodules
file. I want to be positive that the submodule is aligned with a given tag (or commit if the tag is not available) and I want to have this testable automatically.
Upvotes: 2
Views: 4751
Reputation: 488183
Let's note a few basic facts:
git checkout hash
(or git switch --detach hash
), by hash ID, to get a detached HEAD in the submodule Git repository. The hash ID used for this operation is the one recorded in a commit in the superproject. (That's the hash ID you froze by committing.)In other words, Git really doesn't use a tag to achieve this, even though you did use one to get the state that you then froze into a commit. So...
How can I explicitly get the information regarding tag the submodule is aligned within the detached head from the superproject?
... there are two different ways to deal with this:
One way is to record, in the superproject—in something that Git itself won't use, because Git is going to use the hash ID in the commit—the tag name.
The other way is to use the hash ID anyway, and then enter the submodule and see if there are some tag(s) that correspond to that hash ID.
These are the two overall strategies. They have different consequences if someone chooses to do something that's technically-possible but recommended-against. In particular, suppose someone who controls the submodule repository alters the tags in some way. A tag like v2.1
used to name commit a123456
bu now names commit b789abc
. Perhaps a123456
now has a tag 2.1alpha
instead. Then:
Asking the submodule what tag(s) do you have for a123456
comes up with a new and different answer (2.1alpha
), but you—or rather, your superproject Git, when you do git submodule update
—will still check out and thus use a123456
(the hash ID you froze earlier by committing).
Asking the submodule what hash ID do you have for v2.1
comes up with a new and different answer, b789abc
.
If nobody does this frowned-upon thing, the question won't ever arise in the first place. But you'll have to decide for yourself how it should be handled, if you think it might happen.
Now, as for recording items in .gitmodules
, there is a way to do this. The problem is that what Git will record is not a tag name but rather a branch name:
git submodule set-branch -b <name> submodule
will add:
branch = <name>
to the entry in the .gitmodules
file. The name
you use here need not actually exist! For instance, I just did:
git submodule set-branch -b v1.0 scripts
in a temporary test repo to which I added a submodule. There is no v1.0
, neither as branch nor tag, anywhere in the actual submodule. Git completely ignores this name unless you run particular varieties of git submodule update
. I won't go into all of them, but for instance, consider this:
$ git submodule update --remote
fatal: Needed a single revision
Unable to find current origin/v1.0 revision in submodule path 'scripts'
Note that Git assumed v1.0
was a branch name here, and added origin/
in front of it; there is no way to tell it not to make this assumption.
The .gitmodules
file is simply a Git configuration file, though, and can therefore be manipulated directly by git config
. You can read or write the branch
entry, or invent your own entries. There is a bit of danger to inventing your own: some future version of Git might invent the same thing, but use it in some different way. Still, let's see what this does:
$ git config -f .gitmodules submodule.scripts.tag v1.0
$ git diff
diff --git a/.gitmodules b/.gitmodules
index 32170c2..f8cb154 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,5 @@
[submodule "scripts"]
path = scripts
url = ssh://[email protected]/chris3torek/scripts
+ branch = v1.0
+ tag = v1.0
The branch =
is left over from my earlier git submodule
command—let's remove it now, just to clean up:
$ git submodule set-branch --default scripts
$ git diff
diff --git a/.gitmodules b/.gitmodules
index 32170c2..71dcdd2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,3 +1,4 @@
[submodule "scripts"]
path = scripts
url = ssh://[email protected]/chris3torek/scripts
+ tag = v1.0
You can now retrieve this tag with git config -f .gitmodules --get
:
$ git config -f .gitmodules --get submodule.scripts.tag
v1.0
The git config
command is quite suitable for scripting; consult the documentation for more. See also git submodule foreach
in the git submodule
documentation for a way to automatically run commands for each submodule.
(You don't have to store this tag information directly in .gitmodules
, of course; any file will work, and that can insulate you from potential future Git changes.)
To turn a hash ID into a tag name, consider using git tag --points-at
(assuming your Git version is at least 1.7.10):
git tag --points-at <hash>
will print the name(s) of the tag(s) that select that particular commit. Note that you must run this Git command in the submodule, after using the superproject to find the hash ID.1
Last, to automate checking of submodules, consider the git submodule status
and/or git submodule summary
commands, which are also described in the git submodule
documentation.
1One way to use the superproject to find the submodule's hash ID is to let Git run git checkout
/ git switch
in the submodule, by running git submodule update
in the superproject. But if you just want to read the correct hash ID, use git rev-parse
. The submodule hash ID is stored in each commit as a gitlink entry, which is then copied to Git's index when checking out the superproject commit. So you can, for instance, use:
git rev-parse <commit>:path/to/submodule
in the superproject to see the hash ID in the given commit, or:
git rev-parse :path/to/submodule
to see the hash ID currently stored in the superproject Git's index.
Upvotes: 5