LearnThatStack Ace your next interview

Git & Version Control

Commands for Git 2.23+ Checked Sep 2026 114 commands

The model

  • A commit is a full snapshot of the project plus a pointer to its parent. Its hash is its name. History is a graph of commits.
  • A branch is a movable pointer to one commit. HEAD points at the branch you're on, or straight at a commit when it's detached.
  • git commit saves what's staged, not what's on disk.
  • Every clone holds the whole history, so you can commit, branch and read the log offline. A server is just another copy.
  • Anything committed can come back through git reflog, for 30 days by default. Edits you never committed can't.
Working diryour filesStaging (index)the next commitLocal repo.git, HEADRemoteoriginOUTaddcommitpushBACKfetchpull = fetch + updateswitch <branch>restore --stagedrestore
OUT arrows send work toward the team, and BACK arrows bring it to you. The red one overwrites edits you haven't committed.

Under the hood

what Git actually stores

What's in .git

committreeblobblob
objectsA commit points to one root tree. The tree names each file's blob by hash.
Read any commit, tree or blobgit cat-file -p <hash>
Show the tree the latest commit points togit cat-file -p HEAD^{tree}
List every file in the index with its blob hashgit ls-files --stage
Pack loose objects into a packfilegit gc
Delete unreachable commits nowgit reflog expire --expire-unreachable=now --allgit gc --prune=now
Get the hash Git would give a filegit hash-object <file>

TipGit names each object by the hash of its content. So an unchanged file keeps the same blob in every commit.

Stage & commit

turn edits into commits

Stage and commit

Stage one file, or everythinggit add <file>git add -A
Stage part of a filegit add -p
Commit what's stagedgit commit -m "msg"
Stage all tracked changes and commitgit commit -am "msg"
See edits you haven't stagedgit diff
See what's stagedgit diff --staged
Unstage a file, keep the editgit restore --staged <file>

Tipadd -p asks about each change in turn, so you can commit the fix and leave out the debug print.

Trapgit add stages the file as it is at that moment. Later edits stay out of the commit until you add the file again.

Fix the last commit

Change the last commit's message or filesgit commit --amend
Add staged changes, keep the messagegit commit --amend --no-edit

TrapAmending makes a new commit with a new hash. If you already pushed, force-push it only to a branch that's yours alone.

Ignore files

Stop tracking a file, keep it on diskgit rm --cached <file>
Find which rule ignores a filegit check-ignore -v <file>
Ignore editor junk in every repogit config --global core.excludesfile ~/.gitignore_global

Trap.gitignore only affects untracked files. A tracked file stays tracked until you git rm --cached it.

Undo & rescue

take a change back, or get lost work back

Undo commits with reset

git reset --soft HEAD~1Undo the last commit, keep its changes staged
git reset HEAD~1Undo the last commit, keep its changes unstaged
git reset --hard HEAD~1Undo the last commit and discard all changes
reset to the target commitleft aloneuncommitted edits lost

TipWith no flag, reset uses --mixed. Only --hard touches your files.

Throw away local edits

Discard edits to one filegit restore <file>
Discard all unstaged editsgit restore .
Discard staged and unstaged editsgit restore --staged --worktree .
List untracked files before you delete themgit clean -nd
Delete untracked files and foldersgit clean -fd

TrapThe reflog only tracks commits. Uncommitted work you delete here is gone for good.

Get lost work back

List everywhere HEAD has beengit reflog
Put a branch on a lost commitgit switch -c rescue <hash>
Undo a rebase that's still in progressgit rebase --abort
Undo a rebase you just finishedgit reset --hard ORIG_HEAD

TipThe reflog keeps a lost commit for 30 days by default. After that, git gc can delete it.

TrapThe reflog lives only in your clone. A fresh clone can't rescue anything.

Branch & stash

hold two lines of work at once

Branches

