“tag already exists in the remote" error after rec

2019-01-12 17:14发布

I get the following error after I run the steps below:

To git@provider.com:username/repo-name.git
 ! [rejected]        dev -> dev (already exists)
error: failed to push some refs to 'git@provider.com:username/repo-name.git'
hint: Updates were rejected because the tag already exists in the remote.
  1. Created the repository
  2. Cloned the repo on the local machine.
  3. Modified the README file, commited the changes and pushed the commit.
  4. Created tag dev: git tag dev
  5. Pushed tags: git push --tags
  6. Modified the README file, commited the changes and pushed the commit.
  7. Deleted tag dev , created it again and pushed tags:

    git tag -d dev
    git tag dev
    git push --tags
    

Why is this happening?

I am on Mac. My friends that use Linux (Ubuntu) don't have this problem. I know that I can use git push --tags -f to force the tag update, but this is dangerous (e.g. rewriting a commit made by mistake only in the tag, not in the branch).

7条回答
趁早两清
2楼-- · 2019-01-12 17:27

It's quite simple if you're using SourceTree.

enter image description here Basically you just need to remove and re-add the conflicting tag:

  1. Go to tab Repository -> Tag -> Remove Tag
  2. Select the conflicting tag name
  3. Check Remove tag from all remotes
  4. Press Remove
  5. Create new tag with the same name to the proper commit
  6. Make sure to check Push all tags when pushing your changes to remote
查看更多
做自己的国王
3楼-- · 2019-01-12 17:30

In Mac SourceTree only deselect the Push all tags checkbox:

enter image description here

查看更多
We Are One
4楼-- · 2019-01-12 17:47

It seems that I'm late on this issue and/or it has already been answered, but, what could be done is: (in my case, I had only one tag locally so.. I deleted the old tag and retagged it with:

git tag -d v1.0
git tag -a v1.0 -m "My commit message"

Then:

git push --tags -f

That will update all tags on remote.

Could be dangerous! Use at own risk.

查看更多
看我几分像从前
5楼-- · 2019-01-12 17:48

Edit, 24 Nov 2016: this answer is apparently popular, so I am adding a note here. If you replace a tag on a central server, anyone who has the old tag—any clone of that central-server repository that already has the tag—could retain its old tag. So while this tells you how to do it, be really sure you want to do it. You'll need to get everyone who already has the "wrong" tag to delete their "wrong tag" and replace it with the new "right tag".

Testing in Git 2.10/2.11 shows that retaining the old tag is the default behavior for clients running git fetch, and updating is the default behavior for clients running git fetch --tags.

(Original answer follows.)


When you ask to push tags, git push --tags sends (along with any commits and other objects needed and any other ref updates from the push settings) to the remote an update request of the form new-sha1 refs/tags/name. (Well, it sends however many: one of those for each tag.)

The update request is modified by the remote to add an old-sha1 (or again, one for each tag), then delivered to the pre-receive and/or update hooks (whichever hooks exist on the remote). Those hooks can decide whether to allow or reject the tag create/delete/update.

The old-sha1 value is the all-zeros "null" SHA-1 if the tag is being created. The new-sha1 is the null SHA-1 if the tag is being deleted. Otherwise both SHA-1 values are real, valid values.

Even with no hooks, there's a sort of "built-in hook" that is also run: the remote will refuse to move a tag unless you use the "force" flag (though the "built-in hook" is always OK with both "add" and "delete"). The rejection message you're seeing is coming from this built-in hook. (Incidentally, this same built-in hook also rejects branch updates that are not fast-forwards.)1

But—here's one of the keys to understanding what's going on—the git push step has no idea whether the remote has that tag now, and if so, what SHA-1 value it has. It only says "here's my complete list of tags, along with their SHA-1 values". The remote compares the values and if there are additions and/or changes, runs the hooks on those. (For tags that are the same, it does nothing at all. For tags you don't have that they do, it also does nothing!)

If you delete the tag locally, then push, your push simply does not transfer the tag. The remote assumes no change should be made.

If you delete the tag locally, then create it pointing to a new place, then push, your push transfers the tag, and the remote sees this as a tag-change and rejects the change, unless it's a force-push.

Thus, you have two options:

  • do a force-push, or
  • delete the tag on the remote.

The latter is possible via git push2 even though deleting the tag locally and pushing has no effect. Assuming the name of the remote is origin, and the tag you want it to delete is dev:

git push origin :refs/tags/dev

This asks the remote to delete the tag. The presence or absence of the tag dev in your local repository is irrelevant; this kind of push, with :remoteref as a refspec, is a pure-delete push.

The remote may or may not allow tag deletion (depending on any extra hooks added). If it allows the deletion, then the tag will be gone, and a second git push --tags, when you have a local dev tag pointing to some commit or annotated tag repo object, send your new dev tag. On the remote, dev will now be a newly created tag, so the remote will probably allow the push (again this depends on any extra hooks added).

The force-push is simpler. If you want to be sure not to update anything other than the tag, just tell git push to push only that one refspec:

git push --force origin refs/tags/dev:refs/tags/dev

(note: you don't need --tags if you're explicitly pushing just one tag ref-spec).


1Of course, the reason for this built-in hook is to help enforce the behavior that other users of that same remote-repo expect: that branches are not rewound, and tags do not move. If you force-push, you should let the other users know you are doing this, so that they can correct for it. Note that "tags don't move at all" is newly enforced by Git 1.8.2; previous versions would allow the tag to "move forward" in the commit graph, much like branch names. See the git 1.8.2 release notes.

2It's trivial if you can log in on the remote. Just go to the Git repository there and run git tag -d dev. Note that either way—deleting the tag on the remote, or using git push to delete it—there's a period of time when anyone who accesses the remote will find that the dev tag is missing. (They will continue to have their own old tag, if they already have it, and they might even push their old tag back up before you can push the new one.)

查看更多
来,给爷笑一个
6楼-- · 2019-01-12 17:49

The reason you are getting rejected is that your tag lost sync with the remote version. This is the same behaviour with branches.

sync with the tag from the remote via git pull --rebase <repo_url> +refs/tags/<TAG> and after you sync, you need to manage conflicts. If you have a diftool installed (ex. meld) git mergetool meld use it to sync remote and keep your changes.

The reason you're pulling with --rebase flag is that you want to put your work on top of the remote one so you could avoid other conflicts.

Also, what I don't understand is why would you delete the dev tag and re-create it??? Tags are used for specifying software versions or milestones. Example of git tags v0.1dev, v0.0.1alpha, v2.3-cr(cr - candidate release) and so on..


Another way you can solve this is issue a git reflog and go to the moment you pushed the dev tag on remote. Copy the commit id and git reset --mixed <commmit_id_from_reflog> this way you know your tag was in sync with the remote at the moment you pushed it and no conflicts will arise.

查看更多
我欲成王,谁敢阻挡
7楼-- · 2019-01-12 17:52

If you want to UPDATE a tag, let's say it 1.0.0

  1. git checkout 1.0.0
  2. make your changes
  3. git ci -am 'modify some content'
  4. git tag -f 1.0.0
  5. delete remote tag on github: git push origin --delete 1.0.0
  6. git push origin 1.0.0

DONE

查看更多
登录 后发表回答