solutions to tricky git situations and advanced git operations that are easy to forget
# Rewrite multiple commits
git rebase -i HEAD~5 # Last 5 commits
# Common operations:
# fixup - combine with previous commit, discard message
# squash - combine with previous, keep both messages
# reword - change commit message
# drop - remove commit entirely
# edit - stop for amending
# Force push with lease (safer than force push)
git push --force-with-lease # Fails if remote has new commits
# Cherry-pick specific commits
git cherry-pick <commit-hash> # Single commit
git cherry-pick <hash1>..<hash2> # Range of commits
# Create patch files
git format-patch -1 HEAD # Last commit
git format-patch -n <commit> # Last n commits
git format-patch master..feature # Branch differences
# Apply patches
git apply --check my.patch # Test if patch applies
git am < my.patch # Apply patch with commit info
# Find lost commits (after reset --hard)
git reflog
git checkout -b recovery-branch <hash>
# Recover deleted branch
git reflog | grep -A 1 'to <branch-name>'
git checkout -b <branch-name> <hash>
# Recover stashed changes
git fsck --unreachable | grep commit | cut -d' ' -f3 | xargs git show
# Reset specific paths
git reset --hard origin/master -- path/to/file
# Clean working directory but keep specific files
git stash --keep-index
git stash drop
# Unstage parts of a file
git reset -p HEAD <file>
git bisect start
git bisect bad # Current commit is broken
git bisect good <commit> # Last known good commit
# Automate with test script
git bisect run ./test-script.sh
# After finding the culprit
git bisect reset
# Create new worktree
git worktree add ../path/to/folder branch-name
# List worktrees
git worktree list
# Remove worktree
git worktree remove ../path/to/folder
# Recursive strategy with options
git merge -X ignore-space-change feature
git merge -X theirs feature # Prefer their changes
git merge -X patience feature # Better handling of renamed files
# Octopus merge (multiple branches)
git merge branch1 branch2 branch3
# .pre-commit-config.yaml
default_language_version:
python: python3.9
default_stages: [commit, push]
fail_fast: true
repos:
- repo: local
hooks:
- id: custom-script
name: Custom Validation
entry: ./scripts/validate.sh
language: script
files: \\.py$
stages: [commit]
additional_dependencies: ['pytest']
- repo: https://github.com/psf/black
rev: 22.10.0
hooks:
- id: black
args: ['--check']
exclude: ^(venv/|docs/)
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v0.982
hooks:
- id: mypy
additional_dependencies:
- 'types-requests'
- 'types-PyYAML'
#!/bin/bash
# .git/hooks/pre-push
protected_branches=('main' 'master' 'production')
current_branch=$(git symbolic-ref HEAD | sed -e 's,.*/\(.*\),\1,')
for branch in "${protected_branches[@]}"; do
if [[ $current_branch = $branch ]]; then
read -p "You're about to push to $branch, is that what you intended? [y|n] " -n 1 -r < /dev/tty
echo
if echo $REPLY | grep -E '^[Nn]$' > /dev/null; then
exit 1
fi
fi
done
exit 0
# Update specific submodule to latest
git submodule update --remote --merge specific-submodule
# Execute command in each submodule
git submodule foreach 'git checkout -b feature'
# Remove a submodule
git submodule deinit -f path/to/submodule
git rm -f path/to/submodule
rm -rf .git/modules/path/to/submodule
# Add subtree
git subtree add --prefix=lib/core https://github.com/org/lib.git master
# Update subtree
git subtree pull --prefix=lib/core https://github.com/org/lib.git master
# Push changes back to subtree remote
git subtree push --prefix=lib/core https://github.com/org/lib.git master
# Debug git commands
GIT_TRACE=1 git push origin master
# Debug specific areas
GIT_TRACE_PACKET=1 # Network operations
GIT_TRACE_PERFORMANCE=1 # Performance data
GIT_TRACE_SETUP=1 # Setup info
# Find large files in history
git rev-list --objects --all \
| git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' \
| sed -n 's/^blob //p' \
| sort -k2nr \
| head -10