Git Interview Questions: Real Practice Quiz
Reviewed by Mark Dickie · Last updated
Git is a distributed version control system that tracks changes to files across a repository's history through a directed acyclic graph of commits. For interviews, you should understand the three-stage architecture (working directory, staging area, repository), how branching and merging work under the hood, and the difference between merge and rebase. You also need to know how to recover from common mistakes — detached HEAD, accidental commits, botched merges — because recovery questions come up often.
| Topic | What to know |
|---|---|
| Staging area | How git add moves changes from the working directory to the index, and why the index exists |
| Branching | Branches are lightweight pointers to commits; HEAD points to the current branch |
| Merging vs. rebasing | merge preserves history; rebase rewrites commits onto a new base, producing linear history |
| Remote operations | fetch downloads objects without changing your working tree; pull runs fetch then merge |
| Internals | Objects (blobs, trees, commits, tags), reflog, and how content-addressable storage works |
How does git rebase differ from git merge?
Both integrate changes from one branch into another, but they produce different histories. git merge creates a merge commit that ties two divergent lines of development together — the original commits stay intact. git rebase takes your branch commits and replays them one by one on top of the target branch, rewriting their SHA-1 hashes. The result is a linear history with no merge commit, which reads cleanly but rewrites shared history if pushed.
What should I practice for a Git interview?
- Undoing changes —
git reset --soft,--mixed, and--hardand what each does to the index and working tree. - Cherry-picking — applying a single commit from another branch without merging the whole branch.
- Stashing —
git stashto save uncommitted work,git stash popto restore it, and how stashes interact with conflicts. - Reflog recovery — using
git reflogto find "lost" commits after a hard reset or botched rebase. - Interactive rebase — squashing, reordering, and editing commits with
git rebase -ito clean up history before sharing.
How are Git objects stored internally?
Git stores every piece of content as an object identified by the SHA-1 hash of its content. A blob holds file data, a tree holds directory structure (pointing to blobs and subtrees), and a commit points to a tree plus metadata like author, timestamp, and parent commit(s). Tags are a fourth object type for annotated tags. This content-addressable design means identical content is stored once, and any corruption is detectable by recomputing the hash.
Key facts
- Tarmac has 90 Git interview questions on this topic, 10 of them on this page, at difficulty 2–4 of 5.
- Tarmac last reviewed these Git interview questions on 10 August 2026.
At a glance
| Questions | 10 shown · 90 in the bank |
|---|---|
| Difficulty | 2–4 of 5 |
| Formats | Multiple choice, True / false, Flashcard, Multiple answer, Find the bug, Short answer, Ordering, Fill in the blank |
What you'll review
- reset modes
- fetch pull push
- amend cherry pick
- merge vs rebase
- interactive rebase
- conflict resolution
- stash
- clone init
Practice questions
Git/recovery-debugging/reset-modes
You want to undo your last 3 commits but keep every change they introduced staged, ready to re-commit differently. Which command does this?#
Options
Show answer
git reset --soft HEAD~3 undoes the last 3 commits while keeping their changes staged. --soft moves the branch pointer back but leaves the index and working directory untouched, so everything those commits introduced is ready to re-commit immediately. --mixed (the default) also unstages the changes; --hard discards them from the working directory too. git revert is different again — it adds new commits that undo old ones rather than moving the branch pointer.
git reset --soft moves the branch pointer (HEAD) back to the target commit but leaves the index and working directory untouched, so everything those 3 commits changed is now staged and ready to re-commit. --mixed (the default) also moves HEAD but resets the index too, so the changes end up unstaged instead. --hard resets the working directory as well, discarding the changes entirely. git revert doesn't rewrite history at all — it adds new commits that undo the old ones, which is the right tool for undoing already-pushed commits, not for restaging local ones.
Git/remote-workflows/fetch-pull-push
git pull is equivalent to running git fetch followed by git merge (or git rebase, if configured).#
Options
Show answer
True. git pull is git fetch (download new commits from the remote) immediately followed by git merge — or git rebase, if pull.rebase is configured or --rebase is passed — to integrate those commits into the current branch. Running git fetch alone downloads the same data without touching your working branch, which is why it's the safer way to inspect incoming changes first.
True. git fetch downloads new commits and refs from the remote without touching your working branch; git pull does that fetch and then immediately integrates the result into your current branch — by default with a merge, or with a rebase if pull.rebase is configured (or git pull --rebase is passed). Understanding pull as these two separate steps is what makes it clear why a pull can produce a merge commit, and why git fetch alone is the safer way to inspect incoming changes before deciding how to integrate them.
Git/history-rewriting/amend-cherry-pick
What does git cherry-pick <commit> do?#
Show answer
Applies the changes introduced by a single existing commit onto your current branch as a brand-new commit (its own hash, same diff and message by default) — without merging the whole branch that commit came from. It's the tool for porting one specific fix, like a hotfix, into another branch (e.g. a release branch) without pulling in unrelated commits.
Cherry-pick replays exactly one commit's diff, so it's the precise, surgical alternative to a full merge when you only want a single change to travel between branches.
Git/branching-merging/merge-vs-rebase
A feature branch has diverged from main, and two teammates have already pulled it and are committing on top of it. You need to bring it up to date with main before opening a PR. Which approach is safe for a branch other people are actively building on?#
Options
Show answer
For a shared branch other people have already pulled and are committing on, git merge main into feature is the safe move — merging adds a new commit and never changes the hash of an existing one, so teammates' clones stay valid on their next pull. Rebasing feature onto main and force-pushing rewrites every commit feature already has, giving them new hashes; anyone who already pulled the old ones is left with local commits based on history that no longer exists upstream. This is git's 'golden rule of rebasing' — rebase only commits nobody else has pulled yet.
Merging leaves every existing commit's hash untouched and just adds a new merge commit joining the two histories, so teammates' local clones stay valid — their next git pull just fetches the merge commit. Rebasing rewrites feature's commits onto a new base, giving them new hashes; force-pushing that means every commit your teammates already have diverged from the shared history is now based on commits that no longer exist upstream, which turns their next pull into a confusing pile of duplicate/phantom conflicts. This is git's 'golden rule of rebasing': rebase freely on commits only you have, never on commits others have already pulled and built on.
Git/history-rewriting/interactive-rebase
Which of these change the hash of an already-made commit (rewrite history), and so should never be run on commits already pushed and pulled by teammates? Select all that apply.#
Options
Pick every one that applies.
Show answer
Three of these rewrite history: git rebase replays commits onto a new base so each one gets a new hash, git commit --amend replaces the tip commit with a different one under a new hash, and git reset --hard followed by a force-push moves the branch pointer and overwrites the remote's history. All three are unsafe on commits teammates have already pulled, because a force-push replaces shared commits with different ones and leaves everyone's local work based on history that no longer exists upstream. git merge and git revert are additive by contrast: merge creates a new commit joining two histories without altering either, and revert creates a new commit whose diff undoes an earlier one while leaving the original commit's hash untouched. That is why merge and revert are the safe defaults for anything already shared.
Rebase, commit --amend, and a hard-reset-plus-force-push all replace existing commits with different ones under new hashes — exactly what a force-push overwrites the shared history with, breaking anyone who already based work on the old commits. Merge and revert are both additive: merge creates a new commit that references the existing history without changing any of it, and revert creates a new commit whose diff undoes an earlier one, leaving the original commit (and its hash) exactly as it was. That's why merge and revert are the safe defaults for undoing or integrating already-shared history, while rebase/amend/force-push are reserved for commits nobody else has pulled yet.
Git/branching-merging/conflict-resolution
A teammate resolved a merge conflict and pushed. CI is now failing with a syntax error in config.ts. Which line is the bug?#
1| export const RETRY_LIMIT =
2| <<<<<<< HEAD
3| 5;
4| =======
5| 3;
6| >>>>>>> feature/lower-retry-limit
7| export const TIMEOUT_MS = 2000;Options
Show answer
Lines 2, 4, and 6 — the conflict markers were never removed, so this literal text got committed as part of the file
When git can't auto-merge a conflicting region, it writes both versions into the file surrounded by <<<<<<<, =======, and >>>>>>> markers and leaves it to the human to edit the file down to the intended result before staging and committing. Here the conflict was never actually resolved — the markers themselves got committed verbatim, which is invalid syntax in any language. The fix is to open the file, decide which value (or a combination) is correct, delete all three marker lines along with whichever side is unwanted, then git add the cleaned-up file and commit. This is one of the most common real-world git mistakes: git commit doesn't validate that conflict markers are gone, so a rushed resolution ships broken code straight into CI.
Git/branching-merging/merge-vs-rebase
Explain the practical difference between git merge and git rebase, and when you'd reach for each.#
Show answer
Merge joins two branches by creating a new merge commit with two parents; it never changes an existing commit's hash, so it's safe on shared branches and preserves the true, non-linear history of when work actually happened. Rebase replays one branch's commits onto a new base one at a time, giving each replayed commit a new hash and producing a linear history with no merge commit. I'd rebase a local, not-yet-shared feature branch to clean up messy WIP commits and pick up the latest main before opening a PR, since a clean linear history is easier to review. I'd merge (not rebase) any branch other people have already pulled and are committing on, since rewriting shared history breaks their clones.
The core distinction is additive vs rewriting: merge adds a new commit and touches nothing that came before it; rebase replays commits onto a new base, which changes their hashes. That single fact drives the workplace rule — rebase is for cleaning up commits only you have (before a PR, or to keep a personal branch current with main), merge is for anything already shared, because rewriting history teammates have pulled leaves their local repos out of sync with the remote's new hashes.
Git/branching-merging/conflict-resolution
Order the steps of resolving a merge conflict that git pull reports.#
Put these in order
Show answer
Resolving a merge conflict from git pull follows a fixed sequence: the pull reports which files conflict, you open each one and find its <<<<<<< / ======= / >>>>>>> marker blocks, edit the file down to the intended final content while deleting the markers, git add the resolved file to mark it settled, then git commit to finalize the merge. Leaving the marker text in place and committing it anyway is a common real mistake that ships broken syntax.
A merge conflict is git's way of saying it couldn't automatically reconcile two changes to the same region of a file, so it pauses mid-merge and leaves both versions in the file for you to decide between. The flow is always the same: find the marker blocks, edit each file down to what it should actually say (deleting the markers themselves, not just picking a side), stage the resolved files with git add so git knows the conflict is settled, then commit to complete the merge. Skipping the edit step and committing the raw markers is a classic mistake — see the related find-the-bug question.
Git/recovery-debugging/stash
git stash with no flags saves your changes to _____ files only, so a brand-new file you have never added is left behind in the working directory and follows you onto whatever branch you switch to next. Passing the _____ flag stashes untracked files as well, while still leaving ignored files alone.#
Show answer
git stash with no flags saves your changes to tracked files only, so a brand-new file you have never added is left behind in the working directory and follows you onto whatever branch you switch to next. Passing the -u flag stashes untracked files as well, while still leaving ignored files alone.
The gap between what stash saves and what people assume it saves is a reliable source of confusion, because the failure is silent: you stash, switch branch, and your new files are simply there, on the wrong branch, looking like you created them by mistake. Plain git stash takes modifications to tracked files; -u (--include-untracked) adds untracked files and cleans them out of the working directory; -a (--all) goes further and includes ignored files too. Two neighbouring surprises are worth holding at the same time. git stash pop restores everything as unstaged, flattening the staged/unstaged split you had before — --index is what preserves it. And pop only drops the stash entry if applying it succeeded, so a conflicting pop leaves the entry in the list and needs a manual git stash drop once you have sorted the conflict out.
Git/remote-workflows/clone-init
Your CI job clones a large repository with --depth 1 to save time. The build now fails at the versioning step, which runs git describe --tags, and the code-owners step, which runs git merge-base. Which statements about the fix are correct? Select all that apply.#
Options
Pick every one that applies.
Show answer
Three things are true. --depth 1 truncates history to one grafted commit with no ancestors, so git describe, git merge-base, git blame and git log have nothing to walk — which is why both CI steps broke. --filter=blob:none cuts along a different axis: it downloads the complete commit and tree graph and fetches file contents lazily, so history-walking commands keep working. And --depth also implies --single-branch, so deepening the history later still leaves only one branch until the refspec is widened. Recovering a shallow clone is done with git fetch --unshallow; garbage collection cannot reconstruct commits that were never downloaded. Tags are a red herring — a shallow clone fetches none, and fetching them afterwards still leaves git describe with no ancestry to count.
Shallow and partial clone solve the same problem — do not download the whole repository — by cutting along different axes, and the difference decides whether CI still works. --depth 1 truncates history: you get one commit with its ancestry grafted off, so every command that needs to walk backwards has nothing to walk. That is why describe (which counts commits since the nearest tag) and merge-base (which finds a common ancestor) both fail, and why blame in a shallow clone attributes every line to the single grafted commit. --filter=blob:none truncates file contents instead: the full commit and tree graph comes down, blobs are fetched lazily when something actually reads a file, so history-walking commands behave normally. The correction that matters for CI defaults is that --depth also implies --single-branch, so deepening later still leaves you on one branch until you fix the refspec as well. Recovering from a shallow clone is done with git fetch --unshallow, not with gc — garbage collection cannot invent commits that were never downloaded. Tags are a red herring in both directions: a --depth 1 clone does not bring them down at all, and fetching them afterwards still does not help — git fetch --tags retrieves the refs and git describe --tags continues to fail with "No tags can describe", because there is no ancestry between HEAD and the tag left to count. Only git fetch --unshallow restores it.
Related interview questions
The other 80 questions
This page shows 10. A free account opens the rest and marks your answers, which a page of answers can't do. Miss one and it returns a few days later, then a week after that, until you stop missing it.
Free · the whole bank · 100 graded answers per 30 days · written feedback and job-ad quizzes on the paid plan