| title | Playbook |
|---|---|
| description | Step-by-step recipes for common Git tasks — undoing changes, branching, merging, rebasing, stashing, tagging, and debugging. |
| section | playbook |
| order | 7 |
This chapter is a quick-reference collection of recipes for common Git tasks. Each recipe shows the problem, the commands to solve it, and what to watch out for.
For command syntax, see Appendix. For definitions, see Glossary.
In this chapter you will learn:
- How to undo changes at every level — unstage, reset, revert, and recover
- Branching recipes — create, delete, rename, and inspect branches
- Merging recipes — fast-forward, no-ff, squash, conflict resolution
- Rebasing — linearize history and squash commits interactively
- Remote operations — push, pull, force push safely, sync forks
- Cherry-picking — apply individual commits across branches
- Stashing — save and restore work in progress
- Tagging — create, push, and delete annotated tags
- Submodules — add, clone, update, and remove submodules
- Debugging — bisect, blame, and search commit history
- Configuration — identity, defaults, aliases, and diagnostics
$ git restore <file>
Reverts the file in your working tree to match the last commit. Uncommitted edits are lost.
$ git restore --staged <file>
Removes the file from the index but keeps your edits in the working tree.
$ git reset --soft HEAD~1
The commit is removed but your changes stay in the index, ready to recommit.
$ git reset HEAD~1
The default (--mixed). Changes go back to the working tree as
unstaged edits.
$ git reset --hard HEAD~1
The commit and all changes are gone. Recover with git reflog if
needed.
$ git revert <hash>
Creates a new commit that undoes the changes. Safe to use on shared branches.
$ git reflog # find the hash
$ git branch recovered <hash> # or: git reset --hard <hash>
Works as long as the reflog entry has not expired (default: 30 days).
$ git switch -c feature/name
$ git branch -d feature/name # local (safe — only if merged)
$ git branch -D feature/name # local (force — even if unmerged)
$ git push origin --delete feature/name # remote
$ git branch -m new-name
$ git branch --merged main
Safe to delete any branch listed here (except main itself).
$ git switch main
$ git merge feature/name
$ git merge --no-ff feature/name
$ git merge --squash feature/name
$ git commit -m "Add feature"
$ git merge --abort
Returns everything to the pre-merge state.
$ git status # identify conflicting files
# ... edit each file, remove markers ...
$ git add <file> # stage resolved file
$ git commit # complete the merge
$ git switch feature/name
$ git rebase main
$ git rebase -i HEAD~3
# change "pick" to "squash" for commits to combine
$ git rebase --abort
$ git add <file>
$ git rebase --continue
$ git push -u origin feature/name
$ git pull --rebase origin main
$ git pull origin main # fetch + merge
$ git push origin main # retry
$ git push --force-with-lease origin feature/name
Never use --force on shared branches.
$ git fetch upstream
$ git switch main
$ git merge upstream/main
$ git push origin main
$ git fetch origin
$ git log main..origin/main --oneline # what's new on remote
$ git diff main origin/main # line-by-line changes
$ git merge origin/main # integrate when ready
$ git cherry-pick <hash>
$ git cherry-pick --no-commit <hash>
Useful when you want to modify the change before committing.
$ git stash push -m "description"
$ git stash push -u -m "description"
$ git stash pop # restore and remove from stash
$ git stash apply # restore but keep in stash
$ git stash list # show all entries
$ git stash drop stash@{0} # delete a specific entry
$ git stash clear # delete all entries
$ git tag -a v1.0.0 -m "First release"
$ git tag -a v0.9.0 -m "Beta release" <hash>
$ git push origin v1.0.0 # single tag
$ git push origin --tags # all tags
$ git tag -d v1.0.0 # local
$ git push origin --delete v1.0.0 # remote
$ git submodule add <url> <path>
$ git commit -m "Add submodule"
$ git clone --recurse-submodules <url>
Or after a regular clone:
$ git submodule update --init --recursive
$ git submodule update --remote
$ git add <path>
$ git commit -m "Update submodule"
$ git submodule deinit <path>
$ git rm <path>
$ rm -rf .git/modules/<path>
$ git commit -m "Remove submodule"
$ git bisect start
$ git bisect bad # current commit has the bug
$ git bisect good <hash> # this older commit was fine
# ... test each midpoint, mark good/bad ...
$ git bisect reset # done — return to original branch
$ git bisect start HEAD <good-hash>
$ git bisect run ./test.sh
$ git blame <file>
$ git log --grep="fix" --oneline
$ git log -S "functionName" --oneline
$ git config --global user.name "Your Name"
$ git config --global user.email "you@example.com"
$ git config --global init.defaultBranch main
$ git config --global pull.rebase true
$ git config --global alias.hist "log --oneline --graph --all"
$ git config --list --show-origin