HALRAD Research

Git basics

Git Basics — Dev & Code Flow

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 dir ] → git add → [ staging area ] → git commit → [ local repo ] ↓ git push [ remote repo ]

The everyday loop

git status # what's changed, what's staged, what branch git diff # see unstaged changes line by line git diff --staged # see what you've already added git add <file> # stage one file (or `git add -A` for everything) git commit -m "message" # snapshot the staged changes git push # send commits to remote git pull # get others' commits from remote

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.

git branch # list local branches; * marks current git branch feature-x # create a branch from current commit git switch feature-x # move to it (older syntax: git checkout) git switch -c feature-x # create + switch in one step git branch -d feature-x # delete (must be merged; -D forces)

Typical feature flow

  1. git switch main
  2. git pull — start from latest
  3. git switch -c add-volume-rocker — new branch
  4. … edit, test, edit, test …
  5. git add -A && git commit -m "fix: rocker" — commit often
  6. … more work, more commits …
  7. git switch main && git pull — main may have moved
  8. git switch add-volume-rocker
  9. git rebase main — replay your work on top of new main
  10. git push -u origin add-volume-rocker — publish branch
  11. open PR / merge request on the host
  12. 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

git log # full history, newest first git log --oneline # one line per commit (sha + message) git log --oneline -10 # last 10 git log --all --graph --oneline # ASCII tree of all branches git show <sha> # what changed in that commit git blame <file> # who-touched-each-line

Undoing things — by intent

"I edited the file but want it back to last commit"

git restore <file> # discard unstaged changes

"I staged something I didn't mean to"

git restore --staged <file> # unstage; edits stay in working dir

"I committed too early, want to amend the last commit"

git add <missed-file> git commit --amend # only for commits NOT YET PUSHED

"I committed something broken, want to undo but keep the work"

git reset --soft HEAD~1 # uncommit; keep changes staged git reset HEAD~1 # uncommit; keep changes unstaged

"I want to wipe a commit completely"

git reset --hard HEAD~1 # destructive — loses uncommitted work

"It's already pushed; I need to undo it without rewriting history"

git revert <sha> # creates a new commit that undoes it

Rule of thumb: revert for shared history, reset for local-only.

Merge vs rebase

Both end with the same files; they differ in what the history graph shows.

Pictured:

Before: main A — B — C \ feature D — E After `git rebase main` (run on feature): main A — B — C \ feature D' — E'

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:

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

git remote -v # list configured remotes git fetch # download remote state, don't merge git pull # fetch + merge (or rebase) into current branch git push # send current branch git push -u origin <branch> # first push of a new branch (sets upstream) git push --force-with-lease # safely rewrite remote history (rarely)

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

  1. Pull before you push. Always. If you push and someone else pushed first, your push gets rejected. Pull, resolve, push.
  2. Commit small, commit often. Each commit should do one thing. "Add foo, fix bar, rename baz" is three commits, not one.
  3. 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.
  4. Branch for anything risky. Free safety net.
  5. Don't rewrite shared history. Once a commit is on the remote and someone else has pulled it, leave it alone. Use revert, not reset/rebase.

Conflict resolution

When pull/merge/rebase says "conflict":

git status # shows files with conflicts

Open each conflicted file. You'll see:

<<<<<<< HEAD your changes ======= their changes >>>>>>> other-branch

Edit the file to the version you actually want (delete the markers, pick lines, combine, whatever). Then:

git add <file> git commit # if merging git rebase --continue # if rebasing

If you panic: git merge --abort or git rebase --abort puts everything back where it was.

Things that look scary but aren't

A vocabulary cheat sheet

TermPlain meaning
HEAD"Where I am right now" — the current commit / branch tip
originDefault name for your main remote (GitHub / Azure DevOps)
upstreamThe remote branch your local branch tracks
fast-forwardMerge where one branch is a strict ancestor — no merge commit needed
ancestorA commit further back in history
detached HEADNot on any branch (checked out a raw commit)
stashHidden shelf for uncommitted changes — git stash, git stash pop
tagA named pointer to a specific commit, immutable, used for releases

Three good habits

  1. git status before any operation. Five seconds of clarity prevents an hour of confusion.
  2. git diff --staged before every commit. Catch the "oh I didn't mean to include that" before it's in history.
  3. git pull --rebase not plain git 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.