Vibe Engines
YouTube

Vibe Engines is free, interactive AI engineering & system design — runs in your browser. Pick a learning path

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.email the same way
git config --global pull.rebase true
make git pull rebase instead of creating merge commits
git config alias.st "status -sb"
aliases: now git st works; add --global for 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 log and blame stop short
git remote -v
list remotes and URLs; git remote add origin url adds one, set-url changes it
git push -u origin feat
first push of a branch; -u sets upstream so plain git push works 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 -u includes untracked
git diff --staged
what the next commit contains; plain git diff shows 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 fetch first
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 (--ours for 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; --skip drops that commit, --abort undoes 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-tags sends 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=fix matches 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 -d to include folders
git clean -fd
delete untracked files and folders for real; -x also deletes ignored files
git worktree add -b fix ../fix main
branch fix from 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 main folds 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 --abort

Run 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-hotfix

Worktrees 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.