A plain-English working overview of Git: where your changes live, how the everyday loop runs, what branches really are, how to undo things by intent, the difference between merge and rebase, and a few habits that keep day-to-day work calm.
The three areas
Every file you change lives in one of three places at a time:
- Working directory — what you actually see in your editor. Edits live here until staged.
- Staging area (the "index") — the next commit's draft. You hand-pick which changes belong in the next commit by adding them here.
- Local repo — your
.git/directory. History, branches, all committed snapshots. Lives entirely on your disk; doesn't need internet. - Remote repo — origin (GitHub, Azure DevOps, etc.). Where your team's shared copy lives.
The everyday loop
That's 80% of git usage right there.
Branches — what they actually are
A branch is just a named pointer to a commit. Creating one is cheap and instant. There's no copying of files. main is the convention for the trunk; everything else branches off it.
Typical feature flow
git switch maingit pull— start from latestgit switch -c add-volume-rocker— new branch- … edit, test, edit, test …
git add -A && git commit -m "fix: rocker"— commit often- … more work, more commits …
git switch main && git pull— main may have movedgit switch add-volume-rockergit rebase main— replay your work on top of new maingit push -u origin add-volume-rocker— publish branch- open PR / merge request on the host
- PR gets merged → branch deleted → return to step 1
Step 3 keeps work isolated. Step 5 saves your state — commit often, you're saving snapshots, not publishing them. Step 9 keeps history linear and avoids ugly merge commits in your PR.
Reading history
Undoing things — by intent
"I edited the file but want it back to last commit"
"I staged something I didn't mean to"
"I committed too early, want to amend the last commit"
"I committed something broken, want to undo but keep the work"
"I want to wipe a commit completely"
"It's already pushed; I need to undo it without rewriting history"
Rule of thumb: revert for shared history, reset for local-only.
Merge vs rebase
git merge featureon main → preserves the fork visually; creates a merge commit.git rebase mainon feature → replays your commits on top of latest main; linear history.
Both end with the same files; they differ in what the history graph shows.
Pictured:
D' and E' are new commits with the same patch content as D/E but new SHAs, because their parent changed. The originals get garbage-collected eventually.
When to use which:
- Merge — preserves true history, shows when work diverged and rejoined. Required when others have based work on your commits.
- Rebase — produces clean linear history. Good for keeping a personal feature branch up to date with
mainbefore opening a PR, or for squashing a messy commit series into one tidy patch.
Interactive rebase (git rebase -i) lets you edit the replay list — squash multiple commits into one, reorder them, reword messages, drop a bad commit entirely. Powerful for tidying a branch before sharing.
Golden rule: never rebase commits that exist on a remote branch others might be using. Rewriting shared history breaks their clones. Your own private branch — rebase freely. Pushed and shared — merge instead.
Remote operations
fetch is read-only — it never changes your files. pull = fetch + integrate. Run fetch first when you want to inspect what's incoming without committing to merging it.
Working with others — the rhythm
- Pull before you push. Always. If you push and someone else pushed first, your push gets rejected. Pull, resolve, push.
- Commit small, commit often. Each commit should do one thing. "Add foo, fix bar, rename baz" is three commits, not one.
- Write the commit message for future you. Explain why, not what — the diff already shows what. "fix(player): clear queue resets currentIndex" is fine; "stuff" is not.
- Branch for anything risky. Free safety net.
- Don't rewrite shared history. Once a commit is on the remote and someone else has pulled it, leave it alone. Use
revert, notreset/rebase.
Conflict resolution
When pull/merge/rebase says "conflict":
Open each conflicted file. You'll see:
Edit the file to the version you actually want (delete the markers, pick lines, combine, whatever). Then:
If you panic: git merge --abort or git rebase --abort puts everything back where it was.
Things that look scary but aren't
- Detached HEAD — you checked out a commit by SHA instead of a branch name. Just
git switch <branch>to get back. - Tracking branch out of date — somebody pushed; you didn't pull.
git pull. - Push rejected — same thing.
git pull --rebasethengit push.
A vocabulary cheat sheet
| Term | Plain meaning |
|---|---|
| HEAD | "Where I am right now" — the current commit / branch tip |
| origin | Default name for your main remote (GitHub / Azure DevOps) |
| upstream | The remote branch your local branch tracks |
| fast-forward | Merge where one branch is a strict ancestor — no merge commit needed |
| ancestor | A commit further back in history |
| detached HEAD | Not on any branch (checked out a raw commit) |
| stash | Hidden shelf for uncommitted changes — git stash, git stash pop |
| tag | A named pointer to a specific commit, immutable, used for releases |
Three good habits
git statusbefore any operation. Five seconds of clarity prevents an hour of confusion.git diff --stagedbefore every commit. Catch the "oh I didn't mean to include that" before it's in history.git pull --rebasenot plaingit pull. Keeps your local history linear instead of littering it with auto-merge commits.
That's the foundation. Add git stash, git cherry-pick, and git bisect to your vocabulary as you hit the need for them — they're powerful but situational.