List local and remote branchesgit branch -a
Create a branch and switch to itgit switch -c <name>
Switch to a branchgit switch <name>
Go back to the previous branchgit switch -
Rename the current branchgit branch -m <new>
Delete a merged branchgit branch -d <name>
Force-delete an unmerged branchgit branch -D <name>

TrapIf you commit on a detached HEAD, no branch holds the new commits. Run git switch -c <name> before you switch away.

Stash unfinished work

Set aside changes to tracked filesgit stash
Stash untracked files toogit stash -u
List your stashesgit stash list
Apply the newest stash and drop itgit stash pop
Apply a stash and keep itgit stash apply
Delete a stashgit stash drop stash@{0}
Work on another branch without stashinggit worktree add ../app-hotfix hotfix/loginA second checkout of the same repo, in its own folder. No stash, no second clone.

TrapA stash isn't tied to a branch. Pop it on the wrong branch and it can apply cleanly in the wrong place.

Merge & rebase

join branches and settle conflicts

Merge or rebase

ABCMDE
mergeHistory as it happened, plus merge commit M.
ABCD′E′DE
rebaseA straight line, but D and E get new hashes.
Merge a branch into yoursgit merge <branch>
Replay your commits on top of a branchgit rebase <branch>

TrapNever rebase commits someone else may have pulled. Their next pull brings the old copies back as duplicate commits.

Pick a PR merge strategy

Land a PR as one commitSquash merge. It's the usual default, because one revert undoes the whole PR.
Keep every commit and show where the branch joinedMerge commit. Pick it when each commit means something on its own.
Keep every commit in one straight lineRebase merge. Use it only for curated commits. CI usually tested the tip, not each replayed commit.

TrapSquash leaves the branch's own commits off main, so its step-by-step history lives only in the pull request.

Tidy commits before a PR

Make a backup branch firstgit branch backup-tip
Squash, reorder or reword commitsgit rebase -i <base>
Mark a fix for an earlier commitgit commit --fixup <hash>
Squash the fixup commits into placegit rebase -i --autosquash <base>

TipAfter squashing, git diff backup-tip prints nothing if the files came out identical.

Resolve a conflict

<<<<<<< HEADyoursyour code=======their code>>>>>>> featuretheirs
List the files with conflictsgit status
Mark a file resolvedgit add <file>
Continue the merge or rebasegit merge --continuegit rebase --continue
Give up and go backgit merge --abortgit rebase --abort
Replay a conflict fix you made beforegit config --global rerere.enabled true

TrapIn a rebase, ours and theirs swap. HEAD is the branch you're replaying onto, so your own commit is "theirs".

Keep a long-lived branch in sync

Count commits behind and ahead of maingit rev-list --left-right --count origin/main...HEAD
Test-merge main without touching your files (2.38+)git merge-tree --write-tree origin/main HEAD
Pick a branching modelGit Flow keeps develop beside main. GitHub flow and trunk-based keep only main, with short branches.

TipSync with main every day, not at the end. The longer you wait, the worse the conflicts. Better still, land small pieces behind a feature flag.

TrapA clean merge can still break the build. A rename on main and a new caller on your branch never touch the same lines.

Remotes, push & pull

move commits between you and the team

Clone and remotes

Copy a repo with all its historygit clone <url>
Connect a repo you made with git initgit remote add origin <url>
Show remotes and their URLsgit remote -v
Switch the remote from HTTPS to SSHgit remote set-url origin <url>
See where fetch puts remote branchesgit config --get-all remote.origin.fetch
Delete a branch on the remotegit push origin --delete <branch>

TipGit runs on your machine and works offline. GitHub hosts a copy and adds pull requests, review and CI.

Forks and server repos

Propose changes to a repo you can't push toFork it on the host and clone your fork. Then open a pull request against the upstream.
Track the original repogit remote add upstream <url>
Bring your fork up to dategit fetch upstreamgit merge upstream/mainRun it on main, then push to your fork. A fork never updates itself.
Make a server repo with no working treegit init --bare app.git

Fetch, pull, push

