Git Interview Questions

TL;DR

35+ Git interview questions organized by topic. Click "Show Answer" to reveal detailed answers. Covers fundamentals, branching, merging, remotes, advanced operations, branching strategies, and real-world scenarios.

Short on time? Focus on Git Fundamentals and Branching & Merging — they come up most often.

Git Fundamentals

Q: What is Git and why is it important?

Git is a distributed version control system created by Linus Torvalds in 2005. It tracks every change to your codebase, lets multiple developers work in parallel, and provides a full history of who changed what and when. It's important because it enables collaboration, safe experimentation via branches, and the ability to roll back to any previous state.

Q: What is the difference between a distributed and a centralized version control system?

In a centralized VCS (e.g., SVN), there is a single central server that stores the full history — developers check out only the latest version and must be online to commit. In a distributed VCS (e.g., Git), every developer has a complete copy of the entire repository and its history. You can commit, branch, merge, and view history entirely offline. There is no single point of failure.

Q: What is inside the .git directory?

The .git directory is Git's internal database. Key contents: objects/ — stores all blobs (file content), trees (directories), commits, and tags, identified by SHA-1 hashes. refs/ — branch and tag pointers. HEAD — points to the current branch or commit. index — the staging area. config — repository-level configuration. hooks/ — client-side and server-side hook scripts.

Q: How does Git use SHA-1 hashing?

Git computes a SHA-1 hash (40-character hex string) for every object — blobs, trees, commits, and tags. The hash is derived from the object's content, so identical content always produces the same hash. This serves as a content-addressable identifier: Git can detect corruption, verify integrity, and deduplicate identical files. Two files with the same content share one blob object regardless of filename.

Q: What are the four types of Git objects?

(1) Blob — stores the raw content of a file (no filename or metadata). (2) Tree — represents a directory listing, mapping filenames to blob or tree SHAs. (3) Commit — points to a tree (snapshot), its parent commit(s), author, committer, timestamp, and message. (4) Tag — a named, optionally signed pointer to a specific commit (used for release versions).

Q: Explain the three states a file can be in within Git.

Modified — the file has been changed in your working directory but not yet staged. Staged — the changed file has been marked (via git add) to go into the next commit; it lives in the staging area (also called the index). Committed — the data is safely stored in your local .git database as a commit object. The flow is: modify → git addgit commit.

Branching & Merging

Q: What is a branch in Git and why is branching so fast?

A branch in Git is a lightweight movable pointer to a commit. Internally, it's just a small file containing a 40-character SHA hash. Creating a branch is O(1) — Git writes 41 bytes to a file. No files are copied, no directories are duplicated. This is why branching in Git is nearly instant compared to centralized systems like SVN that copy the entire directory tree.

Q: What is the difference between a fast-forward merge and a three-way merge?

Fast-forward merge: when the target branch has no new commits since the feature branch diverged, Git simply moves the target pointer forward — no merge commit is created. Three-way merge: when both branches have diverged with new commits, Git finds their common ancestor and compares three snapshots (ancestor, source tip, target tip) to create a new merge commit with two parents. Use --no-ff to force a merge commit even when fast-forward is possible.

Q: How do you resolve a merge conflict in Git?

(1) Git marks conflicts in files with <<<<<<<, =======, and >>>>>>> markers. (2) Open each conflicted file and decide: keep one version, combine both, or write new code. (3) Remove all conflict markers. (4) git add the resolved files to stage them. (5) git commit to complete the merge. You can also use git mergetool to launch a visual merge tool like VS Code or KDiff3.

Q: What is the difference between git rebase and git merge?

Merge creates a new merge commit that combines two branches, preserving the full non-linear history. Rebase replays your commits one by one on top of another branch, rewriting their SHAs to produce a linear history. Merge is safe for shared/public branches. Rebase is ideal for cleaning up local work before pushing. The golden rule: never rebase commits that have been pushed to a shared branch.

Q: What merge strategies does Git support?

Git supports several merge strategies: ort (default since Git 2.34, successor to recursive) — handles most merges, including renames. recursive — the older default for two-branch merges. octopus — merges more than two branches simultaneously. ours — keeps the current branch's version entirely, discarding the other branch's changes. subtree — merges a subtree of one project into another. You select a strategy with git merge -s <strategy>.

Q: How do you abort a merge that has conflicts?

Run git merge --abort to cancel the merge and return to the state before the merge started. This restores your working directory and staging area. If you've already committed the merge and want to undo it, use git revert -m 1 <merge-sha> to create a new commit that undoes the merge (safe for shared branches), or git reset --hard HEAD~1 if the merge hasn't been pushed.

Collaboration & Remotes

Q: What is the difference between git fetch and git pull?

git fetch downloads new commits, branches, and tags from the remote but does not modify your working directory or current branch. git pull = git fetch + git merge (or git rebase with --rebase). Fetch is safer because you can inspect what changed on the remote before integrating. Many teams prefer git fetch followed by a manual merge or rebase.

