Git Interview Questions with Answers: Detailed Explanations

Mastering Git Interviews: Beyond Memorization

This comprehensive guide provides detailed answers to the most common Git interview questions. Unlike simple one-line responses, each answer includes thorough explanations, underlying concepts, practical examples, and common pitfalls. Understanding the "why" behind each answer will help you handle follow-up questions and demonstrate genuine expertise.

How to Use This Guide: Read each question and try to answer it yourself before looking at the explanation. Pay attention to the "Key Concept" and "Common Mistakes" sectionsβ€”these are what interviewers use to distinguish between superficial and deep understanding.

πŸ“š Basics 🌿 Branching πŸ”„ Merging ⚑ Rebase πŸ”§ Workflows πŸ› οΈ Internals πŸ” Debugging πŸ†˜ Recovery πŸ” Security πŸš€ Advanced

πŸ“š Section 1: Git Basics - Foundational Concepts

Q1: What is Git and how does it differ from other version control systems? Easy

Answer: Git is a distributed version control system (DVCS) created by Linus Torvalds in 2005 for Linux kernel development. Key characteristics:

πŸ”‘ Key Concept: Distributed vs. Centralized

Unlike centralized systems (SVN, CVS) where the repository exists on a central server, Git gives every developer a complete copy of the entire repository, including full history, on their local machine.

Detailed Comparison:

  • Architecture: Git is distributed; SVN is client-server
  • Data Storage: Git stores snapshots (not deltas); SVN stores file differences
  • Branching: Git branches are lightweight pointers (create in seconds); SVN branches are directory copies (slow and heavy)
  • Offline Work: Git works fully offline; SVN requires server connection for most operations
  • Integrity: Git checksums everything with SHA-1; SVN has no built-in integrity checking
# Git stores snapshots, not file differences # Each commit is a complete snapshot of your repository $ git cat-file -p HEAD # Shows commit object with tree hash $ git ls-tree HEAD # Shows complete snapshot
πŸ’‘ Interview Tip: Mention that Git's distributed nature enables multiple workflows (centralized, feature branch, GitFlow) and provides redundancyβ€”every clone is a full backup.
⚠️ Common Mistake: Saying Git stores file differences. It actually stores snapshots and uses delta compression only for packfiles as an optimization.
Q2: Explain the three areas of Git and how changes flow through them. Easy

Answer: Git has three main areas where changes live:

  1. Working Directory (Working Tree): The actual files you edit on your filesystem. Changes here are untracked or modified.
  2. Staging Area (Index): A intermediate area where you prepare changes for commit. Files here are "staged" and will be included in the next commit.
  3. Repository (.git directory): Where Git permanently stores committed snapshots (objects).

Flow of Changes:

Working Directory β†’ (git add) β†’ Staging Area β†’ (git commit) β†’ Repository Repository β†’ (git checkout) β†’ Working Directory

Visual Representation:

[Working Directory] [Staging Area] [Repository (HEAD)] | | | modified files β†’ git add β†’ staged files β†’ git commit β†’ committed ↑ | | git checkout β†β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜ | git reset (--hard) β†β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”€β”˜

Commands for each area:

  • git status - Show status across all areas
  • git add - Move from working to staging
  • git commit - Move from staging to repository
  • git checkout - Move from repository to working
  • git reset - Move between all three (depending on flags)
πŸ’‘ Why Three Areas? The staging area lets you craft commits thoughtfullyβ€”you can stage only parts of a file, review what you're committing, and separate related changes from unrelated ones.
Q3: What is the difference between git pull and git fetch? Easy

Answer: git fetch and git pull both download data from a remote, but they differ in integration:

git fetch: Downloads new data from the remote but does not integrate it into your working files. It updates remote-tracking branches (like origin/main) while leaving your local branches and working directory unchanged. This is a safe, non-destructive operation that lets you inspect changes before merging.

git pull: Combines two operations: git fetch followed by either git merge or git rebase. It downloads changes and immediately tries to integrate them into your current branch.

# Fetch only - safe inspection $ git fetch origin $ git log origin/main # See what changed $ git diff main origin/main # Compare local vs remote # Pull - fetch and integrate $ git pull origin main # Fetch + merge (default) $ git pull --rebase # Fetch + rebase

When to use each:

  • Use git fetch when you want to review changes before integrating
  • Use git pull for routine updates when you trust the incoming changes
  • Use git pull --rebase to maintain a linear history
⚠️ Common Pitfall: Many developers use pull without understanding it does a merge by default, which can create unnecessary merge commits. Configure git config --global pull.rebase true to make pull rebase by default.
πŸ’‘ Senior Developer Tip: Always fetch first in CI/CD scripts. Pull can create merge conflicts in automated environments. Use fetch + git reset --hard origin/main for clean deployments.
Q4: How do you resolve a merge conflict? Walk through the process. Easy

