Skip to main content

Version Control Overview

This domain covers Git from the object model up — understanding how Git actually stores data as immutable content-addressable objects, how branching strategies shape CI/CD pipelines, when to rebase vs. merge and why the choice matters, how remotes and remote-tracking branches work, how to resolve conflicts confidently, and how hooks enforce quality gates locally. These topics surface in senior backend interviews and team tech discussions constantly.

Key Concepts at a Glance

  • Version control: tracks every change to every file over time; lets you restore any past state, collaborate without overwriting each other, and experiment in isolation.
  • Distributed VCS: every developer holds a complete, independent copy of the repository including full history — no single point of failure; most operations are local.
  • Three-area model: working directory (files on disk) → staging area/index (.git/index) → local repository (.git/objects/); git add moves to staging, git commit moves to repo, git push sends to remote.
  • Staging area (index): the intermediate holding space that lets you craft precise commits — choose exactly which changes (even individual hunks with git add -p) go into each commit.
  • git restore: git restore <file> discards working directory changes; git restore --staged <file> unstages without losing changes — confusing these two causes accidental work loss.
  • git reset modes: --soft moves HEAD only; --mixed (default) also clears staging; --hard also discards working directory — never use --hard unless you're sure, and check git reflog if you do.
  • git reflog: records every HEAD movement; lets you recover "lost" commits after a reset --hard for up to 90 days by default.
  • Conventional Commits: structured message format <type>(<scope>): <subject> (feat, fix, chore, refactor, docs, test) — enables automated changelogs and semantic versioning.
  • Commit: stores a root tree pointer, zero or more parent commit SHA-1s, author/committer info, and message; immutable.
  • Tag (annotated): a full Git object with tagger name, date, and message; points to a commit; preferred for release markers.
  • Content-addressable: the object's key (SHA-1) is derived from its content — changing any byte produces a different key; this makes history tamper-evident.
  • HEAD: the symbolic ref in .git/HEAD; normally points to a branch name; in "detached HEAD" state it points directly to a commit SHA-1.
  • Remote: a named alias for a URL (e.g., origin); stored in .git/config.
  • Remote-tracking branch: a local read-only snapshot of a remote branch (e.g., origin/main); lives in .git/refs/remotes/; only updates on git fetch.
  • Upstream tracking: a branch configured to know its remote counterpart so git push/pull can work without specifying the remote explicitly.
  • Fast-forward merge: moves a branch pointer forward when no divergence exists; no merge commit created.
  • Merge commit: a commit with two or more parents that ties diverged branches together; preserves full branch topology in history.
  • Rebase: replays commits from one branch onto another, rewriting SHA-1s; produces linear history; never use on shared/public branches.
  • Interactive rebase (-i): lets you squash, fixup, reorder, or drop commits before a PR; essential for clean history.
  • Three-way merge: uses base + ours + theirs to auto-resolve non-conflicting changes; conflicts only appear when both sides changed the same lines differently.
  • Conflict markers: <<<<<<< / ======= / >>>>>>> delimit the two conflicting versions in a file; diff3 style adds the base version in the middle.
  • git rerere: records conflict resolutions and automatically re-applies them on recurring conflicts — enable with rerere.enabled=true.
  • Git Flow: two long-lived branches (main, develop) plus feature/*, release/*, hotfix/*; designed for scheduled versioned releases.
  • GitHub Flow: one permanent branch (main), short-lived feature branches, PRs merged directly; designed for continuous delivery.
  • Trunk-based development (TBD): all developers integrate to a single trunk/main multiple times per day; requires feature flags and strong CI/CD.
  • Feature flag: a runtime toggle that hides incomplete features in production; enables merging unfinished code to trunk without affecting users.
  • Git hook: an executable script in .git/hooks/ that Git runs at a specific lifecycle point; pre-commit and commit-msg are client-side; pre-receive is server-side.
  • core.hooksPath: Git config key (since v2.9) that redirects hook resolution to a committed directory (e.g., .githooks/), enabling team-wide shared hooks.
  • Shallow clone (--depth=N): downloads only N commits; smaller and faster for CI pipelines; loses bisect, blame, and full merge-base capability.

Quick-Reference Table

Core Commands (Basics)

CommandPurposeKey Note
git initInitialize a new repositoryCreates .git/ directory
git clone <url>Clone a remote repo locallyCreates origin remote automatically
git statusShow working tree and staging stateTells you which area each file is in
git add -p <file>Stage hunks interactivelyCraft precise commits; avoid git add . blindly
git commit -m "msg"Create commit from staged changesFollow Conventional Commits format
git diffUnstaged changes (working dir vs. index)Shows what git add would stage
git diff --stagedStaged changes (index vs. last commit)Shows what git commit would record
git restore <file>Discard working directory changesDestructive — changes gone
git restore --staged <file>Unstage a fileChanges preserved in working dir
git reset --soft/mixed/hardMove HEAD + optionally clear index/working dir--hard is destructive; check reflog to undo
git revert <sha>Undo a commit by creating a new oneSafe for pushed/shared branches
git stash push -u -m "desc"Stash uncommitted work + untracked filesgit stash pop to reapply
git log --oneline --graph --allVisual commit graphAdd --decorate for branch/tag labels

Advanced Commands

CommandPurposeKey Note
git cat-file -p <sha1>Pretty-print any Git objectUse to explore blobs, trees, commits
git log --oneline --graph --allVisual commit graphAdd --decorate for branch/tag labels
git fetch --pruneDownload remote changes + clean stale tracking refsSet fetch.prune=true globally
git pull --rebaseFetch + rebase local commits on topAvoids merge-commit clutter; set pull.rebase=true globally
git push -u origin <branch>Push and set upstream trackingEnables plain git push/git pull after
git push --force-with-leaseForce-push safely after rebaseFails if remote has new commits since last fetch
git rebase -i origin/mainInteractive rebase before PRsquash/fixup WIP commits; reword messages
git rebase --abortCancel an in-progress rebaseReturns to pre-rebase state with no damage
git merge --no-ffMerge with explicit merge commitPreserves feature branch boundary in history
git mergetoolLaunch visual three-pane conflict toolConfigure VS Code: merge.tool=vscode
git rerereReuse recorded conflict resolutionsEnable: rerere.enabled=true
git reflogView all HEAD movementsRecover "lost" commits after reset
git stash push -u -m "desc"Stash uncommitted work + untracked filesgit stash pop to reapply
git merge-base main feature/xFind common ancestor commitUseful for understanding conflict context
git remote add upstream <url>Add original repo in fork workflowConvention: origin = your fork, upstream = original

Hook Quick Reference

HookWhen it runsCommon use
pre-commitBefore commit object createdLint, conflict-marker detection
commit-msgAfter message writtenEnforce Conventional Commits format
pre-pushBefore push sent to remoteBlock push to main; run tests
post-mergeAfter successful mergeInstall dependencies if package.json changed
pre-receiveServer: before push acceptedBranch protection, commit policy (cannot bypass)

Branching Strategy Comparison

DimensionGit FlowGitHub FlowTrunk-Based Dev
Permanent branchesmain + developmain onlytrunk/main only
Release mechanismrelease/* branchTag on mainContinuous / feature flags
Deploy frequencyScheduled (weekly/monthly)Per PR mergeMultiple times / day
Best forVersioned libraries, enterprise softwareWeb apps with CDHigh-velocity SaaS

Learning Path

Suggested reading order for a returning Java developer:

  1. Git Basics — start here; the three-area model, core commands (add/commit/restore/stash), and the daily workflow. If you know these, skim for gaps.
  2. Git Object Model — start here; understanding SHA-1 content addressing makes every other Git command intuitive.
  3. Working with Remotes — fetch vs. pull, upstream tracking, fork workflows — the daily collaboration model.
  4. Branching Strategies — choose the model that matches your team's deploy frequency and CI/CD maturity.
  5. Rebase vs. Merge — the most-debated Git topic; know both options and the Golden Rule.
  6. Conflict Resolution — three-way merge, rerere, and tools to resolve conflicts efficiently.
  7. Git Hooks & Workflows — automate quality gates locally before code reaches CI.

Top 5 Interview Questions

Q1: What are Git's four object types and how do they relate to each other? A: Blob stores raw file bytes. Tree stores a directory listing mapping filenames to blobs and sub-trees. Commit stores a root tree pointer + parent commit SHA-1(s) + author/message. Tag is an annotated tag object pointing to a commit with tagger metadata. A commit points to a tree snapshot; that tree points to blobs and sub-trees. Commits chain via parent pointers to form the history graph.

Q2: What is the difference between git merge and git rebase? A: Both integrate changes from one branch into another. git merge creates a merge commit with two parents, preserving the original branch topology. git rebase replays commits from the feature branch onto the target, rewriting their SHA-1s and producing a linear history. Code outcome is identical; only history structure differs. The Golden Rule: never rebase a branch other developers have pushed to.

Q3: What is the difference between git fetch and git pull? A: git fetch downloads new objects and updates remote-tracking branches (e.g., origin/main) without touching your working tree or local branches. git pull is git fetch + git merge (or git rebase if pull.rebase=true). Prefer git fetch followed by git rebase origin/main for explicit, merge-free integration.

Q4: When would you choose trunk-based development over Git Flow? A: Trunk-based development is better for teams that deploy continuously (multiple times per day), have comprehensive automated test coverage, and use feature flags for incomplete work. Git Flow suits teams with scheduled versioned releases — libraries, packaged software, enterprise products — or those maintaining multiple live versions simultaneously.

Q5: What is a Git hook and how do you share it across a team? A: A Git hook is an executable script in .git/hooks/ that runs at specific lifecycle points (pre-commit, commit-msg, pre-push, etc.). Returning a non-zero exit aborts the operation. .git/ is never committed, so hooks must be shared via git config core.hooksPath .githooks (pointing to a committed directory) or the pre-commit framework (which installs from a .pre-commit-config.yaml committed to the repo).


All Notes in This Domain

NoteDescription
Git BasicsThree-area model, core commands, branching basics, .gitignore, and the daily workflow from init to push.
Git Object ModelBlobs, trees, commits, tags — how Git stores every snapshot as content-addressable objects.
Branching StrategiesGit Flow, GitHub Flow, and trunk-based development — choosing the model that fits your team.
Rebase vs. MergeWhen to use each, interactive rebase for clean PRs, and the Golden Rule you must never break.
Working with Remotesfetch, pull, push, upstream tracking, fork workflows, and diverged branch handling.
Conflict ResolutionThree-way merge algorithm, conflict markers, git rerere, and merge tools.
Git Hooks & WorkflowsClient-side and server-side hooks, sharing hooks across a team, pre-commit framework.