Top 74 Git & GitHub Interview Questions & Answers (2026)
About Git & GitHub
Top 100 Git and GitHub interview questions covering version control, branching, merging, rebasing, pull requests, workflows, and collaboration best practices. Companies hiring for Git & GitHub roles test this knowledge at every stage — from first-round screens that check the basics to final-round conversations about real-world trade-offs.
What to Expect in a Git & GitHub Interview
Expect a mix of conceptual and practical Git & GitHub questions: clear definitions and core-concept checks for junior roles, hands-on scenario questions for mid-level roles, and architecture or trade-off discussions for senior roles. Interviewers usually move from foundational topics toward the kind of problems you'd actually face on the job.
How to Use This Guide
Work through the Git & GitHub questions in order — Beginner, then Intermediate, then Advanced — so each concept builds on the last. Every question has its own page; bookmark the ones that trip you up and revisit them the day before your interview.
Curated by Tech Baithak Editorial Team · Last updated: May 2026
Core concepts every Git & GitHub developer must know.
01
What is Git?
Git is a free, open-source distributed version control system (DVCS) created by Linus Torvalds in 2005 to manage the Linux kernel source code. It tracks changes to files over time, allowing you to recall specific versions later, compare changes, collaborate with others, and revert to previous states if something goes wrong. Key characteristics: Distributed — every developer has a full copy of the repository (history + files) on their local machine, not just a snapshot; this means you can work offline, and there is no single point of failure. Fast — most operations are local (no network required). Integrity — every object (commit, file, directory) is checksummed with SHA-1, making it impossible to change history without detection. Non-linear development — powerful branching and merging enables multiple parallel lines of development. Git stores data as snapshots of the project, not as a list of file-based changes (unlike older VCS like SVN/CVS which store deltas).
02
What is the difference between Git and GitHub?
Git is the version control tool — a command-line program installed on your local machine that tracks changes to files, manages branches, and handles merges. It works entirely locally; you can use Git on a project with no internet connection and no remote server. GitHub is a cloud-based hosting service for Git repositories, owned by Microsoft. It provides a web interface for repositories, adds collaboration features (pull requests, code review, issues, project boards, Actions for CI/CD), and hosts your remote repository so team members can push and pull code. GitHub is NOT Git — it is one of several services built on top of Git. Alternatives to GitHub: GitLab (self-hostable, built-in CI/CD), Bitbucket (Atlassian, integrates with Jira), Gitea (lightweight, self-hosted), Azure DevOps Repos. The analogy: Git is like a word processor (e.g., Microsoft Word), and GitHub is like Google Drive — you use the word processor to create documents, and Google Drive is where you store and share them.
03
What is a Git repository?
A Git repository (repo) is a directory that Git tracks. It contains all project files plus a hidden .git/ folder that stores the complete history, configuration, and metadata. The .git/ folder contains: objects/ (all file content, commits, trees — stored as SHA-1 hashed blobs), refs/ (branches, tags — pointers to commits), HEAD (pointer to the current branch or commit), config (repo-level configuration), index (the staging area). Two types: Local repository — on your machine; Remote repository — hosted on a server (GitHub, GitLab, etc.) for sharing with collaborators. Initialize a new repo: git init — creates the .git/ folder in the current directory. Clone an existing repo: git clone https://github.com/user/repo.git — creates a local copy including the full history. A bare repository (git init --bare) stores only the Git data without a working directory — used for remote/server repositories.
04
What are the three stages of a file in Git?
Git has three main areas where files can exist: (1) Working Directory (Working Tree): the actual files on your disk that you edit. Changes here are untracked by Git until you explicitly add them. Files can be untracked (new files Git has never seen) or modified (tracked files that have changed since last commit); (2) Staging Area (Index): a preparation area between the working directory and the repository. You explicitly choose which changes to include in the next commit by running git add. The staging area lets you craft precise commits — you can stage only part of a file's changes, stage some files but not others. git add file.txt moves changes to staging; (3) Repository (.git directory): the permanent, versioned history. git commit takes everything in the staging area and creates a commit in the repository. The transition: Working Directory → (git add) → Staging Area → (git commit) → Repository. View which stage files are in: git status. View staged changes: git diff --staged. View unstaged changes: git diff.
05
What is git init?
git init initializes a new Git repository in the current directory by creating a .git/ subdirectory. This is the first command you run when starting version control on a new project. The .git/ folder contains all Git metadata: object database, refs, configuration, and hooks. The directory itself is not committed — it is Git's private workspace. After git init, the directory becomes a Git repository, but it has no commits and no tracking history yet. Create initial commit: git add . then git commit -m "Initial commit". Create a bare repository (for servers): git init --bare repo.git — contains only Git data, no working files; this is what GitHub/GitLab host internally. Initialize with a custom branch name: git init -b main (sets default branch to "main" instead of legacy "master"). If you accidentally run git init in the wrong directory, simply delete the .git/ folder: rm -rf .git.
06
What is git clone?
git clone creates a local copy of a remote repository, including the full history, all branches, and all files. Syntax: git clone <url> [directory]. Examples: git clone https://github.com/user/repo.git — clones into a folder named "repo"; git clone https://github.com/user/repo.git myproject — clones into "myproject". What clone does: (1) Creates a new directory; (2) Initializes a .git/ repository; (3) Downloads all objects and refs from the remote; (4) Creates a remote tracking reference called origin pointing to the URL; (5) Checks out the default branch. Clone options: --depth 1 — shallow clone (only latest commit, no full history — faster for large repos in CI/CD); --branch feature-x — clone and checkout a specific branch; --recurse-submodules — also clone submodules. Protocols: HTTPS (easiest, prompts for password or token), SSH (git@github.com:user/repo.git — key-based, no password), Git protocol (read-only, rarely used). After cloning, origin is automatically configured as the remote.
07
What is git add?
git add moves changes from the working directory to the staging area (index), marking them to be included in the next commit. Variants: git add file.txt — stage a specific file; git add src/ — stage all changes in a directory; git add . — stage all changes in the current directory and subdirectories (new files + modifications + deletions); git add -A / --all — stage all changes everywhere in the repo (including deletions); git add *.js — stage all JS files; git add -p / --patch — interactively stage parts of a file (hunks) — extremely useful for making clean, focused commits even when you've changed multiple things in one file; git add -u — stage modifications and deletions but NOT new untracked files. After staging, verify with git status (shows what's staged in green, unstaged in red) or git diff --staged (shows exact diff of what will be committed). To unstage: git restore --staged file.txt (modern) or git reset HEAD file.txt (classic).
08
What is git commit?
git commit saves the staged changes as a permanent snapshot in the repository history. Each commit has: a unique SHA-1 hash, author name and email, timestamp, commit message, a pointer to its parent commit(s), and a snapshot of the staged files. Variants: git commit -m "message" — commit with inline message; git commit — opens your configured editor to write a message; git commit -am "message" — automatically stage all tracked modified files and commit (skips git add for modified files, but NOT new untracked files); git commit --amend — modify the most recent commit (change message or add forgotten files). Good commit message practices: (1) Subject line ≤50 chars, capitalized, imperative mood ("Fix bug" not "Fixed bug"); (2) Blank line separating subject from body; (3) Body explains WHAT and WHY, not HOW; (4) Reference issue numbers. Commits are immutable once pushed and shared — --amend and rebase should only be used on commits not yet pushed.
09
What is git status?
git status shows the current state of the working directory and staging area — which changes have been staged, which haven't, and which files aren't tracked by Git. Output categories: Staged changes (ready to commit): green — these will be included in the next commit; Unstaged changes (modified tracked files): red — working directory differs from last commit, but not staged yet; Untracked files: red — new files Git has never tracked. Common outputs: "nothing to commit, working tree clean" — all tracked files match the last commit; "Changes to be committed" — something is staged; "Changes not staged for commit" — tracked files modified but not staged; "Untracked files" — new files not yet added. Short format: git status -s or --short — compact two-column output (M = modified, A = added, ? = untracked, left column = staged, right = unstaged). Branch info: status also shows current branch and relationship to the tracked remote (Your branch is ahead of "origin/main" by 2 commits). Use git status constantly — it guides your next action.
10
What is git log?
git log displays the commit history of the current branch, showing commits in reverse chronological order (newest first). Default output: commit hash, author, date, and message. Useful options: git log --oneline — compact single-line format (short hash + message); git log --graph — ASCII art graph of branch topology; git log --oneline --graph --all — visual history of all branches (very useful for understanding repo state); git log -5 — show last 5 commits; git log --author="Alice" — filter by author; git log --since="2024-01-01" --until="2024-06-01" — date range; git log --grep="bug fix" — search commit messages; git log -p / --patch — show diff for each commit; git log --stat — show changed files summary per commit; git log file.txt — history of a specific file; git log main..feature — commits in feature not yet in main. Format: git log --format="%h %an %s" — custom output (hash, author name, subject). git log is one of the most powerful and frequently used Git commands for understanding a project's history.
11
What is git diff?
git diff shows the difference (changes) between various states of files. Common usages: git diff — shows unstaged changes (working directory vs last commit for tracked files); git diff --staged / --cached — shows staged changes (what will be committed); git diff HEAD — shows all changes (staged + unstaged) vs last commit; git diff branch1 branch2 — diff between two branches; git diff commit1 commit2 — diff between two commits; git diff HEAD~3 — changes since 3 commits ago; git diff file.txt — diff for a specific file only. Output format: lines starting with + (green) are additions, - (red) are deletions, context lines have no prefix. git diff --stat — summary of changed files and line counts. git diff --word-diff — shows word-level changes instead of line-level (useful for prose). External diff tools: git difftool opens a visual diff tool (VS Code, vimdiff, meld). Understanding git diff is essential for reviewing changes before committing and for code review.
12
What is a Git branch?
A branch in Git is a lightweight, movable pointer to a specific commit. Branches enable isolated, parallel lines of development — you can work on a feature, bug fix, or experiment without affecting the main codebase. When you create a new branch, Git simply creates a new pointer — no files are copied. The HEAD pointer tracks which branch you're currently on. When you commit, the current branch pointer moves forward to the new commit. Default branch: main (modern) or master (legacy). Branch commands: git branch — list all local branches (* shows current); git branch -a — list all branches including remotes; git branch feature-login — create a new branch (doesn't switch to it); git checkout feature-login — switch to a branch; git checkout -b feature-login — create AND switch (shorthand); git switch feature-login / git switch -c feature-login — modern equivalent (Git 2.23+); git branch -d feature-login — delete merged branch; git branch -D feature-login — force delete. Branches are cheap and fast — use them liberally for every feature, bug fix, and experiment.
13
What is git merge?
git merge integrates changes from one branch into another. To merge: switch to the receiving branch first, then merge the source branch. cd main; git merge feature-login. Types of merges: (1) Fast-forward merge: when the target branch has no new commits since the feature branch diverged, Git simply moves the pointer forward — no merge commit created. Clean linear history. git merge --ff-only feature — only merge if fast-forward is possible; (2) Three-way merge (non-fast-forward): when both branches have diverged, Git finds the common ancestor commit, merges changes from both, and creates a new merge commit with two parents. This preserves the complete history but adds merge commits; (3) Squash merge: git merge --squash feature — combines all feature branch commits into one set of staged changes; you then make one commit. Loses individual feature branch history but keeps the main branch clean. Handle merge conflicts when Git can't auto-merge: resolve conflicts in the files, then git add and git commit.
14
What is git pull?
git pull fetches changes from the remote repository and immediately merges them into the current local branch. It is a combination of two operations: git fetch (downloads remote changes) + git merge (integrates them). Basic usage: git pull — pull from the tracked remote branch; git pull origin main — explicitly pull from origin's main branch. Pull with rebase (cleaner history): git pull --rebase — instead of creating a merge commit, replays your local commits on top of the fetched commits. This creates a linear history. Can be set as default: git config --global pull.rebase true. Pull options: git pull --no-commit — fetch and merge but don't automatically commit; git pull --ff-only — only pull if fast-forward is possible (fails if diverged — safe option to avoid accidental merge commits). Common issue: if local and remote have diverged, a simple pull creates a merge commit. Best practice: git fetch first to review remote changes, then decide to merge or rebase. git pull is one of the most used commands in daily workflow.
15
What is git push?
git push uploads local commits to the remote repository. Basic usage: git push — push current branch to its tracked remote; git push origin main — explicitly push local main to remote origin's main; git push origin feature-login — push a feature branch. First push of a new branch: git push -u origin feature-login — -u sets the upstream tracking reference so future git push/git pull work without specifying remote and branch. Push tags: git push --tags — push all local tags; git push origin v1.0 — push a specific tag. Force push (dangerous): git push --force / -f — overwrites remote history with local history. Used after rebase or amend. NEVER force push to shared branches (main, develop) — you overwrite teammates' history. Use git push --force-with-lease instead — safer: only force pushes if no one else has pushed since your last fetch (prevents overwriting others' work). Delete remote branch: git push origin --delete feature-login.
16
What is git fetch?
git fetch downloads commits, files, and refs from the remote repository to your local repo, but does NOT merge them into your working branch. Your local working files and branches are untouched — fetch only updates the remote tracking references (like origin/main). This makes fetch safe — you can review what's changed before integrating. Usage: git fetch — fetch from the default remote (origin); git fetch origin — explicitly fetch from origin; git fetch --all — fetch from all remotes; git fetch origin main — fetch only the main branch. After fetch, inspect changes: git log HEAD..origin/main — commits on remote not yet local; git diff HEAD origin/main — diff between local and remote. Then integrate: git merge origin/main or git rebase origin/main. Fetch vs Pull: git pull = git fetch + git merge. Best practice: use fetch to see what's coming, then consciously decide to merge or rebase. This gives you more control than git pull which automatically merges.
17
What is git stash?
git stash temporarily shelves (saves) changes you've made to your working directory and staging area so you can work on something else, then come back and re-apply them. Your working directory is cleaned to match the last commit. Commands: git stash / git stash push — stash all uncommitted changes (tracked files); git stash -u / --include-untracked — also stash untracked files; git stash -a / --all — also stash ignored files; git stash save "message" — stash with a descriptive name; git stash list — list all stashes (stash@{0} is most recent); git stash apply — re-apply the most recent stash (keeps stash in list); git stash pop — re-apply AND remove from stash list; git stash apply stash@{2} — apply a specific stash; git stash drop stash@{0} — delete a stash; git stash clear — delete all stashes; git stash branch feature — create a new branch from a stash. Common use case: you're mid-feature when an urgent bug comes in — stash your work, fix the bug, then pop the stash to resume.
18
What is .gitignore?
The .gitignore file tells Git which files or directories to ignore — to not track and not include in commits. Git will not stage or commit ignored files. Syntax: each line is a pattern; # = comment; / at start = relative to .gitignore location; * = wildcard; ** = any level of subdirectories; ! = negate (un-ignore). Examples: node_modules/ — ignore directory; *.log — ignore all .log files; !important.log — except this one; .env — ignore environment secrets; dist/ — ignore build output; *.pyc — Python bytecode; .DS_Store — macOS metadata; *.class — Java compiled files. Scope: .gitignore in the root applies to the whole repo; you can have .gitignore in subdirectories for directory-specific rules. Global gitignore (for your machine): git config --global core.excludesfile ~/.gitignore_global — ignore files like .DS_Store globally. Already tracked files: adding a file to .gitignore does not un-track it if it's already committed — you must run git rm --cached file first. Generate .gitignore for your stack at gitignore.io.
19
What is HEAD in Git?
HEAD is a special pointer in Git that refers to the currently checked-out commit — the commit your working directory is based on. Normally, HEAD points to a branch name (like main), which in turn points to the latest commit on that branch. This is called "attached HEAD." When you make a commit, the branch pointer advances and HEAD follows. HEAD references: HEAD — current commit; HEAD~1 or HEAD^ — parent commit (one before current); HEAD~3 — 3 commits back; HEAD^^ — grandparent. Detached HEAD state: when you checkout a specific commit hash or tag instead of a branch (git checkout abc1234), HEAD points directly to that commit rather than a branch. You can look around and make experimental commits, but those commits won't belong to any branch and may be lost if you switch away. To save work in detached HEAD: git checkout -b new-branch — creates a branch at the current commit. The .git/HEAD file contains the current HEAD value — either a branch reference (ref: refs/heads/main) or a raw commit hash (detached).
20
What is git checkout?
git checkout is one of Git's most versatile commands with multiple purposes. Main uses: (1) Switch branches: git checkout main — move to main branch; Git updates the working directory to match; (2) Create and switch: git checkout -b feature-x — create and immediately switch to a new branch; (3) Checkout a specific commit (detached HEAD): git checkout abc1234 — view the repo at that point in history; (4) Restore files: git checkout -- file.txt (legacy) or git restore file.txt (modern) — discard working directory changes to a file, reverting to the staged or last-committed version. Irreversible — discards unsaved changes! (5) Checkout a file from another branch: git checkout feature -- src/utils.js — bring a specific file from another branch into current working directory. Git 2.23+ split checkout's concerns into: git switch (for changing branches) and git restore (for restoring files) — clearer semantics. git checkout still works for backward compatibility but the newer commands are recommended for clarity.
21
What is git reset?
git reset moves the HEAD (and optionally the branch pointer) to a specified commit, with different effects on the staging area and working directory. Three modes: (1) --soft: git reset --soft HEAD~1 — moves HEAD back one commit but keeps all changes staged. The commit is undone, changes are in staging ready to re-commit. Safest for "undo last commit, keep changes."; (2) --mixed (default): git reset HEAD~1 — moves HEAD back, unstages changes (returns them to working directory, not staged). Useful for "undo commit and unstage."; (3) --hard: git reset --hard HEAD~1 — moves HEAD back AND discards all changes in both staging and working directory. The changes are gone. Dangerous — cannot be recovered unless you have the SHA. Use cases: git reset HEAD file.txt — unstage a file (keeps working directory); git reset --hard origin/main — discard all local commits and changes, match remote. Important: Never reset commits that have been pushed and shared with others — it rewrites history and will break teammates' repos. Use git revert instead for shared history.
22
What is git revert?
git revert creates a new commit that undoes the changes made by a specified commit, without altering the existing history. It is the safe way to undo published commits. Unlike git reset which moves the branch pointer backward (rewriting history), git revert adds a new commit on top that applies the inverse of the specified commit's changes. Usage: git revert abc1234 — creates a new commit reverting abc1234; git revert HEAD — revert the last commit; git revert HEAD~3..HEAD — revert the last 3 commits (creates multiple revert commits). Options: --no-commit / -n — stage the revert changes without committing, letting you combine multiple reverts into one commit or review before committing. When to use revert vs reset: use git revert for commits already pushed to a shared/remote branch — it preserves history and doesn't break others' repos. Use git reset only for local, unpushed commits. The revert commit message explains what was undone: "Revert 'Fix login bug' — This reverts commit abc1234."
23
What is a merge conflict and how do you resolve it?
A merge conflict occurs when Git can't automatically merge changes because two branches modified the same part of the same file in different ways. Git doesn't know which version to keep, so it marks the conflict in the file for you to resolve manually. Conflict markers in the file: <<<<<<< HEAD (current branch changes), ======= (separator), >>>>>>> feature-branch (incoming changes). Resolution steps: (1) Run git status to see all conflicted files ("both modified"); (2) Open each conflicted file; (3) Find the conflict markers; (4) Edit the file to keep the correct content (either/both/neither version); (5) Remove ALL conflict markers (<<<<<<<, =======, >>>>>>>); (6) git add the resolved files; (7) git commit to complete the merge. Abort a conflicted merge: git merge --abort. Visual merge tools: git mergetool opens a configured tool (VS Code, vimdiff, IntelliJ). VS Code has excellent built-in conflict resolution UI. Prevention: pull frequently, keep branches short-lived, communicate with teammates about which files you're modifying.
24
What is git tag?
Tags are named references to specific commits, typically used to mark release versions. Unlike branches, tags don't move — they always point to the same commit. Two types: (1) Lightweight tag: git tag v1.0 — just a pointer to a commit, no extra metadata. Like a branch that doesn't move; (2) Annotated tag: git tag -a v1.0 -m "Version 1.0 release" — stored as a full Git object with author, date, message, and optional GPG signature. Recommended for releases. Tag a specific commit: git tag v1.0-fix abc1234. List tags: git tag or git tag -l "v1.*" (wildcard filter). Show tag info: git show v1.0. Push tags to remote: git push origin v1.0 (single tag) or git push --tags (all tags). Push with commit: git push origin main --follow-tags (only annotated tags). Delete local tag: git tag -d v1.0. Delete remote tag: git push origin --delete v1.0. Checkout a tag: git checkout v1.0 — creates detached HEAD. For production deployment: git checkout -b hotfix v1.0.
25
What is the difference between git merge and git rebase?
Both merge and rebase integrate changes from one branch into another but differ in how they do it and the resulting history. git merge: combines branches by creating a merge commit with two parents. Preserves complete, accurate history — you can see exactly when and how branches diverged and joined. The history tells the true story. Results in a "diamond" shaped graph. Best for: shared branches (main, develop), feature branch merges into main, when you want to preserve the full history. git rebase: re-applies commits from one branch on top of another, rewriting commit history. The feature branch commits are replayed as if they were created after the latest main commit. Creates a clean, linear history — no merge commits, looks like development happened sequentially. But it rewrites commit SHAs (history is altered). Best for: cleaning up local feature branches before merging, keeping feature branch up-to-date with main. Golden rule of rebase: never rebase public/shared branches. Only rebase your own local, unpushed commits. If you rebase pushed commits, you rewrite history everyone else has, causing chaos. Use interactive rebase (git rebase -i) to clean up commits before a PR.
26
What is git rebase?
git rebase moves or replays a sequence of commits to a new base commit. Instead of merging, it takes the commits from your branch and reapplies them one by one on top of the target branch. The result is a linear, clean history. Basic usage: while on feature branch, git rebase main — replays feature branch commits on top of the latest main. This is like saying "pretend I started working on this feature from the current tip of main." The feature branch commits get new SHA hashes (they're technically new commits with the same changes). Interactive rebase: git rebase -i HEAD~3 — open an editor to manipulate the last 3 commits. Commands in the editor: pick (keep as-is), reword (change message), edit (pause to amend), squash (combine into previous), fixup (squash silently), drop (delete commit), reorder (drag lines). Interactive rebase is powerful for cleaning up messy commit history before opening a PR — squash WIP commits into meaningful ones. Abort: git rebase --abort. Continue after resolving conflicts: git rebase --continue.
27
What is a pull request (PR)?
A pull request (PR) — called a merge request (MR) in GitLab — is a feature of hosted Git platforms (GitHub, GitLab, Bitbucket) that provides a structured way to propose, review, and merge changes from one branch to another. It is NOT a Git feature — it's a collaboration workflow on top of Git. A PR notifies team members that you've pushed changes and requests them to review and "pull" (merge) your branch. PR features: (1) Code review: reviewers can comment on specific lines, request changes, or approve; (2) Discussion: threaded conversations about the changes; (3) Diff view: easy to see all changes; (4) CI/CD integration: automated tests run on the PR; (5) Merge options: merge commit, squash merge, or rebase merge; (6) Branch protection: require approvals before merging. Good PR practices: small, focused changes; descriptive title and description; link related issues; respond to feedback promptly; don't ignore review comments. Draft PRs signal "work in progress" and request early feedback without being ready to merge.
28
What is a remote in Git?
A remote in Git is a named reference to another Git repository, typically hosted on a server (GitHub, GitLab, etc.). Remotes allow you to push your changes to and pull changes from shared repositories. The default remote name when you clone is origin. Commands: git remote — list remote names; git remote -v — list remotes with their URLs (fetch and push URLs); git remote add upstream https://github.com/original/repo.git — add a second remote called "upstream" (common when you fork a repo and want to pull from the original); git remote remove upstream — remove a remote; git remote rename origin oldname — rename; git remote set-url origin https://new-url.git — change the URL (useful when migrating from HTTPS to SSH or changing hosts); git remote show origin — detailed info including tracked branches. Multiple remotes: a repo can have multiple remotes — "origin" (your fork), "upstream" (original repo), "production" (deployment server). Remote tracking branches (like origin/main) are local references to the last known state of remote branches — updated by git fetch.
29
What is a fork in GitHub?
A fork is a personal copy of someone else's repository on GitHub (or GitLab). Forking creates an independent copy under your GitHub account — you can modify it freely without affecting the original (upstream) repository. Forks are the foundation of open-source contribution: (1) You find a project you want to contribute to; (2) Fork it to your account; (3) Clone your fork locally; (4) Create a branch and make changes; (5) Push to your fork; (6) Open a pull request from your fork to the upstream repository. Fork vs Branch: a fork is a server-side copy (different GitHub account/organization); a branch is within the same repository. Staying in sync with upstream: git remote add upstream https://github.com/original/repo.git; git fetch upstream; git merge upstream/main or git rebase upstream/main. When to fork vs branch: fork when you don't have write access to the original repo (open-source contributions); branch when you're working within your own or your team's repo. Forks maintain a connection to the upstream repository, enabling pull requests back to the original.
30
What is git cherry-pick?
git cherry-pick applies the changes from a specific commit (or range of commits) onto the current branch. Instead of merging an entire branch, you pick just the commits you want. Usage: git cherry-pick abc1234 — apply commit abc1234 to the current branch; creates a new commit with the same changes but a different SHA hash. Multiple commits: git cherry-pick abc1234 def5678 — apply two commits; git cherry-pick abc1234..def5678 — apply a range (exclusive of first); git cherry-pick abc1234^..def5678 — range inclusive of first. Options: --no-commit / -n — apply changes without committing (stage only); -e — edit the commit message; -x — append original commit hash to message (useful for tracking). Common use cases: (1) Hotfix — a bug was fixed on a feature branch but needs to go to main immediately; (2) Undo a bad merge — apply only the good commits; (3) Port a specific fix to a release branch. Handle conflicts like merge conflicts. Abort: git cherry-pick --abort. Cherry-pick creates new commits, so the same change exists in multiple branches with different SHAs.
31
What is git blame?
git blame shows who last modified each line of a file, along with the commit hash and timestamp. It's used to understand why a piece of code was written and who to ask about it. Usage: git blame file.txt — shows every line annotated with author, date, and commit hash; git blame -L 10,20 file.txt — only lines 10-20; git blame -w file.txt — ignore whitespace changes; git blame -M file.txt — detect lines moved within the same file; git blame --since="2024-01-01" file.txt — only blame before a date. Output format: abc1234 (Alice 2024-01-15 10:30:00 -0500 42) const MAX_SIZE = 1024; — hash, author, date, line number, content. Jumping back further: if a line shows a commit that only moved it, use git log -p abc1234 to find the original authorship. IDE integration: most IDEs and editors (VS Code Git Lens, IntelliJ) show inline blame annotations. Note: the name "blame" is a bit misleading — it's more about understanding context and history than assigning fault. If you see something strange, use blame to find the commit, then git show abc1234 to understand why the change was made.
32
What is git show?
git show displays information about a specific Git object — most commonly used to view a commit's metadata and diff. For commits: git show — show the last commit; git show abc1234 — show a specific commit (hash, author, date, message, and full diff); git show HEAD~2 — show the commit 2 back from HEAD. Output: commit metadata at the top, followed by the diff (+ added lines, - removed lines). Options: --stat — show only file change summary, not full diff; --no-patch — show only commit message, no diff; git show abc1234:src/app.js — show the contents of a file at a specific commit (useful for viewing old versions without checking out). For tags: git show v1.0 — show tag info + the tagged commit. For trees/blobs: git show abc1234^{tree} — show the tree object. Difference from git log -p: git show shows one commit in detail; git log -p shows many commits with their diffs. git show is ideal when you know the exact commit hash and want full details.
33
What are Git hooks?
Git hooks are scripts that Git executes automatically before or after specific Git events, allowing you to automate tasks and enforce policies. They live in .git/hooks/ as executable scripts (shell, Python, Node.js, etc.). Hooks are not committed to the repo by default (they're in .git/) — to share hooks, use tools like husky (Node.js) or put them in a directory and symlink. Common client-side hooks: pre-commit — runs before a commit is created; used to run linters, formatters, tests. Exit non-zero to abort the commit; commit-msg — validates the commit message format (e.g., enforce conventional commits); pre-push — runs before pushing; used to run full test suite; post-commit — runs after commit (notifications); pre-rebase — can prevent rebasing certain branches. Server-side hooks (on the remote): pre-receive — validate pushes before accepting; enforce branch protection; post-receive — trigger deployments, send notifications after push. Tools: Husky — popular Node.js tool for managing Git hooks in package.json; lint-staged — run linters only on staged files.
34
What is git rm?
git rm removes files from both the working directory and the Git staging area (index), staging the deletion for the next commit. It is different from simply deleting a file with rm — if you delete a file manually, you still need to git add the deletion; git rm does both in one step. Usage: git rm file.txt — delete from working directory and stage the deletion; git rm -r dir/ — recursively remove a directory; git rm --cached file.txt — CRITICAL: removes the file from tracking (index) WITHOUT deleting from disk. Used when you accidentally committed a file that should be in .gitignore (secrets, node_modules). After git rm --cached, add the file to .gitignore and commit the removal; git rm -f file.txt — force removal even if file has uncommitted changes; git rm "*.log" — remove files matching a pattern. After git rm, the deletion is staged — commit to finalize: git commit -m "Remove obsolete file". Common workflow: accidentally committed .env file → git rm --cached .env → add to .gitignore → commit → rotate any exposed secrets.
35
What is git mv?
git mv moves or renames a file in both the working directory and the staging area. It is equivalent to running mv old new + git rm old + git add new. Usage: git mv old_name.txt new_name.txt — rename a file; git mv src/utils.js lib/helpers.js — move and rename; git mv src/ lib/ — move an entire directory. Git detects renames heuristically based on content similarity — even if you rename manually with rm + add, Git usually recognizes it as a rename in git log --follow. git log --follow file.txt — show history of a file even across renames (follows the rename). Why it matters: git blame and git log without --follow won't show history before a rename. Rename detection threshold: git diff -M50% — treat as rename if 50%+ content matches. In practice, git mv is mainly useful for explicit rename staging — Git's rename detection works well even with manual moves.
36
What is git config?
git config reads and writes Git configuration settings at three levels: (1) System: /etc/gitconfig — applies to all users on the machine (--system flag); (2) Global: ~/.gitconfig or ~/.config/git/config — applies to all repos for the current user (--global flag, most common); (3) Local: .git/config — applies only to the current repo (default, --local flag). Higher level overrides lower. Essential initial setup: git config --global user.name "Your Name"; git config --global user.email "you@example.com" — required for commits; git config --global core.editor "code --wait" — set VS Code as editor; git config --global init.defaultBranch main — default branch name. View config: git config --list — show all settings; git config user.name — show specific setting; git config --global --edit — open global config in editor. Useful settings: git config --global pull.rebase true — always rebase on pull; git config --global alias.lg "log --oneline --graph --all" — create command alias; git config --global core.autocrlf input — handle line endings (important on Windows).
37
What is git bisect?
git bisect is a powerful debugging tool that uses binary search to find the exact commit that introduced a bug. Instead of manually checking each commit, bisect narrows it down in O(log n) steps. Workflow: (1) git bisect start — begin bisect session; (2) git bisect bad — mark current commit as bad (has the bug); (3) git bisect good v1.0 — mark a known-good commit/tag; Git checks out the middle commit; (4) Test whether this commit has the bug; (5) git bisect good or git bisect bad based on the test; (6) Repeat until Git identifies the first bad commit: "abc1234 is the first bad commit"; (7) git bisect reset — return to original HEAD. Automated bisect: git bisect run npm test — run a test script automatically; exits 0 = good, non-zero = bad. Git handles the binary search entirely. Example: a bug was introduced sometime in the last 100 commits → manual check = 100 steps → bisect = ~7 steps. View bisect log: git bisect log. Skip a commit (can't test it): git bisect skip. Bisect is especially powerful when combined with an automated test that reproduces the bug.
38
What is the difference between git reset and git restore?
Git 2.23 (2019) split the overloaded git checkout command into two more focused commands: git restore and git switch. git restore is specifically for restoring files in the working tree or staging area — discarding changes: git restore file.txt — discard working directory changes to file.txt (revert to staged version or last commit); git restore --staged file.txt — unstage a file (remove from staging area, keep working directory changes); git restore --source HEAD~2 file.txt — restore file to its state 2 commits ago; git restore . — discard all working directory changes. git reset is for moving the branch pointer and adjusting the commit history: git reset HEAD~1 — undo last commit (unstages changes); git reset --hard — discard commits and changes; git reset --soft — undo commits but keep staged. Summary: use git restore to discard file changes without touching commit history; use git reset to undo commits and rewrite commit history. Both git restore and git switch are now recommended over git checkout for clarity, though checkout still works.
39
What is git shortlog?
git shortlog summarizes commit history by grouping commits by author, making it useful for project statistics and contributor recognition. Usage: git shortlog — groups commits by author (alphabetical), listing each commit message; git shortlog -s — summary only (count per author, no messages); git shortlog -sn — sort by number of commits (most active contributors first); git shortlog -sne — include email addresses; git shortlog -n --since="2024-01-01" — filter by date; git log --oneline | git shortlog — can pipe log output. Typical output: 15 Alice (15 commits listed)\n 8 Bob (8 commits listed). With -s -n: 15 Alice\n 8 Bob\n 3 Carol. Use cases: (1) Generate a contributor list for release notes; (2) Understand team activity; (3) Identify who to ask about specific areas of the codebase. Difference from git log: log shows chronological commit history; shortlog summarizes by contributor. Combined with --since/--until to analyze contributions per sprint or release cycle.
Practical knowledge for developers with hands-on experience.
01
What is GitFlow workflow?
GitFlow is a branching model proposed by Vincent Driessen that defines a strict branching structure for managing releases. It uses two permanent branches and three types of supporting branches. Permanent branches: main (production-ready code, every commit is a release) and develop (integration branch for features). Supporting branches: (1) feature/* — branch off develop, merge back to develop when complete: git checkout -b feature/login develop; (2) release/* — branch off develop when ready for a release, only bug fixes allowed, merge into both main and develop when complete; (3) hotfix/* — branch off main for urgent production fixes, merge into both main and develop. Flow: feature → develop → release → main. Tags are created on main for each release. Pros: clear structure, good for scheduled releases, supports multiple versions. Cons: complex, too many long-lived branches, not suited for continuous deployment (CD) where you deploy frequently. Better alternatives for CD: GitHub Flow (simpler: one main branch, short-lived feature branches, deploy from PR) or trunk-based development (everyone commits to main daily, feature flags for in-progress work).
02
What is trunk-based development?
Trunk-based development (TBD) is a branching strategy where all developers commit to a single shared branch (usually main or trunk) at least once a day. Branches are short-lived (hours to days, not weeks) and merged back quickly. Key practices: (1) Small, frequent commits to main — no long-running feature branches; (2) Feature flags — incomplete features are hidden behind flags rather than living in separate branches; (3) Automated tests — fast test suite (minutes) that runs on every commit; (4) Branch by abstraction — for large refactors, create an abstraction layer first; (5) CI validates every commit immediately. Variants: pure TBD (commit directly to main) and scaled TBD (short-lived branches <2 days, always branched from and merged to main). Advantages: no merge hell (long-lived branches diverge significantly), smaller batches reduce risk, enables true CI/CD (continuous integration means integrating continuously), faster feedback. Who uses it: Google, Facebook, Netflix. Comparison: GitFlow is for teams doing periodic releases; TBD is for teams doing continuous deployment. TBD requires mature CI/CD, automated tests, and feature flag infrastructure.
03
What is squash merging and when should you use it?
Squash merging combines all commits from a feature branch into a single commit when merging into the target branch. The feature branch's individual commits (including WIP, typo fixes, "oops" commits) are squashed into one clean commit on main. GitHub pull request squash option: "Squash and merge." Command line: git merge --squash feature-branch — this stages all changes without committing; you then create one commit: git commit -m "Add user authentication feature". The feature branch history is discarded — only the final squashed commit appears on main. Pros: (1) Clean main branch history — each commit represents one complete feature or fix; (2) Easy to revert a feature (one commit to revert); (3) git log on main is readable without feature branch noise. Cons: (1) Lose the detailed commit history of the feature (can't see the intermediate steps); (2) Makes individual commits harder to bisect; (3) The feature branch must be deleted after — it can't be "merged" again. When to squash: small-medium features with messy commit history; when team prefers a clean, readable main branch history. When NOT to squash: large features where individual commits have meaning; when you want to cherry-pick specific commits later.
04
What is interactive rebase and how is it used for cleaning up commits?
Interactive rebase (git rebase -i) lets you edit, reorder, combine, or drop commits in the current branch before merging. It's the standard way to clean up "work in progress" commits before a PR. Usage: git rebase -i HEAD~5 — interactive editor showing last 5 commits. The editor lists commits oldest-first (opposite of git log). Commands for each commit: pick — use as-is; r/reword — keep commit but edit message; e/edit — pause to amend the commit; s/squash — melt into previous commit, combine messages; f/fixup — like squash but discard this commit's message; d/drop — delete commit entirely; reorder — drag lines to change commit order. Common workflow: develop with many small commits, then before PR: git rebase -i origin/main → squash WIP commits → reword messages → result: 2-3 clean, meaningful commits. Example: squash "Add login form", "fix typo", "fix again", "add tests" into one clean "Add login feature with tests" commit. After interactive rebase: git push --force-with-lease (since history was rewritten). Use only on private/unshared branches.
05
What is git reflog?
git reflog shows a log of all places HEAD has pointed in the local repo — every commit, checkout, reset, merge, and rebase, with a timestamp. It's Git's safety net: even if you lose commits with git reset --hard, git rebase, or branch deletion, reflog lets you recover them as long as the 90-day retention hasn't expired. Usage: git reflog — shows recent HEAD movements with relative refs (HEAD@{0}, HEAD@{1}...); git reflog show main — reflog for a specific branch; git reflog --date=iso — show actual timestamps. Recovery scenarios: (1) Undo a bad reset: git reset --hard HEAD@{3} — go back to where you were 3 moves ago; (2) Recover deleted branch: find the last commit hash in reflog, then git checkout -b recovered-branch abc1234; (3) Undo a rebase gone wrong: find the pre-rebase HEAD in reflog, reset to it. Reflog is local only — it is NOT synced to the remote. Every entry has a hash — you can checkout, cherry-pick, or create a branch from any reflog entry. Reflog entries expire after 90 days (configurable).
06
What is git submodule?
Git submodules allow you to include one Git repository as a subdirectory inside another. The parent repo stores a reference (specific commit) to the submodule repo — not the content itself. When you clone the parent, submodules are empty until initialized. Adding a submodule: git submodule add https://github.com/other/lib.git vendor/lib — creates .gitmodules config file and adds the submodule directory. The parent repo records the exact commit of the submodule (pinned version). Cloning with submodules: git clone --recurse-submodules url or after cloning: git submodule update --init --recursive. Update to latest: git submodule update --remote. Working in a submodule: cd into the submodule, work normally (it's a regular Git repo), commit, then back in parent: git add submodule/path && git commit to update the tracked commit. Remove a submodule: git submodule deinit vendor/lib && git rm vendor/lib. Drawbacks: complex workflow, easy to forget updating, confusing for new contributors. Alternatives: git subtree (merges history), language package managers (npm, Maven, pip), monorepo tools. Use submodules when you need to pin to specific commits of a shared library.
07
What is git subtree?
git subtree is an alternative to submodules for including one repository inside another. Instead of references (submodules), subtree merges the external repo's history directly into the parent repo. Adding a subtree: git subtree add --prefix=vendor/lib https://github.com/other/lib.git main --squash. Pulling updates: git subtree pull --prefix=vendor/lib https://github.com/other/lib.git main --squash. Pushing changes back: git subtree push --prefix=vendor/lib https://github.com/other/lib.git main. Advantages over submodules: (1) Normal clone works — no --recurse-submodules; (2) No .gitmodules complexity; (3) Contributors don't need to know it's a subtree; (4) History is preserved in the parent. Disadvantages: (1) Parent repo history becomes polluted with external repo commits; (2) Harder to see which code is "yours" vs external; (3) Pushing back is complex. Use cases: when you want simpler workflows than submodules; when the external code is mostly read (vendor libraries). In modern development, package managers (npm, pip, Maven) are preferred over both submodules and subtree for third-party dependencies.
08
What are GitHub Actions?
GitHub Actions is GitHub's built-in CI/CD automation platform. It allows you to automate workflows (build, test, deploy, notify) triggered by GitHub events (push, pull_request, schedule, release). Workflows are defined in YAML files in .github/workflows/. Key components: Workflow — a YAML file defining the automation; Event — what triggers the workflow (push to main, PR opened, scheduled cron); Job — a unit of work running on a virtual machine (ubuntu-latest, windows-latest, macos-latest); Step — individual task within a job (run a command or use an action); Action — reusable unit of code from the marketplace (actions/checkout, actions/setup-node). Example workflow: on: [push]\njobs:\n test:\n runs-on: ubuntu-latest\n steps:\n - uses: actions/checkout@v4\n - uses: actions/setup-node@v4\n with:\n node-version: "20"\n - run: npm ci\n - run: npm test. Features: parallel jobs, job dependencies, matrix builds (test across multiple OS/versions), artifacts, caching (speed up builds by caching node_modules), secrets management, environment deployments, required status checks for branch protection.
09
What is branch protection in GitHub?
Branch protection rules in GitHub enforce certain conditions before code can be merged into protected branches (usually main or develop). Configured in Settings → Branches → Branch protection rules. Protection options: (1) Require pull request reviews before merging — minimum number of approvals required; (2) Dismiss stale pull request approvals — re-review required if new commits are pushed after approval; (3) Require review from code owners — files with designated owners (CODEOWNERS file) require their approval; (4) Require status checks to pass — specify which CI checks must pass (tests, linting) before merge; (5) Require conversation resolution — all PR comments must be resolved; (6) Require signed commits — commits must have GPG signatures; (7) Include administrators — apply rules even to repo admins; (8) Restrict who can push — only specified teams/people; (9) Require linear history — prevent merge commits (enforce squash or rebase). CODEOWNERS file: define file owners with patterns: src/auth/* @security-team\n*.sql @dba-team. Branch protection is essential for maintaining code quality and preventing direct pushes to production branches.
10
What is the CODEOWNERS file in GitHub?
The CODEOWNERS file (located at /.github/CODEOWNERS, /CODEOWNERS, or /docs/CODEOWNERS) specifies which team members are responsible for specific files or directories in the repo. When a PR modifies a file with a defined owner, GitHub automatically requests a review from those owners. Syntax: same as .gitignore patterns, followed by GitHub usernames or team names. Examples: * @default-owner — default owner for all files; src/auth/ @security-team — auth directory requires security team review; *.sql @data-team — all SQL files require data team; package.json @frontend-lead — specific file. Multiple owners: src/api/ @alice @backend-team — any one of them can approve. Last matching rule wins. Combined with branch protection "Require review from Code Owners": no PR can merge unless all appropriate code owners have approved changes to their files. Benefits: ensures domain experts review changes to critical code (security, database schema, API contracts), reduces oversight of critical areas, documents implicit code ownership. Update as team structure and ownership evolves.
11
How does GitHub handle SSH authentication?
SSH authentication lets you interact with GitHub without typing username/password for every push/pull. Setup: (1) Generate SSH key pair: ssh-keygen -t ed25519 -C "your@email.com" — creates ~/.ssh/id_ed25519 (private key, NEVER share) and ~/.ssh/id_ed25519.pub (public key); (2) Add public key to GitHub: Settings → SSH and GPG keys → New SSH key → paste contents of ~/.ssh/id_ed25519.pub; (3) Add private key to SSH agent: eval "$(ssh-agent -s)"; ssh-add ~/.ssh/id_ed25519; (4) Test connection: ssh -T git@github.com — should print "Hi username! You've successfully authenticated."; (5) Use SSH URLs: git clone git@github.com:user/repo.git or change existing remote: git remote set-url origin git@github.com:user/repo.git. Multiple GitHub accounts: configure ~/.ssh/config with different Host entries using different keys. SSH vs HTTPS: SSH is more convenient (no token re-entry), better for developers. HTTPS with personal access tokens is preferred in CI/CD environments. GitHub also supports Fine-grained personal access tokens for repository-specific permissions.
12
What is git worktree?
Git worktrees allow you to check out multiple branches simultaneously in separate directories, all sharing the same repository data. Each worktree has its own working directory and index. Without worktrees, switching branches means stashing or committing your current work. With worktrees, you can work on multiple branches concurrently. Commands: git worktree add ../hotfix hotfix-branch — check out hotfix-branch into a sibling directory ../hotfix; git worktree add -b new-feature ../feature — create and check out a new branch; git worktree list — list all worktrees; git worktree remove ../hotfix — remove a worktree; git worktree prune — clean up stale worktree metadata. Use cases: (1) Urgent hotfix while mid-feature — add a hotfix worktree, fix, build, test, push — without disturbing feature work; (2) Running tests on another branch while continuing to develop; (3) Comparing behavior between branches; (4) Long-running builds in a separate worktree. Constraints: you can't check out the same branch in two worktrees simultaneously. Each worktree uses minimal extra disk space (shared object store, just different working files).
13
What is git sparse-checkout?
git sparse-checkout allows you to check out only a subset of files from a large repository, ignoring directories you don't need. Introduced for monorepos where a single repo contains many projects and you only need to work on one. Enable: git sparse-checkout init --cone — enable cone mode (faster, simpler pattern matching); git sparse-checkout set packages/frontend — only check out the frontend package; git sparse-checkout add packages/shared — also include shared; git sparse-checkout list — see current patterns; git sparse-checkout disable — revert to full checkout. Cone mode: matches top-level directories (recommended); Non-cone mode: full glob patterns like .gitignore. Combined with shallow clones for maximum speed in CI: git clone --depth=1 --filter=blob:none --sparse url; git sparse-checkout set path/to/service. This is used by large companies with giant monorepos (Google, Meta use custom tools built on this concept). Benefits: much faster checkouts, less disk space, better performance for tools that scan working directory (IDEs, linters). Practically useful when you have a monorepo with 100 packages and only need 3.
14
What is GitHub's issue tracking and project management?
GitHub provides integrated project management tools directly in repositories. Issues are the fundamental unit — used for bug reports, feature requests, tasks, and discussions. Features: markdown formatting, labels (categorize: bug, enhancement, documentation), milestones (group issues for a release/sprint), assignees, linked PRs, reactions. Closing issues from commits: git commit -m "Fix login redirect, closes #123" — when pushed to default branch, automatically closes issue #123. Keywords: closes, fixes, resolves (and variants). Labels: color-coded tags for filtering and triage. Common labels: bug, enhancement, good first issue (for contributors), help wanted, wontfix, duplicate, priority:high. Milestones: group issues/PRs for a sprint or release. Track progress as percentage complete. Projects (GitHub Projects): board and table views with custom fields, status columns, and automation. Can aggregate issues/PRs across multiple repositories. Views: board (Kanban), table (spreadsheet), roadmap. Automation: auto-add new issues to projects, auto-move to "Done" when PR merges. Discussions: forum-style conversations for Q&A, announcements, and general discussions separate from issues.
15
What is git archive?
git archive creates an archive (zip or tar) of a Git tree without including the .git/ history directory. Useful for distributing source code releases without the full Git history. Usage: git archive --format=zip HEAD > release.zip — create a zip of current HEAD; git archive --format=tar.gz --prefix=myproject/ v1.0 > myproject-v1.0.tar.gz — archive a specific tag with a directory prefix inside the archive; git archive HEAD src/ > src.zip — only archive the src/ directory; git archive HEAD:src/ > src.zip — archive the contents of src/ (no src/ prefix in archive). --prefix adds a directory prefix to all files in the archive (common for release tarballs where you want myapp-1.0/ at the root). Remote archive: git archive --remote=https://github.com/user/repo.git HEAD > archive.zip (requires server support). .gitattributes can mark files with export-ignore attribute to exclude from archives (e.g., tests, docs, CI config that aren't needed in a source release).
16
What is git clean?
git clean removes untracked files and directories from the working tree. It is the complement to git restore (which handles tracked files). Since it permanently deletes files, always use -n first. Commands: git clean -n / --dry-run — preview what would be removed (safe — doesn't delete); git clean -f — delete untracked files (required flag — no accidental cleaning); git clean -fd — also delete untracked directories; git clean -fx — also delete ignored files (e.g., node_modules, build output); git clean -fdx — delete untracked files, directories, AND ignored files. Interactive: git clean -i — interactively choose what to remove. -e pattern — exclude pattern from cleaning. Use cases: (1) Clean build artifacts before a fresh build; (2) Remove generated files; (3) Reset to a pristine working state (combine with git reset --hard HEAD). WARNING: deleted files are NOT recoverable through Git — they aren't committed, so no history. Double-check with -n before -f. Comparison: git restore . reverts modified tracked files; git clean -fd removes untracked files; together they fully reset the working directory.
17
What is git notes?
Git notes allow you to attach additional information to commits without altering the commit itself (preserving the SHA). Notes are stored separately from commits in refs/notes/commits. Usage: git notes add -m "Reviewed by Alice" abc1234 — add a note to a commit; git notes show abc1234 — show note for a commit; git log --show-notes — include notes in log output; git notes edit abc1234 — edit a note; git notes remove abc1234 — remove a note. Notes are not pushed by default: git push origin refs/notes/commits — push notes; git fetch origin refs/notes/commits:refs/notes/commits — fetch notes. In ~/.gitconfig: [notes] displayRef = refs/notes/* to auto-display. Use cases: (1) Add build metadata to commits without changing them; (2) Attach code review results; (3) CI systems annotate commits with test results; (4) Link external ticket IDs to historical commits. Git notes are less commonly used than commit messages but are useful when you need to annotate commits after the fact (e.g., add CVE references to old security commits) without rewriting history.
18
What is git filter-branch and BFG Repo Cleaner?
Both tools rewrite Git history to remove files from ALL commits in the repository — essential when you've accidentally committed secrets, large binary files, or sensitive data. git filter-branch (built-in): git filter-branch --force --index-filter "git rm --cached --ignore-unmatch secrets.txt" --prune-empty --tag-name-filter cat -- --all — removes secrets.txt from every commit. Slow and complex. BFG Repo Cleaner (recommended): a faster, simpler Java tool specifically designed for this purpose: bfg --delete-files secrets.txt repo.git — 10-100x faster than filter-branch; bfg --strip-blobs-bigger-than 50M repo.git — remove files larger than 50MB. Git 2.22+ introduced git filter-repo (replaces filter-branch): git filter-repo --path secrets.txt --invert-paths. After rewriting history: (1) Force push all branches: git push origin --force --all; (2) Force push tags; (3) Every collaborator must reclone (their local repos have the old history). If you pushed secrets: ROTATE THEM IMMEDIATELY — even after removal from Git, GitHub caches content for some time, and anyone who already cloned has the old history.
19
What is Conventional Commits?
Conventional Commits is a specification for adding human and machine-readable meaning to commit messages. It provides a structured format that enables automated changelog generation, semantic versioning, and better communication. Format: <type>[optional scope]: <description>\n[optional body]\n[optional footer]. Common types: feat — new feature (triggers minor version bump in semver); fix — bug fix (triggers patch bump); docs — documentation; style — formatting (no logic change); refactor — code restructure (no feature/fix); test — adding/updating tests; chore — build process, tooling; perf — performance improvement; ci — CI configuration. Breaking change: append ! after type (feat!:) or add BREAKING CHANGE: footer — triggers major version bump. Scope (optional): parentheses specifying what was changed: feat(auth): add OAuth login. Examples: feat(cart): add quantity adjustment; fix(api): handle null response from payment service; feat!: redesign authentication API. Tools: commitlint + husky enforce format; semantic-release automates versioning and changelog from commits.
20
What is a monorepo and how does Git support it?
A monorepo (monolithic repository) contains code for multiple projects, packages, or services in a single repository — as opposed to having separate repos per project (polyrepo). Examples: Google (one repo for all code), Facebook, Twitter, Microsoft. Benefits: (1) Atomic commits across projects; (2) Single place for CI/CD; (3) Easy code sharing between packages; (4) Consistent tooling and versioning; (5) Simplified dependency management. Challenges: (1) Repo becomes very large over time; (2) Build/test times increase; (3) CI must be smart about what changed; (4) Permissions (hard to restrict access per service). Git features for monorepos: sparse-checkout — only check out relevant packages; partial clone (--filter=blob:none) — download objects on demand; shallow clone (--depth=1) — faster for CI. Monorepo tools: Turborepo (caching, parallel task execution, affected package detection); Nx (dependency graph, affected builds); Lerna (Node.js packages, versioning); Bazel (Google's build system, used for large monorepos). CI optimization: run tests only for packages affected by a commit using the repo's dependency graph — don't rebuild/test everything on every commit.
21
What is git large file storage (Git LFS)?
Git LFS (Large File Storage) is a Git extension that replaces large files (images, videos, datasets, compiled binaries, design files) with small text pointer files in Git, while storing the actual file contents on a separate LFS server. Git was not designed for large binary files — they inflate repo size permanently (every version of a 50MB Photoshop file is stored forever in .git/objects), make clones slow, and binary diffs are useless. With LFS: the repo contains only the pointer (a few hundred bytes); the actual binary is on the LFS server; Git LFS downloads the file transparently when you check it out. Setup: (1) Install Git LFS: git lfs install; (2) Track file patterns: git lfs track "*.psd" "*.mp4" "design/*.png" — this creates/updates .gitattributes; (3) Commit .gitattributes: git add .gitattributes && git commit; (4) Add files normally: git add assets/logo.psd && git commit. LFS-stored files appear normal in the working directory. Clone with LFS files: automatically handled. GitHub, GitLab, Bitbucket support LFS. Storage is metered separately. Alternative: keep large files out of Git entirely (store in S3, Google Drive, or artifact registries) and reference them by URL.
22
What is semantic versioning (SemVer) and how does it relate to Git tags?
Semantic versioning (SemVer) is a versioning convention that encodes meaning into the version number: MAJOR.MINOR.PATCH (e.g., 2.4.1). Rules: increment MAJOR (2→3) for breaking changes — incompatible API changes; increment MINOR (2.4→2.5) for new backward-compatible features; increment PATCH (2.4.1→2.4.2) for backward-compatible bug fixes. Pre-release: 2.5.0-alpha.1, 2.5.0-rc.1. Build metadata: 2.5.0+build.123. Git tags are the mechanism for marking specific commits as version releases: git tag -a v2.4.1 -m "Release 2.4.1 - Fix login timeout"; git push origin v2.4.1. GitHub creates release packages from tags. Automation: semantic-release npm package analyzes Conventional Commit messages and automatically: determines the next version number; creates the Git tag; generates a changelog; publishes to npm. Integration: CI/CD pipelines trigger on tag creation (on: push: tags: ["v*"] in GitHub Actions) to automatically build and deploy. Consumers use ~ (patch updates) and ^ (minor updates) in package.json, trusting SemVer semantics for safe upgrades.
Deep expertise questions for senior and lead roles.
01
How does Git store objects internally?
Git uses a content-addressable file system. Every object is stored by the SHA-1 (or SHA-256 in newer Git) hash of its content. The .git/objects/ directory stores four types of objects: (1) Blob: stores raw file content. No filename, no metadata — just bytes. Same content = same hash, stored once regardless of how many files reference it; (2) Tree: represents a directory listing — contains references to blobs (files) and other trees (subdirectories) with their names and permissions. A commit's directory structure is a tree of trees; (3) Commit: points to a tree (snapshot), references parent commit(s), and contains author, committer, timestamp, and message. The SHA of a commit covers all of this — any change in history or content creates a different hash; (4) Tag: (annotated tags only) points to another object (usually a commit) with tag name, message, and tagger. Storage on disk: objects are stored in .git/objects/ab/cdef... (first 2 chars = directory, rest = filename). Packfiles (.git/objects/pack/) bundle many objects together with delta compression for efficiency. Understand this and you understand why Git is a "stupid content tracker" — a hash → content mapping with commit graph on top.
02
What is a fast-forward merge and when does it happen?
A fast-forward merge occurs when the target branch has not diverged from the source branch — the target branch pointer is an ancestor of the source branch. In this case, Git simply moves the target branch pointer forward to the latest commit of the source branch. No merge commit is created — the result is a perfectly linear history. When does it happen: you create a feature branch from main, add commits to the feature, and main has no new commits in the meantime. Merging: git checkout main; git merge feature — since main's HEAD is a direct ancestor of feature's HEAD, Git fast-forwards. Prevent fast-forward: git merge --no-ff feature — always creates a merge commit even when fast-forward is possible. This is useful when you want the merge commit to document that feature branches were used (common in GitFlow). Enforce fast-forward only: git merge --ff-only feature — fails with an error if fast-forward isn't possible (the branches have diverged). Used in automation to prevent accidental merge commits. GitHub "Rebase and merge" option performs a fast-forward by rebasing the feature onto main first, then fast-forward merging. The distinction matters for audit trails and understanding project history.
03
What is the difference between origin/main and main in Git?
main is your local branch — the branch pointer in your local repository that moves when you make commits. origin/main is a remote-tracking branch — a read-only local reference that represents the last known state of main on the remote named "origin." It is updated only when you run git fetch, git pull, or git push. The two can diverge: you commit locally → main advances, origin/main stays; someone pushes to remote → after git fetch, origin/main advances, local main stays. Commands: git log main..origin/main — commits on remote not yet local (remote is ahead); git log origin/main..main — commits local not yet on remote (local is ahead); git diff main origin/main — see differences. git branch -vv — shows tracking relationships and ahead/behind counts. git status also shows "Your branch is behind origin/main by 3 commits." Tracking setup: when you git clone, local main automatically tracks origin/main. For new local branches: git push -u origin feature sets the tracking relationship.
04
What is git rerere?
git rerere (reuse recorded resolution) is a Git feature that records how you resolve merge conflicts and automatically re-applies the same resolution next time the same conflict occurs. This is extremely useful in workflows with frequent rebases or long-lived branches. Enable: git config --global rerere.enabled true. How it works: when you resolve a conflict and commit, Git records the conflicted state and your resolution. Next time Git encounters the same conflict (same "left side" and "right side"), it automatically applies the previous resolution. Rerere storage: .git/rr-cache/. Commands: git rerere status — show files rerere will resolve; git rerere diff — show recorded resolutions that will be applied; git rerere forget file.txt — forget a recorded resolution. Use case: you maintain a long-running feature branch that you regularly rebase onto main. The same files often conflict repeatedly. With rerere, Git remembers your resolutions and applies them automatically, turning a manual process into an automated one. Another use: when a merge conflict resolution was wrong — forget it and redo. Rerere data is local (not pushed to remote).
05
What is git replace?
git replace allows you to create transparent substitutions for Git objects — replacing one commit (or tree/blob) with another without modifying the original. Replacements are stored in refs/replace/ and are applied transparently when you traverse history. Usage: git replace old-commit-sha new-commit-sha. After this, any Git command that encounters the old SHA transparently uses the new one. Listing: git replace -l. Remove: git replace -d old-sha. Replacements are not pushed by default: git push origin refs/replace/* to share them. Use cases: (1) Grafting history: attach a new repo's history to an old repo without rewriting all commits — create a new orphan commit that is equivalent to the old final commit and replace it, then the histories appear connected; (2) Fix an old commit non-destructively for everyone — replace the old commit with a corrected version, push the replacement, others fetch it without needing to reset anything; (3) Reconnect truncated history — when you used shallow clones and now need full history, graft can connect them. Git replace is a relatively obscure feature used for advanced history manipulation scenarios.
06
What is the packfile format in Git?
Initially, Git stores each object as a separate loose object file in .git/objects/. As a repo grows, this creates thousands of files (slow for filesystem operations). Packfiles consolidate many objects into two files: .pack (compressed data) and .idx (index for fast lookup). Git creates packfiles: automatically via git gc (garbage collection); during git push/git clone (creates a packfile to transfer efficiently); when triggered by git repack. Delta compression: packfiles store many objects as deltas (differences) from similar objects rather than full copies. Different versions of the same file may differ by a few bytes — the packfile stores one full version and deltas for others. This dramatically reduces storage. Pack commands: git gc — run garbage collection (creates packfiles from loose objects, removes unreachable objects); git gc --aggressive — more thorough repack (slower); git count-objects -v — count loose objects and packed objects; git repack -a -d -f --depth=250 --window=250 — manual repack with aggressive settings. Verification: git verify-pack -v .git/objects/pack/*.idx — inspect pack contents. Pack format version 2 supports SHA-1 and SHA-256.
07
What is the git commit-graph feature?
The commit-graph is a binary cache file (.git/objects/info/commit-graph) that stores a precomputed representation of the commit graph — parent pointers, commit dates, root tree hashes, and generation numbers. It dramatically speeds up Git operations that need to traverse commit history: git log, git merge-base, reachability queries. Without commit-graph, Git must parse commit objects one by one by loading from disk. With commit-graph, the traversal uses the binary cache — 10-100x faster for large repos. Generation numbers: each commit has a generation number (1 + max of parent generation numbers). This allows efficient "is commit A an ancestor of commit B?" queries without full traversal — if A's generation number > B's, A cannot be an ancestor. Enable: git config --global core.commitGraph true; git config --global fetch.writeCommitGraph true — update on fetch. Generate: git commit-graph write --reachable. Update: git commit-graph write --reachable --changed-paths (also stores bloom filters for path-based queries — enables fast git log -- path). This feature is especially impactful for large monorepos with hundreds of thousands of commits.
08
What is Git's garbage collection and how does it work?
Git garbage collection (git gc) cleans up unnecessary files and optimizes the local repository. Operations performed: (1) Pack loose objects: consolidates many small loose object files into packfiles; (2) Expire stale refs: remove reflog entries older than the expiry time (default 90 days for reachable, 30 days for unreachable objects); (3) Prune unreachable objects: remove objects not reachable from any branch, tag, or reflog entry (these are the "dangling" commits from hard resets, dropped branches, etc.); (4) Repack pack files: consolidate multiple pack files. Git runs gc automatically (git gc --auto) when certain thresholds are reached (e.g., too many loose objects). Force run: git gc. Aggressive GC (thorough but slow): git gc --aggressive — better delta compression; only beneficial occasionally. Prune immediately without waiting for expiry: git gc --prune=now. Why unreachable objects are kept temporarily: the 14-day grace period prevents data loss if you're in the middle of an operation (e.g., mid-rebase, stash not yet applied). Server-side: hosting platforms run gc on their infrastructure. git remote prune origin — remove stale remote-tracking references (branches deleted on remote).
09
What are GitHub's repository security features?
GitHub provides multiple layers of security for repositories: (1) Dependabot: automatically scans dependencies for known CVEs and creates PRs to update vulnerable packages. Configured via .github/dependabot.yml. Dependabot alerts notify of vulnerabilities without auto-PRs; (2) Code scanning (CodeQL): GitHub's static analysis engine scans for security vulnerabilities and code quality issues. Configured as a GitHub Action. Supports many languages; (3) Secret scanning: scans pushed code for known secret patterns (API keys, tokens, passwords) from 200+ providers. Automatically notifies providers to revoke compromised tokens. Push protection prevents pushing secrets; (4) GHAS (GitHub Advanced Security): paid tier with additional security features; (5) Signed commits (GPG/SSH): verified commits confirm the committer is who they claim; (6) Security advisories: private namespace to triage, fix, and publish security vulnerabilities; coordinate disclosure; (7) SBOM (Software Bill of Materials): export dependency inventory; (8) Dependency review: in PR diff, shows newly added vulnerable dependencies; (9) Private vulnerability reporting: security researchers can privately report vulnerabilities. Security tab in each repo aggregates all findings.
10
How does GitHub's pull request merge queue work?
Merge queue (GitHub feature) solves a common problem: multiple approved PRs pile up, all passing CI, but each was tested against the main branch from before the others merged. If PR #1 and PR #2 both pass tests independently but conflict when both are merged, you discover the issue after merging. Merge queue addresses this by testing PRs in merge order before actually merging them. How it works: (1) PR is approved and added to the queue; (2) GitHub creates a temporary merge branch combining main + all queued PRs in order; (3) CI runs on this temporary branch; (4) If all checks pass, the actual merge happens; (5) If a PR's check fails, it's removed from the queue; subsequent PRs are retested without it. Benefits: guarantees the state of main after merge matches what was tested; eliminates "merge race conditions"; allows multiple PRs to progress simultaneously; especially valuable for high-throughput teams with many concurrent PRs. Configuration: in branch protection rules, enable "Require merge queue" and configure batch size and timeout. Alternative without merge queue: require PRs to be up to date with main before merging (manually rebase/merge main into PR) — works but creates bottleneck serializing all merges.
11
What is git commit signing with GPG or SSH?
Commit signing cryptographically proves that a commit was authored by a specific person with possession of a private key. Without signing, anyone who knows your email can create commits that appear to be from you (trivially done with git config user.email). GPG signing: (1) Generate GPG key: gpg --full-generate-key; (2) Get key ID: gpg --list-secret-keys --keyid-format=long; (3) Configure Git: git config --global user.signingkey KEY_ID; git config --global commit.gpgsign true; (4) Add public key to GitHub (Settings → SSH and GPG keys → New GPG key); (5) Commits show "Verified" badge on GitHub. SSH signing (simpler, modern approach): (1) Configure: git config --global gpg.format ssh; git config --global user.signingkey ~/.ssh/id_ed25519.pub; (2) Add SSH key to GitHub as "Signing key" (separate from authentication key); (3) Commit: git commit -S -m "message" (or automatic with commit.gpgsign). Verify locally: git verify-commit abc1234. Sign tags: git tag -s v1.0 -m "release". Signing is important for audited environments, open-source release management, and proving supply chain integrity.
12
What is the difference between git fetch --prune and git remote prune?
Both commands remove stale remote-tracking references (like origin/deleted-branch) that no longer exist on the remote, but they operate differently: git fetch --prune (or git fetch -p) fetches all updates from the remote AND simultaneously removes any remote-tracking references that no longer exist on the remote. One command does both. Set as default: git config --global fetch.prune true — prune automatically on every fetch. git pull --prune — same but also merges. git remote prune origin only prunes stale remote-tracking refs for the specified remote — it does NOT fetch new changes. Use it when you want to clean up without pulling. Why pruning is needed: when a remote branch is deleted (after merging a PR), your local origin/feature-x reference remains. Over time, you accumulate many stale references. git branch -a shows them all. Check what would be pruned: git remote prune origin --dry-run. After pruning, git branch -vv shows local branches whose tracking is gone: [origin/feature: gone] — these can be safely deleted: git branch -d feature. Automate: git config --global fetch.prune true in your global gitconfig.
13
How do you handle a monorepo with different services needing independent versioning in Git?
Managing a monorepo where different services need independent versioning requires a combination of tagging conventions, tooling, and CI/CD automation. Strategies: (1) Namespaced tags: instead of v1.0, use service-name/v1.0: api/v2.3.1, frontend/v1.8.0, worker/v3.0.0. Each service has its own tag namespace and version history. CI triggers on matching tag patterns; (2) Path-based CI triggers: only run a service's pipeline when its files change: in GitHub Actions: on: push: paths: ["api/**", "packages/shared/**"]; (3) Monorepo tools: Nx tracks which projects are "affected" by a commit based on the dependency graph — only builds/tests/deploys affected projects; Turborepo does the same with caching; (4) Independent CHANGELOG.md per service generated from commits touching that service's path using conventional commits with scopes; (5) Semantic-release with path filters: run semantic-release per service directory, each managing its own version; (6) Lerna (for npm packages) — manages independent versioning of packages in a monorepo, handles cross-package dependencies, publishes to npm. The key challenge is mapping code commits to service versions without conflating unrelated changes across services.