~$ delxium

git · version-control · workflow

Git: A Pocket Companion

Beyond add-commit-push. The branch workflow, the history-rewriting tools, and the commands that un-break things.

Everyone knows git add, git commit, git push. This guide is the next layer — the branch workflow you’ll use every day, and the recovery commands that turn “I think I broke git” into a five-second fix. The through-line is a single feature, shipped the way we ship real work: a branch, a clean history, and a safe merge.

$ git switch -c feat/contact-form
# ...work...
$ git push -u origin feat/contact-form    # open a PR, merge when green

The three places a change lives

Git has three states, and most confusion comes from mixing them up:

  • Working tree — your files, as edited.
  • Staging area (index) — what you’ve marked to go in the next commit (git add).
  • Repository — the committed history (git commit).
$ git status           # what's changed, what's staged
$ git diff             # working tree vs staged
$ git diff --staged    # staged vs last commit
$ git add -p           # stage *hunks* interactively — review as you go

Stage in pieces with git add -p

add -p walks you through each change and asks whether to stage it. It’s how you turn a messy working tree into small, focused commits — review your own diff before anyone else does.

Branching is the workflow

$ git switch -c feat/x      # create + switch to a branch (new-style)
$ git switch main           # switch back
$ git branch -d feat/x      # delete a merged branch

switch and restore are the modern, clearer replacements for the overloaded git checkout (which did both, plus more). You’ll still see checkout everywhere — it works — but switch says what it means.

Keeping history clean: rebase vs merge

Two ways to combine branches, and the difference is the shape of history:

  • Merge preserves exactly what happened, with a merge commit tying the branches together. Honest, but the graph can get tangled.
  • Rebase replays your commits on top of the latest main, as if you’d started from there. A clean, linear story — at the cost of rewriting your branch’s commits.
# update your feature branch onto the latest main, linearly
$ git switch feat/x
$ git fetch origin
$ git rebase origin/main

# squash/reorder your own commits before opening a PR
$ git rebase -i origin/main

Only rebase what you haven’t shared

Rebasing rewrites commits (new hashes). Rebase your own un-pushed branch freely — never rebase main or anything others have pulled, or you’ll force everyone into a painful reconciliation. Golden rule: rebase local, merge public.

Stashing: park changes without committing

$ git stash            # shelve working changes, clean the tree
$ git stash pop        # bring them back
$ git stash list       # see what's stashed
$ git stash -u         # include untracked files

You’re mid-edit and need to switch branches to fix something urgent — git stash, switch, fix, switch back, git stash pop. Your work was never lost.

Undoing things (the part that feels scary)

You want to… Command
Unstage a file (keep edits) git restore --staged file
Discard edits to a file git restore file
Fix the last commit message / add a file git commit --amend
Undo last commit, keep changes staged git reset --soft HEAD~1
Undo last commit, keep changes unstaged git reset HEAD~1
Throw away local commits entirely git reset --hard origin/main
Revert a public commit safely (new commit) git revert <sha>

reset --hard and revert are not the same

reset --hard rewrites history (fine locally, dangerous if pushed). revert makes a new commit that undoes an old one — safe on shared branches. On main, reach for revert.

The safety net: reflog

Even a reset --hard isn’t truly gone. Git records every move of HEAD in the reflog:

$ git reflog                 # every HEAD position, with its sha
$ git reset --hard HEAD@{2}  # jump back to where you were 2 moves ago

Deleted a branch? Bad rebase? git reflog finds the commit you were on, and you reset back to it. This is the command that makes git mistakes survivable.

Finding the commit that broke things: bisect

$ git bisect start
$ git bisect bad                 # current state is broken
$ git bisect good v1.2.0         # this old tag worked
# git checks out the midpoint; you test and mark each:
$ git bisect good   # or: git bisect bad
# ...repeat  git binary-searches to the exact culprit...
$ git bisect reset

Bisect turns “somewhere in the last 200 commits” into a handful of tests — a binary search for the commit that introduced a bug.

A clean commit, and a quick look back

$ git log --oneline --graph --decorate --all   # the history, visualised
$ git show <sha>                                 # what a commit changed
$ git blame file                                 # who last touched each line

Pocket cheat-sheet

Do Command
Stage interactively git add -p
New branch / switch / delete git switch -c x / git switch main / git branch -d x
Rebase onto latest main git rebase origin/main
Squash/reorder your commits git rebase -i origin/main
Park / restore changes git stash / git stash pop
Fix last commit git commit --amend
Undo last commit (keep work) git reset HEAD~1
Recover from anything git reflog
Find a regression git bisect

Master the recovery commands — reflog, revert, reset — and git stops being scary, because nothing you do is truly irreversible. For the branch-and-release flow we use on this platform, see how it ties into shipping versioned releases.

← all field notes