Is Git installed?
Prints the version. “command not found”? See the Academy lesson “Install Git”.
$ git --version
// Copy & Paste
Every snippet comes with a plain explanation and its risk level. Copy responsibly.
122 commands
Install Git, create a repository, make the first commit and put it on GitHub.
Prints the version. “command not found”? See the Academy lesson “Install Git”.
$ git --version
With winget, built into Windows 10 and 11. Open a new terminal afterwards.
$ winget install --id Git.Git -e --source winget
Installs Apple's command line tools, Git included. Or: brew install git.
$ xcode-select --install
Fedora: sudo dnf install git. Arch: sudo pacman -S git.
$ sudo apt install git
Turns the current folder into a Git repository. Once per project, at its root.
$ git init
Add a .gitignore first, so node_modules and .env don't end up in it.
$ git add .$ git commit -m "chore: initial commit"
Create an empty repository on GitHub first. -u links main to origin for the next pushes.
$ git remote add origin git@github.com:OWNER/REPO.git$ git push -u origin main
Copies the whole repository and its history into a new folder.
$ git clone git@github.com:OWNER/REPO.git
Downloads only the last commit: much faster for big repositories. Get the rest later with git fetch --unshallow.
$ git clone --depth 1 https://github.com/owner/repo.git
Downloads one branch and checks it out, ignoring the others.
$ git clone -b dev --single-branch https://github.com/owner/repo.git
Set Git up once, save time forever.
Written into every commit. Drop --global to set it for one repository only.
$ git config --global user.name "Your Name"$ git config --global user.email "you@example.com"
Used by git init for every new repository.
$ git config --global init.defaultBranch main
No more “Merge branch 'main' of …” commits cluttering the history.
$ git config --global pull.rebase true
Then just type git lg.
$ git config --global alias.lg "log --oneline --graph --decorate --all"
Every setting and the file it comes from. Great to debug a weird behaviour.
$ git config --list --show-origin
Commit messages and interactive rebases open in VS Code instead of Vim. --wait makes Git wait until you close the tab.
$ git config --global core.editor "code --wait"
Converts to CRLF when you check out, back to LF when you commit. On macOS/Linux use input.
$ git config --global core.autocrlf true
Day-to-day commands to see where you stand and prepare a commit.
Short status with the current branch and how far ahead/behind the remote you are.
$ git status -sb
Review each change interactively and pick what goes in the commit.
$ git add -p
Shows the staged changes. Run it before every commit, your reviewers will love you.
$ git diff --staged
Git records the rename directly. Use -f to change only upper/lower case.
$ git mv old-name.ts new-name.ts
Deletes it from disk and stages the deletion. --cached to keep it on disk.
$ git rm path/to/file
Files changed and number of lines added / removed, without the details.
$ git diff --stat
-a stages every tracked file that changed. New (untracked) files are NOT included.
$ git commit -am "fix: message"
Instead of whole lines, shows exactly which words changed. Great for text and docs.
$ git diff --word-diff
Go back on a change, a commit or a push — from the gentlest to the most brutal.
Change the message or add forgotten files. Rewrites the commit: avoid if already pushed.
$ git commit --amend
The commit disappears, its changes stay staged. Perfect to redo a commit.
$ git reset --soft HEAD~1
Puts the file back to its last committed state. Uncommitted changes are lost for good.
$ git restore path/to/file
Removes the file from the next commit, keeps your changes.
$ git restore --staged path/to/file
Creates a new commit that does the opposite. History stays intact: the team-friendly undo.
$ git revert <commit>
Throws away local commits AND uncommitted changes. Create a backup branch first.
$ git reset --hard origin/main
Replaces the file in your working copy with its version from that commit. Commit it to keep it.
$ git restore --source=<commit> -- path/to/file
Creates a commit that cancels the merge. -m 1 keeps the main line (the branch you merged into).
$ git revert -m 1 <merge-hash>
Removes every file from the index. Your changes stay on disk.
$ git restore --staged .
Create, switch, clean up and move work between branches.
The modern equivalent of git checkout -b.
$ git switch -c feature/my-feature
Find the branches you can safely delete.
$ git branch --merged main
-d refuses if the branch is not merged. -D forces it (and you will end up on our Emergency page).
$ git branch -d feature/old
Local rename only. Push the new name and delete the old one on the remote if needed.
$ git branch -m new-name
Replays your commits on the latest main. Rewrites your branch: only on branches you own.
$ git fetch origin$ git rebase origin/main
Applies the changes of one specific commit here.
$ git cherry-pick <commit>
Like cd - in a terminal: jumps back to the branch you were on before.
$ git switch -
Remote ones appear as remotes/origin/… Run git fetch first to be up to date.
$ git branch -a
Finds the branch you worked on yesterday among fifty.
$ git branch --sort=-committerdate
Understand what happened, when, and who did it.
The best way to understand what is going on before fixing anything.
$ git log --oneline --graph --decorate --all
Every position HEAD went through, even after resets and deleted branches.
$ git reflog
Spoiler: it was probably you. -w ignores whitespace, -C follows moved code.
$ git blame -w -C path/to/file
Lists commits that added or removed this string.
$ git log -S "functionName" --oneline
Binary search through history. Finish with git bisect reset.
$ git bisect start$ git bisect bad$ git bisect good <commit>
Message, author and full diff of one commit.
$ git show <commit>
Three dots: only what the branch added since it left main. Exactly what a PR shows.
$ git diff main...feature/my-feature
--follow keeps going through renames.
$ git log --follow --oneline -- path/to/file
Prints it without touching your working copy. Redirect with > to save it.
$ git show <commit>:path/to/file
Also works with --until, --grep="fix" to search messages.
$ git log --author="Alice" --since="2 weeks ago" --oneline
Number of commits per author. Add --no-merges to skip merge commits.
$ git shortlog -sn
Fast, ignores node_modules and other ignored files. Add a commit to search an old version.
$ git grep -n "TODO"
What you have and the server doesn't (after a git fetch).
$ git log origin/main..HEAD --oneline
The commits you will get with your next pull, without pulling yet.
$ git fetch && git log HEAD..origin/main --oneline
Every commit whose message contains the word. Add -i to ignore case.
$ git log --oneline --grep="login"
Commits of the last two weeks. Also works with “yesterday”, “2026-11-01”…
$ git log --since="2 weeks ago" --oneline --no-merges
Blame only lines 10 to 30, with the commit and author of each.
$ git blame -L 10,30 path/to/file
Talk to GitHub / GitLab: fetch, push, remotes and tags.
Lists the remotes and their URLs.
$ git remote -v
After renaming a repo or switching from HTTPS to SSH.
$ git remote set-url origin git@github.com:OWNER/REPO.git
Tags are not pushed by default: push them explicitly.
$ git tag -a v1.2.0 -m "v1.2.0"$ git push origin v1.2.0
Keeps a linear history by replaying your local commits on top.
$ git pull --rebase
Publishes the current branch and links it to the remote.
$ git push -u origin HEAD
Overwrites the remote only if nobody pushed since your last fetch. Never use plain --force.
$ git push --force-with-lease
Cleans up origin/* references that no longer exist on the server.
$ git fetch --prune
The branch disappears for everyone. Its commits stay if merged elsewhere.
$ git push origin --delete feature/old
upstream = the original project, origin = your fork.
$ git remote add upstream git@github.com:ORIGINAL/REPO.git$ git fetch upstream$ git rebase upstream/main
Replace 123 with the PR number.
$ git fetch origin pull/123/head:pr-123$ git switch pr-123
Refuses to create a merge commit: either it simply moves your branch forward, or it stops and lets you decide.
$ git pull --ff-only
Handy after forking: the original becomes upstream, your fork becomes origin.
$ git remote rename origin upstream
Open, update, review and clean up a pull request (or a GitLab merge request).
Always from an up-to-date main, so the PR only contains your work.
$ git switch main$ git pull$ git switch -c feat/my-change
Git prints a link to create the pull request. Then a plain git push updates it.
$ git push -u origin HEAD
--fill takes the title and description from your commits. Add --draft for a draft PR.
$ gh pr create --fill
Works for PRs from forks too.
$ gh pr checkout 123
Your open PRs, the reviews waiting for you, and CI results.
$ gh pr status$ gh pr checks
Replace 42 with the merge request number.
$ git fetch origin merge-requests/42/head:mr-42$ git switch mr-42
The three dots compare with where your branch started: exactly the PR's diff.
$ git diff --stat origin/main...HEAD
Should list only your work. Anything else? Your branch started from the wrong place.
$ git log --oneline origin/main..HEAD
Brings the latest main into your branch. No force push.
$ git fetch origin$ git merge origin/main$ git push
Replays your commits on the latest main: linear history, needs a force push.
$ git fetch origin$ git rebase origin/main$ git push --force-with-lease
Folds your fixup! commits into the ones they fix.
$ git rebase -i --autosquash origin/main$ git push --force-with-lease
Needs the original added as upstream (git remote add upstream …).
$ git fetch upstream$ git switch main$ git merge --ff-only upstream/main$ git push origin main
After a squash merge, -d refuses: use -D once you checked the PR is merged.
$ git switch main$ git pull$ git branch -d feat/my-change
Compares two versions of a PR branch commit by commit: perfect for reviewers.
$ git range-diff origin/main old-head new-head
Put unfinished work aside without committing it.
Saves changes, including untracked files (-u), with a message you will thank yourself for.
$ git stash push -u -m "wip: something"
Applies the last stash and removes it. On conflict, the stash is kept.
$ git stash pop
stash@{0} is the most recent.
$ git stash list
The full diff of the stash, without applying it.
$ git stash show -p stash@{0}
Creates a branch where the stash was made and applies it there: no conflict with what changed since.
$ git stash branch fix/from-stash stash@{0}
Gone from the list. Recoverable for a while with git fsck if it was a mistake.
$ git stash drop stash@{0}
Remove what Git should not track or keep.
Dry run: shows what git clean would delete, deletes nothing.
$ git clean -nd
Irreversible: Git never knew these files, so it cannot bring them back. Run the dry run first.
$ git clean -fd
Removes it from Git, keeps it on disk. Add it to .gitignore right after.
$ git rm -r --cached node_modules
Prints the .gitignore file and line that match. No output: the file is not ignored.
$ git check-ignore -v path/to/file
Capital X: only files listed in .gitignore. Replace -n with -f to really delete.
$ git clean -ndX
An interactive menu: you choose what to delete. Safer than clean -fd.
$ git clean -i
Clean up commits before sharing them: squash, reword, reorder. Only on commits not pulled by others.
pick, reword, squash, fixup, drop, or reorder lines. Only on commits not shared yet.
$ git rebase -i HEAD~5
The fix is merged into the target commit automatically during the rebase.
$ git commit --fixup <commit>$ git rebase -i --autosquash main
Keeps the message. Not pushed yet only.
$ git add forgotten-file.ts$ git commit --amend --no-edit
Every change since the branch left main, in one commit.
$ git reset --soft $(git merge-base HEAD main)$ git commit -m "feat: my feature"
Takes the commits of my-branch after old-base and replays them on main.
$ git rebase --onto main old-base my-branch
Git remembers how you resolved a conflict and replays it next time.
$ git config --global rerere.enabled true
Worktrees, submodules, Git LFS, hooks, signing and maintenance.
A second folder with another branch, sharing the same repository. No stash needed.
$ git worktree add ../hotfix main$ git worktree list
Refuses if it has uncommitted changes (unless --force).
$ git worktree remove ../hotfix
Pins a specific commit of the other repository. Commit .gitmodules too.
$ git submodule add https://github.com/OWNER/lib.git vendor/lib
Or clone with --recurse-submodules from the start.
$ git submodule update --init --recursive
The repository stores a pointer; the file lives on the LFS server.
$ git lfs install$ git lfs track "*.psd"$ git add .gitattributes
Hooks in .githooks/ (versioned) instead of .git/hooks/ (not versioned).
$ git config core.hooksPath .githooks
Only tracked files, without .git.
$ git archive --format=zip -o release.zip HEAD
gc compresses the database. Never use --prune=now while recovering lost commits.
$ git count-objects -vH$ git gc
Add the key as a “Signing key” on GitHub to get the Verified badge.
$ git config --global gpg.format ssh$ git config --global user.signingkey ~/.ssh/id_ed25519.pub$ git config --global commit.gpgsign true
Finds corrupt or missing objects, and lost commits (dangling).
$ git fsck --full
Git tests commits by binary search with your command, then names the first bad one. Finish with git bisect reset.
$ git bisect start HEAD v1.0.0$ git bisect run npm test
The other folders disappear from your disk (not from the repository). git sparse-checkout disable brings everything back.
$ git sparse-checkout set apps/web packages/ui