Q: Describe the typical push/pull workflow when collaborating with a team.

(1) git pull (or fetch + merge) to get the latest changes. (2) Create a feature branch: git switch -c feature/xyz. (3) Make changes, commit locally. (4) git push -u origin feature/xyz to push the branch to the remote. (5) Open a pull request for code review. (6) After approval, merge into the main branch. (7) Delete the feature branch. Keeping branches short-lived reduces merge conflicts.

Q: What is upstream tracking and how do you set it?

An upstream tracking branch links your local branch to a remote branch so Git knows where to push and pull by default. Set it with git push -u origin branch-name (the -u flag sets the upstream) or git branch --set-upstream-to=origin/branch-name. Once set, git push, git pull, and git status (ahead/behind counts) work without specifying the remote.

Q: What is the difference between forking and cloning a repository?

Cloning (git clone) creates a local copy of a repository on your machine — it's a Git operation. Forking creates a server-side copy of someone else's repository under your own account — it's a GitHub/GitLab feature, not a Git concept. You fork to propose changes to projects you don't have write access to, then submit a pull request from your fork back to the original repository.

Q: What is a pull request and how does it fit into the development workflow?

A pull request (PR) is a proposal to merge one branch into another on a hosting platform like GitHub. It enables: (1) Code review — peers review changes before they reach the main branch. (2) Discussion — inline comments on specific lines of code. (3) CI validation — automated tests run before the merge is allowed. (4) Audit trail — a record of who approved what, when, and why. PRs are the standard gate for quality in modern development.

Advanced Operations

Q: What is interactive rebase and when would you use it?

git rebase -i HEAD~n opens an editor listing the last n commits. You can pick (keep), reword (change message), squash (combine with previous), fixup (squash silently), edit (pause to amend), drop (delete), or reorder commits. Use it to clean up messy local history before pushing — turning 10 WIP commits into 3 logical ones. Never interactive-rebase commits already pushed to a shared branch.

Q: What is git cherry-pick and when should you use it?

git cherry-pick <sha> applies a specific commit from one branch onto your current branch as a new commit with a new SHA. Common uses: applying a hotfix from a development branch to the production branch without merging everything, or selectively moving a commit that landed on the wrong branch. Avoid overusing it — cherry-picked commits become duplicates, which can cause confusion during later merges.

Q: What is git bisect and how does it work?

git bisect performs a binary search through commit history to find which commit introduced a bug. Steps: (1) git bisect start. (2) git bisect bad — mark the current commit as broken. (3) git bisect good <sha> — mark the last known working commit. Git checks out the midpoint; you test and mark it good or bad. It repeats until the culprit commit is found. Complexity is O(log n) instead of checking every commit linearly. End with git bisect reset.

Q: What is git reflog and how can it save your work?

git reflog records every time HEAD moves — every commit, checkout, rebase, reset, merge, and stash operation. Even after a destructive git reset --hard, the old commits still exist in the reflog for approximately 90 days. To recover lost work: run git reflog, find the SHA of the commit you want, then git reset --hard <sha> or git checkout -b recovery <sha>. The reflog is local-only — it's never pushed to remotes.

Q: What does git stash do and what are its common subcommands?

git stash temporarily shelves uncommitted changes (both staged and unstaged) and reverts your working directory to a clean state. Common subcommands: git stash list — view all stashes. git stash pop — apply the latest stash and remove it. git stash apply — apply but keep the stash. git stash drop — delete a stash. git stash branch <name> — create a branch from a stash. Use -u to include untracked files.

Q: What is the difference between git reset and git revert?

git reset moves HEAD backward to a specified commit, potentially rewriting history. Three modes: --soft (keeps changes staged), --mixed (keeps changes unstaged), --hard (discards all changes). git revert creates a new commit that undoes the changes of a specified commit without rewriting history. Use reset for local/unpushed commits. Use revert for shared branches where history must not be rewritten.

Branching Strategies

Q: What is Git Flow and when is it appropriate?

