Introduction
Git proficiency goes beyond basic commits. Senior engineers need to manage complex branching strategies, resolve conflicts efficiently, and maintain clean project history. This guide covers practical Git workflows and techniques for professional development teams.
Branch Management
Feature Branch Workflow
# Start from updated main
git checkout main
git pull origin main
# Create feature branch
git checkout -b feature/user-authentication
# Work, commit, push
git add .
git commit -m "Add login form component"
git push -u origin feature/user-authentication
# Keep branch updated with main
git fetch origin
git rebase origin/main
# Or merge if you prefer
git merge origin/main
# After review, merge to main
git checkout main
git merge --no-ff feature/user-authentication
git push origin main
# Clean up
git branch -d feature/user-authentication
git push origin --delete feature/user-authentication
Branch Naming Conventions
feature/add-user-auth # New features
bugfix/fix-login-error # Bug fixes
hotfix/security-patch # Urgent production fixes
release/v1.2.0 # Release preparation
docs/update-readme # Documentation
refactor/cleanup-models # Code refactoring
Commit Best Practices
Atomic Commits
Each commit should be a single logical change:
# Bad: One commit with multiple changes
git commit -m "Add login, fix header, update styles"
# Good: Separate commits
git commit -m "Add login form validation"
git commit -m "Fix header alignment on mobile"
git commit -m "Update button styles for consistency"
Commit Message Format
<type>(<scope>): <subject>
<body>
<footer>
Example:
feat(auth): add JWT token refresh mechanism
Implement automatic token refresh when access token expires.
The refresh happens 5 minutes before expiration to prevent
interruption of user sessions.
Closes #123
Types:
feat: New featurefix: Bug fixdocs: Documentationstyle: Formatting (no code change)refactor: Code restructuringtest: Adding testschore: Maintenance tasks
Interactive Staging
# Stage specific hunks
git add -p
# y: stage this hunk
# n: skip this hunk
# s: split into smaller hunks
# e: edit hunk manually
# Stage specific files interactively
git add -i
Merging Strategies
Merge vs Rebase
# Merge: Preserves history, creates merge commit
git checkout main
git merge feature/branch
# Rebase: Linear history, rewrites commits
git checkout feature/branch
git rebase main
git checkout main
git merge feature/branch # Fast-forward
When to Use Each
| Scenario | Strategy |
|---|
| Public branches | Merge |
| Personal feature branches | Rebase |
| Shared feature branches | Merge |
| Updating from main | Rebase (usually) |
Resolving Conflicts
# During merge
git merge feature/branch
# CONFLICT in file.txt
git status # See conflicted files
# Edit files to resolve conflicts
# Remove conflict markers: <<<<<<<, =======, >>>>>>>
# Mark as resolved
git add file.txt
git commit
# During rebase
git rebase main
# CONFLICT in file.txt
# Resolve conflicts
git add file.txt
git rebase --continue
# Or abort if needed
git rebase --abort
Keeping Local or Remote Version
# Keep our version (current branch)
git checkout --ours file.txt
# Keep their version (incoming branch)
git checkout --theirs file.txt
# For binary files
git checkout --ours -- path/to/binary.png
git add path/to/binary.png
Rewriting History
Amend Last Commit
# Fix commit message
git commit --amend -m "New message"
# Add forgotten files
git add forgotten-file.txt
git commit --amend --no-edit
Interactive Rebase
# Edit last 5 commits
git rebase -i HEAD~5
# In the editor:
# pick abc1234 First commit
# reword def5678 Second commit # Change message
# squash ghi9012 Third commit # Combine with previous
# fixup jkl3456 Fourth commit # Combine, discard message
# drop mno7890 Fifth commit # Remove entirely
# edit pqr1234 Sixth commit # Stop to edit
# During edit stop
# Make changes
git add .
git commit --amend
git rebase --continue
Squash Commits Before Merge
# Squash all feature branch commits into one
git checkout feature/branch
git rebase -i main
# Mark all but first as 'squash'
# Or squash merge
git checkout main
git merge --squash feature/branch
git commit -m "Add complete feature"
Stashing Changes
Basic Stash
# Stash current changes
git stash
# Stash with message
git stash save "WIP: working on login form"
# Include untracked files
git stash -u
# Include untracked and ignored files
git stash -a
# List stashes
git stash list
# Apply and remove
git stash pop
# Apply without removing
git stash apply
# Apply specific stash
git stash apply stash@{2}
# Drop stash
git stash drop stash@{0}
# Clear all stashes
git stash clear
Stash Workflow
# Working on a feature; an urgent bug comes in
git stash save "feature: in progress"
# Fix bug on main
git checkout main
git checkout -b hotfix/urgent
# ... fix bug ...
git commit -m "fix: urgent bug"
git checkout main
git merge hotfix/urgent
# Return to feature
git checkout feature/branch
git stash pop
Comparing and Reviewing
Diff Commands
# Working directory vs. staging
git diff
# Staging vs. last commit
git diff --staged
git diff --cached
# Between commits
git diff abc123..def456
# Between branches
git diff main..feature/branch
# Specific file
git diff HEAD~3 -- path/to/file.txt
# Summary only
git diff --stat
Compare Branches
# Commits in feature not in main
git log main..feature/branch
# Commits in either but not both
git log main...feature/branch
# Which branch contains a commit
git branch --contains abc123
Recovering From Mistakes
Undo Changes
# Discard working directory changes
git checkout -- file.txt
git restore file.txt # Git 2.23+
# Unstage file
git reset HEAD file.txt
git restore --staged file.txt # Git 2.23+
# Undo last commit (keep changes)
git reset --soft HEAD~1
# Undo last commit (discard changes)
git reset --hard HEAD~1
# Revert commit (creates new commit)
git revert abc123
Recover Deleted Branches
# Find the commit
git reflog
# Recreate branch
git checkout -b recovered-branch abc123
Recover Lost Commits
# Find in reflog
git reflog
# Cherry-pick or reset to recover
git cherry-pick abc123
# or
git reset --hard abc123
Git Hooks
Pre-commit Hook
# .git/hooks/pre-commit
#!/bin/sh
# Run linter
npm run lint
if [ $? -ne 0 ]; then
echo "Lint failed. Commit aborted."
exit 1
fi
# Run tests
npm test
if [ $? -ne 0 ]; then
echo "Tests failed. Commit aborted."
exit 1
fi
Commit-msg Hook
# .git/hooks/commit-msg
#!/bin/sh
# Enforce conventional commits
commit_regex='^(feat|fix|docs|style|refactor|test|chore)(\(.+\))?: .{1,50}'
if ! grep -qE "$commit_regex" "$1"; then
echo "Invalid commit message format."
echo "Example: feat(auth): add login functionality"
exit 1
fi
Working with Remote Repositories
SSH Key Setup
# Generate SSH key
ssh-keygen -t ed25519 -C "[email protected]"
# Start SSH agent
eval "$(ssh-agent -s)"
# Add key to agent
ssh-add ~/.ssh/id_ed25519
# Copy public key
cat ~/.ssh/id_ed25519.pub
# Add to GitHub/GitLab settings
Multiple SSH Keys
# ~/.ssh/config
Host github.com
HostName github.com
User git
IdentityFile ~/.ssh/github_key
Host gitlab.company.com
HostName gitlab.company.com
User git
IdentityFile ~/.ssh/gitlab_key
Remove Files from Repo but Keep Locally
# Remove from repo; keep local file
git rm --cached sensitive-file.txt
echo "sensitive-file.txt" >> .gitignore
git commit -m "Remove sensitive file from tracking"
# Remove ignored files from repo
git rm -r --cached .
git add .
git commit -m "Remove files listed in .gitignore"
Compact Repository
# Remove unreferenced objects
git gc --prune=now --aggressive
# Check repository size
git count-objects -vH
Fix Encoding Issues During Merge
# For files with mixed encodings
git config --global merge.renormalize true
# Or manually fix a specific file
git checkout --ours problematic-file.txt
iconv -f WINDOWS-1252 -t UTF-8 problematic-file.txt > temp.txt
mv temp.txt problematic-file.txt
git add problematic-file.txt
git commit
Configuration
Useful Aliases
# Add to ~/.gitconfig
[alias]
st = status -sb
co = checkout
br = branch
ci = commit
lg = log --oneline --graph --decorate -20
lga = log --oneline --graph --decorate --all
last = log -1 HEAD --stat
unstage = reset HEAD --
amend = commit --amend --no-edit
undo = reset --soft HEAD~1
wip = !git add -A && git commit -m 'WIP'
cleanup = !git branch --merged | grep -v main | xargs git branch -d
# Show current branch name
current = rev-parse --abbrev-ref HEAD
# Show files changed in last commit
changed = diff-tree --no-commit-id --name-only -r HEAD
# List branches sorted by last commit
recent = for-each-ref --sort=-committerdate refs/heads --format='%(committerdate:short) %(refname:short)'
Global Configuration
git config --global user.name "Your Name"
git config --global user.email "[email protected]"
git config --global init.defaultBranch main
git config --global pull.rebase true
git config --global core.editor "vim"
git config --global merge.tool vimdiff
# Handle line endings
git config --global core.autocrlf input # Linux/Mac
git config --global core.autocrlf true # Windows
# Better diff algorithm
git config --global diff.algorithm histogram
# Show untracked directories
git config --global status.showUntrackedFiles all
Advanced Workflows
Interactive Rebase for Clean History
Clean your history before merging to main:
# Edit last 5 commits
git rebase -i HEAD~5
# Actions available:
# - Squash: Combine "WIP" commits into "Feature X"
# - Reword: Fix typos in commit messages
# - Drop: Remove accidental commits
# - Edit: Stop to modify a commit
Senior Tip: Treat your git history as a product. A clean history (linear, atomic commits) allows you to revert specific features without reverting the whole release.
Bisecting Bugs with Binary Search
When did the bug appear?
- Manual search: Log n steps
git bisect: Binary search (O(log n))
git bisect start
git bisect bad HEAD
git bisect good v1.0
# Automate the testing!
git bisect run npm test
Git will check out the middle commit, run the test, and narrow the range automatically until it finds the exact commit that introduced the bug.
Submodules vs. Monorepo
Submodules: Pointers to specific commits of other repos.
- Pain: Forget to update the pointer? Build breaks.
- Senior View: Avoid submodules unless strictly necessary. Prefer monorepos (Nx, Turborepo) or package managers (npm private registry).
# If you must use submodules
git submodule update --init --recursive
git submodule foreach git pull origin main
Conclusion
Mastering Git workflows improves team collaboration and code quality. Use feature branches for isolation, meaningful commits for history, and rebase for clean merges. Know your recovery options—reflog is your friend when things go wrong. Remember: a clean git history is documentation—make it tell a clear story of how your code evolved.