Advanced Git techniques for maintaining clean, linear history and managing branches effectively.
Use rebasing to maintain clean, linear history and keep feature branches up to date.
Use interactive rebase to clean up commits before creating pull requests.
# Clean up commits before creating PR
git rebase -i HEAD~4
# Interactive options:
# pick = keep commit as-is
# reword = keep commit but edit message
# edit = keep commit but allow editing
# squash = combine with previous commit
# fixup = like squash but discard commit message
# drop = remove commit entirely
# Example session:
pick f7f3f6d feat: add login form
reword 310154e fix: typo in form validation
squash a5f4a0d fix: address code review feedback
drop c5f4a0d debug: temporary logging
# Git will process each command and prompt for input when needed Regularly rebase your feature branch onto the latest develop branch to avoid conflicts.
# Update your feature branch with latest changes
git checkout feature/new-dashboard
git fetch origin
git rebase origin/develop
# If there are conflicts, resolve them:
# 1. Edit files to resolve conflicts
# 2. Stage the resolved files
git add .
# 3. Continue the rebase
git rebase --continue
# If you need to skip a commit (rare):
git rebase --skip
# If you need to abort and start over:
git rebase --abort
# Force push the rebased branch (only for feature branches!)
git push --force-with-lease origin feature/new-dashboard Understanding when to use rebase versus merge is crucial for maintaining good Git history.
# Before rebase (with merge):
* a1b2c3d (main) Add payment feature
|\
| * d4e5f6g (feature) Fix validation bug
| * g7h8i9j Add user input form
|/
* j9k8l7m Update dependencies
# After rebase:
* d4e5f6g (feature) Fix validation bug
* g7h8i9j Add user input form
* a1b2c3d (main) Add payment feature
* j9k8l7m Update dependencies # Merge commit preserves branch context:
* m1n2o3p (main) Merge branch 'feature/user-auth'
|\
| * p4q5r6s Add logout functionality
| * s7t8u9v Add login form
|/
* v1w2x3y Update base authentication Use rebase --onto to move commits to a different base.
# Move commits from one branch to another
# Scenario: You branched off feature-a, but want to base on main instead
# Original state:
# main: A - B - C
# feature-a: A - B - C - D - E
# feature-b: A - B - C - D - E - F - G (branched from feature-a)
# Move feature-b commits (F, G) to be based on main (C):
git rebase --onto main feature-a feature-b
# Result:
# feature-b: A - B - C - F' - G' (F' and G' are rebased F and G) Create fixup commits during development and squash them automatically.
# During development, create a fixup commit
git add .
git commit --fixup abc1234
# This creates a commit like: "fixup! original commit message"
# Later, auto-squash during rebase
git rebase -i --autosquash HEAD~5
# Git will automatically arrange fixup commits next to their targets
# and mark them for squashing Sometimes you need to rebase but preserve merge commits.
# Preserve merge commits during rebase
git rebase --rebase-merges origin/main
# This maintains the branch structure while rebasing If rebase goes wrong, use the reflog to recover:
# Find the commit before rebase started
git reflog
# Reset to the commit before rebase (replace abc1234 with actual commit)
git reset --hard abc1234
# Alternative: use ORIG_HEAD (points to pre-rebase state)
git reset --hard ORIG_HEAD git branch backup-branch