Git interview questions and answers
26 Git questions from real DevOps and engineering interviews, sorted by seniority: merge against rebase, reset against revert, the reflog, detached HEAD, and recovering work you thought you had lost.
All 26 questions
1
What is a Git commit, and what is a Git branch?
JuniorA commit is an immutable snapshot of the whole tree, plus metadata: author, message, timestamp, and one or more parent commit hashes. It is not a diff. Git computes diffs on demand by comparing snapshots.
A branch is a forty-byte file containing a commit hash. That is all.
git branch featurewrites one file; it copies nothing.That is why branching in Git is instant while it was expensive in older systems, and why the chain of parent pointers is the history rather than the branch names.
HEADis a pointer to the branch you are on, which is in turn a pointer to a commit. Committing moves the branch pointer forward; HEAD follows because it points at the branch.Being able to say "a branch is a movable label on a commit" answers a surprising number of later questions on its own.
2
What is the Git staging area for?
JuniorIt is a middle layer between your working directory and a commit, letting you choose what goes into the next snapshot rather than committing everything you happen to have changed.
Area Holds Working directory Your files as they are right now Staging area, or index What the next commit will contain Repository Committed history The practical value is committing in coherent pieces. If you fixed a bug and also reformatted a file,
git add -plets you stage the fix alone and commit it separately, which makes review and later bisecting far easier.git add -p git diff # working directory against the index git diff --staged # the index against the last commitThose two diffs are worth knowing apart, because "why does git diff show nothing" almost always means the change is already staged.
3
What is the difference between git fetch and git pull?
Juniorgit fetchdownloads new commits and updates your remote-tracking branches, such asorigin/main. It changes nothing you are working on.git pullisfetchfollowed immediately by a merge, or a rebase with--rebase, into your current branch.fetchpullDownloads Yes Yes Changes your branch No Yes Can cause a conflict No Yes The habit worth stating: fetch, look at what arrived with
git log HEAD..origin/main, then decide how to integrate it.pullon a branch with local work is how people end up with a surprise merge commit or a conflict mid-thought.git pull --rebaseavoids the merge commit, and settingpull.rebase truemakes that the default.4
How do you change the last Git commit?
Juniorgit commit --amendThat replaces the last commit with a new one containing the staged changes and an editable message. To fix only the message, use
--amendalone; to add a forgotten file, stage it first and use--amend --no-edit.The critical caveat, and the reason this is asked: amend does not edit a commit, it creates a different one. The hash changes. If the original was already pushed, your history and the remote's have diverged, and the next push is rejected as non-fast-forward.
So amend freely on local commits, and treat a pushed commit as published. On a personal branch,
--force-with-leaseis acceptable; on a shared one,git revertis the answer instead.5
What is the difference between git merge and git rebase?
MidBoth integrate one branch into another. They differ in what happens to the history.
Merge keeps what actually happened and adds a commit with two parents. Rebase produces a straight line, but the replayed commits are new objects with new hashes, which is exactly why rebasing a branch other people have pulled breaks their history. Merge creates a new commit with two parents. History is preserved exactly, including the fact that the work happened in parallel, at the cost of a graph with many merge commits.
Rebase replays your commits one at a time onto the new base, producing new commits with new hashes. History is linear and easy to read, at the cost of no longer being what actually happened.
Merge Rebase History Preserved, branching Rewritten, linear Commit hashes Unchanged All new Safe on a shared branch Yes No Conflicts Resolved once Possibly once per replayed commit The rule interviewers listen for: rebase your own unpushed work to tidy it before review, merge when integrating into a shared branch. Never rebase something other people have pulled.
6
What is the difference between git reset --soft, --mixed and --hard?
MidAll three move the branch pointer. They differ in what else they touch.
Branch pointer Staging area Working directory --softMoves Untouched Untouched --mixed(default)Moves Reset Untouched --hardMoves Reset Overwritten So
--soft HEAD~1undoes the commit and leaves everything staged, which is how you redo a commit message or combine two commits.--mixedadditionally unstages, leaving the changes as working edits.--hardthrows the changes away.--hardis the only one that can lose work, and only uncommitted work: any commit it moves off is still in the reflog for about ninety days.git reset --soft HEAD~1 # undo the commit, keep everything staged git reset --hard ORIG_HEAD # undo the last dangerous operation entirely7
When would you use git revert rather than git reset?
MidWhen the commit is already shared.
resetmoves the branch pointer backwards, so the commits stop being in the history. On a branch other people have pulled, that is a rewrite: their history and yours no longer agree, and the next person to pull gets a mess.revertcreates a new commit that applies the inverse change. Nothing is rewritten, the original stays in the history, and everyone's clone stays consistent.git revert a1b2c3d # one commit git revert -m 1 a1b2c3d # a merge commit, keeping the first parentThat
-mflag is worth knowing: reverting a merge is ambiguous because the commit has two parents, so Git needs to be told which line of history to treat as the mainline.The rule: reset for private history, revert for public history. On production incidents revert is almost always right, because it is auditable and it does not require anyone else to do anything.
8
Your git push is rejected as non-fast-forward. What do you do?
MidIt means the remote has commits you do not, so your push would discard them. Git is refusing to lose someone's work.
See exactly how far apart you are:
git fetch origin git rev-list --left-right --count origin/main...HEADThe two numbers are commits you are missing and commits you would add. Anything non-zero on the left is why it was refused.
Then integrate before pushing:
git pull --rebase origin main git push origin mainThe wrong answer, and interviewers are listening for it, is
git push --force. That resolves the rejection by deleting the other person's commits. If you genuinely rewrote your own branch, use--force-with-lease, which refuses if the remote moved since your last fetch.9
How do you resolve a Git merge conflict?
MidA conflict happens when two branches change the same region of the same file and Git cannot decide. It marks the file and stops.
git status # which files are conflicted git diff # the conflicting hunksThe markers show three regions: your side between
<<<<<<<and=======, theirs between=======and>>>>>>>. You edit the file to what it should actually be, which is often neither side verbatim, remove the markers, then stage it.git add path/to/file git commit # or: git rebase --continue git merge --abort # to back out entirelyTwo things that mark experience.
git checkout --oursand--theirstake one side wholesale, and during a rebase those two are swapped relative to what you expect, because your commits are being replayed onto theirs. Andgit rerere, once enabled, remembers how you resolved a conflict and reapplies it, which is genuinely useful on a long-lived branch you rebase repeatedly.10
How do you clean up Git commits before opening a pull request?
MidInteractive rebase:
git rebase -i origin/mainThat opens the list of your commits with a verb against each:
Verb Does pickKeep as is rewordKeep the change, edit the message squashMerge into the previous commit, combine messages fixupMerge into the previous commit, discard the message editStop so you can amend dropRemove the commit The reason to bother is review and bisect. Six commits reading "wip", "fix", "fix again" tell a reviewer nothing and make
git bisectuseless later.The workflow worth naming is
git commit --fixup=<hash>while you work, thengit rebase -i --autosquash, which positions the fixups automatically. And because this rewrites hashes, it belongs on your own branch before review rather than after.11
What does git cherry-pick do, and when is it the right tool?
MidIt applies the change introduced by one commit onto your current branch, as a new commit with a new hash.
git cherry-pick a1b2c3d git cherry-pick a1b2c3d..f4e5d6c # a rangeLegitimate uses: backporting a hotfix from
mainto a release branch, or rescuing one commit from a branch that is otherwise being abandoned.The caution worth adding is that frequent cherry-picking is usually a smell. It duplicates commits, so the same change exists twice under different hashes, and later merges between those branches can conflict with themselves. If you are cherry-picking regularly, the branching strategy is wrong.
git cherry -v main releaseis a useful companion: it lists which commits exist on one branch and not the other, accounting for already-picked changes.12
What does git stash do, and what are its limits?
MidIt saves your uncommitted changes onto a stack and gives you a clean working directory, so you can switch context without committing half-finished work.
git stash push -m "wip on auth" git stash list git stash pop # apply and remove from the stack git stash apply # apply and keep itThe limits that catch people out:
- Untracked files are not stashed by default. You need
-u, and ignored files need-a. Losing a new file to this is common. - It is a stack, so
poptakes the most recent entry, not the one you meant, unless you name it:git stash pop stash@{2}. - A dropped stash is hard to find, though
git fsck --unreachablecan still surface the commit object.
Worth saying that a throwaway branch with a real commit is often better than a stash for anything you will not resume within the hour, because it has a name and survives being forgotten.
- Untracked files are not stashed by default. You need
13
What is the Git reflog, and how does it save you?
SeniorThe reflog records every position
HEADand each branch has pointed at in this clone, with a timestamp. It is local, it is not pushed, and entries live about ninety days by default.Because commits are only garbage collected when nothing references them, and the reflog references them, almost anything you "lost" is still there.
git reflog --date=iso git branch recovered a1b2c3dThat recovers a deleted branch, a bad
reset --hard, an interrupted rebase, or a commit you amended away. Branching onto the hash is better than checking it out, which leaves you detached.Two extensions worth having.
git reflog show origin/mainremembers where the remote ref pointed before your last fetch, which is how you recover from somebody else's force push. Andgit fsck --unreachable --no-reflogsfinds dangling objects the reflog has already expired.The framing that lands: Git is append-only in practice, and the reflog is why almost nothing is truly destroyed until
gcruns.14
What happens when you are in detached HEAD state in Git?
SeniorHEADnormally points at a branch, which points at a commit. Detached means HEAD points straight at a commit, with no branch in between.You get there by checking out a hash, a tag, or a remote-tracking ref such as
origin/main.Everything works, and that is the trap. You can edit and commit normally, and those commits are real. But nothing references them: because no branch moved, checking out another branch leaves them unreferenced and eventually collectable. Git warns you, and the warning is easy to scroll past.
git switch -c rescue # name the work before leaving git reflog # if you already leftWorth adding that CI systems very often run in detached HEAD, because they check out a specific commit rather than a branch. That explains a category of "the script works locally and not in the pipeline" problems where something depends on the branch name.
16
How does git bisect work, and when would you use it?
SeniorIt binary-searches history for the commit that introduced a bug. You mark one commit known bad and one known good, and Git checks out the midpoint repeatedly, halving the range each time. A thousand commits become about ten checkouts.
git bisect start git bisect bad # current commit is broken git bisect good v1.4.0 # this tag was fine # test, then: git bisect good | git bisect bad git bisect resetThe part worth volunteering is automation. If the test is scriptable, Git will do the whole search unattended:
git bisect run ./scripts/reproduce.shThe script exits 0 for good, non-zero for bad, and 125 if the commit cannot be tested.
This is also the practical argument for clean commit history: bisect is only useful if each commit builds and runs, so a branch squashed into one enormous commit, or a history full of broken intermediate states, defeats it.
17
Which Git branching strategy would you choose for a team, and why?
SeniorThe answer should start with what the team actually needs, not with a named model.
Trunk-based development: short-lived branches merged to
mainwithin a day or two, with features hidden behind flags. Suits continuous deployment and a team that can keepmainreleasable. It requires real test automation and feature flags, and without those it becomes chaos.GitHub Flow: branch, pull request, merge, deploy from
main. A gentler version of the same idea and a sensible default for most web teams.Git Flow: long-lived
develop,releaseandhotfixbranches. Designed for versioned software with supported releases, such as a shipped product or a mobile app with review delays. On a web service deploying several times a day it adds ceremony without benefit, and its own author has said as much.Release branches per version when you genuinely support several versions at once.
The strongest answer names the real constraint: merge pain grows with branch lifetime, so the question is mostly how long a branch lives, not what the diagram looks like. Then it says what would change the choice, such as a regulated release process or a customer who pins versions.
18
What is the difference between a Git submodule and a subtree?
SeniorBoth include one repository inside another. They make opposite trade-offs.
Submodule Subtree Stores A pointer to a commit in another repo The actual files, merged in Clone Needs --recurse-submodulesJust works Consumers need to know Yes No Updating git submodule updategit subtree pullHistory Kept separate Merged into yours Repo size Small Larger Submodules are precise and unforgiving: the parent records an exact commit, which is excellent for reproducibility and a constant source of "I cloned it and the directory is empty" and detached HEAD confusion inside the submodule.
Subtrees are invisible to consumers, which is their whole advantage, at the cost of a bulkier history and awkward contribution back upstream.
The answer worth giving is that in most cases neither is right, and a package registry is. Reach for these when you genuinely need source-level inclusion of code you also develop.
19
What is the difference between git push --force and --force-with-lease?
Mid--forceoverwrites the remote branch unconditionally. If a colleague pushed since your last fetch, their commits are gone and Git will not warn you.--force-with-leasefirst checks that the remote is still where your remote-tracking ref says it is. If somebody pushed in the meantime, it refuses.git push --force-with-lease origin feature/user-searchSo it is not "force but safe" in general; it is "force, unless the thing I am about to overwrite has changed since I last looked".
One caveat worth knowing, because it turns the safety off: running
git fetchimmediately before updates your remote-tracking ref, so the lease now matches and the check passes. Fetch then force-with-lease is functionally plain force.And neither belongs on a shared branch. The real protection is branch protection rules on the host refusing force pushes to
mainat all.20
What is a .gitignore file, and why does it not work on already-tracked files?
Junior.gitignorelists paths Git should not start tracking. It has no effect on files already in the index, which is the part that confuses people: adding.envto.gitignoreafter committing it changes nothing.To stop tracking without deleting it locally:
git rm --cached .env git commit -m "Stop tracking .env"The critical follow-up for a secret: it is still in the history, and therefore in every clone. Removing it from the current commit does not remove it from the repository, so the credential must be rotated. Purging it properly needs
git filter-repoor BFG plus a coordinated force push, and rotating is faster and more reliable.A sensible file excludes dependencies, build output, local environment files and editor directories, and
git check-ignore -v <path>tells you which rule is matching when something is ignored unexpectedly.21
How do you find which Git commit introduced a specific line?
Midgit blame -L 40,60 path/to/fileThat annotates each line with the commit, author and date.
-Llimits it to a range, which matters on a large file.The problem with blame alone is that a reformat or a file move puts everyone's name on a line they never wrote. Two flags fix that:
git blame -w -C -M path/to/file-wignores whitespace changes,-Mdetects lines moved within the file, and-Cdetects lines copied from other files.For the change rather than the line,
git log -Ssearches for commits where an occurrence count of a string changed, which finds where something was introduced or removed even if the line has since moved:git log -S 'DATABASE_URL' --oneline git log -L 40,60:path/to/file22
What are the four Git object types, and how do they fit together?
SeniorEverything in
.git/objectsis one of four, addressed by the SHA of its contents.Object Holds blob File contents, with no name and no metadata tree A directory listing: names, modes, and the blob or tree each points at commit A root tree, parent commits, author, committer, message tag An annotated tag: a target object, tagger and message A commit points at one tree, which points at blobs and further trees. Because objects are content-addressed, two identical files anywhere in history are one blob, and an unchanged directory between commits is the same tree object reused.
That is why a commit is a full snapshot yet costs almost nothing: the snapshot mostly re-points at objects that already exist.
git cat-file -p HEAD git cat-file -t a1b2c3dRefs, including branches and tags, are just files under
.git/refscontaining a hash, which is what makes a branch cheap and a checkout fast.23
What are Git hooks, and what would you use them for?
MidScripts Git runs at points in its lifecycle, kept in
.git/hooks.Client-side:
pre-commitruns before a commit is created,commit-msgcan validate the message,pre-pushruns before a push. Server-side:pre-receiveandupdatecan reject a push outright.The limitation that matters: `.git/hooks` is not cloned, so a hook you add locally does not reach the team. That is what tools such as pre-commit and Husky exist to solve, by committing the configuration and installing the hooks on setup.
git config core.hooksPath .githooksSetting a tracked hooks directory is the dependency-free version of the same idea.
The judgement worth adding: client-side hooks are a convenience, not a control, because anyone can skip them with
--no-verify. Anything that genuinely must hold belongs in CI or in a server-side rule, and treating a pre-commit hook as a security boundary is a mistake.24
How do you undo changes to a file in Git before committing?
JuniorIt depends which stage the change is at, which is the point of the question.
git restore path/to/file # discard working directory changes git restore --staged path/to/file # unstage, keep the edit git restore --source=HEAD~2 file # restore it as it was two commits agogit restoreandgit switchwere introduced to split whatgit checkoutused to do, becausecheckoutoverloaded branch switching and file restoration into one command and that ambiguity caused real accidents.The older forms still work and still appear everywhere:
git checkout -- path/to/file # same as git restore git reset HEAD path/to/file # same as git restore --stagedWorth saying that discarding working directory changes is one of the few genuinely irreversible Git operations, because those edits were never committed and so never became an object.
25
What is the difference between a Git tag and a branch, and what is an annotated tag?
MidBoth are refs pointing at a commit. A branch is expected to move as you commit; a tag is not, which is what makes it suitable for marking a release.
Two kinds of tag:
Lightweight Annotated Stored as A ref pointing straight at the commit A full tag object Carries Nothing Tagger, date, message, and can be signed Use for A private bookmark Releases git tag -a v1.4.0 -m "Release 1.4.0" git push origin v1.4.0Two things people miss. Tags are not pushed by
git push; they need to be pushed explicitly or with--follow-tags. And a tag is not immutable by policy, only by convention: it can be moved and force-pushed, which is exactly why build systems that pin to a tag rather than a commit hash can be surprised.26
How would you remove a committed secret from a Git repository?
SeniorRotate it first. Everything else is secondary, because the moment it was pushed you must assume it is compromised, and a rewrite cannot un-clone what people already have.
Then, in order:
- 1Rotate the credential. Immediately, before touching history.
- 2Rewrite the history with
git filter-repo, which is the supported tool now thatfilter-branchis discouraged, or BFG for the common cases. - 3Force-push every affected branch and tag, and have everyone re-clone rather than merge, because a stale clone reintroduces the objects.
- 4Ask the host to garbage collect. On GitHub the old objects remain reachable by hash until support clears cached views, and forks are a separate problem entirely.
- 5Add prevention: secret scanning in CI, a pre-commit hook, and push protection on the host.
The answer that marks experience is leading with rotation and being honest that the rewrite is cleanup, not remediation. A candidate who starts with
filter-repoand never mentions rotating the key has answered the wrong question.
Live exercise: you have lost work in Git
Asked in almost every Git round in one form or another. The answer being scored is that you stay calm and know Git rarely deletes anything until garbage collection runs.
Are the changes committed, or only in the working directory?
git status- yes
- Committed work is recoverable. Every commit HEAD has pointed at is in the reflog for about ninety days, even on a deleted branch.
- no
- Uncommitted changes that were never staged are genuinely gone; Git never saw them. If they were staged at some point,
git fsck --lost-foundcan still surface the blob.
Can you find the commit in the reflog?
git reflog --date=iso- yes
- Note the hash.
git branch recovered <hash>puts a name back on it, which is safer than checking it out and ending up detached. - no
- Widen it.
git reflog show --allcovers every ref, andgit fsck --unreachable --no-reflogsfinds dangling commits the reflog has already dropped.
Did somebody force-push over the branch on the remote?
git reflog show origin/main- yes
- Your local remote-tracking ref still remembers the old tip from before the fetch. Branch from it, then talk to whoever pushed before you push anything back.
- no
- If your clone never saw the old commits, the remote's own reflog is the only copy, and that needs whoever administers the host.
Was it a bad merge or reset you want to undo rather than recover?
git reset --hard ORIG_HEAD- yes
- Git writes ORIG_HEAD before any operation that moves HEAD dangerously, so this undoes the last merge, rebase or reset in one step.
- no
- If the history is already shared, do not rewrite it. Use
git revertso the undo is itself a commit and nobody else has to recover.
What each level is testing
- 1
Junior6 questions
Whether the model is real. What a commit and a branch actually are, the staging area, the difference between
fetchandpull, and how to readgit statusandgit log. Being able to say a branch is just a moving pointer is most of the junior answer. - 2
Mid12 questions
Whether you have worked on a team. Merge against rebase and when each is right,
resetagainstrevert, resolving conflicts, cleaning up history before review, and what--force-with-leaseprotects you from that--forcedoes not. - 3
Senior8 questions
Whether you have recovered from a mess. The reflog, detached HEAD,
bisect, what happens to a shared branch after a rebase, submodules against subtrees, and how you would set a branching strategy for a team rather than pick your favourite.
What the round is like
Git questions look easy and are not. Almost everyone can describe add, commit and push,
so interviewers move quickly to the parts where people are actually vague: what a branch is,
what rebase really does, and what to do when somebody has force-pushed over an afternoon of
work. The scenario questions are the round, and they are testing whether you understand the
object model rather than whether you have memorised commands.
How to prepare with this
- 1Be able to say what a commit object actually contains. Almost every confusing Git behaviour follows from commits being immutable snapshots with parent pointers, and branches being nothing but a movable label.
- 2Practise the recovery flow below. "I deleted the branch" and "somebody force-pushed" are asked constantly, and a calm reflog answer is worth more than any amount of command recitation.
- 3Learn
resetproperly: soft, mixed and hard, and which of the three moves the working directory. This is the most commonly fumbled Git answer there is. - 4Never say you would use
git push --forceon a shared branch. If you mean force, say--force-with-leaseand explain what it checks. - 5Have an opinion on trunk-based development against Git Flow, and be able to say which team size and release cadence each suits. Interviewers ask to see whether you reason about the team rather than the tool.