Skip to main content

How to Tag a Git Repository with a Version Number from Batch Script

Git tags mark specific commits with meaningful identifiers, most commonly version numbers. Tagging releases in Git creates permanent reference points that link a deployed version back to the exact source code that produced it. Automating this process through Batch Script ensures that tags are created consistently, follow naming conventions, and integrate smoothly into build and release pipelines.

In this guide, we will explore how to create, manage, and push Git tags from a Batch Script, covering lightweight and annotated tags, version validation, and automated release workflows.

Understanding Git Tag Types

TypeCommandContentsUse Case
Lightweightgit tag v1.0.0Just a pointer to a commitQuick bookmarks, temporary markers
Annotatedgit tag -a v1.0.0 -m "msg"Full object with tagger name, date, message, and optional GPG signatureOfficial releases, production deployments
tip

Always use annotated tags for releases. They record who created the tag, when, and why. Lightweight tags are essentially bookmarks with no metadata.

Method 1: Creating a Simple Version Tag

@echo off
setlocal

set "version=1.0.0"

echo Tagging current commit as v%version%...

git tag -a "v%version%" -m "Release version %version%"

if %errorlevel%==0 (
echo [SUCCESS] Tag v%version% created.
echo.
echo Push with: git push origin v%version%
) else (
echo [ERROR] Tagging failed. The tag may already exist.
)

pause

Method 2: Reading Version from File and Tagging

Automatically read the version from a VERSION file:

@echo off
setlocal enabledelayedexpansion

set "version_file=VERSION"

:: Read version
if not exist "%version_file%" (
echo [ERROR] %version_file% not found.
pause
exit /b 1
)

set /p "version=" < "%version_file%"

echo Current version: %version%

:: Check if tag already exists
set "tag_exists="
for /f "delims=" %%T in ('git tag -l "v%version%"') do set "tag_exists=1"
if defined tag_exists (
echo [ERROR] Tag v%version% already exists.
echo Bump the version in %version_file% before tagging.
pause
exit /b 1
)

:: Check for uncommitted changes
set "dirty="
for /f "delims=" %%L in ('git status --porcelain 2^>nul') do set "dirty=1"
if defined dirty (
echo [WARNING] You have uncommitted changes.
set /p "proceed=Tag anyway? (Y/N): "
if /i not "!proceed!"=="Y" (
echo [CANCELLED]
pause
exit /b 0
)
)

:: Create annotated tag
echo Creating tag v%version%...
git tag -a "v%version%" -m "Release %version%"

if %errorlevel%==0 (
echo [SUCCESS] Tag v%version% created.
) else (
echo [ERROR] Failed.
)

pause

Method 3: Tag and Push in One Step

@echo off
setlocal

set "version_file=VERSION"
set "remote=origin"

:: Read version
set /p "version=" < "%version_file%"

echo =============================================
echo RELEASE TAG: v%version%
echo =============================================
echo.

:: Verify clean state
set "dirty="
for /f "delims=" %%L in ('git status --porcelain 2^>nul') do set "dirty=1"
if defined dirty (
echo [WARNING] Working directory is not clean.
git status --short
echo.
)

:: Check tag does not exist
set "tag_exists="
for /f "delims=" %%T in ('git tag -l "v%version%"') do set "tag_exists=1"
if defined tag_exists (
echo [ERROR] Tag v%version% already exists locally.
pause
exit /b 1
)

:: Create tag
echo Creating annotated tag...
git tag -a "v%version%" -m "Release %version% - %date%"

if %errorlevel% neq 0 (
echo [ERROR] Tag creation failed.
pause
exit /b 1
)

:: Push tag
echo Pushing tag to %remote%...
git push "%remote%" "v%version%"

if %errorlevel%==0 (
echo.
echo [SUCCESS] Tag v%version% created and pushed to %remote%.
) else (
echo.
echo [ERROR] Push failed. Check remote connectivity.
)

pause

Method 4: Interactive Release Workflow

A complete release script that bumps the version, commits, tags, and pushes:

@echo off
setlocal enabledelayedexpansion

set "version_file=VERSION"
set "remote=origin"

:: Read current version
if not exist "%version_file%" (
echo [ERROR] %version_file% not found.
pause
exit /b 1
)

set /p "version=" < "%version_file%"

for /f "tokens=1,2,3 delims=." %%A in ("%version%") do (
set "major=%%A"
set "minor=%%B"
set "patch=%%C"
)

:: Pre-compute next values for display
set /a "next_major=major+1"
set /a "next_minor=minor+1"
set /a "next_patch=patch+1"

echo =============================================
echo RELEASE WORKFLOW
echo Current version: %version%
echo =============================================
echo.
echo Bump type:
echo [1] Major (%version% -^> !next_major!.0.0^)
echo [2] Minor (%version% -^> %major%.!next_minor!.0^)
echo [3] Patch (%version% -^> %major%.%minor%.!next_patch!^)
echo [4] Custom
echo [5] Cancel
set /p "bump=Select: "

