Pro Git Ch2 — Git Basics


  • Description: Notes on Pro Git (2nd ed.) Chapter 2 — initializing/cloning repos, recording changes (add/commit/rm/mv/.gitignore/diff), viewing history (log/show/reflog), undoing things (restore/reset/revert/amend), working with remotes, tagging, aliases. Plus git stash (technically Pro Git §7.3) placed here for workflow proximity.
  • My Notion Note ID: K2B-2-2
  • Created: 2020-06-02
  • Updated: 2026-05-19
  • License: Reuse is very welcome. Please credit Yu Zhang and link back to the original on yuzhang.io

Table of Contents


1. Getting a Git Repository

1.1 git init

  • git init — turn current directory into a Git repo (creates .git/).
  • git init --bare — bare repo (no working tree); used for server-side hosting.

1.2 git clone

  • git clone <url> [name] — full copy including history; optional rename.
  • URL schemes:
    • SSH: [email protected]:user/repo.git
    • HTTPS: https://github.com/user/repo.git
    • Git (read-only): git://github.com/user/repo.git
    • Local: /path/to/repo or file:///path/to/repo
  • git clone -o <remote> — set remote name (default origin).
  • git clone --depth <n> — shallow clone (last n commits only — saves disk/bandwidth).
  • git clone --branch <branch> — clone a single branch.

2. Recording Changes

File lifecycle: untracked → (add) → staged → (commit) → tracked/unmodified → (edit) → modified → (add) → staged → ...

2.1 git status

  • git status — staging snapshot.
  • git status -s / --short — compact: MM = staged+modified, ?? = untracked, A = newly staged.

2.2 .gitignore

  • Glob rules: *, ?, [abc].
  • # — comment.
  • ! — negate (re-include).
  • Leading / — anchor (no recursive match).
  • Trailing / — directory only.
  • Templates: github.com/github/gitignore.

2.3 git diff

  • git diff — working tree vs staged (unstaged changes).
  • git diff --staged / --cached — staged vs last commit.
  • git diff <commit>..<commit> — between two commits.
  • git difftool — open in external diff tool.
  • VSCode as difftool:
git config --global diff.tool vscode
git config --global difftool.vscode.cmd 'code --wait --diff $LOCAL $REMOTE'

2.4 git add

  • git add <files> — stage. Globs work. git add . = all in cwd.
  • git add -A / --all — all changes including deletions.
  • git add -p / --patch — interactive hunk-by-hunk staging.

2.5 git commit

  • git commit — open editor for message.
  • git commit -m "msg" — one-liner.
  • git commit -a -m "msg" — auto-stage tracked-modified files (skips add; does NOT add untracked).
  • git commit --amend — replace last commit (only on unpushed commits — see § 4).

2.6 git rm

  • git rm <files> — delete from working tree + stage deletion.
  • git rm -f — force when staged + modified.
  • git rm --cached <file> — untrack but keep on disk (e.g. after committing something that should have been ignored).
  • git rm -r <dir> — recursive.

2.7 git mv

  • git mv from to — rename + stage. Equivalent to mv from to; git rm from; git add to.

3. Viewing Commit History

3.1 git log

  • git log — full commit history (default verbose).
  • git log -<n> — last n commits.
  • git log --oneline — one commit per line.
  • git log -p / --patch — show diff per commit.
  • git log --stat — file change stats.
  • git log --graph — ASCII branch graph.
  • git log --decorate — show branch/tag pointers.
  • git log --all — include all refs (not just current branch).
  • git log --no-merges — skip merge commits.
  • git log --pretty=<format>oneline, short, full, fuller, format:"%h %an %s".

3.2 Filters

  • --since="2 weeks ago", --until=2026-01-01.
  • --author="Jon", --committer=.
  • --grep="bug" — match commit message.
  • -S "func_name"pickaxe: commits where the count of occurrences of func_name changed.
  • -G <regex> — commits where any line matching regex changed (broader than -S).

3.3 Range syntax

  • <branch1>..<branch2> — commits in branch2 not in branch1.
  • <a>...<b> (triple-dot, with --left-right) — symmetric difference.
  • <not_shown>..<shown><shown> ^<not_shown><shown> --not <not_shown>.

3.4 git show

  • git show <ref> — commit/tag/blob/tree contents.
  • git show <ref>:<path> — file at that revision.

3.5 git reflog (technically §7.1)

  • git reflog — local history of every HEAD update (commit, checkout, reset, merge, rebase).
  • Refs like HEAD@{2}, HEAD@{yesterday}, <branch>@{1}.
  • Recovery lifeline — most "lost" commits findable here for ~90 days (default).
  • git reflog expire --expire=now --all — manually expire (used in large-file cleanup; see [[worktree-and-git-lfs]] § 4.5).

4. Undoing Things

4.1 git commit --amend

  • Replace the last commit (message and/or content).
  • Workflow: edit, git add forgotten files, then git commit --amend.
  • ⚠️ Rewrites history → never on pushed commits.