Answer: A merge conflict occurs when Git cannot automatically reconcile changes from two branches that modified the same part of a file. Here's the complete resolution process:

Step 1: Identify the conflict

$ git status On branch main You have unmerged paths. (fix conflicts and run "git commit") Unmerged paths: both modified: src/app.js

Step 2: Understand conflict markers

<<<<<<< HEAD console.log("This is your version from current branch"); ======= console.log("This is the incoming version from other branch"); >>>>>>> feature-branch
  • <<<<<<< HEAD - Start of your changes (current branch)
  • ======= - Separator between versions
  • >>>>>>> feature-branch - End of incoming changes

Step 3: Resolve the conflict - Edit the file to keep one version, combine both, or write new code. Remove conflict markers.

Step 4: Mark as resolved and continue

$ git add src/app.js # Mark resolved $ git status # Verify all conflicts resolved $ git commit # Complete the merge

Alternative: Using merge tools

# Configure and use a merge tool $ git config --global merge.tool vscode $ git mergetool # Launches visual merge tool

Abort if things go wrong:

$ git merge --abort # Return to pre-merge state
πŸ’‘ Pro Strategy: For complex conflicts, use git checkout --ours or git checkout --theirs to choose entire files, then manually adjust:
$ git checkout --ours src/file.js # Keep our version
⚠️ Common Mistake: Forgetting to remove conflict markers. Always verify resolved files don't contain <<<<, ====, or >>>> before committing.
Q5: What is the difference between git merge and git rebase? Medium

Answer: Both integrate changes from one branch into another, but they create different history and have different implications for collaboration.

πŸ”‘ Merge: Preserves history as it happened

Merge: Creates a new "merge commit" with two parents, showing exactly when branches diverged and were reunited. Preserves the true timeline of development.

# Before merge: A---B---C main \ D---E feature # After merge (git merge feature): A---B---C---F main \ / D---E # F is a merge commit with two parents: C and E
πŸ”‘ Rebase: Creates linear history

Rebase: Takes commits from your branch and reapplies them on top of another branch, creating new commits with new hashes. Results in a linear history as if work was done sequentially.

# Before rebase: A---B---C main \ D---E feature # After rebase (git rebase main while on feature): D'--E' feature / A---B---C main # D' and E' are new commits with new hashes

Trade-offs and when to use each:

Aspect Merge Rebase
History Preserves exact timeline Creates linear history
Commit IDs Original IDs preserved New IDs created
Safety on shared branches βœ… Safe ❌ Dangerous (rewrites history)
Complexity Simpler, one-step Can have multiple conflicts
πŸ’‘ Best Practice: Use rebase for local cleanup before sharing (make history pretty). Use merge for integrating shared branches (preserve context). The "rebase before merge" workflow is common: rebase feature branch onto main to test with latest changes, then merge (with --no-ff) to record the integration.
⚠️ Golden Rule: Never rebase commits that have been pushed to a shared branch. Others may have based work on those commits, and rebasing creates orphans.

🌿 Section 2: Branching and Merging - Deep Dive

Q6: What are Git branches and how are they implemented internally? Medium

Answer: In Git, a branch is simply a lightweight movable pointer to a commit. Internally, branches are just files in .git/refs/heads/ containing a 40-character SHA-1 hash of the commit they point to.

# A branch is just a file with a commit hash $ cat .git/refs/heads/main a1b2c3d4e5f678901234567890abcdef12345678 # Creating a branch creates a new file $ git branch feature $ cat .git/refs/heads/feature a1b2c3d4e5f678901234567890abcdef12345678 # Same hash as main

Why this matters:

  • Speed: Creating a branch is just writing a 40-byte fileβ€”instant, regardless of repository size
  • Lightweight: No duplication of files; branches share commits
  • Flexibility: You can easily create, delete, and move branches

HEAD pointer: Git uses HEAD to know which branch you're on. HEAD is usually a symbolic reference pointing to a branch:

$ cat .git/HEAD ref: refs/heads/main # HEAD points to main branch # When you commit, Git updates the branch HEAD points to $ git commit # Updates .git/refs/heads/main

Remote branches: Stored in .git/refs/remotes/ and track remote repository state. They move only when you fetch or push.

πŸ’‘ Interview Insight: Mentioning that branches are just files demonstrates understanding of Git's internal simplicity. This impresses interviewers looking for depth.
Q7: Explain fast-forward vs. non-fast-forward merges. When would you use --no-ff? Medium

