Skip to content

repository-layout Git Repository Structure Guide

The git repository-layout documentation describes the structure and organization of Git repositories, including directory layouts, file purposes, and repository types.

Git Repository Types:
├── Working Directory Repository: .git/ at root of working tree
├── Bare Repository: <project>.git/ without working tree
├── Gitfile Repository: .git file pointing to real repository
└── Submodule Repository: Managed via git submodule/worktree
Repository Detection:
├── .git/ directory or file indicates Git repository
├── Bare repos have core.bare=true in config
├── Gitfiles contain "gitdir: <path>" format
└── Submodules use gitfiles for repository sharing
Git Repository Layout:
├── .git/ (or <project>.git/)
│ ├── objects/ # Object storage
│ ├── refs/ # Reference storage
│ ├── HEAD # Current branch pointer
│ ├── index # Staging area
│ ├── config # Repository configuration
│ ├── logs/ # Reflog storage
│ └── hooks/ # Git hooks
├── [working tree] # Files (non-bare repos only)
└── .gitmodules # Submodule definitions
Object Database Structure:
├── objects/
│ ├── [0-9a-f][0-9a-f]/ # Loose objects by SHA-1 prefix
│ ├── pack/ # Packed object archives
│ ├── info/ # Object database info
│ └── [custom]/ # Alternative object storage
├── Object Types:
│ ├── commit # Commit objects
│ ├── tree # Directory snapshots
│ ├── blob # File contents
│ └── tag # Annotated tags
└── Storage Details:
├── Loose: Individual files in subdirs
├── Packed: Compressed archives (.pack/.idx)
├── Alternates: Shared object storage
└── Info: Packs, alternates, and skiplists
Reference Hierarchy:
├── refs/
│ ├── heads/ # Local branches
│ ├── remotes/ # Remote tracking branches
│ ├── tags/ # Tags
│ ├── notes/ # Notes
│ ├── replace/ # Object replacements
│ └── [custom]/ # Custom references
├── Reference Types:
│ ├── Symbolic: Point to other refs (HEAD)
│ ├── Direct: Point to objects (branches/tags)
│ └── Packed: Stored in packed-refs file
└── Special Refs:
├── HEAD: Current branch/commit
├── ORIG_HEAD: Pre-merge/reset state
├── MERGE_HEAD: Merge target
└── CHERRY_PICK_HEAD: Cherry-pick target
Configuration Storage:
├── config # Repository-specific config
├── .gitmodules # Submodule definitions
├── .gitattributes # Path-specific attributes
├── .gitignore # Ignore patterns
└── info/
├── exclude # Global ignore patterns
├── attributes # Global attributes
├── grafts # Parent relationship overrides
└── sparse-checkout # Sparse checkout patterns
Staging Area Components:
├── index # Main index file
├── .git/index.lock # Index lock file
└── [working tree] # Working directory files
Index Contents:
├── File entries with stat info
├── Object IDs for staged content
├── Conflict resolution data
├── Untracked cache (optional)
└── Split index (optional)
Packed References:
├── packed-refs # Packed reference file
├── Format: <SHA-1> <ref-name>
├── Performance optimization
├── Automatic packing
└── Manual unpacking with git pack-refs
Packing Process:
├── Collect loose refs
├── Write to packed-refs
├── Remove loose ref files
└── Update reflogs
Reflog Storage:
├── logs/
│ ├── HEAD # HEAD movement history
│ ├── refs/heads/ # Branch reflogs
│ ├── refs/remotes/ # Remote reflogs
│ └── [custom]/ # Custom reflogs
├── Reflog Format:
│ ├── <old-sha> <new-sha> <user> <timestamp> <operation>
│ ├── Recovery mechanism
│ ├── Configurable expiration
│ └── Size limits
└── Reflog Operations:
├── git reflog show
├── git reflog expire
└── git reflog delete
Git Hooks Directory:
├── hooks/
│ ├── pre-commit # Before commit creation
│ ├── commit-msg # Commit message validation
│ ├── post-commit # After commit creation
│ ├── pre-push # Before push operations
│ ├── post-receive # After receive operations
│ └── [custom] # Custom hooks
├── Hook Types:
│ ├── Client-side: pre-commit, commit-msg, etc.
│ ├── Server-side: pre-receive, post-receive, etc.
│ └── Special: post-rewrite, post-checkout, etc.
└── Hook Management:
├── Executable scripts
├── Exit codes control Git behavior
├── Environment variables available
└── Hook chaining possible
Worktree Storage:
├── worktrees/
│ ├── <worktree-name>/
│ │ ├── gitdir # Path to main repo .git
│ │ ├── HEAD # Worktree HEAD
│ │ ├── index # Worktree index
│ │ └── [custom] # Worktree-specific files
├── Worktree Features:
│ ├── Multiple working trees
│ ├── Shared object storage
│ ├── Independent branches
│ └── Sparse checkouts
└── Worktree Management:
├── git worktree add
├── git worktree remove
├── git worktree prune
└── git worktree list
GC-Related Files:
├── .git/gc.log # Last GC operation log
├── .git/shallow # Shallow clone boundaries
└── .git/modules/ # Submodule repositories
GC Process:
├── Loose object packing
├── Reflog expiration
├── Unreachable object removal
└── Repository optimization
Submodule Storage:
├── modules/
│ ├── <submodule-name>/
│ │ ├── objects/ # Submodule objects
│ │ ├── refs/ # Submodule refs
│ │ └── [other]/ # Standard repo structure
├── Submodule Features:
│ ├── Nested repositories
│ ├── Independent histories
│ ├── Shared vs separate clones
│ └── Update strategies
└── Submodule Management:
├── .gitmodules file
├── git submodule commands
└── Recursive operations
Extension Areas:
├── rebase-apply/ # Rebase state
├── rebase-merge/ # Interactive rebase state
├── CHERRY_PICK_HEAD # Cherry-pick state
├── MERGE_HEAD # Merge state
└── [custom]/ # Tool-specific state
State Management:
├── Temporary operation state
├── Recovery information
├── Progress tracking
└── Cleanup on completion
Terminal window
# Inspect repository structure
find .git -type f | head -20 # List all files
du -sh .git/* # Directory sizes
git count-objects -v # Object statistics
# Reference inspection
git show-ref # All references
git for-each-ref --format='%(refname)' # Formatted refs
ls -la .git/refs/ # Reference files
# Configuration inspection
git config --list --local # Local config
cat .git/config # Raw config
Terminal window
# General maintenance
git gc --aggressive # Full garbage collection
git fsck --full --strict # Integrity check
git repack -a -d --threads=4 # Repack objects
# Reference maintenance
git pack-refs --all # Pack references
git reflog expire --expire=90.days --all # Clean reflogs
git update-ref --stdin # Bulk ref updates
# Index maintenance
git update-index --refresh # Refresh index
git update-index --really-refresh # Force refresh
Terminal window
# Repository backup
tar czf repo-backup.tar.gz .git/ # Full backup
git bundle create repo.bundle --all # Bundle backup
# Repository recovery
git fsck --unreachable | grep commit # Find lost commits
git update-ref refs/heads/recovered <commit> # Recover refs
git reflog --all # Check reflog history
# Repository cloning
git clone --mirror <source> <backup> # Mirror clone
git remote add backup <backup-url> # Add backup remote
Working Directory Repository:
├── .git/ at working tree root
├── Working files present
├── Can perform all operations
├── Default for development
└── git init creates this type
Bare Repository:
├── <project>.git/ directory only
├── No working tree
├── Push/fetch operations only
├── Server-side repositories
└── git init --bare creates this type
Key Differences:
├── Working tree: Present vs absent
├── Operations: Full vs limited
├── Use cases: Development vs sharing
└── Commands: All vs subset available
Traditional Layout:
├── All data in .git/
├── Single working tree
├── Simple structure
└── Standard operations
Modern Layout:
├── Worktrees for multiple trees
├── Submodules for composition
├── Alternates for sharing
├── Sparse checkouts
└── Advanced features
Evolution:
├── Git 1.x: Basic structure
├── Git 2.x: Worktrees, submodules
├── Current: Advanced features
└── Future: More optimizations
Terminal window
# Diagnose corruption
git fsck --full # Check integrity
git fsck --unreachable # Find unreachable objects
# Recover from corruption
git reflog # Check recent operations
git reset --hard HEAD@{1} # Go back one step
git update-ref -d refs/heads/corrupted # Remove bad refs
# Rebuild corrupted index
rm .git/index # Remove corrupted index
git reset --mixed HEAD # Rebuild index
Terminal window
# Analyze repository size
du -sh .git/ # Total size
du -sh .git/objects/ # Objects size
du -sh .git/logs/ # Reflog size
# Clean up large repositories
git gc --aggressive --prune=now # Aggressive GC
git repack -a -d --depth=250 --window=250 # Deep repack
git reflog expire --expire=1.month --all # Clean reflogs
Terminal window
# Fix permission problems
find .git -type f -exec chmod 644 {} \; # File permissions
find .git -type d -exec chmod 755 {} \; # Directory permissions
chown -R user:group .git/ # Ownership
# Check permissions
ls -la .git/HEAD # Check key files
git status # Test operations
Terminal window
# Handle path problems
git config core.ignorecase false # Case sensitivity
git config core.precomposeunicode true # Unicode handling
git config core.protectHFS false # HFS protection
git config core.protectNTFS false # NTFS protection
# Check path encoding
locale # System locale
git config --get gui.encoding # Git encoding
Terminal window
# Fix config issues
git config --list --local # Check current config
cp .git/config .git/config.backup # Backup config
git config --unset-all <key> # Remove bad config
git config --replace-all <key> <value> # Fix config
# Reinitialize config
rm .git/config # Remove corrupted config
git init # Reinitialize (keeps objects)
Terminal window
# Advanced repository operations
git update-ref -d refs/heads/old-branch # Delete refs
git update-ref refs/heads/new-branch <commit> # Create refs
git replace <old> <new> # Object replacement
git filter-branch --tree-filter <command> # History filtering
# Repository restructuring
git mv <old-path> <new-path> # Move files
git rm -r <old-dir> # Remove directories
git read-tree --prefix=<dir>/ <tree> # Add subtrees
Terminal window
# Set up mirroring
git clone --mirror <source> <mirror> # Create mirror
git remote add mirror <mirror-url> # Add mirror remote
git config remote.mirror.mirror true # Configure mirroring
# Backup strategies
git bundle create backup.bundle --all # Bundle backup
tar czf repo-backup.tar.gz .git/ # Directory backup
rsync -av .git/ /backup/location/ # Incremental backup
Terminal window
# Migrate repository
git clone --bare <old-repo> <new-repo> # Clone bare
git remote add old <old-repo> # Add old remote
git push --all new # Push everything
git push --tags new # Push tags
# Update remotes
git remote set-url origin <new-url> # Update origin
git remote remove old # Remove old remote
Terminal window
# Repository analysis scripts
analyze_repo() {
echo "=== Repository Analysis ==="
# Size analysis
echo "Repository size:"
du -sh .git/
du -sh .git/objects/
du -sh .git/logs/
# Object analysis
echo "Object counts:"
git count-objects -v
# Reference analysis
echo "Reference counts:"
echo "Branches: $(git branch -a | wc -l)"
echo "Tags: $(git tag | wc -l)"
echo "Remotes: $(git remote | wc -l)"
# History analysis
echo "Commit statistics:"
echo "Total commits: $(git rev-list --all --count)"
echo "Authors: $(git shortlog -sn | wc -l)"
}
analyze_repo
Repository Structure Guidelines:
├── Keep .git/ clean and optimized
├── Use descriptive branch names
├── Regular maintenance (gc, repack)
├── Backup critical repositories
├── Document repository structure
└── Use consistent naming conventions
Maintenance Schedule:
├── Daily: git status, basic operations
├── Weekly: git gc, reflog cleanup
├── Monthly: Full backup, integrity checks
└── Quarterly: Major cleanup, migration review
Repository Security:
├── Protect .git/ directory permissions
├── Use signed commits for important work
├── Validate hooks before installation
├── Monitor repository access
├── Regular security audits
└── Backup encryption
Access Control:
├── SSH key management
├── HTTPS authentication
├── Repository permissions
├── Hook security
└── Audit logging
Performance Tuning:
├── Regular garbage collection
├── Object packing optimization
├── Reference packing
├── Sparse checkout for large repos
├── Alternates for shared storage
└── SSD storage for better I/O
Monitoring:
├── Repository size trends
├── Operation performance
├── Disk space usage
├── Network transfer rates
└── User activity patterns
#!/bin/bash
# Repository health monitoring
monitor_repo_health() {
echo "=== Repository Health Report ==="
echo "Generated: $(date)"
echo ""
# Basic checks
if [ ! -d .git ]; then
echo "ERROR: Not a Git repository"
exit 1
fi
# Repository type
if git config --get core.bare 2>/dev/null | grep -q true; then
echo "Repository type: Bare"
else
echo "Repository type: Working directory"
fi
# Size information
echo "Size information:"
echo " Total: $(du -sh .git/ | cut -f1)"
echo " Objects: $(du -sh .git/objects/ 2>/dev/null | cut -f1)"
echo " Refs: $(du -sh .git/refs/ 2>/dev/null | cut -f1)"
echo " Logs: $(du -sh .git/logs/ 2>/dev/null | cut -f1)"
# Object statistics
echo "Object statistics:"
git count-objects -v 2>/dev/null | sed 's/^/ /'
# Reference counts
echo "Reference counts:"
echo " Branches: $(git branch 2>/dev/null | wc -l)"
echo " Remote branches: $(git branch -r 2>/dev/null | wc -l)"
echo " Tags: $(git tag 2>/dev/null | wc -l)"
# Integrity check
echo "Integrity check:"
if git fsck --quiet 2>/dev/null; then
echo " ✓ Repository integrity OK"
else
echo " ✗ Repository integrity issues found"
echo " Run 'git fsck' for details"
fi
# Configuration check
echo "Configuration check:"
if [ -f .git/config ]; then
echo " ✓ Config file exists"
else
echo " ✗ Config file missing"
fi
# Hook check
if [ -d .git/hooks ]; then
hook_count=$(find .git/hooks -type f -executable | wc -l)
echo " Executable hooks: $hook_count"
fi
echo ""
echo "Health check complete"
}
monitor_repo_health
Terminal window
# Repository migration with layout preservation
migrate_repository() {
local source_repo="$1"
local target_repo="$2"
local migration_type="${3:-full}"
echo "Migrating repository from $source_repo to $target_repo"
echo "Migration type: $migration_type"
# Validate source
if [ ! -d "$source_repo" ]; then
echo "Source repository not found: $source_repo"
return 1
fi
# Create target directory
mkdir -p "$target_repo"
case "$migration_type" in
"bare")
# Bare repository migration
echo "Creating bare repository..."
git clone --bare "$source_repo" "$target_repo"
# Preserve configuration
if [ -f "$source_repo/.git/config" ]; then
cp "$source_repo/.git/config" "$target_repo/config"
fi
;;
"mirror")
# Mirror migration
echo "Creating mirror..."
git clone --mirror "$source_repo" "$target_repo"
# Convert to bare if needed
cd "$target_repo"
git config core.bare true
cd -
;;
"full")
# Full migration with working tree
echo "Creating full repository..."
git clone "$source_repo" "$target_repo"
# Preserve additional files
cp "$source_repo/.gitignore" "$target_repo/" 2>/dev/null || true
cp "$source_repo/.gitattributes" "$target_repo/" 2>/dev/null || true
;;
esac
# Verify migration
echo "Verifying migration..."
if GIT_DIR="$target_repo" git fsck --quiet 2>/dev/null; then
echo "✓ Migration successful"
else
echo "✗ Migration verification failed"
return 1
fi
# Show statistics
echo "Migration statistics:"
echo " Source objects: $(GIT_DIR="$source_repo/.git" git count-objects -v 2>/dev/null | grep 'count:' | cut -d: -f2)"
echo " Target objects: $(GIT_DIR="$target_repo" git count-objects -v 2>/dev/null | grep 'count:' | cut -d: -f2)"
echo "Migration complete"
}
migrate_repository "/path/to/source" "/path/to/target" "bare"
Terminal window
# Automated repository cleanup
automated_cleanup() {
local cleanup_level="${1:-standard}"
echo "Starting automated repository cleanup: $cleanup_level"
# Backup current state
backup_ref=$(git rev-parse HEAD)
echo "Backup reference: $backup_ref"
case "$cleanup_level" in
"light")
# Light cleanup
git gc --quiet
git reflog expire --expire=30.days --all
;;
"standard")
# Standard cleanup
git gc --aggressive --quiet
git repack -a -d --quiet
git pack-refs --all
git reflog expire --expire=90.days --all
;;
"deep")
# Deep cleanup
git gc --aggressive --prune=now
git repack -a -d --depth=250 --window=250
git pack-refs --all
git reflog expire --expire=1.year --all
git clean -fd # Remove untracked files
;;
esac
# Update statistics
echo "Cleanup results:"
echo " Objects: $(git count-objects | cut -d' ' -f1) loose, $(git count-objects | cut -d' ' -f3) packs"
echo " Size: $(du -sh .git/ | cut -f1)"
# Verify repository
if git fsck --quiet; then
echo "✓ Repository integrity verified"
else
echo "✗ Repository integrity issues - check backup: $backup_ref"
git reset --hard "$backup_ref" # Restore if needed
fi
echo "Cleanup complete"
}
automated_cleanup "standard"
Terminal window
# Repository auditing and compliance
audit_repository() {
echo "=== Repository Audit Report ==="
echo "Audit date: $(date)"
echo ""
# Repository identification
if [ -f .git/config ]; then
repo_url=$(git config --get remote.origin.url 2>/dev/null || echo "No remote configured")
echo "Repository URL: $repo_url"
fi
# Access control audit
echo "Access control:"
if [ -d .git/hooks ]; then
hook_count=$(find .git/hooks -type f -executable | wc -l)
echo " Executable hooks: $hook_count"
# Check for security hooks
if [ -x .git/hooks/pre-receive ]; then
echo " ✓ Pre-receive hook present"
else
echo " ⚠ No pre-receive hook"
fi
fi
# Data integrity
echo "Data integrity:"
if git fsck --quiet 2>/dev/null; then
echo " ✓ Object integrity OK"
else
echo " ✗ Object integrity issues"
fi
# Backup status
echo "Backup status:"
if [ -d "../backup" ] || [ -f "../backup.tar.gz" ]; then
echo " ✓ Backup appears to exist"
else
echo " ⚠ No backup found"
fi
# Compliance checks
echo "Compliance checks:"
# Check for large files
large_files=$(find .git/objects -type f -size +50M 2>/dev/null | wc -l)
if [ "$large_files" -gt 0 ]; then
echo " ⚠ Large files detected: $large_files"
fi
# Check for sensitive data patterns
sensitive_patterns=("password" "secret" "key" "token")
for pattern in "${sensitive_patterns[@]}"; do
matches=$(git log --all --grep="$pattern" --oneline | wc -l)
if [ "$matches" -gt 0 ]; then
echo " ⚠ Potential sensitive data: $pattern ($matches matches)"
fi
done
# Configuration audit
echo "Configuration audit:"
if git config --get receive.denyNonFastForwards | grep -q true; then
echo " ✓ Non-fast-forward protection enabled"
else
echo " ⚠ Non-fast-forward protection disabled"
fi
echo ""
echo "Audit complete"
}
audit_repository
Terminal window
# Create repository templates
create_repo_template() {
local template_name="$1"
local template_dir="$HOME/.git-templates/$template_name"
echo "Creating repository template: $template_name"
# Create template directory
mkdir -p "$template_dir"
# Initialize template
cd "$template_dir"
git init --bare
# Set up standard hooks
mkdir -p hooks
cat > hooks/post-commit << 'EOF'
#!/bin/bash
# Template post-commit hook
echo "Commit created in $(basename "$GIT_DIR")"
EOF
chmod +x hooks/post-commit
# Set up standard config
cat > config << EOF
[core]
repositoryformatversion = 0
filemode = true
bare = true
[receive]
denyNonFastForwards = true
[gc]
auto = 1000
EOF
# Create template description
cat > description << EOF
Repository template: $template_name
Created: $(date)
Purpose: Standard repository setup
EOF
# Set up standard structure
mkdir -p info
cat > info/exclude << EOF
# Template exclude patterns
*.tmp
*.log
.DS_Store
Thumbs.db
EOF
cd -
echo "Template created at: $template_dir"
echo "Use with: git clone --template=$template_dir <repo>"
}
create_repo_template "standard-project"

What are the main differences between bare and non-bare repositories?

Section titled “What are the main differences between bare and non-bare repositories?”

Bare repositories have no working tree and are used for sharing (pushing/pulling), while non-bare repositories have a working tree for development. Bare repos end in .git and have core.bare=true.

Objects are stored in .git/objects/ directory, either as loose files in subdirectories (first two chars of SHA-1) or in packed archives (.pack/.idx files) for efficiency.

What is the purpose of the refs/ directory?

Section titled “What is the purpose of the refs/ directory?”

refs/ contains reference files for branches (heads/), remote tracking branches (remotes/), tags (tags/), and other references like notes and replacements.

Repository configuration is stored in .git/config file. Global config in ~/.gitconfig, system config in /etc/gitconfig. Some settings also in info/ subdirectory.

The index (staging area) is stored in .git/index file. It contains file stat info, object IDs, and tracks staged changes for the next commit.

What are reflogs and where are they stored?

Section titled “What are reflogs and where are they stored?”

Reflogs track reference changes and are stored in .git/logs/ directory. They enable recovery from mistakes and provide reference history.

How do Git hooks work and where are they stored?

Section titled “How do Git hooks work and where are they stored?”

Hooks are executable scripts in .git/hooks/ directory. They run at specific points in Git operations and can modify behavior or enforce policies.

packed-refs is a file that stores multiple references efficiently instead of individual files. It’s created by git pack-refs —all for performance.

Alternates allow sharing object storage between repositories. .git/objects/info/alternates contains paths to other object stores to search.

What is the purpose of the info/ directory?

Section titled “What is the purpose of the info/ directory?”

.git/info/ contains repository-specific information like global excludes, attributes, grafts, and sparse-checkout patterns that shouldn’t be shared.

Worktrees allow multiple working directories for the same repository. Information is stored in .git/worktrees/ with each worktree having its own HEAD and index.

What are the different ways to initialize a Git repository?

Section titled “What are the different ways to initialize a Git repository?”

git init (working directory), git init —bare (bare), git clone (copy existing), git worktree add (add worktree to existing).

Use git fsck to check object connectivity and integrity, git count-objects for statistics, and manual inspection of .git structure.

git gc performs garbage collection: packs loose objects, expires reflogs, removes unreachable objects, and optimizes repository structure.

How do I migrate a repository to a new structure?

Section titled “How do I migrate a repository to a new structure?”

Use git filter-branch, git fast-export/git fast-import, or manual restructuring with git read-tree and update-ref for complex changes.

What are the security implications of repository layout?

Section titled “What are the security implications of repository layout?”

Protect .git directory permissions, validate hooks, monitor access, encrypt backups, and be aware of information leakage through reflogs and object storage.

Applications of the git repository-layout command

Section titled “Applications of the git repository-layout command”
  1. Repository Administration: Understanding and managing Git repository structure
  2. Troubleshooting: Diagnosing repository corruption and performance issues
  3. Migration Planning: Planning repository moves and restructuring
  4. Backup Strategy: Designing effective repository backup approaches
  5. Security Auditing: Assessing repository security and access controls
  6. Performance Tuning: Optimizing repository layout for better performance