GitPushing Tags to Remote

Pushing Tags to Remote

One of the most common surprises for Git newcomers is that tags are not automatically pushed when you run git push. This is an intentional design choice: tags are often used as local bookmarks and temporary markers that you never intend to share. You must push tags explicitly. This page covers every push option, how to fetch tags from a remote, and what to be careful about.

Why tags are not pushed automatically
  • Many developers use tags as local scratchpad markers (e.g., git tag pre-merge) that should never reach the remote.

  • Automatic tag pushing could accidentally expose internal milestone names or build identifiers.

  • Explicit control keeps the remote tag namespace clean — only intentional, well-named tags get published.

  • This design is consistent with Git's general philosophy: local operations are safe by default; sharing requires explicit action.

Push a single tag

Push one specific tag to the remote

Bash
# Push just one tag
git push origin v1.0.0
# Enumerating objects: 1, done.
# Counting objects: 100% (1/1), done.
# Writing objects: 100% (1/1), 175 bytes | 175.00 KiB/s, done.
# Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
# To github.com:alice/myproject.git
#  * [new tag]         v1.0.0 -> v1.0.0

# Verify it appears on the remote
git ls-remote --tags origin
# 3f8a2c9...  refs/tags/v1.0.0
# a4b9c3e...  refs/tags/v1.0.0^{}  (dereferenced commit, annotated only)
Push all local tags at once
--tags pushes everything, including lightweight tags
Using `--tags` pushes every local tag that the remote does not already have — including lightweight tags that may have been intended only as local bookmarks. Review your local tag list before using this option in a shared repository.

Push all tags

Bash
git push origin --tags
# Enumerating objects: 3, done.
# Counting objects: 100% (3/3), done.
# Delta compression using up to 8 threads
# Compressing objects: 100% (3/3), done.
# Writing objects: 100% (3/3), 463 bytes | 463.00 KiB/s, done.
# Total 3 (delta 0), reused 0 (delta 0), pack-reused 0
# To github.com:alice/myproject.git
#  * [new tag]         v0.9.0 -> v0.9.0
#  * [new tag]         v1.0.0 -> v1.0.0
#  * [new tag]         v1.1.0 -> v1.1.0
Push only annotated tags (recommended)

The --follow-tags flag is the safest choice for most workflows. It pushes commits and annotated tags that are reachable from the commits being pushed, but skips lightweight tags. This means only your "official" annotated release tags go to the remote.

Push commits and annotated tags together

Bash
# Push the current branch AND any reachable annotated tags
git push --follow-tags origin main
# Counting objects: 5, done.
# Delta compression ...
# To github.com:alice/myproject.git
#    abc1234..7e4f1a2  main -> main
#  * [new tag]         v1.0.0 -> v1.0.0

# Note: v1.0.0 was annotated; lightweight tags are NOT pushed
Make --follow-tags the default push behavior
Set this globally so every `git push` automatically includes reachable annotated tags: `git config --global push.followTags true`
Comparing the three push options

Option

Command

What gets pushed

When to use

Single tag

git push origin v1.0.0

Exactly one named tag

Publishing a specific release

All tags

git push origin --tags

Every local tag the remote lacks

Bulk sync; be careful with lightweights

Follow tags

git push --follow-tags

Commits + reachable annotated tags only

Standard release workflow — recommended default

Push tags to a non-origin remote

Push a tag to a specific remote (not origin)

Bash
# List remotes first
git remote -v
# origin    https://github.com/alice/myproject.git (fetch)
# upstream  https://github.com/org/myproject.git (fetch)

# Push a tag to upstream
git push upstream v1.0.0

# Push all annotated tags to upstream
git push --follow-tags upstream main
Fetch tags from a remote

Tags are fetched automatically during a normal git fetch if the tags are reachable from commits being fetched. However, if tags were added to the remote without associated commit fetching (e.g., someone re-tagged an old commit), you may need to explicitly fetch tags.

Fetch tags from remote

Bash
# Fetch all tags from the remote (plus any missing commits they point to)
git fetch --tags

# Output example:
# From github.com:alice/myproject
#  * [new tag]         v1.1.0     -> v1.1.0
#  * [new tag]         v1.2.0     -> v1.2.0

# Fetch tags from a specific remote
git fetch origin --tags

# Normal fetch also grabs tags reachable from fetched commits:
git fetch origin
# (tags pointing to fetched commits are downloaded automatically)
git pull vs git fetch for tags
`git pull` runs `git fetch` followed by `git merge`. Tags fetched during the fetch phase are stored locally. You do not need to do anything extra to have them available after a pull.
Checking which tags exist on a remote

Inspect remote tags without fetching

Bash
# List all tags on the remote
git ls-remote --tags origin
# From github.com:alice/myproject.git
# 3f8a2c9d...  refs/tags/v1.0.0
# a4b9c3e2...  refs/tags/v1.0.0^{}
# 7e4f1a2c...  refs/tags/v1.1.0
# b5c0d4f3...  refs/tags/v1.1.0^{}

# The ^{} lines are dereferenced (tag object → commit) for annotated tags

# Check if a specific tag exists on remote
git ls-remote --tags origin v2.0.0
# (empty output = tag does not exist on remote)
Warning: pushing tags others have already fetched
Force-pushing a tag breaks other developers' local copies
If you have already pushed `v1.0.0` and other developers have fetched it, then you force-push a different commit to `v1.0.0`, those developers will still have the old tag pointing to the old commit. Git will not automatically update their local tags. They must manually delete and re-fetch the tag: `git tag -d v1.0.0 && git fetch --tags` This inconsistency can cause hard-to-debug build and deployment problems. Treat pushed release tags as immutable.

Force-push a corrected tag (use only when necessary)

Bash
# First: move the local tag to the correct commit
git tag -f -a v1.0.0 <correct-commit-sha> -m "Release 1.0.0 (corrected)"

# Force-push (requires remote to allow force-push on tags)
git push origin --force v1.0.0
# To github.com:alice/myproject.git
#  + 3f8a2c9...7e4f1a2 v1.0.0 -> v1.0.0 (forced update)

# Communicate to team:
# "Please run: git tag -d v1.0.0 && git fetch --tags"
Typical release workflow with tag pushing

End-to-end release tagging workflow

Bash
# 1. Ensure main is up to date
git switch main
git pull origin main

# 2. Run final checks (tests, linting, build)
npm test && npm run build

# 3. Create the annotated release tag
git tag -a v2.0.0 -m "Release 2.0.0

Changes in this release:
- New GraphQL API layer
- React 18 upgrade
- Removed deprecated endpoints
- Performance: 50% reduction in bundle size"

# 4. Push the commit and the tag together
git push --follow-tags origin main
# (or: git push origin main && git push origin v2.0.0)

# 5. Verify on remote
git ls-remote --tags origin v2.0.0
# a4b9c3e2...  refs/tags/v2.0.0