Answer: The difference depends on whether the target branch has diverged from the source.

πŸ”‘ Fast-forward merge

Possible when the branch being merged has not divergedβ€”its commits are direct descendants of the target branch. Git simply moves the target branch pointer forward.

# Before: main has not moved since feature branched A---B---C main \ D---E feature # git merge feature (fast-forward) A---B---C---D---E main, feature # main pointer simply moved to E
πŸ”‘ Non-fast-forward merge (true merge)

Occurs when both branches have diverged. Git creates a new merge commit with two parents.

# Before: main has new commits A---B---C---F main \ D---E feature # git merge feature (non-fast-forward) A---B---C---F---G main \ / D-------E # G is a merge commit with parents F and E
πŸ”‘ Using --no-ff

The --no-ff flag forces a merge commit even when fast-forward is possible. This is used to:

  • Preserve branch history (show that a feature branch existed)
  • Make it easy to revert entire features (revert the merge commit)
  • Group related commits under a feature branch context
# Force merge commit even when fast-forward possible $ git merge --no-ff feature # Creates: A---B---C---D---E main \ / (feature)
πŸ’‘ Team Workflow: Many teams standardize on git merge --no-ff for feature branches into main. This creates a clear record of when features were integrated and makes reverting entire features trivial.
Q8: How do you undo a commit that has already been pushed to a shared branch? Medium

Answer: There are two main approaches, with different implications for collaboration:

πŸ”‘ Approach 1: git revert (Safe for shared branches)

git revert creates a new commit that undoes the changes of a previous commit. It doesn't rewrite history, so it's safe for shared branches.

# Find the commit to revert $ git log --oneline a1b2c3d Add payment feature # Bad commit e4f5g6h Fix login bug # Create revert commit $ git revert a1b2c3d # Git creates new commit that undoes a1b2c3d's changes $ git log --oneline x1y2z3w Revert "Add payment feature" a1b2c3d Add payment feature e4f5g6h Fix login bug # Push normally $ git push

Advantages: Safe for teams, preserves history, other developers can pull without issues.

πŸ”‘ Approach 2: git reset + force push (Dangerous on shared branches)

git reset removes commits from history. Only use on branches that haven't been shared or when you can coordinate with the entire team.

# Remove the last commit locally $ git reset --hard HEAD~1 # Force push (use --force-with-lease for safety) $ git push --force-with-lease origin main

Risks: Rewrites history. Anyone who has pulled the old commits will have divergent history and must reset locally.

⚠️ Critical Rule: On shared branches (main, develop), ALWAYS use revert. Reset+force push should only be used on personal branches that no one else uses.
πŸ’‘ Reverting a Merge Commit: Reverting a merge commit is specialβ€”you need to specify which parent to keep:
$ git revert -m 1 a1b2c3d # -m 1 keeps first parent (usually main)
Q9: What is the difference between git reset, git checkout, and git restore? Medium

Answer: These commands have overlapping functionality, but distinct purposes. Modern Git (2.23+) introduced git restore and git switch to clarify responsibilities.

git reset: Moves branch pointers and optionally modifies working directory and staging area. Operates at the commit level.

# Three modes of reset $ git reset --soft HEAD~1 # Move branch, keep staged and working $ git reset --mixed HEAD~1 # Move branch, unstage changes (default) $ git reset --hard HEAD~1 # Move branch, discard all changes

git checkout: An overloaded command that does two things:

  • Switch branches: git checkout main
  • Restore files: git checkout -- file.txt

git restore (new in 2.23): Specifically for restoring files, separating branch switching concerns.

# Restore working tree files $ git restore file.txt # Discard changes in working directory $ git restore --staged file.txt # Unstage file (like git reset HEAD) $ git restore --source=HEAD~2 file.txt # Restore from older commit

git switch (new in 2.23): Specifically for switching branches.

$ git switch main # Switch to branch $ git switch -c feature # Create and switch

Comparison Table:

Operation Old Way New Way
Switch branch git checkout main git switch main
Create branch git checkout -b feature git switch -c feature
Unstage file git reset HEAD file git restore --staged file
Discard changes git checkout -- file git restore file
πŸ’‘ Best Practice: Use the newer commands for clarity:
  • git switch for branches
  • git restore for files
  • git reset for rewriting history

πŸ”„ Section 3: Advanced Workflows and Scenarios

Q10: Explain GitFlow branching strategy. What are its pros and cons? Medium

Answer: GitFlow is a branching model popularized by Vincent Driessen, designed for projects with scheduled releases.

