Can I ask git what it thinks which tags are on a git remote last time git checked without an online connection?
Does git keep track on to which remotes it already pushed tags?
With git branches it's possible by using git diff --stat branch-name remote-name/branch-name
. If the branch does not exist on the remote, git diff will point that out by showing an error and non-zero exit code.
git ls-remote origin tag
does what I want but it requires an online connection which I want to avoid.
What I would expect to work is git diff --stat tag-name remote-name/tag-name
but seems like local tags cannot be easily compared with remote tags that way.
Purpose: With a lot of git repositories, automated, scripted, checking which tags have already been pushed, avoiding unnecessary internet connections which are slow and avoid bumping into rate limits.
CodePudding user response:
Can I ask git what it thinks which tags are on a git remote last time git checked without an online connection?
Not without advanced preparation. With the proper advanced preparation, yes.
Does git keep track on to which remotes it already pushed tags?
No.
There's a couple of technical issues here:
- What you're actually pushing, with
git push
, are commits. - But at the end of
git push
, you also ask the other Git software to create, delete, or update certain names in its repository.
Tag names are simply names ("refs") that begin with the magic sequence refs/tags/
; the remainder of the name is the tag name. However, tag names have special status in Git, similar to the way that branch and remote-tracking names have special statuses:
A branch name (one beginning with
refs/heads/
) is constrained to point to a commit only, never to any other object. When a branch name is transferred viagit fetch
it generally1 becomes a remote-tracking name instead, i.e., a name beginning withrefs/remotes/
and including the name of the remote and another slash.A tag name is auto-fetched under certain conditions.
The
git push
operation turns into a kind of operation that, on the receiving end, enforces some checking by default:branch names must move in a fast-forward fashion; and
tag names must not move at all
(except that in Git versions before 1.8.2, Git accidentally used the branch rules for tag names here).
A local repository doing a
git push
to a remote repository via remote-name will, on a success status, update its remote-tracking name: that is, pushing from your laptop to GitHub, ifmain
is successful, your laptop updates yourorigin/main
remote-tracking names.
So we see here that branch names have all kinds of special weirdness applied, with this remote-tracking name stuff. This keeps the branch names specific to any one given repository. My branch names, in my clone of some GitHub repo, are mine, yours are yours, and we use GitHub to synchronize.
Tag names don't get this much weirdness (and tag names often point to tag objects rather than to commit objects), but tag names are generally considered universal substitutes for one particular commit hash ID: if I create tag v1.2
at commit a123456
and push to a shared repository on GitHub, GitHub accept this if and only if v1.2
is totally new to them, and from now until forever, everyone agrees that v1.2
is just a human-readable name for a123456
. Humans use v1.2
, Git uses a123456
, and we're all always talking about the same commit.
Since tags are only ever created, and never move, if you have tag v1.2, and I have tag v1.2, on some repository that has sharing going on, these had better refer to the exact same commit hash ID, and therefore there's no need to synchronize our v1.2
tags.
Of course, all synchronization happens only when we can talk, on line, from my repository to your repository or to some shared third repository (e.g., on GitHub). So it's entirely possible for me to create a v1.2
by accident at the same time you're creating a v1.2
for a different commit. Git just sort of assumes that this Doesn't Happen. If it does happen, you're pretty much on your own.
With all that understood, we can now tackle whatever your real underlying issues is by noting the following:
git push
andgit fetch
transfer references through refspecs.- refspecs allow the creation of maps. This is how remote-tracking names work.
The default refspec when I fetch from origin
is refs/heads/*:refs/remotes/origin/*
. This is what makes remote-tracking names appear in my repository. If I add --tags
, Git adds refs/tags/*:refs/tags/*
. This is what causes tags not to have the equivalent of remote-tracking. (The special weirdness is that without an explicit --no-tags
, if I fetch commit b789abc
and that commit is tagged v2.2
my Git will create refs/tags/v2.2
as well. This special code has undergone numerous revisions in Git over many years and suffers from some degree of Git version drift, but the main effects are pretty consistent.)
The advanced preparation you can make is to add to your default fetch refspecs so that git fetch origin
will force-update refs/rtags/origin/*
or some other similar name-space based on the refs/tags/*
names found on origin
. There won't be any magic push-like opportunistic update, but git fetch
will pick these up.
To make this happen you just add:
fetch = refs/tags/*:refs/rtags/origin/*
to the git config
lines for your origin
remote; then git fetch origin
will update your remote-tag-tracking-names.
Purpose: With a lot of git repositories, automated, scripted, checking which tags have already been pushed ...
The above might work, but it might be simpler to keep track, using some script, of a list of remotes and/or URLs to which you've successfully run git push <remote> <tag>
from your automated scripts. You could store these in your .git/config
using git config
. You choose the format; use something that makes your job easy for yourself. If some remote doesn't list some tag, you can run git push
to it, or git ls-remote
to it, to try to push the tag and/or to check, and then update the status using git config
to update the .git/config
file.
(You don't have to store this in .git/config
either, and if you're doing other Git things at the same time, you might want to use git config --file $GIT_DIR/tag-tracking
, for instance, so that you don't run into lock contention.)
1Note that mirror clones have a different default refspec, such that there are no remote-tracking names.