Download new commits, leave your branches alonegit fetch
Fetch, then fast-forward your branchgit pullIt stops if your branch has diverged. git pull --no-rebase merges instead.
Fetch and rebasegit pull --rebase
Push the current branchgit push
Push a new branch and set its upstreamgit push -u origin <branch>
Tie an existing branch to its remotegit branch -u origin/<branch>
Count commits ahead of and behind your upstreamgit status -sb
Force-push your own branchgit push --force-with-lease
Force-push only if your branch has what you fetched (2.30+)git push --force-with-lease --force-if-includes

TipCommit or stash before you pull. If the merge conflicts, git merge --abort may not bring your uncommitted edits back.

TrapPlain --force overwrites the remote branch, teammates' commits included. --force-with-lease refuses if that branch moved since your last fetch.

Investigate

find the commit that did it

Find a bad commit with bisect

1234goodfirst badbad

Each test halves the suspects. 16 commits take 4 tests, and 10,000 take about 14.

Start with a bad and a good commitgit bisect start <bad> <good>
Mark the commit Git checked outgit bisect goodgit bisect bad
Let a script test each commitgit bisect run ./repro.sh
Skip a commit that won't buildgit bisect skip
Finish and return to where you startedgit bisect reset

TipBisect assumes good stays good until one commit flips it. A flaky test breaks that.

Read history

See who last changed each linegit blame <file>
Find the commit that added or removed a stringgit log -S "text"
Find commits whose diff matches a regexgit log -G "regex"
Follow one file across renamesgit log --follow -- <file>
Draw every branch as a graphgit log --oneline --graph --all
See what one commit changedgit show <hash>

TipGit stores no renames. It guesses them at diff time from content similarity, 50% by default.

TrapA rename plus heavy edits in one commit can fall under the threshold and lose the link. Move the file in one commit, then edit it.

Big repos & safety

what senior interviews add

Big repos

Clone only the latest commit, for CIgit clone --depth=1 <url>
Clone history, fetch file contents on demandgit clone --filter=blob:none <url>
Check out only some foldersgit sparse-checkout set --cone <dir>
Make git status fast in a huge repogit config core.fsmonitor true
Keep large binaries out of historygit lfs track "*.psd"

TrapA shallow clone has no history past the cutoff. blame and bisect can't see it, and a rebase across it fails.

Share code between repos

Pin another repo at one commitgit submodule add <url> <dir>Clones then need --recurse-submodules.
Copy another repo's files in as commitsgit subtree add --prefix=<dir> <url> main --squashA plain clone gets everything, but your repo grows.

TrapMoving a submodule to a new commit is a change you must commit. Forget it and teammates keep building the old version.

Hooks and signed commits

Skip the client hooks on one commitgit commit --no-verifyAnyone can, so client hooks give feedback, not enforcement.
Enforce a rule nobody can skipUse a pre-receive hook on the server. On GitHub, use protected branches.
Sign every commitgit config --global commit.gpgsign true
Check a commit's signaturegit log --show-signature -1

TipA signature proves which key made a commit, not who owns that key.

Clean up a leaked secret

Rotate the secret firstTreat it as public. Clones, CI logs and backups may already hold it.
Scrub the secret from every commitgit filter-repo --replace-text replacements.txt
Add back the remote filter-repo removedgit remote add origin <url>
Publish the rewritten historygit push --force --mirror

TipThe host still serves the old commit by its hash. Ask your provider to garbage-collect it.

TrapTell everyone to re-clone. Anyone who pulls instead merges the old history back in, secret included.

Found this useful? Pass it on.
Pro · $10/mo

This sheet stays free. With Pro, you can:

  • Every answer on this sheet. Read the full answer to all 54 questions it links, not just the 7 free ones.
  • My sheet. The cards you star and the ones you miss in Quiz me, from every sheet, on one page to quiz and print.
  • PDFs. Download this sheet and My sheet, laid out for A4, with every question a link to its answer.
  • The rest of Pro. Every answer in the question library, and 1,000 AI credits a month.