Git
Programming
Version Control
Git Branch
Code Commit

How can I move a tag on a git branch to a different commit?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Delete the old tag and recreate it on the target commit, then force-push to update the remote. For a lightweight tag, use git tag -f to overwrite in one step. For an annotated tag, delete and recreate to preserve a clean tagger timestamp and message.

bash
1# One-step force move (lightweight or annotated)
2git tag -f v1.2.3 <new-commit-hash>
3git push --force origin v1.2.3
4
5# Or: delete and recreate (preferred for annotated tags)
6git tag -d v1.2.3
7git tag -a v1.2.3 <new-commit-hash> -m "Release v1.2.3"
8git push --delete origin v1.2.3
9git push origin v1.2.3

Both approaches produce the same end result, but the delete-and-recreate method gives you a fresh tagger date and message, which matters for release tags that are displayed publicly.

Lightweight vs Annotated Tags

Git supports two types of tags, and the distinction affects how you should handle the move.

Lightweight tags are simple pointers to a commit, similar to a branch that never moves. They store no metadata.

bash
# Create a lightweight tag
git tag v1.0.0 abc1234

Annotated tags are full Git objects with a tagger name, email, date, and message. They can be GPG-signed. These are the standard for release tagging.

bash
# Create an annotated tag
git tag -a v1.0.0 abc1234 -m "Initial stable release"

When moving a tag, annotated tags deserve more care because the original tagger metadata does not automatically transfer. Using git tag -f -a creates a new annotation with the current timestamp.

Method 1: Force-Update the Tag

The quickest approach uses the -f (force) flag to overwrite the existing tag:

bash
1# Move a lightweight tag to a different commit
2git tag -f v1.2.3 def5678
3
4# Move an annotated tag (creates new annotation)
5git tag -f -a v1.2.3 def5678 -m "Release v1.2.3 (re-tagged)"
6
7# Push the updated tag to the remote
8git push --force origin v1.2.3

The --force on git push is necessary because the remote already has a ref named v1.2.3 pointing to a different object. Without --force, Git rejects the push.

Method 2: Delete and Recreate

This approach is more explicit and is preferred in team environments because each step is visible and reversible:

bash
1# Step 1: Delete the local tag
2git tag -d v1.2.3
3
4# Step 2: Delete the remote tag
5git push --delete origin v1.2.3
6
7# Step 3: Create the tag on the new commit
8git tag -a v1.2.3 def5678 -m "Release v1.2.3"
9
10# Step 4: Push the new tag
11git push origin v1.2.3

Finding the Target Commit Hash

If you do not already know the commit hash, find it with git log:

bash
1# Show recent commits with short hashes
2git log --oneline -10
3
4# Find the commit that a tag currently points to
5git rev-parse v1.2.3
6
7# Show full details of the tagged commit
8git show v1.2.3

Updating Tags on the Remote

When a tag has been pushed to a remote (GitHub, GitLab, Bitbucket), other developers and CI pipelines may already reference it. Moving a remote tag requires force-pushing or a delete-then-push sequence.

Force-Push a Single Tag

bash
git push --force origin v1.2.3

Force-Push All Tags

bash
# Push all local tags, overwriting any that differ on the remote
git push --force --tags

Use --force --tags with caution. It overwrites every tag on the remote that differs from your local state, which can affect tags you did not intend to change.

Updating Specific Remote Refs

For more surgical updates, use the refspec syntax:

bash
1# Delete the remote tag using refspec
2git push origin :refs/tags/v1.2.3
3
4# Push the updated tag
5git push origin refs/tags/v1.2.3

What Happens on GitHub and GitLab

On GitHub, tags are tied to releases. Moving a tag does not automatically update the associated release. The release still exists but now points to the new commit. If the release contains auto-generated changelogs or asset URLs that reference the old commit, those may become stale. You may need to edit the release manually.

On GitLab, tags are similarly used as release anchors. Moving a tag updates the tag reference, but existing pipelines triggered by the old tag's commit are not re-triggered unless you explicitly re-run them.

Comparison of Methods

MethodCommandWhen to Use
Force-updategit tag -f <tag> <commit>Quick local fix, solo development
Delete and recreategit tag -d + git tag -aTeam environments, annotated tags
Force-pushgit push --force origin <tag>Updating a single remote tag
Refspec delete + pushgit push origin :refs/tags/<tag>Precise control over remote refs

Notifying Your Team

Moving a tag that others have already fetched creates a discrepancy. Their local copy still points to the old commit. Team members need to update their local tags:

bash
1# Delete the stale local tag and re-fetch
2git tag -d v1.2.3
3git fetch origin tag v1.2.3
4
5# Or: force-fetch all tags (overwrites local tags that differ)
6git fetch --tags --force

Without --force, git fetch --tags does not overwrite existing local tags. This is a deliberate safety measure by Git, which means team members must explicitly opt in to the update.

Automating Tag Verification

After moving a tag, verify that both local and remote point to the same commit:

bash
1# Local tag target
2git rev-parse v1.2.3
3
4# Remote tag target
5git ls-remote --tags origin v1.2.3

Both commands should show the same commit hash. If they differ, the push did not go through or a fetch is needed.

Common Pitfalls

Forgetting --force when pushing an updated tag. Git refuses to update a remote tag that already exists unless you force it. The push will succeed silently (exit code 0) but the tag will not be updated. Always check the push output for rejection messages.

Not notifying the team after moving a remote tag. Other developers' local clones still have the old tag. Builds, deployments, and rollback scripts that reference the tag by name will use the stale commit until they run git fetch --tags --force.

Using git push --force --tags when you only want to move one tag. This overwrites all differing tags on the remote, which can accidentally move tags you did not intend to change. Push specific tags by name when possible.

Moving a tag that CI/CD uses as a deployment trigger. Many CI systems trigger pipelines on tag creation events. Moving a tag may or may not re-trigger the pipeline depending on the CI provider. GitHub Actions, for example, does not fire a new push event for a force-updated tag.

Losing the original annotation. Force-updating an annotated tag with git tag -f (without -a and -m) converts it to a lightweight tag. Always include -a and -m when force-updating annotated tags to preserve the annotation.

Summary

  • Use git tag -f <tagname> <commit> for a quick local move, or delete and recreate for a cleaner audit trail.
  • Always force-push (git push --force origin <tagname>) after moving a tag that has been pushed to a remote.
  • Notify your team so they can run git fetch --tags --force to update their local copies.
  • Verify the move by comparing git rev-parse <tagname> locally with git ls-remote --tags origin <tagname>.
  • For annotated tags, always include -a and -m to preserve the tag metadata.

Course illustration
Course illustration

All Rights Reserved.