4.2 git restore (Git 2.23+, modern)

  • git restore --staged <file> — unstage (keeps working-tree changes).
  • git restore <file> — discard working-tree changes. ⚠️ Permanent loss of uncommitted edits.
  • git restore --source=<commit> <file> — restore file content from a specific commit.
  • git restore --staged --worktree <file> — both at once.

4.3 git reset

  • git reset HEAD <file> — legacy unstage (git restore --staged superseded this).
  • Three modes affecting HEAD + index + worktree:
    • git reset --soft <commit> — move HEAD only; index + worktree untouched.
    • git reset --mixed <commit> (default) — move HEAD + reset index; worktree untouched.
    • git reset --hard <commit> — move HEAD + reset index + reset worktree. ⚠️ Destroys uncommitted work.
  • Common: git reset --soft HEAD~1 — undo last commit but keep changes staged.

4.4 git checkout -- (legacy discard)

  • git checkout -- <file> — discard working-tree changes. ⚠️
  • Avoid in modern Git — use git restore.

4.5 git revert

  • git revert <commit> — create a new commit that undoes the target commit.
  • Safe for pushed history (doesn't rewrite) → preferred way to undo published work.
  • git revert -m <parent-num> <merge> — revert a merge commit.

4.6 Rule of thumb

  • Unshared commits: --amend / reset are fine.
  • Pushed commits: use revert.

5. Working with Remotes

5.1 git remote

  • git remote / -v — list remotes (with URLs).
  • git remote add <name> <url> — add a remote.
  • git remote show <remote> — branches, fetch/push URLs, tracking info.
  • git remote rename <old> <new>.
  • git remote remove <name> (or rm).
  • git remote set-url <name> <url> — change URL.

5.2 Remote-tracking branches

  • <remote>/<branch> (e.g. origin/main) — local read-only pointer to remote state at last sync.
  • Updated by git fetch (or git pull).
  • origin is just convention (set by clone); not special.
  • View tracking + ahead/behind: git branch -vv (cached — git fetch --all first for fresh).
  • Upstream shorthand: @{u} or @{upstream} (e.g. git merge @{u}).

5.3 git fetch

  • git fetch <remote> — download new commits + update remote-tracking branches; doesn't merge.
  • git fetch --all — all remotes.
  • git fetch --prune — delete local remote-tracking branches whose remote was deleted.

5.4 git pull

  • git pull = git fetch + git merge (or git rebase if pull.rebase=true).
  • Less explicit than fetch+merge/rebase; prefer the two-step for clarity.

5.5 git push

  • git push <remote> <branch> — push local branch to remote of same name.
  • git push <remote> <local>:<remote-branch> — push to different name.
  • git push -u origin <branch> — set upstream tracking on first push.
  • git push <remote> --delete <branch> — delete remote branch.
  • git push --force-with-leasesafer force-push (fails if remote moved since last fetch); prefer over --force.

5.6 Tracking branches (creating from remote)

  • git checkout -b <local> <remote>/<branch> — create + track.
  • git checkout --track <remote>/<branch> — auto-name.
  • git checkout <branch> — auto-track if <branch> matches exactly one remote's branch (Git 1.6.2+).
  • git branch -u <remote>/<branch> — set upstream on existing branch.
  • git switch / git switch -c — modern alternative to checkout for branches (Git 2.23+).

6. Tagging

  • Tag = immutable named pointer to a commit (vs branch pointer, which moves).
  • Lightweight — just a ref:
    • git tag <name> · git tag <name> <commit>.
  • Annotated — full object with tagger, date, message, signature:
    • git tag -a <name> -m "msg".
    • git tag -a <name> <commit> -m "msg".
    • Preferred for releases.
  • List: git tag · git tag -l "v1.*".
  • Show: git show <tag>.
  • Delete local: git tag -d <name>.
  • Push tags (not pushed by default):
    • git push <remote> <tag> — one.
    • git push <remote> --tags — all.
  • Delete remote tag: git push <remote> --delete <tag> or git push <remote> :refs/tags/<tag>.

7. Aliases

git config --global alias.co checkout
git config --global alias.br branch
git config --global alias.ci commit
git config --global alias.st status
git config --global alias.unstage 'reset HEAD --'
git config --global alias.last 'log -1 HEAD'

# Pretty log alias
git config --global alias.lg "log --graph --oneline --decorate --all"

# External commands — prefix with `!`
git config --global alias.visual '!gitk'

8. git stash (aside)

Technically Pro Git §7.3, placed here for workflow proximity:

  • git stash / git stash push — save staged + tracked-modified; restore clean working tree.
  • git stash push -u / --include-untracked — also stash untracked.
  • git stash push -p / --patch — interactive hunk select.
  • git stash push -m "msg" — name the stash.
  • git stash list.
  • git stash apply [stash@{n}] — re-apply (keep on stack); free to apply on different branch.
  • git stash pop — apply + drop.
  • git stash drop [stash@{n}] — discard.
  • git stash branch <name> — new branch from stash + apply (handy when stash conflicts with current HEAD).

9. References