Playbook

1. Overview

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:

2. Undoing changes

Discard unstaged changes to a file

$ git restore <file>

Reverts the file in your working tree to match the last commit. Uncommitted edits are lost.

Unstage a file without losing changes

$ git restore --staged <file>

Removes the file from the index but keeps your edits in the working tree.

Undo the last commit but keep changes staged

$ git reset --soft HEAD~1

The commit is removed but your changes stay in the index, ready to recommit.

Undo the last commit and unstage changes

$ git reset HEAD~1

The default (--mixed). Changes go back to the working tree as unstaged edits.

Undo the last commit and discard all changes

$ git reset --hard HEAD~1

The commit and all changes are gone. Recover with git reflog if needed.

Revert a commit without rewriting history

$ git revert <hash>

Creates a new commit that undoes the changes. Safe to use on shared branches.

Recover a lost commit

$ 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).

3. Branching

Create a branch and switch to it

$ git switch -c feature/name

Delete a branch (local and remote)

$ 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

Rename the current branch

$ git branch -m new-name

See which branches are merged into main

$ git branch --merged main

Safe to delete any branch listed here (except main itself).

4. Merging

Merge a feature branch into main

$ git switch main
$ git merge feature/name

Force a merge commit (no fast-forward)

$ git merge --no-ff feature/name

Squash a branch into a single commit

$ git merge --squash feature/name
$ git commit -m "Add feature"

Abort a conflicted merge

$ git merge --abort

Returns everything to the pre-merge state.

Resolve a merge conflict

$ git status                        # identify conflicting files
# ... edit each file, remove markers ...
$ git add <file>                    # stage resolved file
$ git commit                        # complete the merge

5. Rebasing

Rebase a feature branch onto main

$ git switch feature/name
$ git rebase main

Squash commits with interactive rebase

$ git rebase -i HEAD~3
# change "pick" to "squash" for commits to combine

Abort a conflicted rebase

$ git rebase --abort

Continue after resolving a rebase conflict

$ git add <file>
$ git rebase --continue

6. Remote operations

Push a new branch and set up tracking

$ git push -u origin feature/name

Pull with rebase (avoid merge commits)

$ git pull --rebase origin main

Fix a rejected push

$ git pull origin main              # fetch + merge
$ git push origin main              # retry

Force push safely (after rebase)

$ git push --force-with-lease origin feature/name

Never use --force on shared branches.

Sync a fork with upstream

$ git fetch upstream
$ git switch main
$ git merge upstream/main
$ git push origin main

Fetch and inspect before merging

$ 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

7. Cherry-picking

Apply a single commit from another branch

$ git cherry-pick <hash>

Cherry-pick without committing (stage only)

$ git cherry-pick --no-commit <hash>

Useful when you want to modify the change before committing.

8. Stashing

Save work in progress

$ git stash push -m "description"

Save including untracked files

$ git stash push -u -m "description"

Restore the latest stash

$ git stash pop                     # restore and remove from stash
$ git stash apply                   # restore but keep in stash

List and drop stash entries

$ git stash list                    # show all entries
$ git stash drop stash@{0}          # delete a specific entry
$ git stash clear                   # delete all entries

9. Tagging

Create an annotated tag

$ git tag -a v1.0.0 -m "First release"

Tag a past commit

$ git tag -a v0.9.0 -m "Beta release" <hash>

Push tags to remote

$ git push origin v1.0.0            # single tag
$ git push origin --tags            # all tags

Delete a tag (local and remote)

$ git tag -d v1.0.0                 # local
$ git push origin --delete v1.0.0   # remote

10. Submodules

Add a submodule

$ git submodule add <url> <path>
$ git commit -m "Add submodule"

Clone a repo with submodules

$ git clone --recurse-submodules <url>

Or after a regular clone:

$ git submodule update --init --recursive

Update a submodule to latest

$ git submodule update --remote
$ git add <path>
$ git commit -m "Update submodule"

Remove a submodule

$ git submodule deinit <path>
$ git rm <path>
$ rm -rf .git/modules/<path>
$ git commit -m "Remove submodule"

11. Debugging

Find which commit introduced a bug

$ 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

Automated bisect with a test script

$ git bisect start HEAD <good-hash>
$ git bisect run ./test.sh

See who last changed each line

$ git blame <file>

Search commit messages

$ git log --grep="fix" --oneline

Search file contents across history

$ git log -S "functionName" --oneline

12. Configuration

Set identity

$ git config --global user.name "Your Name"
$ git config --global user.email "you@example.com"

Set default branch name

$ git config --global init.defaultBranch main

Set default pull strategy to rebase

$ git config --global pull.rebase true

Create an alias

$ git config --global alias.hist "log --oneline --graph --all"

See where a setting comes from

$ git config --list --show-origin