Git
What this covers
The everyday git operations that come up in normal development: configuring the repo, staging and committing, branching and merging, syncing with a remote, inspecting history, stashing work in progress, and — the part worth getting right — undoing changes safely. For running parallel branches in separate working directories, see Git Worktrees; for the Gitflow branching model, see Git Flow.
When to use it
- Looking up the right command for a common git operation
- Deciding how to undo something (uncommitted changes, a bad commit, a pushed commit) without losing work
- Choosing between merge and rebase for integrating a branch
- Recovering from a mistake (detached HEAD, wrong branch, accidental reset) — see Troubleshooting
Setup & Configuration
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main
git init # new repo in current directory
git clone <url> # copy an existing repo
git clone <url> <dir> # into a named directory
Staging & Committing
git status # what's changed / staged
git add <file> # stage a specific file
git add -p # stage selectively, hunk by hunk
git add . # stage everything under the current directory
git commit -m "message" # commit staged changes
git commit -am "message" # stage tracked files + commit (skips new files)
git commit --amend # rewrite the last commit (only if not pushed)
git add -p is worth the habit — it lets you review and split changes into
focused commits instead of committing unrelated work together.
Branching & Merging
git branch # list local branches
git switch <branch> # change branch (modern; replaces checkout)
git switch -c <branch> # create and switch
git branch -d <branch> # delete a merged branch
git branch -D <branch> # force-delete an unmerged branch
git merge <branch> # merge <branch> into the current one
git merge --no-ff <branch> # always create a merge commit
Merge vs rebase:
git mergepreserves history exactly and creates a merge commit — safe default, especially for shared branches.git rebase <base>replays your commits on top of<base>for a linear history — cleaner, but rewrites commit hashes.- Never rebase commits that others have already pulled. Rewriting shared history forces everyone else into conflicts.
git rebase main # replay current branch on top of main
git rebase -i HEAD~3 # interactively squash/reword/reorder last 3 commits
Remotes & Syncing
git remote -v # list remotes
git remote add origin <url> # add a remote
git fetch # download remote changes, don't merge
git pull # fetch + merge into current branch
git pull --rebase # fetch + rebase (avoids a merge commit)
git push # push current branch
git push -u origin <branch> # push and set upstream (first time)
git push --force-with-lease # force-push safely — refuses if remote moved unexpectedly
Prefer --force-with-lease over --force: it still refuses to overwrite work
someone else pushed after your last fetch, so you don’t clobber a teammate’s commits.
Inspecting History
git log --oneline --graph --all # compact visual history of all branches
git log -p <file> # history with diffs for one file
git show <commit> # a commit's message and diff
git diff # unstaged changes
git diff --staged # staged changes
git blame <file> # who last changed each line
Stashing
git stash # shelve uncommitted changes, clean the tree
git stash -u # include untracked files
git stash list # list stashes
git stash pop # reapply the most recent stash and drop it
git stash apply # reapply but keep it in the stash list
git stash drop # discard a stash
Stashing is for a quick context switch. For anything longer-lived, a branch (or a worktree) is safer — stashes are easy to forget and have no branch name to remind you what they held.
Undoing Changes
The right command depends on how far the change has gone. From least to most drastic:
# Discard unstaged changes to a file (irreversible — the edits are gone)
git restore <file>
# Unstage a file but keep the edits
git restore --staged <file>
# Undo the last commit, KEEP the changes staged
git reset --soft HEAD~1
# Undo the last commit, keep changes in the working tree (unstaged)
git reset HEAD~1
# Undo the last commit AND discard its changes (destructive)
git reset --hard HEAD~1
# Revert a commit by making a new commit that undoes it (safe for shared history)
git revert <commit>
Rules of thumb:
- Not pushed yet →
resetis fine. - Already pushed / shared → use
revert, notreset --hard+ force-push. git reset --hardandgit restorediscard uncommitted work permanently — there’s no undo. Commit or stash first if unsure.
Troubleshooting
| Symptom | Fix |
|---|---|
| Committed to the wrong branch | git switch <right-branch> then git cherry-pick <commit>; remove it from the wrong branch with git reset --hard HEAD~1 (if unpushed) |
| Detached HEAD (“not on a branch”) | git switch -c <new-branch> to keep the commits, or git switch main to discard them |
| Need a “deleted” commit back | git reflog lists recent HEAD positions; git reset --hard <sha> or git switch -c rescue <sha> |
Accidental reset --hard | The commits usually still exist — find them with git reflog and recover |
| Merge conflict | Edit the marked files, git add them, then git commit (or git merge --continue); git merge --abort to back out |
| Pushed a secret | Rotate the secret immediately — history rewriting doesn’t undo the exposure |