Advanced Git Techniques
Beyond the basics, Git has powerful tools for debugging history, automating workflows, managing large projects, and recovering from mistakes. These techniques separate senior developers from juniors.
Reflog - Your Safety Net
The reflog records every time HEAD changes. Even after a hard reset or dropped stash, you can recover lost commits using the reflog. It is the ultimate undo tool.
# View reflog
git reflog
git reflog show HEAD # Default - HEAD movements
git reflog show main # Reflog for a specific branch
# Recover lost commit after hard reset
git reset --hard HEAD~3 # Oops - lost 3 commits
git reflog # Find the SHA before the reset
git reset --hard abc1234 # Restore to that commit
# Recover a dropped stash
git fsck --lost-found # Find dangling objects
git stash apply <dangling-sha>
# Checkout any historical state
git checkout abc1234 # Detached HEAD at specific commit
git checkout -b recovered-branch abc1234 # Create branch from itBisect - Finding Bugs by Binary Search
git bisect uses binary search to find the exact commit that introduced a bug. It halves the search space at each step, making it extremely efficient even with thousands of commits.
# Start bisect session
git bisect start
git bisect bad # Current commit is broken
git bisect good v2.0.0 # This release was working
# Git checks out middle commit - test it, then mark:
git bisect good # This commit is fine
git bisect bad # This commit is broken
# Repeat until Git identifies the culprit
# Automate with a test script (exits 0 = good, non-zero = bad)
git bisect run npm test
git bisect run ./scripts/check-feature.sh
# End bisect (restores HEAD)
git bisect resetGit Hooks
Git hooks are scripts that run automatically at key points in the Git lifecycle. Use them to enforce code standards, run tests, or prevent bad commits from reaching the remote.
# Hooks live in .git/hooks/ - create executable scripts there
# Common hooks:
# pre-commit - runs before commit is created
# commit-msg - validate commit message format
# pre-push - runs before push
# post-merge - runs after merge
# post-checkout - runs after checkout
# Example: pre-commit hook to run linter
# File: .git/hooks/pre-commit (must be executable: chmod +x)
#!/bin/sh
npm run lint
if [ $? -ne 0 ]; then
echo "Linting failed - fix errors before committing"
exit 1
fi
# Example: commit-msg hook to enforce format
#!/bin/sh
COMMIT_MSG=$(cat "$1")
if ! echo "$COMMIT_MSG" | grep -qE "^(feat|fix|docs|style|refactor|test|chore): .+";
then
echo "Commit message must follow Conventional Commits format"
exit 1
fi
# Share hooks with team using husky (Node.js projects)
npx husky init
# Then add hooks to .husky/ directory (version controlled)Interactive Rebase & Cleaning History
# Squash last 4 commits into one before merging
git rebase -i HEAD~4
# In editor: change "pick" to "squash" (or "s") for commits to merge
# Then write a clean combined commit message
# Reorder commits
# In the rebase editor, just rearrange the lines
# Remove a specific file from all history (dangerous - rewrites all SHAs)
git filter-branch --tree-filter 'rm -f secrets.env' HEAD
# Modern alternative:
git filter-repo --path secrets.env --invert-paths
# Clean untracked files
git clean -n # Dry run - shows what would be deleted
git clean -f # Delete untracked files
git clean -fd # Delete untracked files and directories
git clean -fX # Delete only ignored files (.gitignore)
git clean -fdx # Delete everything untracked + ignoredSubmodules
Submodules allow you to embed one Git repository inside another. Useful for shared libraries or when you need to pin a dependency at a specific commit.
# Add a submodule
git submodule add https://github.com/user/lib.git vendor/lib
git submodule add --branch main https://github.com/user/lib.git vendor/lib
# Clone a repo with submodules
git clone --recurse-submodules https://github.com/user/repo.git
# Or initialize submodules after cloning
git submodule update --init --recursive
# Update all submodules to latest remote commit
git submodule update --remote
git submodule update --remote vendor/lib # Specific submodule
# Run a command in all submodules
git submodule foreach git pull origin main
# Remove a submodule
git submodule deinit vendor/lib
git rm vendor/lib
git commit -m "Remove lib submodule"Keep your own version of these notes — editable, searchable, and organised by your stack.
Start free