receive-pack Git Command Guide
The git receive-pack command receives what is pushed into the repository and updates it with information fed from the remote end. It is invoked by git send-pack and handles the server-side operations for push protocols.
git receive-pack Syntax:
Section titled “git receive-pack Syntax:”git receive-pack <git-dir>Parameters:
Section titled “Parameters:”| Parameter | Description |
|---|---|
<git-dir> | Path to the Git directory/repository |
Understanding Push Protocol:
Section titled “Understanding Push Protocol:”Push Operation Flow:
Section titled “Push Operation Flow:”Push Protocol Sequence:1. Client (send-pack) negotiates with server2. Server (receive-pack) advertises refs3. Client determines what to send4. Client sends packfile with objects5. Server receives and validates packfile6. Server updates references7. Server runs hooks (pre/post-receive)8. Server sends success/failure response
Communication Flow: Client ── send-pack ── packfile ──► Server ▲ │ │ ▼ └──────── refs advertisement ◄──────── receive-packReference Updates:
Section titled “Reference Updates:”Reference Update Process:├── Validate incoming references├── Check permissions and policies├── Update refs in transaction├── Run pre-receive hooks├── Apply reference changes├── Run post-receive hooks├── Send confirmation to client
Safety Mechanisms:├── Atomic reference updates├── Hook-based validation├── Configurable access controls├── Backup and recovery optionsPackfile Processing:
Section titled “Packfile Processing:”Packfile Reception:├── Receive compressed packfile├── Validate packfile integrity├── Extract objects to .git/objects├── Update indexes and caches├── Verify object connectivity├── Clean up temporary files
Validation Steps:├── SHA-1 verification├── Object type checking├── Reference validity├── Permission checksConfiguration and Security:
Section titled “Configuration and Security:”Git Configuration for Receive-Pack:
Section titled “Git Configuration for Receive-Pack:”# Configure receive behaviorgit config receive.denyNonFastForwards false # Allow non-fast-forward updatesgit config receive.denyDeletes false # Allow branch/tag deletiongit config receive.denyDeleteCurrent false # Allow deletion of current branchgit config receive.denyCurrentBranch ignore # Handle current branch updates
# Configure access controlgit config receive.fsckObjects true # Verify received objectsgit config receive.advertisePushOptions true # Enable push optionsgit config receive.keepAlive 5 # Connection keep-alive
# Configure hooksgit config core.hooksPath /path/to/hooks # Custom hooks directorygit config receive.procReceiveRefs refs/for/* # Proc-receive refsSecurity Configurations:
Section titled “Security Configurations:”# Access controlgit config receive.denyNonFastForwards true # Require fast-forwardsgit config receive.denyDeletes true # Deny ref deletionsgit config receive.certNonceSeed "random-seed" # Certificate validation
# Object validationgit config receive.fsck.skipList /etc/git/fsck-skiplistgit config receive.fsck.unreachable true # Check unreachable objects
# Network securitygit config receive.maxInputSize 100m # Limit input sizegit config receive.unpackLimit 100 # Unpack limitHook Integration:
Section titled “Hook Integration:”# Pre-receive hook examplecat > .git/hooks/pre-receive << 'EOF'#!/bin/bash# Validate incoming pushes
while read oldrev newrev refname; do echo "Validating $refname: $oldrev -> $newrev"
# Check for force pushes if [ "$oldrev" != "0000000000000000000000000000000000000000" ] && ! git merge-base --is-ancestor "$oldrev" "$newrev" 2>/dev/null; then echo "Non-fast-forward push denied on $refname" exit 1 fi
# Validate commit messages if ! git rev-list "$oldrev..$newrev" --grep="^[^:]*: " | grep -q .; then echo "Commits must follow 'Subject: description' format" exit 1 fidone
echo "Pre-receive validation passed"EOF
chmod +x .git/hooks/pre-receiveServer-Side Operations:
Section titled “Server-Side Operations:”Repository Management:
Section titled “Repository Management:”# Initialize bare repository for receive-packgit init --bare /path/to/repo.git
# Configure for receive-packcd /path/to/repo.gitgit config core.bare truegit config receive.denyNonFastForwards false
# Set up initial refsgit update-ref refs/heads/main <initial-commit>Push Processing:
Section titled “Push Processing:”# Manual receive-pack invocation (rare)git receive-pack /path/to/repo.git < input.pack
# Typical usage via SSH/HTTP# SSH: git-receive-pack '/path/to/repo.git'# HTTP: POST to /git-receive-pack endpoint
# Debug receive-packGIT_TRACE=1 git receive-pack /path/to/repo.gitReference Management:
Section titled “Reference Management:”# Update references manuallygit update-ref refs/heads/feature abc123
# Delete referencesgit update-ref -d refs/heads/old-branch
# Create tagsgit update-ref refs/tags/v1.0 def456
# Atomic reference updatesgit update-ref --stdin << EOFupdate refs/heads/main abc123 def456update refs/heads/feature ghi789 jkl012EOFIntegration with Git Protocols:
Section titled “Integration with Git Protocols:”SSH Protocol Integration:
Section titled “SSH Protocol Integration:”# SSH command restriction# In ~/.ssh/authorized_keys:command="git-receive-pack '/srv/git/myrepo.git'",no-port-forwarding,no-X11-forwarding,no-agent-forwarding ssh-rsa AAAAB3...
# SSH with git-shell# In /etc/shells: /usr/bin/git-shell# User shell: /usr/bin/git-shell# In ~/.git-shell-commands: receive-packHTTP Protocol Integration:
Section titled “HTTP Protocol Integration:”# Apache configuration for receive-pack<Location "/git/myrepo.git/git-receive-pack"> AuthType Basic AuthName "Git Repository" Require valid-user SetEnv GIT_PROJECT_ROOT /srv/git SetEnv GIT_HTTP_EXPORT_ALL</Location>
# Nginx configurationlocation ~ /git/(.+)/git-receive-pack$ { fastcgi_pass unix:/var/run/fcgiwrap.socket; fastcgi_param SCRIPT_FILENAME /usr/lib/git-core/git-http-backend; include fastcgi_params;}Custom Protocol Handlers:
Section titled “Custom Protocol Handlers:”# Custom receive-pack wrappercat > custom-receive-pack << 'EOF'#!/bin/bash# Custom receive-pack with logging
REPO_PATH="$1"LOG_FILE="/var/log/git-receive.log"
echo "$(date): receive-pack $REPO_PATH by $USER from $SSH_CLIENT" >> "$LOG_FILE"
# Pre-processingif [ -x ".git/hooks/pre-receive-custom" ]; then .git/hooks/pre-receive-custom "$REPO_PATH" || exit 1fi
# Execute receive-packexec git receive-pack "$REPO_PATH"
# Post-processing would go hereEOF
chmod +x custom-receive-packTroubleshooting Common Issues:
Section titled “Troubleshooting Common Issues:”Permission Problems:
Section titled “Permission Problems:”# Fix repository permissionsfix_repo_permissions() { local repo_path="$1"
echo "Fixing permissions for $repo_path"
# Set correct ownership chown -R git:git "$repo_path"
# Set directory permissions find "$repo_path" -type d -exec chmod 755 {} \;
# Set file permissions find "$repo_path" -type f -exec chmod 644 {} \;
# Special permissions for hooks chmod 755 "$repo_path/hooks"/* chmod 755 "$repo_path/git-daemon-export-ok" 2>/dev/null || true}
fix_repo_permissions "/srv/git/myrepo.git"Hook Failures:
Section titled “Hook Failures:”# Debug hook executiondebug_hooks() { local repo_path="$1"
echo "Debugging hooks in $repo_path"
# Check hook existence and permissions ls -la "$repo_path/hooks/"
# Test hook execution if [ -x "$repo_path/hooks/pre-receive" ]; then echo "Testing pre-receive hook..." "$repo_path/hooks/pre-receive" < /dev/null fi
# Check hook logs if [ -f "/var/log/git-hooks.log" ]; then tail -20 /var/log/git-hooks.log fi}
debug_hooks "/srv/git/myrepo.git"Network Issues:
Section titled “Network Issues:”# Diagnose network problemsdiagnose_network() { echo "Diagnosing network issues"
# Test basic connectivity nc -z localhost 9418 && echo "Git daemon port open"
# Test SSH access ssh -T git@server "echo 'SSH access OK'"
# Test HTTP access curl -I "https://server/git/repo.git/info/refs"
# Check firewall rules iptables -L | grep 9418}
diagnose_networkRepository Corruption:
Section titled “Repository Corruption:”# Recover from corruptionrecover_corruption() { local repo_path="$1"
echo "Attempting repository recovery"
cd "$repo_path" || exit 1
# Check repository status git fsck --full
# Recover from backup if available if [ -d "../backup" ]; then cp -r ../backup/.git/objects/pack/* .git/objects/pack/ fi
# Rebuild indexes git repack -a -d
# Verify recovery git fsck --full --strict}
recover_corruption "/srv/git/myrepo.git"Performance Issues:
Section titled “Performance Issues:”# Optimize receive-pack performanceoptimize_performance() { local repo_path="$1"
echo "Optimizing receive-pack performance"
cd "$repo_path" || exit 1
# Configure for performance git config core.deltaBaseCacheLimit 512m git config pack.threads 4 git config pack.windowMemory 256m
# Optimize pack files git repack -a -d --threads=4
# Set up bitmap index git repack -b
# Configure GC git config gc.auto 1000 git config gc.autopacklimit 50}
optimize_performance "/srv/git/myrepo.git"Memory and Resource Issues:
Section titled “Memory and Resource Issues:”# Handle large push operationshandle_large_pushes() { echo "Configuring for large push operations"
# Increase memory limits git config receive.unpackLimit 200 git config pack.packSizeLimit 2g git config pack.windowMemory 512m
# Configure timeouts git config receive.keepAlive 30 git config http.postBuffer 524288000 # 500MB
# Set up staging area export GIT_ALTERNATE_OBJECT_DIRECTORIES=/tmp/git-objects}
handle_large_pushesReal-World Usage Examples:
Section titled “Real-World Usage Examples:”Enterprise Git Server Setup:
Section titled “Enterprise Git Server Setup:”#!/bin/bash# Enterprise Git server setup with receive-pack
setup_enterprise_git() { local server_root="/srv/git" local org_name="$1"
echo "Setting up enterprise Git server for $org_name"
# Create organization directory mkdir -p "$server_root/$org_name" cd "$server_root/$org_name"
# Set up base configurations cat > git-config << EOF[receive] denyNonFastForwards = true denyDeletes = true denyDeleteCurrent = true fsckObjects = true
[core] logAllRefUpdates = true
[hooks] path = /etc/git/hooksEOF
# Create repository template mkdir template.git cd template.git git init --bare
# Apply configurations cp ../git-config config
# Set up hooks mkdir -p hooks cat > hooks/pre-receive << 'EOF'#!/bin/bash# Enterprise pre-receive hook
# Load organization policiessource /etc/git/policies/$org_name.sh
# Validate pushwhile read oldrev newrev refname; do # Check branch naming if [[ "$refname" =~ ^refs/heads/ ]]; then branch="${refname#refs/heads/}" if ! valid_branch_name "$branch"; then echo "Invalid branch name: $branch" exit 1 fi fi
# Check commit signatures if ! git verify-commit "$newrev" 2>/dev/null; then echo "Unsigned commits not allowed" exit 1 fi
# Check file size limits git rev-list --objects "$oldrev..$newrev" | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize)' | while read type sha size; do if [ "$type" = "blob" ] && [ "$size" -gt 104857600 ]; then # 100MB echo "File too large: $(git rev-list -1 --oneline "$sha")" exit 1 fi donedone
echo "Enterprise validation passed"EOF
chmod +x hooks/pre-receive
cd .. echo "Enterprise Git server setup complete"}
setup_enterprise_git "acme-corp"CI/CD Integration with Receive-Pack:
Section titled “CI/CD Integration with Receive-Pack:”# CI/CD pipeline integrationsetup_ci_integration() { local repo_path="$1" local ci_system="$2"
echo "Setting up CI/CD integration for $ci_system"
cd "$repo_path" || exit 1
# Configure for CI system case "$ci_system" in "github-actions") cat > hooks/post-receive << 'EOF'#!/bin/bash# Trigger GitHub Actions
while read oldrev newrev refname; do if [[ "$refname" == "refs/heads/main" ]]; then echo "Triggering GitHub Actions workflow..." curl -X POST \ -H "Authorization: token $GITHUB_TOKEN" \ -H "Accept: application/vnd.github.v3+json" \ "https://api.github.com/repos/$GITHUB_REPOSITORY/actions/workflows/ci.yml/dispatches" \ -d '{"ref": "main"}' fidoneEOF ;;
"gitlab-ci") cat > hooks/post-receive << 'EOF'#!/bin/bash# Trigger GitLab CI
while read oldrev newrev refname; do if [[ "$refname" == "refs/heads/main" ]]; then echo "Triggering GitLab CI pipeline..." curl -X POST \ -H "PRIVATE-TOKEN: $GITLAB_TOKEN" \ "https://gitlab.com/api/v4/projects/$CI_PROJECT_ID/trigger/pipeline" fidoneEOF ;;
"jenkins") cat > hooks/post-receive << 'EOF'#!/bin/bash# Trigger Jenkins build
while read oldrev newrev refname; do if [[ "$refname" == "refs/heads/main" ]]; then echo "Triggering Jenkins build..." curl -X POST "$JENKINS_URL/job/my-project/build" fidoneEOF ;; esac
chmod +x hooks/post-receive echo "CI/CD integration setup complete"}
setup_ci_integration "/srv/git/myrepo.git" "github-actions"Access Control and Auditing:
Section titled “Access Control and Auditing:”# Advanced access control with auditingsetup_access_control() { local repo_path="$1"
echo "Setting up advanced access control"
cd "$repo_path" || exit 1
# Create audit log mkdir -p audit
# Enhanced pre-receive hook cat > hooks/pre-receive << 'EOF'#!/bin/bash# Advanced access control and auditing
LOG_FILE="audit/push-log.txt"TIMESTAMP=$(date '+%Y-%m-%d %H:%M:%S')USER=${USER:-$GIT_AUTHOR_NAME}IP=${SSH_CLIENT%% *}
echo "$TIMESTAMP|$USER|$IP|START" >> "$LOG_FILE"
# Read all refs being pushedwhile read oldrev newrev refname; do echo "$TIMESTAMP|$USER|$IP|PUSH|$refname|$oldrev|$newrev" >> "$LOG_FILE"
# Access control logic case "$refname" in refs/heads/main) # Strict rules for main branch if ! user_has_main_access "$USER"; then echo "Access denied to main branch" exit 1 fi
# Require code review if ! commit_has_review "$newrev"; then echo "Main branch requires code review" exit 1 fi ;;
refs/heads/release/*) # Release branch rules if ! user_has_release_access "$USER"; then echo "Access denied to release branches" exit 1 fi ;;
refs/tags/*) # Tag creation rules if ! user_has_tag_access "$USER"; then echo "Access denied for tag creation" exit 1 fi ;; esac
# Size limits objects=$(git rev-list --objects "$oldrev..$newrev") if [ $(echo "$objects" | wc -l) -gt 1000 ]; then echo "Push too large - please split into smaller pushes" exit 1 fidone
echo "$TIMESTAMP|$USER|$IP|SUCCESS" >> "$LOG_FILE"EOF
# Helper functions cat > hooks/access-control.sh << 'EOF'#!/bin/bash# Access control helper functions
user_has_main_access() { local user="$1" # Check against main branch access list grep -q "^$user$" /etc/git/main-access.txt}
user_has_release_access() { local user="$1" # Check against release access list grep -q "^$user$" /etc/git/release-access.txt}
user_has_tag_access() { local user="$1" # Check against tag access list grep -q "^$user$" /etc/git/tag-access.txt}
commit_has_review() { local commit="$1" # Check for review markers in commit message git show -s --format=%B "$commit" | grep -q "Reviewed-by:"}EOF
chmod +x hooks/pre-receive chmod +x hooks/access-control.sh
echo "Advanced access control setup complete"}
setup_access_control "/srv/git/secure-repo.git"Disaster Recovery and Backup:
Section titled “Disaster Recovery and Backup:”# Disaster recovery with receive-packsetup_disaster_recovery() { local primary_repo="$1" local backup_repo="$2"
echo "Setting up disaster recovery"
# Configure backup hooks cd "$primary_repo" || exit 1
cat > hooks/post-receive << EOF#!/bin/bash# Automatic backup to secondary repository
while read oldrev newrev refname; do echo "Backing up \$refname to $backup_repo"
# Push to backup repository git push --mirror "$backup_repo" "\$refname"
# Log backup echo "\$(date): Backed up \$refname \$oldrev -> \$newrev" >> backup.logdoneEOF
chmod +x hooks/post-receive
# Set up backup repository git clone --mirror "$primary_repo" "$backup_repo"
# Configure backup repository cd "$backup_repo" || exit 1 git config receive.denyNonFastForwards false git config receive.denyDeletes false
echo "Disaster recovery setup complete"}
setup_disaster_recovery "/srv/git/primary.git" "/srv/git/backup.git"Custom Push Policies:
Section titled “Custom Push Policies:”# Implement custom push policiessetup_push_policies() { local repo_path="$1"
echo "Setting up custom push policies"
cd "$repo_path" || exit 1
# Create policy configuration cat > push-policies.conf << EOF# Push policy configuration
[branches]main = protected,require-review,require-testsdevelop = require-testsfeature/* = allow-all
[tags]v* = require-gpg,require-reviewrc* = require-tests
[general]max-commit-size = 1000require-signoff = trueallow-force-push = falseEOF
# Policy enforcement hook cat > hooks/pre-receive << 'EOF'#!/bin/bash# Enforce push policies
source ./push-policies.conf
while read oldrev newrev refname; do echo "Checking policies for $refname"
# Determine branch/tag type if [[ "$refname" =~ ^refs/heads/ ]]; then branch="${refname#refs/heads/}" policy=$(get_branch_policy "$branch") elif [[ "$refname" =~ ^refs/tags/ ]]; then tag="${refname#refs/tags/}" policy=$(get_tag_policy "$tag") fi
# Apply policies IFS=',' read -ra POLICIES <<< "$policy" for policy_item in "${POLICIES[@]}"; do case "$policy_item" in protected) if ! is_fast_forward "$oldrev" "$newrev"; then echo "Protected branch requires fast-forward" exit 1 fi ;; require-review) if ! has_review_approval "$newrev"; then echo "Branch requires review approval" exit 1 fi ;; require-tests) if ! tests_pass "$newrev"; then echo "Tests must pass before push" exit 1 fi ;; require-gpg) if ! commits_signed "$oldrev" "$newrev"; then echo "All commits must be GPG signed" exit 1 fi ;; esac done
# General policies if [ "$require_signoff" = "true" ] && ! commits_have_signoff "$oldrev" "$newrev"; then echo "Commits must have signoff" exit 1 fi
commit_count=$(git rev-list --count "$oldrev..$newrev") if [ "$commit_count" -gt "$max_commit_size" ]; then echo "Push too large (max $max_commit_size commits)" exit 1 fidone
echo "All push policies satisfied"EOF
chmod +x hooks/pre-receive echo "Custom push policies setup complete"}
setup_push_policies "/srv/git/policy-repo.git"What’s the difference between receive-pack and send-pack?
Section titled “What’s the difference between receive-pack and send-pack?”receive-pack runs on the server side to receive pushed data; send-pack runs on the client side to send push data. They work together to implement the Git push protocol.
How do I configure receive-pack for a bare repository?
Section titled “How do I configure receive-pack for a bare repository?”Use git init —bare, then configure receive.* settings in the config file. Set up hooks in the hooks/ directory for access control and automation.
What are the security implications of receive-pack?
Section titled “What are the security implications of receive-pack?”receive-pack can modify repository references, so it should be protected with proper authentication, authorization hooks, and input validation to prevent malicious pushes.
How do I debug receive-pack issues?
Section titled “How do I debug receive-pack issues?”Use GIT_TRACE=1 git push to see protocol traces, check server logs, and examine hook outputs. Test hooks manually with sample input.
Can receive-pack work with HTTP?
Section titled “Can receive-pack work with HTTP?”Yes, it’s used by git-http-backend for HTTP push operations. Requires proper web server configuration and authentication.
What’s the difference between pre-receive and post-receive hooks?
Section titled “What’s the difference between pre-receive and post-receive hooks?”pre-receive runs before references are updated (can reject push); post-receive runs after successful updates (for notifications and automation).
How do I limit push size with receive-pack?
Section titled “How do I limit push size with receive-pack?”Configure receive.maxInputSize and receive.unpackLimit in Git config, or implement size checks in pre-receive hooks.
Can receive-pack handle force pushes?
Section titled “Can receive-pack handle force pushes?”Yes, but this can be controlled with receive.denyNonFastForwards and receive.denyDeletes configurations.
How do I set up receive-pack for SSH access?
Section titled “How do I set up receive-pack for SSH access?”Configure SSH authorized_keys with command=“git-receive-pack ‘/path/to/repo.git’” to restrict users to Git operations only.
What’s the role of receive-pack in CI/CD?
Section titled “What’s the role of receive-pack in CI/CD?”receive-pack triggers post-receive hooks that can start CI/CD pipelines, deploy code, or send notifications when pushes occur.
How do I backup repositories using receive-pack?
Section titled “How do I backup repositories using receive-pack?”Set up mirror repositories and use post-receive hooks to automatically push to backup locations after successful receives.
Applications of the git receive-pack command
Section titled “Applications of the git receive-pack command”- Server-Side Push Processing: Handle incoming push operations and update repositories
- Access Control Implementation: Enforce push policies and security restrictions
- Hook-Based Automation: Trigger CI/CD, deployments, and notifications on push
- Repository Mirroring: Maintain backup and mirror repositories
- Protocol Implementation: Support SSH and HTTP Git push protocols
- Enterprise Git Management: Implement organizational policies and compliance