Git Flow uses long-lived branches: main (production), develop (integration), plus short-lived feature/*, release/*, and hotfix/* branches. Features merge into develop; releases branch from develop, get stabilized, then merge into both main and develop. It's appropriate for projects with scheduled, versioned releases (e.g., desktop software, mobile apps). It's too heavy for continuous delivery environments.

Q: How does GitHub Flow differ from Git Flow?

GitHub Flow is much simpler: there's only one long-lived branch (main), which is always deployable. Developers create short-lived feature branches, open pull requests, get reviews, and merge into main. Deployments happen from main after each merge. It works best for teams practicing continuous delivery where code ships multiple times per day. No develop, release, or hotfix branches are needed.

Q: What is trunk-based development?

Trunk-based development is the simplest strategy: all developers commit directly to main (the "trunk") or use very short-lived branches (less than one day). It requires feature flags to hide incomplete work, strong CI/CD to catch regressions immediately, and a culture of small, frequent commits. Used by Google, Netflix, and other high-performing teams. It minimizes merge conflicts and maximizes delivery speed.

Q: How do you choose the right branching strategy for a project?

Consider these factors: Release cadence — frequent deployments favor GitHub Flow or trunk-based; scheduled releases favor Git Flow. Team size — small teams do well with trunk-based; large teams may need more structure. CI maturity — trunk-based requires excellent CI; Git Flow tolerates weaker automation. Compliance — regulated industries may need the audit trail of Git Flow's release branches. Start simple (GitHub Flow) and add complexity only when needed.

Q: How do you handle release management with Git?

Common approaches: (1) Tag-based — tag a commit on main (e.g., v2.1.0) and build from the tag. Simplest approach for continuous delivery. (2) Release branches — branch release/2.1 from develop, stabilize it (bug fixes only), merge into main and tag. Good for parallel release maintenance. (3) Hotfix branches — branch from a tagged release, fix the bug, merge back into main and develop. Use semantic versioning (major.minor.patch) consistently.

History & Debugging

Q: What are the most useful git log options for investigating history?

git log --oneline — compact one-line view. git log --graph --oneline --all — visual branch/merge graph. git log -p — show the diff for each commit. git log --author="name" — filter by author. git log --since="2 weeks ago" — filter by date. git log -- path/to/file — history of a specific file. git log --merges — show only merge commits. Combine flags for powerful queries.

Q: What is git blame and how do you use it effectively?

git blame <file> shows who last modified each line of a file, along with the commit SHA and date. Useful for understanding why a line exists and who to ask about it. Use -L 10,20 to blame a specific line range. Use -w to ignore whitespace changes. Use -M to detect moved lines within a file and -C to detect lines copied from other files — this helps trace the true origin.

Q: How do you use git diff to compare different states?

git diff — working directory vs staging area (unstaged changes). git diff --staged — staging area vs last commit (what will be committed). git diff HEAD — working directory vs last commit (all uncommitted changes). git diff branch1..branch2 — compare two branches. git diff abc123..def456 — compare two commits. Add --stat for a summary of changed files instead of the full diff.

Q: How do you search through Git history for a specific string change?

Use git log -S "search_term" (the pickaxe option) to find commits where the number of occurrences of that string changed — i.e., where it was added or removed. Use git log -G "regex" for regex-based search. Add -p to see the actual diffs. For searching within commit messages, use git log --grep="pattern". For searching file content at a specific point, use git grep "pattern" <commit>.

Behavioral & Scenario Questions

Q: You accidentally committed to main instead of a feature branch. How do you fix this?

(1) Create a new branch at the current commit to save your work: git branch feature/my-work. (2) Reset main back to where it should be: git reset --hard HEAD~1 (adjust the number for multiple commits). (3) Switch to the feature branch: git switch feature/my-work. Your changes are now safely on the feature branch and main is clean. If you already pushed to main, you'll need to coordinate a force push with your team.

Q: You lost commits after a bad rebase or reset. How do you recover them?

(1) Run git reflog to see the history of where HEAD has been — every checkout, commit, rebase, and reset is recorded. (2) Find the SHA of the commit you want to return to. (3) Create a recovery branch: git checkout -b recovery <sha>, or hard-reset to it: git reset --hard <sha>. Reflog entries are kept for approximately 90 days, so act before garbage collection runs. This is why git reflog is called Git's "undo button."

Q: Your feature branch has a messy history with dozens of WIP commits. How do you clean it up before merging?

Use interactive rebase: git rebase -i main. In the editor, squash or fixup related commits into logical units, reword commit messages to be clear, and drop any experimental commits. Alternatively, if merging via pull request, use the "Squash and merge" option to compress all PR commits into one clean commit on main. The goal is a history that tells a clear story.

Q: Two developers modified the same function differently. How do you resolve this complex conflict?

(1) Understand both changes — read the conflict markers and understand the intent of each developer's work. (2) Talk to the other developer if the changes are semantically conflicting (not just textual). (3) Write a combined solution that incorporates both changes correctly — don't just pick one side. (4) Test thoroughly after resolving. (5) Use a visual merge tool (git mergetool) for complex conflicts. Prevention: keep branches short-lived and communicate about shared code areas.

Q: What are the best practices for a .gitignore file?

(1) Start with a template from github/gitignore for your language/framework. (2) Ignore build artifacts (dist/, build/), dependencies (node_modules/, .venv/), IDE files (.idea/, .vscode/), OS files (.DS_Store, Thumbs.db), and environment files (.env). (3) Use a global ~/.gitignore_global for personal IDE/OS files. (4) Never commit secrets — add them to .gitignore and use push protection. (5) If a file was already tracked, git rm --cached <file> removes it from tracking without deleting it locally.