Groundwork

Version control

Git: a save button you can rewind, branch, and share.

Git records snapshots of your project over time. That means you can go back to any earlier version, try risky things without fear, and work alongside other people on the same code. It's the safety net under everything else.

When you'll want this You're scared to change things, need to review what an agent touched, or want a clean way back.
Quick reference · daily Git
git status what changed
git add . stage everything
git commit -m save a snapshot
git push / pull sync with GitHub
git switch -c new branch
git log --oneline history at a glance

#The mental model

You've already been on the receiving end of Git. Groundwork itself lives in a Git repository on GitHub, and this page is a file inside it. When this page gets improved, Jason, who created and maintains Groundwork (in Git terms, its maintainer), edits the file, stages it (picks it for the next snapshot), commits it (saves the snapshot, with a note saying why), and pushes it to GitHub. The next time your machine updates Groundwork, it pulls: the new snapshot comes down, and the page you're reading quietly gets better. Every project you'll ever touch moves the same way; only the names change.

Jason's laptop (Groundwork's maintainer) 1 · edit this page 2 · stage pick the change 3 · commit snapshot saved GitHub the shared copy 4 · push your machine your Groundwork clone 5 · pull: your update brings it down
The loop behind every docs improvement you receive: edit, stage, commit on one machine; push to the shared copy; pull on every other.

That's the whole model: four places. Three live on any machine doing the writing: the working files being edited, the staging area where the next snapshot is assembled, and the history of saved snapshots. The fourth, GitHub, is the shared copy every machine pushes to and pulls from. Every command on this page just moves change between these four places, and when it's your project, you're the one on the laptop at the top of the diagram.

#The daily loop

Ninety percent of Git is five commands. Check what changed, stage it, commit it with a message, and sync with GitHub.

daily
$ git status              # what changed?
$ git add .               # stage everything
$ git commit -m "message" # save a snapshot
$ git push                # send it to GitHub
$ git pull                # get others' changes

Write commit messages that say what changed and why, not "stuff" or "fix". Future-you reads them constantly.

1

Try it now: your first commits

You already have a repo to practice on: the knowledge wiki from new-wiki. Add a line to its README.md, then run git status, git add ., and git commit -m "edit readme". Do it twice so you have a small history, then read it back with git log --oneline. You just ran steps 1 to 3 of the diagram above; in this repo, you're the maintainer.

#Branches — the superpower

A branch is a parallel line of work. You split off from main, build something without touching the working version, and merge it back when it's ready. This is how you try ideas — and how teams avoid stepping on each other.

main merge feature
Split off main, do the work safely on a branch, then merge it back when it's done.
branching
$ git switch -c new-feature  # create and move onto a branch
$ git switch main            # hop back to main
$ git merge new-feature      # fold the branch into main
2

Try it now: branch and merge

