Git — Advanced Workflows
Interactive Rebase
Rewrite local commit history before pushing — squash, reorder, edit, drop.
git rebase -i HEAD~5 # Rebase last 5 commits
git rebase -i main # Rebase onto main
Editor opens with a pick list:
pick abc1234 feat: add login page
pick def5678 fix: typo in login
pick ghi9012 feat: add logout button
pick jkl3456 fix: button alignment
pick mno7890 test: add login tests
| Command | Effect |
|---|---|
pick |
Keep commit as-is |
reword |
Keep commit, edit message |
squash |
Merge into previous commit, combine messages |
fixup |
Merge into previous commit, discard message |
edit |
Pause rebase to amend commit |
drop |
Remove commit entirely |
Squash Workflow
git rebase -i HEAD~3
# Change 2nd and 3rd to "squash" or "fixup"
# Save → edit combined message → done
Rule: Only rebase commits that have not been pushed to a shared branch.
Autosquash
Mark fixup commits at creation time — rebase auto-reorders them:
git commit --fixup abc1234 # Creates "fixup! <original msg>"
git commit --squash abc1234 # Creates "squash! <original msg>"
git rebase -i --autosquash main # Fixups auto-placed after target
Enable globally:
git config --global rebase.autoSquash true
Stash
Temporarily shelve changes without committing.
git stash # Stash tracked changes
git stash push -m "AUTH-123: wip oauth flow" # Stash with descriptive message
git stash -m "AUTH-123: wip oauth flow" # Short form of stash push -m
git stash -u # Include untracked files
git stash -a # Include untracked + ignored
git stash list # Show all stashes
git stash show stash@{0} # Summary of latest stash
git stash show -p stash@{0} # Full diff of stash
git stash pop # Apply latest + remove from list
git stash apply stash@{2} # Apply specific stash (keep in list)
git stash drop stash@{0} # Delete specific stash
git stash clear # Delete all stashes
Tip: Always use -m "message" — unnamed stashes become unreadable in git stash list.
Stash to Branch
git stash branch new-feature stash@{0} # Create branch from stash + apply
Cherry-Pick
Copy specific commits from another branch.
git cherry-pick abc1234 # Apply single commit
git cherry-pick abc1234 def5678 # Multiple commits
git cherry-pick abc1234..ghi9012 # Range (exclusive..inclusive)
git cherry-pick -n abc1234 # Apply without committing (stage only)
git cherry-pick -x abc1234 # Append "(cherry picked from ...)" to msg
Handling Conflicts
git cherry-pick abc1234
# CONFLICT → resolve files → then:
git add .
git cherry-pick --continue
# Or abort:
git cherry-pick --abort
Use cases: hotfix backport, pulling a single feature commit across branches.
Worktree
Check out multiple branches simultaneously in separate directories — no stashing needed.
git worktree add ../hotfix hotfix/urgent # Checkout branch in new dir
git worktree add -b feature/new ../new-feature # Create + checkout new branch
git worktree list # Show all worktrees
git worktree remove ../hotfix # Remove worktree
repo/ ← main (primary)
../hotfix/ ← hotfix/urgent (worktree)
../new-feature/ ← feature/new (worktree)
Each worktree has its own working directory and index, but shares the same .git object store.
Submodules
Embed one Git repo inside another.
git submodule add https://github.com/org/lib.git libs/lib
git submodule update --init --recursive # Clone submodules after clone
git submodule update --remote # Pull latest from submodule remote
git clone --recurse-submodules https://github.com/org/repo.git # Clone with submodules
Gotcha: Submodules pin to a specific commit — you must explicitly update and commit the new reference.
Alternative: For simpler dependency management, consider git subtree or package managers.
Rebase onto
Move a branch to a new base point.
git rebase --onto main feature/base feature/child
Before:
main ──●──●
\
base ──●──●
\
child ──●──●
After:
main ──●──●──●──● (child commits replayed on main)
Partial Operations
git add -p file.py # Stage specific hunks
git restore -p file.py # Discard specific hunks (modern)
git restore --staged -p file.py # Unstage specific hunks (modern)
git stash push -p # Stash specific hunks
The -p (patch) flag opens an interactive prompt for each change hunk: y accept, n skip, s split, q quit.
Classic equivalents (still work):
git checkout -p file.py(discard),git reset -p file.py(unstage).