2

I did the following just now:

// make change
// git add .
// git commit -m "2.0.0"
// git tag -a v2.0.0 <sha-of-above-commit>
// git push --tags

However, I forgot to push the actual commit, and now my tag isn't showing up (presumably because I didn't actually push the commit that I tagged.)

I then re-cloned the repo and ran git tag, and saw my tag there.

However, my commit wasn't listed under git log. So I remade my change, and ran git push. This ran successfully. However, git push --tags failed, as it said the tag already existed on the remote.

Any solution to this? I tried to delete the remote v2.0.0 tag, but got a (hook declined) error.

4
  • “presumably because I didn't actually push the commit that I tagged” – that shouldn’t matter. You can’t push dangling references. Are you sure the remote tag actually exists? Commented Aug 13, 2016 at 0:23
  • How are you trying to delete the tag, and does your server disallow deleting tags in general? (Also, if git log v2.0.0 shows your commit and it's the commit you want, you can just push that to whichever branch.) Commented Aug 13, 2016 at 0:27
  • 1
    Tried to delete the tag with git push --delete origin v2.0.0. Not sure if my server disallows deleting tags in general, but probably? Commented Aug 13, 2016 at 0:28
  • 1
    Tag is also a ref. When you push a tag, the commit that it points to and the commit's parents that exist only in the local are all pushed. So after the clone, it's expected to see that commit via git log v2.0.0. Merely git log is not the right command since it lists the log of currently checked out branch, most probably master. If v2.0.0 and master are diverged, you cannot see the tagged commit in master's log. Commented Aug 13, 2016 at 0:40

1 Answer 1

2

TL;DR: git push origin master

ElpieKay's comment has the correct answer: your git push pushed the commit and the tag, but no branch in the other (remote) Git repository contains the commit. That is, in terms of your own Git commit graph, you have—here I'm going to guess that the branch in question is master— this:

...--o     <-- origin/master
      \
       o   <-- master, tag:v2.0.0

You then sent, to the Git on origin, the commit itself (as required in order to send the tag v2.0.0), along with a request for it to create a tag named v2.0.0 pointing to the new commit—which it did, giving it the graph:

...--o     <-- master
      \
       o   <-- tag:v2.0.0

Anyone who now clones the repository from the other Git, including yourself, will see the tag in git tag output, but will not see the commit on branch master, because it's not on branch master. The only link to the new commit is the tag.

The solution is simple: do a git push to have your Git ask their (origin's) Git to set their branch master to point to the new commit. For instance, if the commit's hash ID is 1234567, this would do the trick:

git push origin 1234567:master

even from your new clone. Or, since the tag v2.0.0 resolves to the correct commit hash:1

git push origin v2.0.0^{commit}:master

The simplest version of this simple fix, though, is to do the push operation from your original development repository, where your master branch points to the same commit as your v2.0.0 tag. In this case you don't need to specify both halves of the refspec argument:

git push origin master

(this does, of course, assume that master on origin has not grown new commits since this point).


1I say "resolves to" and then introduce this funky v2.0.0:^{commit} syntax. What's that all about?

Since v2.0.0 is an annotated tag (made with git tag -a), the name itself actually points to a tag object in the repository. It's the tag object that points to the commit. When I drew the commit graph, I drew the commit graph, not the commit-and-tag graph. :-) So I left this little bit out.

When we go to git push, though, it's probably wise to make sure we ask their Git to set their branch master to point directly to the commit, not to a tag. Branch names are not allowed to point to tags. Perhaps the request will work—perhaps Git will know "branch names are not allowed to point to tags" and will do the appropriate magic to convert the tag object to its target commit-ID—but perhaps not. We can avoid the question entirely by telling our Git to turn any tag name into its target commit.

That is what this funky syntax does. As described in the gitrevisions documentation, name:^{type} tells Git first to resolve the name, then to "peel" that name as needed to find the Git object of the given type.

When you give Git a branch name, the name is guaranteed to resolve to a commit object, because branch names must point to commit objects. Tag names, on the other hand, may point to any Git object at all—commit, tag, tree, or blob. Normally they point only to tag objects or commit objects, but Git won't stop you from making one that points to a blob or tree. I've done this for various experimental purposes in my own repositories. So for a tag, you can use ^{commit} to force it to resolve to a commit. If the name points to an annotated tag object, this follows that tag to its commit. If the name already points to a commit, it does nothing. If the name somehow points to a tree or blob, it produces an error.

You can also use :^{tree} and :^{blob} to verify that a name points to a tree or blob object. Of course if the name is a branch name, it doesn't, but the name part can include a path as well: for instance, master:Documentation names a tree object in branch master in a Git repository for Git itself, and if you were writing a script and needed to be really sure you were getting the hash ID of a tree, you could write:

treehash=$(git rev-parse master:Documentation^{tree}) || die ...

in your script.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.