In your practice repo, run git switch -c experiment, change something, commit it. Switch back with git switch main: your change vanishes from the files (it's safe on the branch). Bring it in with git merge experiment.

3

See branches move

Branches click when you see them. Spend 30 minutes on the beginner levels of Learn Git Branching: it animates every command you just typed, and by the end the diagram above feels obvious instead of mysterious.

#GitHub and the gh command

GitHub is where repositories live online. The gh command talks to it from the terminal — sign in once, then clone repos and open pull requests without leaving the shell.

gh
$ gh auth login --git-protocol https  # one-time sign-in
$ gh auth status                     # see the active account
$ gh repo clone owner/name   # download a repo
$ gh pr create               # propose your branch for merge

A pull request is how you propose a branch be merged — others review it, discuss, and approve. It's the heart of working together on GitHub.

4

Try it now: read a real history

Clone this very repo with gh repo clone jasondockery/groundwork, cd into it, and run git log --oneline -10: Jason's real commits, newest first. Then run git log --oneline -- docs/git.html to see every commit that shaped the page you're reading, including the one you'll receive next time you pull.

#When something goes wrong

Committed work is very hard to truly lose — Git keeps almost everything. Most "mistakes" have a calm fix:

git restore <file>Throw away uncommitted edits to a file and go back to the last commit
git restore --staged <file>Unstage something you added by mistake (keeps your edits)
git commit --amendFix the message or contents of the commit you just made
Ask before "force" or "hard"

Most commands are safe. The few that can actually destroy work contain the words --force or --hard. If you're about to run one and you're not sure, stop and ask first. Visual tools like lazygit (next section) also ask before anything destructive.

5

Try it now: break something on purpose

In your practice repo, edit a file and confirm the damage with git status, then throw the edit away with git restore <file>. Next, stage a change with git add . and unstage it with git restore --staged <file>. Notice that nothing committed was ever at risk; that safety is why you commit early and often.

#lazygit: a picture of Git, not a replacement for it

Now that the commands are in your hands, meet lazygit: a keyboard-driven view of the same repository, where every keystroke maps to a command you already know. Its value is that you can see the model this page taught: files moving from unstaged to staged to committed, branches as a live graph, and every line an agent touched, reviewable one hunk at a time. Learn the commands first (they're the shared language between you, the docs, and your agents); use lazygit to watch what they do and to move faster at things that are genuinely visual, like staging individual lines or reading history. It's the same app however you open it:

lgIn any terminal — the short alias (or type lazygit in full)
Ctrla then gIn tmux — opens in a popup over your current folder
SpaceggIn Neovim — right inside the editor

Finding your way around

The left side holds five panels; the right shows the details of whatever you've selected.

15Jump to a panel: Status, Files, Branches, Commits, Stash
/ or j/kMove within the list
EnterDrill in — e.g. into a file to stage single lines  ·  Esc backs out
?List every shortcut for the panel you're in  ·  q quits lazygit

The everyday actions

SpaceStage / unstage the selected file or line  ·  a stages everything
cCommit what's staged — type a message, confirm
P / pPush to GitHub / pull from it
Space (in Branches)Check out the selected branch  ·  n makes a new one
dDiscard the selected change — asks first
z / ZUndo / redo the last action — even a merge or rebase
A lens, not a crutch

If Git feels abstract, open lazygit right after your commands and watch what they did; the model clicks fastest when you see it move. But keep typing the commands until they're reflex. If you can only work through the TUI, you'll be lost in CI logs, agent transcripts, and anywhere else Git is words. z undoes almost anything, so explore without fear.

Prefer a window? GitUp

If you'd rather click than type, GitUp is a fast graphical app that draws your history as a live map you can drag and edit. lazygit and GitUp read the same repository, so use whichever suits the moment.

6

Try it now: the first drill, visually

Make another edit in your practice repo, then open lg. Stage it with Space, commit with c, and watch it land in the history panel. Notice you just did the same thing as the first drill, but this time you could see it move.

#What's already configured

Your Git arrives pre-tuned, so the sensible behavior is just the default. You never set this up — but it's worth knowing what's on, because it quietly removes a lot of papercuts.

Readable diffs — delta

Plain git diff is a wall of red and green. delta reformats every diff Git shows — syntax highlighting, line numbers, and word-level highlighting of exactly what changed on a line. It's wired in as Git's pager, so git diff, git show, and git log -p all use it automatically.

n / NJump to the next / previous file in a long diff
Space / bPage down / up  ·  q quits

Fetch vs pull, and what "prune" really removes

Two commands bring things from GitHub to you, and they are politely different. git fetch downloads what's new and updates your local bookmarks of the remote (the origin/* names you see in branch lists) while touching nothing of yours: it is purely "go look". git pull is fetch plus "and fold it into the branch I'm on". Prune is bookkeeping on those bookmarks: after a branch is deleted on GitHub (normally because its pull request merged), your local origin/that-branch bookmark points at something that no longer exists. Pruning removes the stale bookmark, and only the bookmark: never your local branches, never your edits, never a commit. Despite the alarming name, it cannot delete work.

fetch, pull, prune
$ git fetch           # download news; change nothing of yours
$ git pull            # fetch + bring it into your current branch
$ git fetch --prune   # also drop origin/* bookmarks for deleted branches
$ git branches        # local branches with state (see the next section)

Seeing your branches clearly

Plain git branch -a does mark the current branch with * (and branches checked out in other worktrees with +), but its default view doesn't combine recency, tracking health, merge evidence, unique work, worktree use, and pull-request state. When you're directing agents that spin up a branch per task, that flat list fills with noise fast. Groundwork adds three read-only aliases that answer the real questions. They never change anything — they only look.

git branchesLocal branches, newest commit first, each with its upstream tracking, a red [gone] marker if its upstream branch is no longer on the remote, the relative date, and the last commit subject
git goneJust the branches whose upstream tracking ref has vanished since your last fetch/prune. That often means the branch merged and was cleaned up — but it can equally mean it was renamed, force-deleted, or removed by hand — so these are candidates to inspect, never proof anything was merged
git recentA compact table ordered by each branch's last commit date: relative date, then branch name
Why not just delete the "gone" ones?

A squash-merge makes a new commit on main, so Git's own --merged check can't see that your branch's work is already in — that's why git gone leans on the upstream-tracking signal (the [gone] marker Git sets locally after a fetch with prune) instead. But "upstream is gone" still isn't proof of anything: a branch could be gone and still hold a commit you never pushed, or have vanished only because it was renamed or deleted by hand. So these aliases only show; deleting is always your deliberate call. Safe deletion (git branch -d, lowercase) refuses to remove a branch that isn't fully merged into its upstream — or into HEAD when it has no upstream — so note that "merged" and "pushed" are not the same test. Even a deleted branch's commits linger in git reflog for a while (Git's default expiry is 90 days for reachable entries, 30 for unreachable) — but the durable habit is to push important work (a draft PR is perfect) rather than trust the reflog.

read your own branch list
$ git fetch --prune   # refresh remote-tracking refs; drop upstreams gone from the remote
$ git branches        # the full picture, newest first
$ git gone            # just the safe-to-review candidates

If git gone lists names, their configured upstream tracking refs are absent from your local remote-tracking refs after the latest fetch/prune — which does not by itself prove the work merged. Look at each with git branches before removing anything, or open the repo in lazygit (lg) to see them visually.

The sensible defaults

A handful of config choices that make day-to-day Git smoother and the history cleaner:

pull.rebase = truegit pull rebases your local commits on top of what you fetched instead of making a noisy "merge branch" commit — so history stays a clean, straight line
push.autoSetupRemoteThe first git push on a new branch just works — no --set-upstream dance
fetch.prune = trueRuns the prune from the section above on every fetch: stale origin/* bookmarks for branches already deleted on GitHub are dropped. Bookmarks only; local branches, edits, and commits are never touched
init.defaultBranch = mainNew repos start on main, matching GitHub
merge.conflictstyle = zdiff3When a merge conflicts, you see three sections — yours, theirs, and the common ancestor — which makes it far clearer why the conflict happened and how to resolve it
branch.sort = -committerdateBranch lists are ordered by each branch's tip commit date, newest first, instead of alphabetically — the branch you committed to last is usually at the top (this sorts by commit date, not by when you last checked a branch out)
rerere.enabled = trueGit records how you resolved a conflict and reapplies that resolution if the same conflict recurs during a rebase — you still review and stage the result, since auto-staging is the separate rerere.autoupdate setting

Signed commits

If you set a signing key during setup, every commit is signed automatically — a cryptographic stamp that it really came from you, shown as a "Verified" badge on GitHub. This uses your SSH key from the password manager (1Password or Bitwarden), so there's no separate key to manage and nothing private ever touches disk.

Two gotchas

Signing needs the password manager's SSH agent unlocked — if commits suddenly won't sign, unlock the app. And verifying signatures locally (git log --show-signature) additionally needs an allowedSignersFile configured; without it, signing still works and GitHub still shows "Verified," but your own machine can't verify the badge. See Troubleshooting if pushes start prompting for a login.

Pulling without prompts — the gh credential helper

GitHub stopped accepting passwords years ago, so Git authenticates through gh for HTTPS remotes. The setup bakes gh in as Git's credential helper (in the managed git config, so it survives every chezmoi apply) for your own GitHub repos after you run gh auth login --git-protocol https. SSH remotes are still fine when your ~/.ssh/config clearly maps the right key to the right GitHub account.

#Going deeper: a ladder, not a pile

Pick your entry point by where you are, not by what looks most authoritative. The classic mistake is starting with Pro Git (the "Git bible"): it is the definitive reference and a rough first week, because it covers internals, hooks, and server setup alongside the basics. This ladder runs from first hour to deep mechanics. Step on at your level, step off when you have what you need, and come back a rung higher later.

First week: the on-ramp (hands-on, about an hour each)

  1. GitHub Skills: Introduction to GitHub: branches, commits, pull requests, and merging in under an hour, inside a real repo, no prerequisites.
  2. GitHub Skills: Introduction to Git: the Git side proper, on the command line and VS Code, still hands-on.
  3. freeCodeCamp's Git & GitHub crash course: one free video covering the real daily workflow: staging, commits, branches, merge conflicts, push/pull, stash, revert, rebase, and pull requests.
  4. Learn Git Branching (beginner levels): the visual sandbox. Type real commands, watch the graph move. This is where branching clicks.

Second stage: depth without the reference manual

  • freeCodeCamp's beginner-friendly handbook: plain-English long form, from "what is Git" gradually into branching, merging, conflicts, and safely undoing mistakes.
  • Git for Professionals (free video): crafting good commits, choosing a branching strategy, and merge versus rebase. Watch it once the commands feel comfortable, not on day one.
  • Oh My Git!: an open-source game that shows Git's internals as you play.
  • GitHub for Beginners: GitHub's own blog-plus-video series on its features, for when you want the official voice.

Reference: for looking things up, not reading through