Skip to content

send-pack Git Command Guide

The git send-pack command is a low-level Git command that pushes objects and references from a local repository to a remote repository. It implements the client side of the Git push protocol, handling the negotiation and transfer of Git objects between repositories.

Terminal window
git send-pack [--dry-run] [--force] [--verbose] [--thin] [--no-progress]
[--all] [--mirror] [--atomic] [--signed | --no-signed]
[--receive-pack=<git-receive-pack>] [--negotiate-only]
[--deepen=<depth>] [--shallow-since=<time>]
[--update-shallow] [<host>:]<directory> [<ref>...]
OptionDescription
--dry-runShow what would be pushed without pushing
--forceForce update of remote refs
--verbose, -vVerbose output
--quiet, -qSuppress output
--progressShow progress (default)
--no-progressDon’t show progress
OptionDescription
--allPush all refs
--mirrorMirror all refs
--atomicUse atomic transactions
--signedGPG-sign the push
--no-signedDon’t GPG-sign the push
--thinUse thin pack
--no-thinDon’t use thin pack
OptionDescription
--negotiate-onlyOnly negotiate, don’t send
--deepen=<depth>Deepen shallow repository
--shallow-since=<time>Shallow since time
--update-shallowUpdate shallow refs
OptionDescription
--receive-pack=<cmd>Receive pack command
--exec=<cmd>Execute command on remote
--remote=<remote>Specify remote name
--push-option=<opt>Pass push option to receiver
ParameterDescription
<host>:<directory>Remote repository location
<ref>References to push
Git Push Protocol (send-pack):
├── Discovery: List remote refs
├── Negotiation: Find common objects
├── Compression: Create pack file
├── Transfer: Send pack to remote
├── Update: Update remote refs
└── Verification: Confirm success
Pack File Generation:
├── Object Discovery: Find objects to send
├── Delta Compression: Create efficient deltas
├── Pack Creation: Bundle objects into pack
├── Index Creation: Create pack index
└── Transfer: Send pack + index to remote
Reference Update Process:
├── Pre-receive: Server validation hooks
├── Update: Atomic ref updates
├── Post-receive: Notification hooks
└── Cleanup: Remove temporary files
Terminal window
# Push to remote repository
git send-pack origin main
# Push multiple branches
git send-pack origin main develop feature-x
# Push all branches
git send-pack --all origin
# Mirror repository
git send-pack --mirror origin
Terminal window
# Show what would be pushed
git send-pack --dry-run origin main
# Verbose dry run
git send-pack --dry-run --verbose origin
# Check remote capabilities
git send-pack --negotiate-only origin
Terminal window
# Force push branch
git send-pack --force origin main
# Force push with atomic transaction
git send-pack --force --atomic origin main develop
# Force mirror push
git send-pack --force --mirror origin
Terminal window
# Use custom receive-pack command
git send-pack --receive-pack=/usr/local/bin/git-receive-pack origin
# Push to non-standard remote
git send-pack user@host:/path/to/repo.git main
# Push over SSH with custom port
git send-pack ssh://user@host:2222/path/to/repo.git main
Terminal window
# Use thin pack for efficiency
git send-pack --thin origin main
# Force thin pack
git send-pack --no-thin origin main
# Thin pack with progress
git send-pack --thin --progress origin
Terminal window
# GPG sign the push
git send-pack --signed origin main
# Don't sign push
git send-pack --no-signed origin main
# Sign with specific key
GIT_COMMITTER_KEY=ABC123 git send-pack --signed origin main
Terminal window
# Push to shallow remote
git send-pack --update-shallow origin main
# Deepen remote shallow repo
git send-pack --deepen=10 origin
# Push since specific time
git send-pack --shallow-since="1 week ago" origin
Terminal window
# Configure send-pack behavior
git config sendpack.verbose true # Verbose output by default
git config sendpack.thin true # Use thin packs by default
git config sendpack.atomic true # Use atomic pushes
# Configure remote operations
git config remote.origin.receivepack /usr/local/bin/git-receive-pack
git config remote.origin.push refs/heads/*:refs/heads/*
# Configure signing
git config push.gpgSign true # Sign all pushes
git config user.signingkey ABC123 # Default GPG key
Terminal window
# Use dry-run first
git send-pack --dry-run origin main
# Use atomic for multi-branch pushes
git send-pack --atomic origin main develop
# Use thin packs for efficiency
git send-pack --thin origin
# Sign important pushes
git send-pack --signed origin main
Terminal window
# Backup before force operations
git branch backup-before-force
# Verify remote state
git ls-remote origin
# Check connectivity
ssh user@host git --version
# Validate local repository
git fsck
#!/bin/bash
# Custom push workflow with send-pack
smart_push() {
local remote="$1"
local branch="$2"
echo "Smart push to $remote/$branch"
# Check if branch exists
if ! git show-ref --verify --quiet "refs/heads/$branch"; then
echo "Branch $branch does not exist"
return 1
fi
# Check remote connectivity
if ! git ls-remote "$remote" >/dev/null 2>&1; then
echo "Cannot connect to remote $remote"
return 1
fi
# Dry run first
echo "Dry run results:"
git send-pack --dry-run "$remote" "$branch"
# Confirm push
read -p "Proceed with push? (y/N): " confirm
if [[ "$confirm" != "y" ]]; then
echo "Push cancelled"
return 0
fi
# Execute push
if git send-pack --verbose "$remote" "$branch"; then
echo "✓ Push successful"
else
echo "✗ Push failed"
return 1
fi
}
smart_push "origin" "main"
Terminal window
# Automated deployment with send-pack
deploy_to_production() {
local prod_remote="$1"
local release_tag="$2"
echo "Deploying $release_tag to production"
# Validate tag
if ! git show-ref --verify --quiet "refs/tags/$release_tag"; then
echo "Tag $release_tag does not exist"
return 1
fi
# Pre-deployment checks
if ! run_pre_deploy_checks; then
echo "Pre-deployment checks failed"
return 1
fi
# Push to production
if git send-pack --atomic "$prod_remote" "$release_tag"; then
echo "✓ Deployment successful"
# Update production ref
git send-pack "$prod_remote" "$release_tag:refs/heads/production"
# Post-deployment tasks
run_post_deploy_tasks "$release_tag"
else
echo "✗ Deployment failed"
return 1
fi
}
deploy_to_production "prod-server:/var/git/app.git" "v2.1.0"
Terminal window
# Repository mirroring with send-pack
mirror_repository() {
local source_repo="$1"
local mirror_repo="$2"
echo "Mirroring $source_repo to $mirror_repo"
# Full mirror push
if git send-pack --mirror "$mirror_repo"; then
echo "✓ Repository mirrored successfully"
# Verify mirror
mirror_refs=$(git ls-remote "$mirror_repo" | wc -l)
local_refs=$(git show-ref | wc -l)
if [ "$mirror_refs" -eq "$local_refs" ]; then
echo "✓ Mirror verification passed"
else
echo "⚠ Mirror verification failed: $mirror_refs vs $local_refs refs"
fi
else
echo "✗ Mirroring failed"
return 1
fi
}
mirror_repository "." "mirror-server:/var/git/mirror.git"
Terminal window
# Test remote connection
ssh user@host git --version
# Check remote repository access
git ls-remote remote-url
# Verify SSH keys
ssh -T user@host
# Test with verbose output
git send-pack --verbose remote-url main
Terminal window
# Check remote permissions
ssh user@host ls -la /path/to/repo.git
# Verify git user access
ssh user@host whoami
# Check repository ownership
ssh user@host stat /path/to/repo.git
# Fix permissions
ssh user@host chmod -R g+w /path/to/repo.git
Terminal window
# Check remote refs
git ls-remote origin
# Verify local refs
git show-ref
# Check for conflicting refs
git show-ref | grep refs/heads/main
git ls-remote origin | grep refs/heads/main
# Force update if needed
git send-pack --force origin main
Terminal window
# Check pack file integrity
git verify-pack .git/objects/pack/*.pack
# Repack repository
git repack -a -d
# Check for corrupted objects
git fsck --full
# Clean up unreachable objects
git gc --prune=now
Terminal window
# Reduce memory usage
git config pack.windowMemory "100m"
git config pack.packSizeLimit "100m"
# Use shallow operations
git send-pack --depth=1 remote-url
# Split large pushes
git send-pack remote-url main
git send-pack remote-url develop
#!/bin/bash
# Advanced push automation with send-pack
automated_push_workflow() {
local remote="$1"
local branches="${2:-main}"
local options="${3:---atomic}"
echo "=== Automated Push Workflow ==="
echo "Remote: $remote"
echo "Branches: $branches"
echo "Options: $options"
# Pre-push validation
echo "Running pre-push validation..."
# Check repository cleanliness
if ! git diff --quiet || ! git diff --cached --quiet; then
echo "✗ Repository has uncommitted changes"
echo "Commit or stash changes before pushing"
return 1
fi
# Check remote connectivity
if ! timeout 10 git ls-remote "$remote" >/dev/null 2>&1; then
echo "✗ Cannot connect to remote $remote"
return 1
fi
# Validate branches exist
for branch in $branches; do
if ! git show-ref --verify --quiet "refs/heads/$branch"; then
echo "✗ Branch $branch does not exist locally"
return 1
fi
done
echo "✓ Pre-push validation passed"
# Execute push
echo "Executing push..."
if eval "git send-pack $options '$remote' $branches"; then
echo "✓ Push successful"
# Post-push operations
echo "Running post-push operations..."
# Update tracking branches
for branch in $branches; do
git branch --set-upstream-to="$remote/$branch" "$branch" 2>/dev/null
done
# Notify team (if configured)
if command -v notify_team >/dev/null 2>&1; then
notify_team "Push completed: $branches to $remote"
fi
# Run post-push hooks
if [ -f .git/hooks/post-push ]; then
.git/hooks/post-push "$remote" "$branches"
fi
else
echo "✗ Push failed"
# Diagnostic information
echo "Diagnostic information:"
echo "- Remote refs:"
git ls-remote "$remote" 2>/dev/null | head -5
echo "- Local refs:"
for branch in $branches; do
git show-ref "refs/heads/$branch" 2>/dev/null
done
return 1
fi
}
# Usage examples
automated_push_workflow "origin" "main develop" "--atomic --signed"
automated_push_workflow "upstream" "feature-x" "--force"
Terminal window
# CI/CD pipeline with send-pack
ci_push_pipeline() {
echo "=== CI/CD Push Pipeline ==="
# Environment variables
CI_COMMIT_REF="${CI_COMMIT_REF:-main}"
CI_REMOTE_URL="${CI_REMOTE_URL:-origin}"
# Determine push strategy
case "$CI_JOB_TYPE" in
"deploy")
# Production deployment
echo "Production deployment push"
# Additional validation for production
if ! run_security_checks; then
echo "Security checks failed"
exit 1
fi
if ! run_performance_tests; then
echo "Performance tests failed"
exit 1
fi
# Signed push to production
git send-pack --signed --atomic "$CI_REMOTE_URL" "$CI_COMMIT_REF"
# Update production ref
git send-pack "$CI_REMOTE_URL" "$CI_COMMIT_REF:refs/heads/production"
;;
"staging")
# Staging deployment
echo "Staging deployment push"
git send-pack --atomic "$CI_REMOTE_URL" "$CI_COMMIT_REF"
git send-pack "$CI_REMOTE_URL" "$CI_COMMIT_REF:refs/heads/staging"
;;
"feature")
# Feature branch push
echo "Feature branch push"
# Check if feature is approved
if ! is_feature_approved "$CI_COMMIT_REF"; then
echo "Feature not approved for push"
exit 1
fi
git send-pack "$CI_REMOTE_URL" "$CI_COMMIT_REF"
;;
*)
# Standard push
echo "Standard push"
git send-pack --quiet "$CI_REMOTE_URL" "$CI_COMMIT_REF"
;;
esac
# Post-push validation
if git ls-remote "$CI_REMOTE_URL" | grep -q "$CI_COMMIT_REF"; then
echo "✓ Push validation successful"
else
echo "✗ Push validation failed"
exit 1
fi
}
ci_push_pipeline
Terminal window
# Repository synchronization with send-pack
sync_repositories() {
local source_repo="$1"
local target_repo="$2"
local sync_branches="${3:-main}"
echo "=== Repository Synchronization ==="
echo "Source: $source_repo"
echo "Target: $target_repo"
echo "Branches: $sync_branches"
# Change to source repository
cd "$source_repo" || {
echo "Cannot access source repository: $source_repo"
return 1
}
# Fetch latest changes
git fetch --all --prune
# Synchronize each branch
for branch in $sync_branches; do
echo "Synchronizing branch: $branch"
# Check if branch exists in source
if ! git show-ref --verify --quiet "refs/heads/$branch"; then
echo "Branch $branch does not exist in source"
continue
fi
# Push to target
if git send-pack --verbose "$target_repo" "$branch"; then
echo "✓ Branch $branch synchronized"
# Update target branch ref if needed
git send-pack "$target_repo" "$branch:refs/heads/$branch"
else
echo "✗ Failed to synchronize branch $branch"
return 1
fi
done
# Synchronize tags
echo "Synchronizing tags..."
git send-pack --tags "$target_repo"
# Verify synchronization
echo "Verifying synchronization..."
source_refs=$(git show-ref | wc -l)
target_refs=$(git ls-remote "$target_repo" 2>/dev/null | wc -l)
if [ "$source_refs" -eq "$target_refs" ]; then
echo "✓ Synchronization verified"
else
echo "⚠ Reference count mismatch: $source_refs vs $target_refs"
fi
echo "Repository synchronization complete"
}
sync_repositories "/path/to/source" "/path/to/target" "main develop"
Terminal window
# Disaster recovery with send-pack
disaster_recovery() {
local backup_repo="$1"
local primary_repo="${2:-origin}"
echo "=== Disaster Recovery ==="
echo "Backup: $backup_repo"
echo "Primary: $primary_repo"
# Assess damage
echo "Assessing repository damage..."
if git fsck --full >/dev/null 2>&1; then
echo "✓ Primary repository is healthy"
return 0
else
echo "✗ Primary repository has issues"
fi
# Attempt recovery from backup
echo "Attempting recovery from backup..."
# Get list of recoverable refs
backup_refs=$(git ls-remote "$backup_repo" 2>/dev/null | awk '{print $2}' | grep -E '^refs/(heads|tags)/')
if [ -z "$backup_refs" ]; then
echo "No recoverable refs in backup"
return 1
fi
# Recover refs one by one
recovered=0
for ref in $backup_refs; do
echo "Recovering $ref..."
# Create recovery branch/tag
if git send-pack "$primary_repo" "$ref:$ref.recovery"; then
echo "✓ Recovered $ref as $ref.recovery"
((recovered++))
else
echo "✗ Failed to recover $ref"
fi
done
if [ "$recovered" -gt 0 ]; then
echo "✓ Recovered $recovered refs"
echo "Manual review and cleanup required"
echo "Use: git branch -m <ref.recovery> <ref> # to restore"
else
echo "✗ No refs could be recovered"
return 1
fi
}
disaster_recovery "backup-server:/var/git/backup.git"

What’s the difference between git send-pack and git push?

Section titled “What’s the difference between git send-pack and git push?”

git push is high-level and user-friendly; git send-pack is low-level implementation. push calls send-pack internally with additional features.

How do I push to a remote repository using send-pack?

Section titled “How do I push to a remote repository using send-pack?”

git send-pack pushes the branch to remote. Remote can be URL or remote name.

Yes, use SSH URLs: git send-pack user@host:/path/to/repo.git branch

—atomic ensures all ref updates succeed or all fail together, preventing partial pushes.

Use —force option: git send-pack —force remote branch

Yes, list multiple branches: git send-pack remote branch1 branch2 branch3

—mirror pushes all refs and sets up mirror configuration for complete repository duplication.

Use —dry-run: git send-pack —dry-run remote branch

Yes, but requires HTTP server with Git support configured.

—thin creates thin packs that reference objects from remote, reducing transfer size.

Use —verbose and check SSH/git server logs for connection issues.

Yes, but may need —update-shallow for shallow repository operations.

What’s the performance impact of send-pack?

Section titled “What’s the performance impact of send-pack?”

Generally fast, but large pushes may take time. Use —progress to monitor.

Basic send-pack works, but LFS objects need separate handling by LFS client.

Configure SSH keys or credentials. For HTTP, use credential helpers.

  1. Low-level Pushing: Direct object and reference pushing to remotes
  2. Automation Scripts: Custom push workflows and deployment automation
  3. Repository Mirroring: Complete repository duplication and backup
  4. CI/CD Integration: Automated deployment and synchronization
  5. Disaster Recovery: Repository restoration from backups
  6. Custom Protocols: Implementation of specialized push workflows
  7. Performance Optimization: Efficient object transfer and delta compression
  8. Security Controls: Signed pushes and access-controlled operations