🌍 Git & Version Control · subject
Git & Version Control Advanced Git Syllabus
The Advanced Git syllabus for Git & Version Control in full: 6 chapters and 20 topics, every one listed below, plus 54 Advanced Git flashcards with answers.
Advanced Git syllabus: all 6 chapters
Expand any chapter to see its topics and sub-topics.
-
Rewriting History
4 topics- Amending and Squashing Commits
- Filtering History
- Cherry-Picking Commits
- Reverting vs Resetting on Shared History
-
Debugging with Git
3 topics- Finding Bugs with git bisect
- Tracing Code with git blame
- Searching History with git log -S and -G
-
Submodules and Subtrees
4 topics- Adding and Updating Submodules
- Cloning Projects with Submodules
- Subtree Merging
- Submodules vs Subtrees Trade-offs
-
Worktrees and Sparse Checkout
3 topics- Multiple Working Trees with git worktree
- Sparse Checkout for Large Repos
- Managing Monorepos
-
Hooks and Automation
3 topics- Client-Side Hooks
- Server-Side Hooks
- Managing Hooks with Frameworks
-
Handling Large Files and Performance
3 topics- Git Large File Storage (LFS)
- Repository Maintenance
- Packfiles and Garbage Collection
Git & Version Control Advanced Git flashcards — 54 questions with answers
What does `git commit --amend` do, and what happens to the commit's SHA?
It replaces the most recent commit with a new one that combines the staged changes and (optionally) a new message. Because commit content/metadata change, it produces a brand-new commit with a different SHA-1 hash; the old commit is discarded (orphaned).
How do you amend only the commit message of the last commit without changing its content?
Run `git commit --amend -m "new message"` (or `git commit --amend` and edit in the editor) with nothing staged. Use `git commit --amend --no-edit` to keep the same message when you only added files.
What is interactive rebase and which command starts it for the last 3 commits?
Interactive rebase lets you rewrite a series of commits (reorder, edit, squash, drop, reword). Start it with `git rebase -i HEAD~3`.
In an interactive rebase, what is the difference between the `squash` and `fixup` actions?
`squash` combines the commit into the previous one AND lets you edit/merge the commit messages. `fixup` also combines it into the previous commit but discards the fixup commit's message, keeping only the earlier one's.
Why is amending or squashing already-pushed commits dangerous, and what is required to publish the result?
Rewriting published history changes SHAs, diverging your branch from the remote. Collaborators' history breaks. Publishing requires a force push (`git push --force` or safer `--force-with-lease`).
What does `git commit --fixup=<commit>` combined with `git rebase -i --autosquash` accomplish?
`--fixup=<commit>` creates a specially-labeled commit; `rebase -i --autosquash` then automatically reorders and marks it as a fixup for the target commit, so you don't hand-edit the todo list. You can set `rebase.autosquash true` to make it default.
What is `git filter-repo` and why is it preferred over `git filter-branch`?
`git filter-repo` is the recommended tool for rewriting history at scale (e.g., removing a file/secret from all commits). It is far faster and safer than the deprecated `git filter-branch`, which is slow and error-prone.
Which `git log` options filter commits by author and by date range?
`--author=<pattern>` filters by author; `--since=<date>`/`--after` and `--until=<date>`/`--before` filter by date. Example: `git log --author="Ann" --since="2 weeks ago"`.
How do you list only the commits that touched a specific file, including across renames?
`git log -- <path>` shows commits touching that path; add `--follow` to track history through renames: `git log --follow -- <path>`.
What does the `git log --grep=<pattern>` option match against?
It filters commits whose commit message matches the given regular expression. Combine multiple with `--all-match` to require all patterns.
What does `git cherry-pick <commit>` do?
It applies the changes introduced by an existing commit onto the current branch as a NEW commit (with a new SHA), without merging the branches. It replays that single commit's diff on top of HEAD.
How do you cherry-pick a range of commits, and does the range include the first endpoint?
`git cherry-pick A..B` applies commits after A up to and including B (A is excluded). Use `git cherry-pick A^..B` to include A itself.
What is the purpose of `git cherry-pick -x`?
It appends a standardized line `(cherry picked from commit <sha>)` to the new commit's message, recording the source commit for traceability.
How do you resolve a conflict during cherry-pick and continue or abort?
Fix the conflicting files, `git add` them, then `git cherry-pick --continue`. To stop and restore the pre-pick state, use `git cherry-pick --abort`; to skip the current commit, `git cherry-pick --skip`.
On shared/public history, why should you prefer `git revert` over `git reset`?
`git revert` creates a new commit that undoes changes, preserving history so collaborators' clones stay consistent (no force push needed). `git reset` rewrites/moves the branch pointer, deleting commits from the line of history, which requires a destructive force push on shared branches.
What does `git revert <commit>` produce?
A new commit whose diff is the inverse of the specified commit, effectively undoing its changes while keeping both commits in history.
Compare the three modes of `git reset`: `--soft`, `--mixed`, and `--hard`.
All move HEAD (the branch pointer). `--soft`: leaves index and working tree unchanged (changes staged). `--mixed` (default): resets the index but not the working tree (changes unstaged). `--hard`: resets index AND working tree, discarding all changes permanently.
What does `git bisect` do and what search strategy does it use?
It finds the commit that introduced a bug by binary search over the commit history. You mark a known-good and known-bad commit; Git checks out the midpoint repeatedly until it isolates the first bad commit.
What is the complexity of git bisect, i.e., roughly how many test steps for N commits?
Because it is a binary search, it needs about $\log_2(N)$ steps. For example, $\sim 10$ tests can isolate a bug among $\sim 1000$ commits since $2^{10}=1024$.
Give the sequence of commands to start and run a manual git bisect session.
`git bisect start`, then `git bisect bad` (current is broken), `git bisect good <old-sha>`. Git checks out a midpoint; test it and mark `git bisect good` or `git bisect bad`. Repeat until it reports the culprit, then `git bisect reset`.
Show the other 34 Advanced Git flashcards
How do you fully automate a bisect with a test script?
`git bisect start <bad> <good>` then `git bisect run <cmd>`. The command's exit status decides each step: 0 = good, 1–124/126/127 = bad, and exit code 125 means "skip/untestable".
What does `git blame <file>` show?
For each line of the file, it shows the commit SHA, author, and timestamp of the last revision that modified that line, letting you trace who last changed each line and why.
How do you use `git blame` to see who authored lines 20–30 of a file?
`git blame -L 20,30 <file>` restricts the annotation to the given line range.
Which `git blame` option helps see past code that was only moved or copied within/between files?
`-C` detects lines moved or copied from other files; `-M` detects lines moved within the same file. They attribute the line to the original commit rather than the move commit.
What does `git blame -w` do?
It ignores whitespace-only changes when assigning blame, so a reformatting commit isn't credited for lines it only reindented.
What does `git log -S<string>` (the 'pickaxe') find?
It finds commits where the number of occurrences of the given string in the diff changed — i.e., commits that ADDED or REMOVED that string. Useful for locating when a function/variable was introduced or deleted.
How does `git log -G<regex>` differ from `git log -S<string>`?
`-S` (with default behavior) reports commits that change the COUNT of occurrences of the literal string (added/removed lines). `-G` reports commits whose diff contains any added or removed line MATCHING the regex, even if the total occurrence count is unchanged. Adding `--pickaxe-regex` makes `-S` treat its argument as a regex.
What is a Git submodule?
A submodule is a repository embedded inside another (the superproject) as a subdirectory. The parent records a specific commit (a gitlink) of the submodule, pinning it to an exact version rather than tracking a branch.
Which command adds a submodule and what file does it create/update?
`git submodule add <url> <path>` clones the repo at <path>, records the gitlink, and creates/updates the `.gitmodules` file (which stores each submodule's path and URL).
After pulling superproject changes that bump a submodule pointer, which command updates your working submodule to the recorded commit?
`git submodule update --init --recursive` initializes any new submodules and checks each out to the exact commit the superproject records (in detached HEAD).
How do you update a submodule to the latest commit of its tracked remote branch?
`git submodule update --remote <path>` fetches and checks out the newest commit of the configured branch. You must then commit the updated gitlink in the superproject to record the new pointer.
Why does a freshly cloned superproject have empty submodule directories, and how do you avoid it?
`git clone` does not populate submodules by default — it only records them. Either run `git submodule update --init --recursive` afterward, or clone with `git clone --recurse-submodules <url>` to fetch everything at once.
What does the `--recurse-submodules` flag do on `git clone` and `git pull`?
On clone it fetches and checks out all nested submodules automatically. On pull/checkout it updates submodule working trees to match the superproject's recorded commits, keeping them in sync.
In which state is a submodule's HEAD after `git submodule update`, and why does it matter?
It is in a DETACHED HEAD at the recorded commit. Commits made there aren't on any branch, so to develop inside a submodule you must first check out a branch, otherwise new work can be lost.
What is subtree merging in Git?
A strategy for embedding one project inside another by merging the second repo's contents into a subdirectory of the first, so its files live directly in the parent's tree/history with no separate metadata (no `.gitmodules`, no gitlink).
How do you add an external project into a subdirectory using `git subtree`?
`git subtree add --prefix=<dir> <repo-url> <ref> --squash`. `--prefix` is the target directory; `--squash` collapses the imported history into a single commit.
How do you pull upstream updates into an existing subtree?
`git subtree pull --prefix=<dir> <repo-url> <ref> --squash`, which merges the latest upstream changes into the subdirectory.
Contrast submodules and subtrees on where the external code's history and files live.
Submodule: external repo stays a separate repository; the parent stores only a pointer (gitlink) + `.gitmodules`, and files aren't in the parent's commits. Subtree: external files and (optionally squashed) history are merged directly INTO the parent repo's tree and history.
Give two advantages of subtrees over submodules for consumers.
1) A plain `git clone` gets all code with no extra commands (no `submodule update` step). 2) Contributors don't need to learn submodule commands — the files are just part of the repo. Trade-off: pushing changes back upstream and keeping history clean is more awkward than with submodules.
Give two advantages of submodules over subtrees.
1) Exact version pinning with a small footprint — the parent stores only a commit pointer, not duplicated files/history. 2) Contributing back upstream is straightforward since the submodule is a real, independent repo you can commit and push from.
What problem does `git worktree` solve?
It lets you check out multiple branches simultaneously in separate working directories that all share ONE underlying repository (.git), avoiding a full second clone and letting you work on several branches in parallel without stashing.
Which command creates a new linked worktree checking out a branch, and where does its directory live?
`git worktree add <path> <branch>` creates a new working directory at <path> with that branch checked out. It's a linked worktree sharing the main repo's object database; use `-b <new-branch>` to create a branch at the same time.
Can two worktrees of the same repo have the same branch checked out simultaneously? How do you clean up worktrees?
No — Git forbids checking out the same branch in two worktrees at once (prevents conflicting updates). Remove a worktree with `git worktree remove <path>`, and prune stale administrative entries with `git worktree prune`.
What is sparse checkout and what problem does it address for large repos?
Sparse checkout populates the working directory with only a selected subset of files/directories instead of the whole tree, reducing on-disk size and speeding operations in very large repositories/monorepos where you only need part of the tree.
How do you enable and configure a cone-mode sparse checkout for a directory?
`git sparse-checkout init --cone`, then `git sparse-checkout set <dir1> <dir2>` to choose the directories to materialize. Cone mode restricts patterns to whole directories, which is faster than the general non-cone pattern matching.
What is a partial clone and which flag creates a blobless clone often paired with sparse checkout?
A partial clone omits some objects at clone time, fetching them on demand. `git clone --filter=blob:none <url>` creates a blobless clone (skips file blobs until needed); `--filter=tree:0` is even more aggressive. Combined with sparse checkout it makes huge monorepos usable.
Name three Git features commonly used to make large monorepos performant.
1) Sparse checkout (cone mode) to limit working-tree files. 2) Partial/blobless clone (`--filter`) to limit downloaded objects. 3) The commit-graph file (`git commit-graph write`) and/or the built-in filesystem monitor (`core.fsmonitor`) to speed up history and status operations. Scalar is a wrapper that bundles these.
What are Git hooks and where are client-side hooks stored by default?
Hooks are scripts Git runs automatically on certain events. Client-side (local) hooks live in `.git/hooks/` as executable files named after the event (e.g., `pre-commit`). They are NOT copied by clone, so they aren't shared through the repo by default.
Name three client-side hooks and when each fires.
`pre-commit` runs before a commit is created (e.g., lint/test; a nonzero exit aborts the commit). `commit-msg` runs after the message is entered, to validate/format it. `pre-push` runs before pushing, to run final checks before updating the remote.
Name the key server-side hooks and their roles.
`pre-receive` runs once before any refs are updated — can reject the entire push. `update` runs once PER ref being updated, allowing per-branch acceptance/rejection. `post-receive` runs after all refs are updated, for notifications/deployment/CI triggers.
How do you make a hook shared across a team, since `.git/hooks` isn't versioned?
Store hook scripts in a tracked directory and point Git at it with `git config core.hooksPath <dir>`, or use a hook-management framework (e.g., pre-commit, Husky, Lefthook) that installs the hooks from versioned config so every clone gets them.
What is Git LFS and what problem does it solve?
Git Large File Storage replaces large binary files in the repo with small text POINTER files, storing the actual file contents on a separate LFS server. This keeps the Git repo small and fast because large binaries don't bloat every clone's history.
Which commands set up Git LFS and start tracking `.psd` files, and what file records the rule?
`git lfs install` (one-time setup), then `git lfs track "*.psd"`. The tracking rule is written to `.gitattributes` (which must be committed), telling Git to route matching files through LFS filters.
What does a Git LFS pointer file actually contain in the repository?
A small text stub with metadata: the LFS spec version, the object's SHA-256 OID, and its size — for example lines like `version https://git-lfs.github.com/spec/v1`, `oid sha256:<hash>`, and `size <bytes>`. The real bytes are fetched from the LFS store on checkout.
Git & Version Control Advanced Git FAQ
What is in the Git & Version Control Advanced Git syllabus?
Git & Version Control Advanced Git is split into 6 chapters: Rewriting History, Debugging with Git, Submodules and Subtrees, Worktrees and Sparse Checkout, Hooks and Automation and Handling Large Files and Performance. Together they contain 20 topics.
How many chapters are in Git & Version Control Advanced Git?
6 chapters, containing 20 topics. The largest chapter is Rewriting History, with 4 topics.
Are there flashcards for Git & Version Control Advanced Git?
Yes — a 54-card Advanced Git deck, printed in full in the flashcards section of this page and available in the Examius app with spaced repetition. The free plan covers one exam of your choice, including its flashcards; Examius Pro, a one-time purchase, unlocks every other exam and deck.