Git Survival Guide
The commands you actually reach for — daily flow, undo anything, branches, and the ones you google every time.
Git stores snapshots (commits) and moves pointers to them. A branch is a movable label on a commit, HEAD is the branch you have checked out, and the index (staging area) is the draft of your next commit. Nearly every command reads or moves one of those three things.
That is why committed work is almost never lost: git reflog remembers where HEAD has been. The dangerous commands are the ones that discard uncommitted changes — reset --hard, restore, clean -f — and the ones that rewrite history you have already pushed.
Setup & remotes
- git config --global user.name "Ana"
- the identity stamped on every commit — set
user.emailthe same way - git config --global pull.rebase true
- make
git pullrebase instead of creating merge commits - git config alias.st "status -sb"
- aliases: now
git stworks; add--globalfor every repo - git config --list --show-origin
- every setting and the file it came from — answers “why is it doing that?”
- git init -b main
- start a repo in the current folder, first branch named
main - git clone --depth 1 url
- clone without history — fast for CI, but
logandblamestop short - git remote -v
- list remotes and URLs;
git remote add origin urladds one,set-urlchanges it - git push -u origin feat
- first push of a branch;
-usets upstream so plaingit pushworks after - git fetch --prune
- update remote-tracking branches and drop the ones deleted on the server
- git branch -vv
- each local branch with its upstream and ahead/behind counts
Daily flow
- git status -sb
- short status + branch — alias it
- git add -p
- stage hunk by hunk — the single best habit
- git commit --amend
- fix the last commit (message or staged files)
- git pull --rebase
- update without merge-commit noise
- git push --force-with-lease
- force-push that refuses to clobber teammates
- git stash / stash pop
- shelve work;
stash -uincludes untracked - git diff --staged
- what the next commit contains; plain
git diffshows only unstaged edits - git commit -am "msg"
- stage tracked changes and commit — brand-new files still need
git add
Undo anything
- Unstage a file
git restore --staged f- Discard local edits
git restore f(gone for real — careful)- Undo last commit, keep work
git reset --soft HEAD~1— the changes stay staged, ready to recommit- git reset HEAD~1
- --mixed, the default: drop the commit and unstage — edits stay in your files
- git reset --hard HEAD~1
- drop the commit and all uncommitted changes to tracked files; untracked files survive
- git reset --hard origin/main
- make the branch match the remote exactly —
git fetchfirst - Undo a pushed commit
git revert <sha>— new inverse commit, history intact- Find lost commits
git reflog— every place HEAD has been; lost commits stay listed 30 days by default- Recover from any mess
- reflog →
git reset --hard <good-sha>
Branches & history
- git switch -c feat
- create + switch (modern
checkout -b) - git switch -
- back to the previous branch, like
cd - - git rebase -i main
- squash/reword/drop before the PR
- git cherry-pick sha
- copy one commit onto this branch
- git log --oneline --graph
- the readable history view
- git bisect
- binary-search history for the breaking commit
- git blame -w -C
- who wrote it — ignoring whitespace and moves
- git branch --merged main
- branches already in main — safe to delete with
git branch -d
Merge & conflicts
- git merge feat
- merge into the current branch; fast-forwards when the current branch has no new commits
- git merge --no-ff feat
- always record a merge commit, so the branch stays visible in history
- git merge --squash feat
- stage all of feat as one change, no merge commit — then you
git commit - <<<<<<< ======= >>>>>>>
- conflict markers: yours above
=======, theirs below; edit, delete the markers,git add - git checkout --theirs f
- take their whole file (
--oursfor yours) — the two swap meaning during a rebase - git merge --abort
- back out of a conflicted merge to where you started
- git rebase --continue
- resume after
git add-ing a fix;--skipdrops that commit,--abortundoes the rebase - git config merge.conflictstyle zdiff3
- markers also show the common ancestor (git 2.35+) — far easier to resolve
- git config rerere.enabled true
- remember how you resolved a conflict and replay it when the same one recurs
Stash & tags
- git stash push -m "wip" -- path
- stash with a name, optionally only some paths
- git stash list
- every stash; the newest is
stash@{0} - git stash apply stash@{2}
- apply but keep it listed;
pop= apply + drop, except it keeps the stash on conflict - git stash show -p stash@{1}
- the diff a stash holds, before you apply it
- git stash branch fix stash@{0}
- new branch at the commit the stash came from, stash applied cleanly
- git tag -a v1.2.0 -m "msg"
- annotated tag (tagger, date, message) — the kind to use for releases
- git push origin v1.2.0
- tags are never pushed by default;
--follow-tagssends annotated ones with commits - git describe --tags
- nearest tag, commits since, hash:
v1.1.0-1-g872afcc— handy build versions
Search history
- git log -S "apiKey"
- pickaxe: commits that added or removed that string — when did it appear?
- git log -G "time(out)?="
- commits with an added or removed line matching a regex — catches moved lines too
- git log -p --follow -- path
- every change to one file, followed across renames
- git log main..feat
- commits on feat that are not on main — what a merge would bring in
- git log --author=ana --since=2.weeks
- filter by author and date;
--grep=fixmatches commit messages - git show sha:path
- a file exactly as it was in that commit — redirect it to save a copy
- git grep -n "TODO"
- search tracked files only — fast, and skips build output
Clean up & worktrees
- git clean -n
- dry run: untracked files that would be deleted; add
-dto include folders - git clean -fd
- delete untracked files and folders for real;
-xalso deletes ignored files - git worktree add -b fix ../fix main
- branch
fixfrom main, checked out in a sibling folder — no stashing - git worktree list
- every checkout of this repo; a branch can be checked out in only one
- git worktree remove ../fix
- delete that checkout and its bookkeeping (refuses if it has uncommitted changes)
- git commit --fixup sha
- record a fix for an older commit;
git rebase -i --autosquash mainfolds it in
The ones you google
- Rename branch
git branch -m new(+ push and set upstream)- Delete remote branch
git push origin --delete old- One file from another branch
git restore --source main -- f- What changed vs main
git diff main...HEAD(three dots: since fork point)- Untrack a committed file
git rm --cached f+ .gitignore
Recipes
Undo the last commit: soft vs mixed vs hard
# pick ONE of these:
git reset --soft HEAD~1 # commit undone, its changes still staged
git reset HEAD~1 # commit undone, changes unstaged but kept (--mixed)
git reset --hard HEAD~1 # commit undone AND its changes discarded
# changed your mind? the old tip is still in the reflog
git reflog
git reset --hard HEAD@{1}All three move the branch pointer back; they differ only in whether the index and your files follow it. Reset only commits nobody else has pulled — for shared history use git revert.
Resolve a merge conflict
git switch main
git merge feature # CONFLICT (content): Merge conflict in app.js
git status # app.js listed under "both modified"
# edit app.js: keep what you want, delete the <<<<<<< ======= >>>>>>> lines
git add app.js
git commit # concludes the merge
# or give up and restore the pre-merge state:
git merge --abortRun git config --global merge.conflictstyle zdiff3 once and every conflict also shows the common ancestor, which usually makes the right resolution obvious.
Set up a new machine and publish a repo
git config --global user.name "Ana Lima"
git config --global user.email "ana@example.com"
git config --global init.defaultBranch main
git config --global pull.rebase true
git init
git add -A && git commit -m "Initial commit"
git remote add origin git@github.com:ana/app.git
git push -u origin main-u on the first push records the upstream, so later git push and git pull need no arguments. The remote repository must already exist and be empty.
Find the commit that introduced (or removed) a string
git log -S "API_KEY" --oneline # commits that changed how often it occurs
git log -S "API_KEY" -p -- config/ # same, with diffs, limited to one folder
git log -G "timeout[[:space:]]*=" --oneline # regex over added/removed lines
git show 3f2a9c1:config/settings.py # the file as it was in that commit-S skips a commit that only moves the line (the count is unchanged); -G catches it. Stick to POSIX classes like [[:space:]] in -G — \s silently matches nothing in some builds.
Ship a hotfix without stashing (worktree)
git worktree add -b hotfix ../app-hotfix main # new branch + folder; your work untouched
cd ../app-hotfix
# fix, test, commit
git push -u origin hotfix
cd -
git worktree remove ../app-hotfixWorktrees share one object store, so this costs no second clone. A branch can be checked out in only one worktree — that is why the hotfix gets its own branch.
Questions about Git Survival Guide Cheat Sheet
What is the difference between git reset --soft, --mixed and --hard?
All three move the current branch to the commit you name; they differ in what happens to your changes. --soft touches nothing else, so the undone commit’s changes stay staged, ready to recommit. --mixed, the default when you pass no flag, also resets the staging area: the changes are still in your files but unstaged. --hard resets the staging area and the working tree too, discarding every uncommitted change to tracked files, while untracked files are left alone. If you reset too far, git reflog still lists the old commit for at least 30 days by default.
What is the difference between git reset and git revert?
git reset moves your branch pointer backward, so the undone commits disappear from the branch. That is fine for local commits, but on a shared branch it rewrites history other people already have, forces a push, and breaks their next pull. git revert leaves history alone and adds a new commit that applies the inverse of an earlier one, which makes it the safe way to undo something already pushed. Reverting a merge commit needs -m 1 to say which parent to keep. Rule of thumb: reset what only you have seen, revert what others may have pulled.
What is the difference between git merge and git rebase?
Both bring the changes from one branch into another. git merge joins the two histories with a merge commit and preserves exactly what happened, including where the branches diverged. git rebase replays your commits one by one on top of the other branch, producing a straight line of history as if you had started from its latest commit, but every replayed commit gets a new hash. Rebase your own unpushed feature branch to tidy it before review; merge when the branch is shared. Never rebase commits that other people have already built on.
How do I resolve a merge conflict in git?
A conflict means both branches changed the same lines, so git stops and asks you to choose. Run git status to see the files marked both modified, then open each one and find the conflict markers: your version sits between the row of less-than signs and the equals line, theirs between the equals line and the row of greater-than signs. Edit the block into the result you want, delete all the marker lines, and git add the file. When every file is resolved, git commit finishes a merge and git rebase --continue resumes a rebase. git merge --abort undoes everything.