πŸ”‘ GitFlow Branches:
  • main: Production-ready code. Every commit is a release.
  • develop: Integration branch for features. Contains latest delivered changes.
  • feature/*: Branches from develop for new features. Merged back to develop.
  • release/*: Branches from develop for release preparation. Only bug fixes, no new features. Merged to both main and develop.
  • hotfix/*: Branches from main for emergency production fixes. Merged to both main and develop.

Workflow Diagram:

main: o-------------------o-------------------o | | | develop: o---o---o---o---o---o---o---o---o---o---o | | | | | | | | | | feature: o---o---o o---o---o release: o---o---o hotfix: o---o

Pros:

  • Clear structure for large teams
  • Separates development from release stabilization
  • Handles hotfixes cleanly
  • Works well for versioned releases

Cons:

  • Complex for simple projects
  • Developers can forget to merge hotfixes back to develop
  • Not ideal for continuous delivery (too much overhead)
  • Can lead to merge hell if branches live too long
πŸ’‘ When to use: GitFlow suits projects with versioned releases, multiple supported versions, and clear separation between development and production. For continuous delivery, consider simpler workflows like GitHub Flow or trunk-based development.
Q11: How would you handle a situation where you need to apply a hotfix to production without including unfinished features from develop? Medium

Answer: This requires creating a hotfix branch from the production tag, applying the fix, and merging carefully to both main and develop.

Step-by-Step Hotfix Process:

# Step 1: Create hotfix branch from production tag $ git checkout v2.1.0 # Current production version $ git checkout -b hotfix/critical-bug # Step 2: Apply and test the fix $ git add src/fix.js $ git commit -m "hotfix: resolve null pointer in payment" # Step 3: Merge to main and tag $ git checkout main $ git merge --no-ff hotfix/critical-bug $ git tag -a v2.1.1 -m "Hotfix release" # Step 4: IMPORTANT - Also merge to develop $ git checkout develop $ git merge --no-ff hotfix/critical-bug # Step 5: Clean up $ git branch -d hotfix/critical-bug # Step 6: Push everything $ git push origin main develop --tags
⚠️ Critical Step: Many developers forget to merge the hotfix into develop. If you skip this, the bug will reappear in the next release when you merge develop into main.

Why this works:

  • Starting from the tag ensures your fix is based exactly on what's in production
  • Using --no-ff preserves the hotfix branch history
  • Tagging the release creates an immutable reference
  • Merging to both branches ensures the fix persists
πŸ’‘ Automation: In team settings, create a hotfix script that automates these steps and enforces the merge to develop:
#!/bin/bash # hotfix.sh - Automate hotfix process echo "Creating hotfix from tag: $1" git checkout $1 git checkout -b hotfix/$2 # ... after fix committed git checkout main && git merge hotfix/$2 git checkout develop && git merge hotfix/$2
Q12: What is git rebase interactive and when would you use it? Hard

Answer: Interactive rebase (git rebase -i) allows you to modify commits as you move them to a new base. It's a powerful tool for cleaning up history before sharing.

Common operations in interactive rebase:

  • pick: Use the commit as-is
  • reword: Change commit message
  • edit: Modify commit content
  • squash: Combine commit with previous, merging messages
  • fixup: Like squash but discard message
  • drop: Remove commit entirely
# Start interactive rebase for last 3 commits $ git rebase -i HEAD~3 # Editor opens with: pick a1b2c3d Add login feature pick e4f5g6h Fix typo in login pick i7j8k9l Update README # Change to: pick a1b2c3d Add login feature fixup e4f5g6h Fix typo in login # Combine, discard message pick i7j8k9l Update README # After save, history becomes: # a1b2c3d Add login feature (with fix included) # i7j8k9l Update README

Common use cases:

  1. Cleaning up WIP commits: Squash multiple "fix typo" commits into logical units
  2. Reordering commits: Make history more logical
  3. Removing commits: Drop commits that shouldn't be shared
  4. Fixing commit messages: Reword unclear messages
  5. Splitting commits: Edit a commit to split into multiple
⚠️ DANGER: Never rebase commits that have been pushed to a shared branch. Rebasing rewrites history, creating new commit hashes. Others' work based on the old commits will be orphaned.
πŸ’‘ Workflow Pattern: Many developers use this workflow:
  1. Work on feature branch with messy commits
  2. Before creating PR, do git rebase -i main to clean up
  3. Force push to update PR (since branch isn't shared)

πŸ› οΈ Section 4: Git Internals and Plumbing

Q13: Explain Git's object model. What are blobs, trees, and commits? Hard

Answer: Git is fundamentally a content-addressable filesystem with four object types stored in .git/objects/. Each object is identified by a SHA-1 hash of its content.

πŸ”‘ Blob (Binary Large Object)

Stores file content, but not the filename. Identical file content = identical blob hash.

# Create a blob manually $ echo "hello world" | git hash-object -w --stdin 3b18e512dba79e4c8300dd08aeb37f8e728b8dad $ git cat-file -t 3b18e51 # "blob" $ git cat-file -p 3b18e51 # "hello world"
πŸ”‘ Tree

Represents a directory. Maps filenames to blobs (or other trees) with file modes.

$ git cat-file -p HEAD^{tree} 100644 blob 3b18e51 file.txt 040000 tree 2c3f4d2 src
πŸ”‘ Commit

Points to a tree (snapshot) and contains metadata: parent(s), author, committer, timestamp, message.

$ git cat-file -p HEAD tree 5c2b3f1a8b7d9c4e2f1a3b5c7d9e1f3a5b7c9d1e parent 9d8e7f6a5b4c3d2e1f0a9b8c7d6e5f4a3b2c1d0e author John Doe 1709123456 -0500 committer John Doe 1709123456 -0500 Initial commit
πŸ”‘ Tag (annotated)

Points to a commit with additional metadata for releases.

Object Storage:

# Objects are stored as: # .git/objects/ab/cdef123456789... # First two chars = directory, rest = filename $ find .git/objects -type f .git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad .git/objects/5c/2b3f1a8b7d9c4e2f1a3b5c7d9e1f3a5b7c9d1e

Packfiles: For efficiency, Git packs loose objects into packfiles with delta compression:

$ git gc # Packs objects $ ls .git/objects/pack/ pack-123...idx pack-123...pack
πŸ’‘ Why This Matters: Understanding the object model explains:
  • Why Git is fast (blob reuse, content-addressable)
  • Why moving files doesn't create new blobs (trees change, blobs stay)
  • How Git ensures data integrity (everything checksummed)
  • How to recover lost data (objects persist until GC)
Q14: What is git reflog and how do you use it for recovery? Hard

Answer: The reflog (reference log) records every movement of HEAD and branch tips in your local repository. It's your safety net for recovering "lost" commits.

$ git reflog a1b2c3d HEAD@{0}: reset: moving to HEAD~3 e4f5g6h HEAD@{1}: commit: Add payment feature i7j8k9l HEAD@{2}: commit: Update API endpoints m0n1o2p HEAD@{3}: checkout: moving from main to feature # Show reflog with dates $ git reflog --date=local # Show reflog for specific branch $ git reflog show main

Recovery Scenarios:

Scenario 1: Recover after bad reset
# You ran git reset --hard HEAD~3 and lost commits # Find the lost commit in reflog $ git reflog | grep "commit:" e4f5g6h HEAD@{1}: commit: Add payment feature # Recover it $ git reset --hard e4f5g6h
Scenario 2: Restore deleted branch
# Find branch's last commit in reflog $ git reflog --all | grep "feature" a1b2c3d refs/heads/feature@{1}: commit: Final feature commit # Recreate branch $ git branch feature a1b2c3d
Scenario 3: Undo rebase
# Find pre-rebase state $ git reflog | grep "rebase" $ git reset --hard HEAD@{2} # Before rebase

Reflog Expiration:

  • Entries expire after 90 days by default
  • Configure with: gc.reflogExpire and gc.reflogExpireUnreachable
  • To preserve forever: git config --global gc.reflogExpire never
⚠️ Important: Reflog is local only. It doesn't exist on the remote. If you clone a repository, the new clone has its own empty reflog.
πŸ’‘ Pro Tip: Create an alias for quick reflog inspection:
git config --global alias.safety "!git reflog --date=local | head -20" # Usage: git safety
Q15: How does Git's garbage collection work and when would you manually trigger it? Expert

Answer: Git's garbage collection (git gc) performs housekeeping tasks to optimize repository performance and reclaim disk space.

What git gc does:

  1. Compresses loose objects: Combines many small loose objects into packfiles (saves space)
  2. Removes unreachable objects: Deletes objects not referenced by any branch/tag older than gc.pruneExpire (default 2 weeks)
  3. Consolidates packfiles: Merges multiple packfiles into one for efficiency
  4. Prunes reflog: Removes old reflog entries
  5. Optimizes repository: Updates auxiliary files like commit-graph

Automatic GC: Git runs git gc --auto automatically when:

  • Number of loose objects exceeds gc.auto (default 6700)
  • Number of packfiles exceeds gc.autoPackLimit (default 50)

Manual GC scenarios:

# Aggressive garbage collection $ git gc --aggressive --prune=now # After large history rewrite (filter-branch) $ git filter-branch --tree-filter '...' $ git gc --aggressive --prune=now # Before archiving repository $ git gc --aggressive # When repository is slow $ git gc --auto --verbose

Configuration options:

# Configure GC thresholds $ git config gc.auto 5000 # Loose objects threshold $ git config gc.autoPackLimit 30 # Packfiles threshold $ git config gc.pruneExpire "1.month.ago" # Keep unreachable objects # Disable auto-GC $ git config gc.auto 0
⚠️ Warning: git gc --aggressive can take a long time on large repositories. It's not needed for regular useβ€”auto-GC is sufficient for most cases.
πŸ’‘ Advanced: You can see what GC would do without actually running it:
$ git gc --dry-run

πŸ” Section 5: Debugging and Troubleshooting

Q16: How do you use git bisect to find which commit introduced a bug? Hard

Answer: git bisect performs a binary search through commit history to identify the exact commit that introduced a bug.

Manual bisect process:

# Start bisect $ git bisect start # Mark current commit as bad (contains bug) $ git bisect bad HEAD # Mark known good commit (working version) $ git bisect good v2.0.0 # Git checks out a middle commit. You test it. Bisecting: 47 revisions left to test # If this commit works: $ git bisect good # If this commit is broken: $ git bisect bad # Git continues binary search until first bad commit found # When done: $ git bisect reset

Automated bisect with script:

# Create test script (exit 0 for good, non-zero for bad) $ cat test.sh #!/bin/bash npm test || exit 1 # Tests pass? good; fail? bad $ chmod +x test.sh # Run automated bisect $ git bisect start HEAD v2.0.0 $ git bisect run ./test.sh # Git automatically runs through all commits

Practical example:

# Find when a specific line was introduced $ git bisect start $ git bisect bad HEAD $ git bisect good v1.0.0 # At each step, check if line exists $ git grep "buggyFunction" || echo "not found" # Continue until commit that introduced it found

Visualizing bisect progress:

$ git bisect visualize # Opens gitk showing remaining commits
πŸ’‘ Pro Tips:
  • Bisect works best when you have a reliable test script
  • For performance issues, use timing tests in your script
  • You can bisect with large ranges: git bisect start v2.0.0 v1.0.0
  • Use git bisect skip for commits you can't test
Q17: A developer says "Git is slow." How would you diagnose and fix performance issues? Hard

Answer: Git performance issues require systematic diagnosis:

Step 1: Diagnose the problem

# Check repository size $ git count-objects -vH count: 12345 size: 500 MiB # Find large files in history $ git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | grep "^blob" | sort -k3 -n -r | head -10 # Profile specific commands $ GIT_TRACE_PERFORMANCE=1 git status $ GIT_TRACE=1 git fetch

Step 2: Common causes and solutions

Problem: Too many loose objects
$ git gc --auto # Compress loose objects
Problem: Large binary files in history
# Identify large files $ git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(objectsize) %(rest)' | awk '$2 == "blob" && $3 > 1000000 {print $4, $3}' # Move to Git LFS or remove from history $ git lfs track "*.psd" $ git filter-repo --path large-file.zip --invert-paths
Problem: Filesystem monitor not enabled
$ git config core.fsmonitor true $ git config core.untrackedCache true
Problem: Deep clone with full history (for CI)
# Use shallow clone $ git clone --depth 1 [url] # Use blobless clone $ git clone --filter=blob:none [url]
Problem: Too many packfiles
$ git repack -a -d --depth=250 --window=250

Step 3: Optimize configuration

# Global performance settings $ git config --global core.preloadindex true $ git config --global core.fscache true $ git config --global gc.auto 5000 $ git config --global pack.threads 8
πŸ’‘ Systematic Approach: Always measure before and after changes. Use hyperfine to benchmark:
$ hyperfine 'git status' # Before $ hyperfine 'git status' # After optimization

πŸ” Section 6: Security and Best Practices

Q18: How do you handle secrets (API keys, passwords) in Git repositories? Hard

Answer: Secrets in Git require a multi-layered approach: prevention, detection, and remediation.

πŸ”‘ Prevention

1. Use .gitignore properly:

# .gitignore .env *.key secrets.yml config/credentials.yml.enc

2. Pre-commit hooks with secret detection:

# .git/hooks/pre-commit #!/bin/sh # Check for potential secrets if git diff --cached | grep -E "(AKIA|SECRET|PASSWORD|API_KEY)" > /dev/null; then echo "Potential secret detected in staged changes" exit 1 fi

3. Use git-secrets (AWS Labs):

$ git secrets --install $ git secrets --register-aws $ git secrets --add 'password\s*=\s*.+'
πŸ”‘ Detection

CI/CD secret scanning:

# GitHub Actions example - name: Secret Scan uses: zricethezav/gitleaks-action@v2

Regular audits:

# Scan entire history $ git log --patch -S "AKIA" --all $ git grep "password" $(git rev-list --all)
πŸ”‘ Remediation (if secrets committed)

IMMEDIATE STEP: Rotate/revoke the exposed secret. Assume compromise.

Then clean history:

# Using git filter-repo $ git filter-repo --path .env --invert-paths # Using BFG $ bfg --delete-files .env # Force push clean history $ git push --force --all # Notify all developers to re-clone or reset $ git fetch --all $ git reset --hard origin/main
⚠️ CRITICAL: Always rotate secrets BEFORE cleaning Git history. The secret was already exposedβ€”cleaning history alone doesn't secure it.
πŸ’‘ Best Practice: Use environment variables or secrets managers (Vault, AWS Secrets Manager, GitHub Secrets) instead of files:
# Don't: config.json with secrets # Do: config.template.json + env vars $ export API_KEY=$(aws secretsmanager get-secret-value ...)
Q19: What are signed commits and why would you use them? Hard

Answer: Signed commits use GPG (GNU Privacy Guard) to cryptographically verify that a commit genuinely came from the claimed author and hasn't been tampered with.

πŸ”‘ Why signed commits matter:
  • Authentication: Proves the commit author is who they claim to be
  • Integrity: Ensures commit content hasn't been altered after signing
  • Non-repudiation: Author cannot deny creating the commit
  • Supply chain security: Prevents malicious commits from impersonators

Setting up signed commits:

# Generate GPG key $ gpg --full-generate-key # List keys $ gpg --list-secret-keys --keyid-format LONG sec rsa4096/A1B2C3D4E5F67890 2024-01-01 [SC] # Configure Git to use key $ git config --global user.signingkey A1B2C3D4E5F67890 # Sign commits automatically $ git config --global commit.gpgSign true # Sign tags automatically $ git config --global tag.gpgSign true

Creating signed commits:

# Sign individual commit $ git commit -S -m "Signed commit" # Sign all commits (if configured) $ git commit -m "Automatically signed" # Create signed tag $ git tag -s v1.0.0 -m "Signed release"

Verifying signatures:

# Verify commit signature $ git log --show-signature -1 # Verify tag signature $ git tag -v v1.0.0 # Show signatures in log $ git log --pretty="%h %G? %s" # G? shows: G (good), B (bad), U (unknown), N (no signature)

Enforcing signed commits in GitHub/GitLab:

  • Enable branch protection rule: "Require signed commits"
  • Set up GPG key verification in user settings
  • CI can check signatures before merging
⚠️ Important: GPG keys expire. When they do, all commits signed after expiration show as invalid. Set up key rotation reminders.
πŸ’‘ Enterprise Use: For organizations, consider using SSH commit signing (simpler) or integrating with hardware security keys (YubiKey) for team members.
Q20: How would you set up branch protection rules for a production branch? Medium

Answer: Branch protection rules prevent force pushes, require reviews, and enforce quality checks before merging to critical branches.

Essential branch protection settings (GitHub example):

# Via GitHub UI: Settings β†’ Branches β†’ Add rule Branch name pattern: main β˜‘ Require pull request reviews before merging - Required approving reviews: 2 - Dismiss stale reviews when new commits pushed β˜‘ Require status checks to pass before merging - Require branches to be up to date - Status checks: continuous-integration, lint, test β˜‘ Require signed commits β˜‘ Require linear history (no merge commits) β˜‘ Include administrators β˜‘ Allow force pushes: ❌ β˜‘ Allow deletions: ❌

Via GitHub CLI:

$ gh api repos/:owner/:repo/branches/main/protection \ --method PUT \ --field required_status_checks='{"strict":true,"contexts":["ci"]}' \ --field enforce_admins=true \ --field required_pull_request_reviews='{"required_approving_review_count":2}' \ --field restrictions=null

Additional security measures:

  • CODEOWNERS file: Require specific teams to review certain files
  • Dependency review: Check for vulnerable dependencies
  • Secret scanning: Block commits with exposed secrets
  • Automatic linking: Require issue references in PRs
πŸ’‘ Defense in Depth: Combine branch protection with:
  • Signed commits (cryptographic verification)
  • CI/CD with security scanning
  • Regular audit log reviews
  • Limited admin access (break glass procedure)

πŸ“Š Section 7: Quick Reference Tables

Quick Reference: Git Command Comparison
Operation Command Effect
Undo last commit (keep changes) git reset --soft HEAD~1 Moves HEAD, keeps changes staged
Undo last commit (unstage changes) git reset --mixed HEAD~1 Moves HEAD, unstages changes (default)
Undo last commit (discard changes) git reset --hard HEAD~1 DANGER: Deletes changes permanently
Fix last commit message git commit --amend -m "new message" Replaces last commit
Add forgotten file to last commit git add file && git commit --amend Updates last commit
Unstage file git restore --staged file Remove from staging
Discard working directory changes git restore file Revert to last committed version
See what changed git diff Working vs staging
See staged changes git diff --staged Staging vs last commit
See changes between commits git diff a1b2c3d e4f5g6h Compare any two commits
Quick Reference: Recovery Scenarios
Scenario Command
Recover deleted branch git reflog β†’ git branch branch-name [hash]
Recover after bad reset git reflog β†’ git reset --hard [pre-reset-hash]
Find dangling commits git fsck --lost-found
Recover lost stash git fsck --unreachable | grep commit | xargs git log
Restore deleted file git checkout [hash]^ -- file
Undo merge (not pushed) git reset --hard ORIG_HEAD
You accidentally committed a file containing database passwords and pushed to the shared develop branch 1 hour ago. Two other developers have already pulled. What's the correct sequence of actions?
  • 1. Rotate database passwords 2. Clean history with filter-repo 3. Force push 4. Notify team to reset
  • 1. Use git revert 2. Push revert commit 3. Tell team to pull
  • 1. Delete the file and commit 2. Force push 3. Hope no one noticed
  • 1. Do nothing - it's already shared, too late

Interview FAQ

How should I prepare for a Git-focused technical interview?

Comprehensive preparation strategy:

  1. Understand concepts, not just commands: Know why Git behaves the way it doesβ€”internals, object model, why certain commands exist.
  2. Practice on real repositories: Create test repos and intentionally break things (delete branches, reset commits) to practice recovery.
  3. Learn the reflog: This is often the difference between junior and senior answers in recovery scenarios.
  4. Study branching strategies: Be able to compare GitFlow, GitHub Flow, trunk-based developmentβ€”pros, cons, when to use each.
  5. Prepare for scenario questions: Think through real-world situations like hotfixes, secret leaks, repository corruption.
  6. Know Git hosting features: GitHub/GitLab-specific features (protected branches, required status checks, merge methods).
  7. Review CI/CD integration: How Git triggers pipelines, path filtering, commit status updates.
  8. Understand security: Signed commits, secret detection, branch protection, access control.
What are interviewers looking for in Git answers?

Interviewers typically evaluate:

  • Depth of understanding: Can you explain underlying mechanisms, not just recite commands?
  • Practical experience: Have you actually dealt with merge conflicts, rebase issues, or recovery scenarios?
  • Problem-solving approach: How do you diagnose Git problems? Do you use trace, fsck, reflog?
  • Team awareness: Do you consider how your actions affect other developers? (e.g., force push implications)
  • Best practices: Do you follow security practices, commit conventions, and workflow patterns?
  • Communication: Can you explain complex Git concepts clearly and concisely?
  • Tool ecosystem: Familiarity with Git hosting platforms, CI/CD integration, GUI tools.
What are the most common Git interview mistakes candidates make?

Common pitfalls to avoid:

  1. Memorization without understanding: Reciting commands without explaining why they work.
  2. Suggesting force push on shared branches: Shows lack of team collaboration awareness.
  3. Not knowing recovery techniques: "I'd just re-clone" isn't acceptable for senior roles.
  4. Confusing fetch and pull: Basic distinction that's surprisingly often missed.
  5. Not understanding branching models: Can't explain when to use GitFlow vs. trunk-based.
  6. Ignoring security: No awareness of signed commits, secret management.
  7. Overcomplicating answers: Simple questions need simple answers first, then depth if asked.
  8. Not asking clarifying questions: In scenario questions, ask about team size, workflow, constraints.
What Git topics are most important for senior/DevOps roles?

For senior and DevOps positions, prioritize:

  • Internals and recovery: Object model, reflog, fsck, corruption recovery
  • Performance optimization: Large repo strategies, shallow clones, partial clones, Git LFS
  • CI/CD integration: Path-based deployment, commit status, webhooks
  • Branching strategies: Design and implement workflows for team size and release cadence
  • Security: Signed commits, secret scanning, branch protection, access control patterns
  • Automation: Git hooks, custom scripts, API integration
  • Migration strategies: Moving from other VCS, splitting monorepos, history rewriting
  • GitOps: Infrastructure as Code with Git, ArgoCD, Flux
Previous: Git Interview Questions Next: Advanced Rebase