Skip to content

update-ref Git Command Guide

The git update-ref command safely updates the object name stored in a reference (ref), providing atomic operations for branch and tag management. It’s a low-level command that offers precise control over Git references with safety guarantees and transactional behavior.

Terminal window
git update-ref [-d] [-z] [--stdin] [--no-deref] [--create-reflog]
[-m <reason>] [--no-refs] <ref> <newvalue> [<oldvalue>]
git update-ref [--no-deref] [-m <reason>] --stdin [--no-refs]
OptionDescription
-d, --deleteDelete reference
-zUse NUL character as delimiter
--stdinRead updates from stdin
--no-derefDon’t dereference symbolic refs
--create-reflogCreate reflog entry
-m <reason>Reason for update (reflog)
OptionDescription
<oldvalue>Expected current value (CAS)
--no-refsDon’t verify refs namespace
ParameterDescription
<ref>Reference to update
<newvalue>New object SHA-1 or ref
<oldvalue>Expected current value
Reference Hierarchy:
├── Direct References: Point to objects
│ ├── refs/heads/main -> commit SHA-1
│ ├── refs/tags/v1.0 -> commit SHA-1
│ └── refs/remotes/origin/main -> commit SHA-1
└── Symbolic References: Point to other refs
├── HEAD -> refs/heads/main
├── ORIG_HEAD -> commit SHA-1
└── MERGE_HEAD -> commit SHA-1
Atomic Reference Updates:
├── Compare-and-Swap (CAS): Verify old value before update
├── Transactional: All-or-nothing updates
├── Lock-Free: Uses filesystem locking
├── Safe Concurrency: Multiple processes can update safely
└── Reflog Integration: Automatic history tracking
Reference Resolution Chain:
Reference Name
├── refs/heads/feature -> Direct ref
├── refs/tags/v1.0 -> Direct ref
├── HEAD -> Symbolic ref -> refs/heads/main
└── refs/remotes/origin/HEAD -> Symbolic ref -> refs/remotes/origin/main
Terminal window
# Update branch to new commit
git update-ref refs/heads/main abc123def456
# Update with safety check
git update-ref refs/heads/main abc123def456 789def012345
# Update symbolic ref
git update-ref HEAD refs/heads/develop
Terminal window
# Create new branch reference
git update-ref refs/heads/feature-branch abc123def456
# Create tag reference
git update-ref refs/tags/v1.0 abc123def456
# Delete reference
git update-ref -d refs/heads/old-branch
# Delete tag
git update-ref -d refs/tags/old-tag
Terminal window
# Update only if current value matches
git update-ref refs/heads/main new-commit-sha current-commit-sha
# Update with reflog message
git update-ref -m "Merge pull request #123" refs/heads/main merge-commit-sha
# Create reflog entry
git update-ref --create-reflog refs/heads/feature new-commit-sha
Terminal window
# Update multiple refs from stdin
git update-ref --stdin << EOF
update refs/heads/main abc123def456 789def012345
update refs/heads/develop def456ghi789 abc123def456
delete refs/heads/old-feature
EOF
# Process refs with NUL delimiter
printf 'update refs/heads/main\x00abc123\x00789def\x00' | git update-ref -z --stdin
Terminal window
# Update HEAD to point to different branch
git update-ref HEAD refs/heads/release
# Update remote HEAD
git update-ref refs/remotes/origin/HEAD refs/remotes/origin/main
# Create symbolic reference
git update-ref refs/heads/current refs/heads/main
Terminal window
# Clean up dangling references
git for-each-ref --format='%(refname)' | while read -r ref; do
if ! git show-ref --verify "$ref" >/dev/null 2>&1; then
echo "Removing dangling ref: $ref"
git update-ref -d "$ref"
fi
done
# Update remote tracking branches
git update-ref refs/remotes/origin/main "$(git rev-parse origin/main)"
Terminal window
# Atomic multi-ref update
update_multiple_refs() {
local main_commit="$1"
local develop_commit="$2"
# Use stdin for atomic updates
git update-ref --stdin << EOF
update refs/heads/main $main_commit $(git rev-parse refs/heads/main)
update refs/heads/develop $develop_commit $(git rev-parse refs/heads/develop)
EOF
if [ $? -eq 0 ]; then
echo "Successfully updated both branches"
else
echo "Failed to update branches atomically"
fi
}
# Usage
update_multiple_refs "abc123" "def456"
Terminal window
# Configure reflog behavior
git config core.logAllRefUpdates true # Enable reflog for all refs
git config core.logAllRefUpdates always # Always create reflog
# Configure reference verification
git config receive.denyDeletes false # Allow ref deletion
git config receive.denyDeleteCurrent false # Allow deleting current branch
# Configure reflog expiration
git config gc.reflogExpire 90.days # Keep reflogs for 90 days
git config gc.reflogExpireUnreachable 30.days
Terminal window
# Always verify before update
safe_update_ref() {
local ref="$1"
local new_value="$2"
local expected_old="${3:-}"
# Get current value
local current_value
current_value=$(git rev-parse "$ref" 2>/dev/null || echo "")
# Check if update is needed
if [ "$current_value" = "$new_value" ]; then
echo "Ref $ref already at $new_value"
return 0
fi
# Perform safe update
if [ -n "$expected_old" ]; then
git update-ref "$ref" "$new_value" "$expected_old"
else
git update-ref "$ref" "$new_value"
fi
}
# Backup refs before major operations
backup_refs() {
echo "Backing up references..."
git for-each-ref --format='%(refname) %(objectname)' > refs-backup.txt
echo "Backup saved to refs-backup.txt"
}
# Restore refs from backup
restore_refs() {
local backup_file="${1:-refs-backup.txt}"
echo "Restoring references from $backup_file..."
while read -r ref sha; do
git update-ref "$ref" "$sha" 2>/dev/null || echo "Failed to restore $ref"
done < "$backup_file"
echo "Reference restoration complete"
}
Terminal window
# Batch reference updates
batch_update_refs() {
echo "Performing batch reference updates..."
# Collect updates
updates_file=$(mktemp)
echo "update refs/heads/main abc123 $(git rev-parse refs/heads/main)" >> "$updates_file"
echo "update refs/heads/develop def456 $(git rev-parse refs/heads/develop)" >> "$updates_file"
# Execute atomically
git update-ref --stdin < "$updates_file"
# Cleanup
rm "$updates_file"
}
# Optimize reflog operations
optimize_reflog() {
echo "Optimizing reflog operations..."
# Disable reflog for performance
git config core.logAllRefUpdates false
# Perform updates
# ... update operations ...
# Re-enable reflog
git config core.logAllRefUpdates true
}
#!/bin/bash
# Automated branch management with update-ref
create_feature_branch() {
local feature_name="$1"
local base_branch="${2:-main}"
echo "Creating feature branch: $feature_name"
# Get base commit
base_commit=$(git rev-parse "$base_branch")
# Create branch reference
git update-ref "refs/heads/feature/$feature_name" "$base_commit"
# Set upstream
git config "branch.feature/$feature_name.remote" origin
git config "branch.feature/$feature_name.merge" "refs/heads/feature/$feature_name"
echo "Feature branch created and configured"
}
# Automated branch cleanup
cleanup_merged_branches() {
echo "Cleaning up merged branches..."
git for-each-ref --format='%(refname:short) %(objectname)' refs/heads/feature/ | while read -r branch sha; do
if git merge-base --is-ancestor "$sha" main; then
echo "Deleting merged branch: $branch"
git update-ref -d "refs/heads/$branch"
fi
done
echo "Branch cleanup complete"
}
# Usage
create_feature_branch "user-authentication"
cleanup_merged_branches
Terminal window
# Release tagging with update-ref
create_release_tag() {
local version="$1"
local release_commit="$2"
echo "Creating release tag: v$version"
# Create annotated tag object
tag_object=$(git mktag << EOF
object $release_commit
type commit
tag v$version
tagger $(git config user.name) <$(git config user.email)> $(date +%s) +0000
Release v$version
Release notes:
- Feature 1
- Feature 2
- Bug fixes
EOF
)
# Update tag reference
git update-ref "refs/tags/v$version" "$tag_object"
echo "Release tag v$version created"
}
# Automated version bumping
bump_version() {
local new_version="$1"
echo "Bumping version to $new_version"
# Update version file
echo "$new_version" > VERSION
# Commit version change
version_commit=$(git commit-tree -p HEAD -m "Bump version to $new_version" VERSION)
# Update main branch
git update-ref refs/heads/main "$version_commit"
# Create version tag
git update-ref "refs/tags/v$new_version" "$version_commit"
echo "Version bumped to $new_version"
}
# Usage
create_release_tag "2.1.0" "abc123def456"
bump_version "2.1.0"
Terminal window
# Repository synchronization with update-ref
sync_repository_refs() {
local remote_name="$1"
echo "Synchronizing references with $remote_name..."
# Fetch remote refs
git fetch "$remote_name"
# Update remote tracking branches
git for-each-ref --format='%(refname:short)' "refs/remotes/$remote_name/" | while read -r remote_ref; do
local_ref="refs/heads/${remote_ref#refs/remotes/$remote_name/}"
# Skip HEAD
if [[ "$remote_ref" == */HEAD ]]; then
continue
fi
remote_commit=$(git rev-parse "$remote_ref")
git update-ref "$local_ref" "$remote_commit" 2>/dev/null && \
echo "Updated $local_ref to $remote_commit" || \
echo "Skipped $local_ref (possibly protected)"
done
echo "Repository synchronization complete"
}
# Usage
sync_repository_refs "origin"
Terminal window
# Handle concurrent update conflicts
handle_update_conflict() {
local ref="$1"
local new_value="$2"
local max_attempts=5
local attempt=1
while [ $attempt -le $max_attempts ]; do
current_value=$(git rev-parse "$ref" 2>/dev/null || echo "")
if git update-ref "$ref" "$new_value" "$current_value"; then
echo "Successfully updated $ref on attempt $attempt"
return 0
fi
echo "Update conflict on attempt $attempt, retrying..."
sleep 1
((attempt++))
done
echo "Failed to update $ref after $max_attempts attempts"
return 1
}
# Usage
handle_update_conflict "refs/heads/main" "new-commit-sha"
Terminal window
# Detect and fix corrupted references
fix_corrupted_refs() {
echo "Checking for corrupted references..."
git for-each-ref | while read -r sha type ref; do
if ! git cat-file -e "$sha" 2>/dev/null; then
echo "Corrupted reference: $ref -> $sha"
# Try to find correct commit
correct_sha=$(git log --oneline -1 --grep="$ref" --format=%H 2>/dev/null || echo "")
if [ -n "$correct_sha" ]; then
echo "Fixing $ref to point to $correct_sha"
git update-ref "$ref" "$correct_sha"
else
echo "Cannot fix $ref, removing..."
git update-ref -d "$ref"
fi
fi
done
echo "Reference corruption check complete"
}
Terminal window
# Handle permission issues with references
fix_ref_permissions() {
echo "Fixing reference permissions..."
# Fix .git/refs permissions
find .git/refs -type f -exec chmod 644 {} \;
find .git/refs -type d -exec chmod 755 {} \;
# Fix packed-refs permissions
[ -f .git/packed-refs ] && chmod 644 .git/packed-refs
# Fix reflog permissions
find .git/logs -type f -exec chmod 644 {} \; 2>/dev/null || true
echo "Reference permissions fixed"
}
# Test reference access
test_ref_access() {
local ref="$1"
if git update-ref "$ref" "$(git rev-parse HEAD)" 2>/dev/null; then
echo "Reference $ref is writable"
else
echo "Reference $ref is not writable"
return 1
fi
}
Terminal window
# Fix reflog problems
repair_reflog() {
local ref="$1"
echo "Repairing reflog for $ref..."
reflog_file=".git/logs/${ref#refs/}"
# Backup current reflog
cp "$reflog_file" "${reflog_file}.backup" 2>/dev/null || true
# Rebuild reflog from history
git log --oneline --format="%H %ct %s" "$ref" | while read -r commit timestamp message; do
echo "$commit $timestamp reflog-entry" >> "${reflog_file}.new"
done
# Replace reflog
mv "${reflog_file}.new" "$reflog_file" 2>/dev/null || echo "Could not update reflog"
echo "Reflog repair attempt complete"
}
# Clean old reflog entries
clean_reflog() {
echo "Cleaning old reflog entries..."
# Expire old entries
git reflog expire --all --expire=30.days
# Pack reflogs
git reflog pack
echo "Reflog cleanup complete"
}
Terminal window
# Fix broken symbolic references
fix_symbolic_refs() {
echo "Checking symbolic references..."
# Check HEAD
if ! git rev-parse HEAD >/dev/null 2>&1; then
echo "HEAD is broken, fixing..."
git update-ref HEAD refs/heads/main
fi
# Check other symbolic refs
for sym_ref in ORIG_HEAD MERGE_HEAD CHERRY_PICK_HEAD; do
if git show-ref "$sym_ref" >/dev/null 2>&1; then
target=$(git rev-parse "$sym_ref" 2>/dev/null)
if [ -z "$target" ]; then
echo "Removing broken $sym_ref"
git update-ref -d "$sym_ref"
fi
fi
done
echo "Symbolic reference check complete"
}
#!/bin/bash
# Advanced branch management with update-ref
# Atomic branch operations
atomic_branch_operations() {
echo "=== Atomic Branch Operations ==="
# Create multiple branches atomically
create_branches() {
local base_commit="$1"
shift
local branches=("$@")
echo "Creating branches atomically..."
# Prepare updates
updates=""
for branch in "${branches[@]}"; do
updates+="update refs/heads/$branch $base_commit\n"
done
# Execute atomically
echo -e "$updates" | git update-ref --stdin
if [ $? -eq 0 ]; then
echo "Successfully created ${#branches[@]} branches"
else
echo "Failed to create branches atomically"
fi
}
# Rename branch safely
rename_branch() {
local old_name="$1"
local new_name="$2"
echo "Renaming branch $old_name to $new_name..."
# Get current commit
current_commit=$(git rev-parse "refs/heads/$old_name")
# Create new branch
git update-ref "refs/heads/$new_name" "$current_commit"
# Update HEAD if pointing to old branch
if [ "$(git rev-parse HEAD)" = "$current_commit" ]; then
git update-ref HEAD "refs/heads/$new_name"
fi
# Delete old branch
git update-ref -d "refs/heads/$old_name"
echo "Branch renamed successfully"
}
# Interactive operations
echo "Branch Operations:"
echo "1. Create multiple branches"
echo "2. Rename branch safely"
read -p "Select operation (1-2): " op
case "$op" in
1)
read -p "Base commit: " base
read -p "Branches (space-separated): " -a branches
create_branches "$base" "${branches[@]}"
;;
2)
read -p "Old branch name: " old
read -p "New branch name: " new
rename_branch "$old" "$new"
;;
esac
}
atomic_branch_operations
Terminal window
# Advanced tag management with update-ref
tag_management_system() {
echo "=== Tag Management System ==="
# Create annotated tag manually
create_annotated_tag() {
local tag_name="$1"
local commit="$2"
local message="$3"
echo "Creating annotated tag: $tag_name"
# Create tag object
tag_object=$(git mktag << EOF
object $commit
type commit
tag $tag_name
tagger $(git config user.name) <$(git config user.email)> $(date +%s) +0000
$message
EOF
)
# Update tag reference
git update-ref "refs/tags/$tag_name" "$tag_object"
echo "Annotated tag $tag_name created"
}
# Batch tag operations
batch_tag_operations() {
echo "Performing batch tag operations..."
# Prepare tag updates
updates=""
# Example: Tag all release commits
git log --oneline --grep="Release" --format="%H %s" | while read -r commit subject; do
version=$(echo "$subject" | sed 's/Release v//')
if [[ "$version" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
updates+="update refs/tags/v$version $commit\n"
fi
done
# Execute batch update
echo -e "$updates" | git update-ref --stdin
echo "Batch tag operations complete"
}
# Tag cleanup
cleanup_tags() {
local pattern="$1"
echo "Cleaning up tags matching: $pattern"
git for-each-ref --format='%(refname)' "refs/tags/$pattern" | while read -r tag_ref; do
echo "Removing tag: ${tag_ref#refs/tags/}"
git update-ref -d "$tag_ref"
done
echo "Tag cleanup complete"
}
# Interactive tag management
echo "Tag Management Options:"
echo "1. Create annotated tag"
echo "2. Batch tag operations"
echo "3. Cleanup tags"
read -p "Select option (1-3): " option
case "$option" in
1)
read -p "Tag name: " tag_name
read -p "Commit: " commit
read -p "Message: " message
create_annotated_tag "$tag_name" "$commit" "$message"
;;
2) batch_tag_operations ;;
3)
read -p "Pattern to match: " pattern
cleanup_tags "$pattern"
;;
esac
}
tag_management_system
Terminal window
# Repository maintenance with update-ref
automated_maintenance() {
echo "=== Automated Repository Maintenance ==="
# Clean up stale branches
cleanup_stale_branches() {
local max_age_days="${1:-90}"
echo "Cleaning up branches older than $max_age_days days..."
git for-each-ref --format='%(refname) %(creatordate:format:%s)' refs/heads/ | while read -r ref create_time; do
current_time=$(date +%s)
age_days=$(( (current_time - create_time) / 86400 ))
if [ "$age_days" -gt "$max_age_days" ]; then
# Check if merged
branch_commit=$(git rev-parse "$ref")
if git merge-base --is-ancestor "$branch_commit" main 2>/dev/null; then
echo "Removing merged branch: ${ref#refs/heads/} (age: ${age_days} days)"
git update-ref -d "$ref"
fi
fi
done
echo "Stale branch cleanup complete"
}
# Update remote references
update_remote_refs() {
local remote="${1:-origin}"
echo "Updating remote references for $remote..."
# Fetch latest
git fetch "$remote"
# Update remote refs
git ls-remote "$remote" | while read -r sha ref; do
if [[ "$ref" == refs/heads/* ]]; then
local_ref="refs/remotes/$remote/${ref#refs/heads/}"
git update-ref "$local_ref" "$sha"
fi
done
echo "Remote references updated"
}
# Reflog maintenance
maintain_reflog() {
echo "Maintaining reflog..."
# Expire old entries
git reflog expire --all --expire=30.days
# Pack reflogs
git reflog pack
# Clean unreachable reflog entries
git reflog expire --all --expire-unreachable=now
echo "Reflog maintenance complete"
}
# Interactive maintenance
echo "Maintenance Options:"
echo "1. Clean up stale branches"
echo "2. Update remote references"
echo "3. Maintain reflog"
read -p "Select option (1-3): " option
case "$option" in
1)
read -p "Max age in days (default: 90): " max_age
cleanup_stale_branches "${max_age:-90}"
;;
2)
read -p "Remote name (default: origin): " remote
update_remote_refs "${remote:-origin}"
;;
3) maintain_reflog ;;
esac
}
automated_maintenance
Terminal window
# Custom Git commands using update-ref
custom_git_commands() {
echo "=== Custom Git Commands ==="
# Safe force push
safe_force_push() {
local branch="$1"
local remote="${2:-origin}"
echo "Performing safe force push of $branch to $remote..."
# Get remote commit
remote_commit=$(git rev-parse "refs/remotes/$remote/$branch" 2>/dev/null || echo "")
# Get local commit
local_commit=$(git rev-parse "refs/heads/$branch")
# Check if force is necessary
if [ "$remote_commit" = "$local_commit" ]; then
echo "Branch is already up to date"
return 0
fi
# Check if local is descendant of remote
if git merge-base --is-ancestor "$remote_commit" "$local_commit" 2>/dev/null; then
echo "Safe to push (local is descendant)"
git update-ref "refs/remotes/$remote/$branch" "$local_commit"
else
echo "Force push required - checking safety..."
# Additional safety checks
if [ -n "$remote_commit" ]; then
# Check how many commits would be lost
lost_commits=$(git rev-list "$remote_commit..HEAD" | wc -l)
if [ "$lost_commits" -gt 10 ]; then
echo "Warning: Would lose $lost_commits commits"
read -p "Continue? (yes/no): " confirm
if [ "$confirm" != "yes" ]; then
return 1
fi
fi
fi
# Perform force update
git update-ref "refs/remotes/$remote/$branch" "$local_commit"
echo "Force push completed safely"
fi
}
# Branch snapshot
create_branch_snapshot() {
local branch="$1"
local snapshot_name="${2:-snapshot-$(date +%Y%m%d-%H%M%S)}"
echo "Creating snapshot of branch $branch as $snapshot_name..."
current_commit=$(git rev-parse "refs/heads/$branch")
git update-ref "refs/snapshots/$snapshot_name" "$current_commit"
echo "Snapshot created: refs/snapshots/$snapshot_name -> $current_commit"
}
# Restore from snapshot
restore_from_snapshot() {
local snapshot_name="$1"
local target_branch="$2"
echo "Restoring $target_branch from snapshot $snapshot_name..."
snapshot_commit=$(git rev-parse "refs/snapshots/$snapshot_name")
git update-ref "refs/heads/$target_branch" "$snapshot_commit"
echo "Branch $target_branch restored from snapshot"
}
# Interactive custom commands
echo "Custom Git Commands:"
echo "1. Safe force push"
echo "2. Create branch snapshot"
echo "3. Restore from snapshot"
read -p "Select command (1-3): " cmd
case "$cmd" in
1)
read -p "Branch: " branch
read -p "Remote (default: origin): " remote
safe_force_push "$branch" "${remote:-origin}"
;;
2)
read -p "Branch: " branch
read -p "Snapshot name (optional): " snapshot
create_branch_snapshot "$branch" "$snapshot"
;;
3)
read -p "Snapshot name: " snapshot
read -p "Target branch: " branch
restore_from_snapshot "$snapshot" "$branch"
;;
esac
}
custom_git_commands