set "bumped=0"
if "!bump!"=="1" (
set /a major+=1
set "minor=0"
set "patch=0"
set "bumped=1"
)
if "!bump!"=="2" (
set /a minor+=1
set "patch=0"
set "bumped=1"
)
if "!bump!"=="3" (
set /a patch+=1
set "bumped=1"
)
if "!bump!"=="4" (
set /p "version=Enter version: "
set "bumped=1"
goto do_release
)
if "!bump!"=="5" exit /b 0

if "!bumped!"=="0" (
echo [ERROR] Invalid selection.
pause
exit /b 1
)

set "version=!major!.!minor!.!patch!"

:do_release
echo.
echo New version: !version!
echo.
set /p "confirm=Proceed with release? (YES/NO): "
if /i not "!confirm!"=="YES" (
echo [CANCELLED]
pause
exit /b 0
)

:: Step 1: Update version file
echo.
echo [1/4] Updating version file...
>"%version_file%" echo !version!

:: Step 2: Commit the version change
echo [2/4] Committing version bump...
git add "%version_file%"
git commit -m "Bump version to !version!"

if !errorlevel! neq 0 (
echo [ERROR] Commit failed.
pause
exit /b 1
)

:: Step 3: Create tag
echo [3/4] Creating tag v!version!...
git tag -a "v!version!" -m "Release !version!"

if !errorlevel! neq 0 (
echo [ERROR] Tag creation failed. Tag v!version! may already exist.
pause
exit /b 1
)

:: Step 4: Push everything
echo [4/4] Pushing to %remote%...
git push "%remote%" && git push "%remote%" "v!version!"

if !errorlevel! neq 0 (
echo [ERROR] Push failed. Check remote connectivity.
pause
exit /b 1
)

echo.
echo =============================================
echo RELEASE v!version! COMPLETE
echo =============================================

pause

Method 5: Listing and Managing Tags

Listing All Version Tags

@echo off
echo Version tags (newest first^):
echo ============================
echo.

git tag -l "v*" --sort=-version:refname

echo.
echo Latest:
git describe --tags --abbrev=0 2>nul

pause

Deleting a Tag (Local and Remote)

@echo off
setlocal EnableExtensions

set /p "tag=Enter tag to delete (e.g., v1.0.0): "

if "%tag%"=="" (
echo [ERROR] Tag cannot be empty.
pause
exit /b 1
)

echo Deleting tag "%tag%" locally...
git tag -d "%tag%"

if errorlevel 1 (
echo [ERROR] Local tag deletion failed. Tag may not exist.
pause
exit /b 1
)

echo [OK] Local tag deleted.

set /p "remote_too=Also delete from remote? (Y/N): "

if /i "%remote_too%"=="Y" (
echo Deleting tag "%tag%" from remote...
git push origin --delete "%tag%"

if errorlevel 1 (
echo [ERROR] Remote deletion failed.
) else (
echo [OK] Remote tag deleted.
)
) else (
echo [INFO] Remote deletion skipped.
)

echo [DONE]
pause
exit /b 0

Viewing Tag Details

@echo off
set /p "tag=Enter tag name: "

echo.
echo Tag details for %tag%:
echo ======================
echo.

git show "%tag%" --no-patch

pause

Common Mistakes

The Wrong Way: Using Lightweight Tags for Releases

:: WRONG - Lightweight tags have no metadata
git tag v1.0.0

Output Concern: Lightweight tags are just pointers to a commit with no author, date, or message. There is no record of who created the tag or why. For releases, always use annotated tags (git tag -a) which store the tagger identity, timestamp, and a descriptive message.

The Wrong Way: Tagging Without Checking for Duplicates

:: WRONG - Fails if tag already exists
git tag -a "v1.0.0" -m "Release"
:: fatal: tag 'v1.0.0' already exists

Always check if the tag exists before attempting to create it. Use git tag -l "v1.0.0" and check the output.

The Wrong Way: Forgetting to Push Tags

:: INCOMPLETE - Tag exists only locally
git tag -a "v1.0.0" -m "Release"
git push origin main
:: Tags are NOT pushed with regular push

git push does not push tags by default. You must explicitly push tags with git push origin v1.0.0 or git push origin --tags.

Best Practices

  1. Use annotated tags: They record who, when, and why the tag was created.
  2. Use a v prefix: Convention is v1.0.0, making tags easily distinguishable from branches.
  3. Check for duplicates: Verify the tag does not already exist before creating.
  4. Push tags explicitly: Tags require a separate push command.
  5. Tag from a clean state: Ensure all changes are committed before tagging.

Conclusion

Tagging a Git repository with version numbers from a Batch Script involves reading the version from a centralized source, creating an annotated Git tag with a descriptive message, and pushing it to the remote repository. By integrating version bumping, tag creation, and pushing into a single release workflow script, teams ensure that every release is consistently tagged, traceable to a specific commit, and immediately available to all collaborators. The combination of semantic versioning and Git tags creates a reliable release history that supports deployment tracking, rollback decisions, and changelog generation.