Setup and Config
git config --global user.name "Your Name" # set name for all repos
git config --global user.email "you@example.com" # set email for all repos
git config --global core.editor "code --wait" # VS Code as default editor
git config --global init.defaultBranch main # default branch name for new repos
git config --global pull.rebase false # merge strategy on pull (safe default)
git config --list # show all config values
git config --list --show-origin # show values + which file they come from
git init # init repo in current directory
git init my-project # init repo in a new folder
The Basic Cycle
git status # what's staged, modified, untracked
git status -s # short format: M=modified, A=added, ??=untracked
git add file.txt # stage one file
git add src/ # stage an entire directory
git add -p # interactively stage hunks (patch mode)
git add . # stage everything in current directory
git commit -m "feat: add login" # commit with inline message
git commit # open editor for multi-line message
git commit -am "fix typo" # stage tracked files + commit in one step
git log # full commit history
git log --oneline # one commit per line
git log --oneline --graph --all # visual branch graph
git log -n 5 # last 5 commits
git log --author="Alice" # commits by author
git log --since="2 weeks ago" # commits since date
git log --stat # files changed per commit
git diff # unstaged changes vs last commit
git diff --staged # staged changes vs last commit
git diff HEAD~1 # working tree vs one commit back
git diff main..feature # all differences between two branches
git diff --stat main..feature # just the file summary
.gitignore
# .gitignore syntax
node_modules/ # ignore a directory
*.log # ignore all .log files
!important.log # un-ignore a specific file
.env # ignore env files
dist/ # ignore build output
**/*.test.js # nested glob — ignore all test files anywhere
git check-ignore -v file.txt # explain why a file is ignored
git rm --cached file.txt # stop tracking a file already committed (keep it on disk)
git rm --cached -r node_modules/ # stop tracking a directory already committed
Branching
git branch # list local branches
git branch -a # list local + remote branches
git branch feature/auth # create a branch (stay on current)
git switch feature/auth # switch to branch (modern)
git switch -c feature/auth # create + switch (modern)
git checkout feature/auth # switch (classic, still works)
git checkout -b feature/auth # create + switch (classic)
git branch -d feature/auth # delete merged branch (safe)
git branch -D feature/auth # force-delete even if unmerged
git merge feature/auth # merge feature into current branch
git merge --no-ff feature/auth # always create a merge commit (keep history)
git merge --squash feature/auth # squash all changes into staging, then commit manually
git merge --abort # bail out of a conflicted merge
Conflict Resolution
git status # see which files conflict
# edit conflicted files, remove conflict markers, then:
git add resolved-file.txt
git commit # complete the merge
Remotes
git clone https://github.com/user/repo.git # clone a repo
git clone https://github.com/user/repo.git my-dir # clone into a specific folder
git clone --depth 1 https://github.com/user/repo # shallow clone (latest snapshot only)
git remote -v # list remotes with URLs
git remote add origin https://github.com/user/repo.git # add a remote
git remote rename origin upstream # rename a remote
git remote remove upstream # remove a remote
git remote set-url origin https://new-url.git # change remote URL
git fetch origin # download changes, don't merge
git fetch --all # fetch all remotes
git fetch --prune # fetch + remove stale remote-tracking branches
git pull # fetch + merge (uses configured strategy)
git pull --rebase # fetch + rebase (cleaner history)
git pull origin main # explicitly pull from origin/main
git push origin feature/auth # push a branch to remote
git push -u origin feature/auth # push + set upstream (use bare "git push" after)
git push # push current branch to its tracked upstream
git push origin --delete old-branch # delete a remote branch
git push --force-with-lease # safe force-push (fails if remote has new commits)
GitHub PR Flow
| Step | Command |
|---|---|
| Create feature branch | git switch -c feat/my-feature |
| Work and commit | git commit -am "..." |
| Push and set upstream | git push -u origin feat/my-feature |
| Open PR | GitHub UI or gh pr create |
| Pull in review feedback | git commit, git push |
| Sync with main before merge | git fetch origin && git rebase origin/main |
| After merge, clean up | git switch main && git pull && git branch -d feat/my-feature |
# GitHub CLI shortcuts (install: brew install gh)
gh pr create --title "feat: add auth" --body "Adds JWT login"
gh pr list
gh pr checkout 42 # check out PR #42 locally
gh pr merge 42 --squash # squash-merge PR #42
Rebasing
git rebase main # replay current branch on top of main
git rebase --abort # bail out, restore original state
git rebase --continue # after resolving conflicts, continue
git rebase --skip # skip the current conflicting commit
git rebase -i HEAD~4 # interactive rebase: last 4 commits
# In the editor, per-commit commands:
# pick = keep as-is
# reword = keep changes, edit message
# edit = pause to amend the commit
# squash = meld into previous commit, combine messages
# fixup = meld into previous commit, discard this message
# drop = delete the commit entirely
git rebase -i --root # interactive rebase from the very first commit
The golden rule: never rebase commits that exist on a shared remote branch. Rebase rewrites history — force-pushing rebased commits onto a shared branch breaks everyone else’s clone. Rebase freely on local or personal branches; merge on shared ones.
Stashing
git stash # stash tracked modifications
git stash -u # also stash untracked files
git stash push -m "WIP: auth form" # stash with a description
git stash list # show all stashes
git stash pop # apply latest stash + drop it
git stash apply stash@{2} # apply a specific stash, keep it in list
git stash drop stash@{0} # delete a specific stash
git stash clear # delete all stashes
git stash branch fix/continue # create a branch from a stash (avoids conflicts)
git stash show -p stash@{0} # inspect stash contents as a diff
Undoing Changes
| I want to… | Command |
|---|---|
| Discard unstaged edits in a file | git restore file.txt |
| Discard all unstaged edits | git restore . |
| Unstage a file (keep edits) | git restore --staged file.txt |
| Undo last commit, keep staged | git reset --soft HEAD~1 |
| Undo last commit, keep unstaged | git reset --mixed HEAD~1 |
| Nuke last commit + all changes | git reset --hard HEAD~1 |
| Safely undo a pushed commit | git revert HEAD |
| Amend the last commit message | git commit --amend -m "new msg" |
| Add a forgotten file to last commit | git add file.txt && git commit --amend --no-edit |
git restore file.txt # discard unstaged changes to a file
git restore . # discard ALL unstaged changes
git restore --staged file.txt # unstage (move out of index), keep edit
git restore --staged . # unstage everything
git reset --soft HEAD~1 # undo commit, changes land back in staging
git reset --mixed HEAD~1 # undo commit, changes land in working tree (default)
git reset --hard HEAD~1 # undo commit, changes are gone (destructive)
git reset --hard origin/main # hard-reset local branch to match remote
git revert HEAD # create a new commit that undoes HEAD (safe for shared history)
git revert abc1234 # revert any commit by hash
git revert HEAD~3..HEAD # revert a range (newest first)
git revert --no-commit HEAD~3..HEAD # stage the reversals without committing yet
git commit --amend -m "better msg" # rewrite last commit message
git commit --amend --no-edit # tack staged changes onto last commit, same message
Recovering Lost Work (Reflog)
Reflog is your safety net. Every time HEAD moves, Git logs it — even after a hard reset or deleted branch.
git reflog # full log of HEAD movements with short hashes
git reflog show feature/auth # reflog for a specific branch
git checkout abc1234 # inspect a "lost" commit (detached HEAD)
git switch -c recovery/auth abc1234 # recreate a branch from a lost commit hash
git reset --hard abc1234 # restore current branch to that point (destructive)
git cherry-pick abc1234 # apply a single lost commit onto current branch
Reflog entries expire after 90 days by default. Act quickly after an accidental --hard reset.
Tags
git tag # list all tags
git tag v1.0.0 # create lightweight tag at HEAD
git tag -a v1.0.0 -m "Release 1.0" # annotated tag (includes tagger info + message)
git tag -a v1.0.0 abc1234 -m "..." # tag a specific commit
git show v1.0.0 # show tag details + tagged commit
git push origin v1.0.0 # push a single tag
git push origin --tags # push all tags
git push origin --follow-tags # push commits + annotated tags (recommended)
git tag -d v1.0.0 # delete a local tag
git push origin --delete v1.0.0 # delete a remote tag
Inspecting History
git show HEAD # show last commit diff + metadata
git show abc1234 # show any commit by hash
git show HEAD:src/app.ts # show a file as it was at that commit
git blame file.txt # who changed each line, and when
git blame -L 10,25 file.txt # blame only lines 10–25
git blame -w file.txt # ignore whitespace changes
git log --oneline --graph --all --decorate # pretty branch graph
git log --oneline --follow -- oldname.ts # follow a renamed file
git log -S "functionName" # commits that added/removed the string
git log --diff-filter=D -- deleted-file.txt # find when a file was deleted
git bisect start # begin binary search for a bad commit
git bisect bad # mark current commit as bad
git bisect good v1.2.0 # mark a known-good commit
# Git checks out commits in between; keep marking good/bad until it narrows down
git bisect reset # end the bisect session
Useful One-Liners
# Discard all local changes (staged and unstaged)
git restore . && git restore --staged .
# Unstage everything
git restore --staged .
# Edit the last commit message
git commit --amend -m "fix: correct typo in header"
# Push to the same remote branch without typing the full ref
git push -u origin HEAD
# Pull with rebase to keep a linear history
git pull --rebase origin main
# See just the files changed in the last commit
git diff --name-only HEAD~1 HEAD
# Count commits since branching from main
git rev-list --count main..HEAD
# Clean up deleted remote branches locally
git remote prune origin
# Remove all untracked files and directories (dry-run first)
git clean -nd
git clean -fd
# List files changed between branches
git diff --name-status main..feature/auth
# Find the commit that introduced a string
git log -S "secretKey" --source --all
# Show remote URL
git remote get-url origin
# Apply changes from one branch file without switching
git checkout feature/auth -- src/config.ts
# Temporarily ignore tracked file changes (useful during experiments)
git update-index --skip-worktree .env
git update-index --no-skip-worktree .env # undo
# Bundle a repo for offline transfer
git bundle create repo.bundle --all
git clone repo.bundle my-repo
Quick Reference
| Situation | Command |
|---|---|
| Committed to main by mistake | git reset --soft HEAD~1 then switch branch |
| Pushed a bad commit | git revert HEAD && git push |
| Need latest main in my branch | git fetch origin && git rebase origin/main |
| Accidentally deleted a branch | find the commit in git reflog, then git switch -c branch-name COMMIT_SHA |
| Forgot to add a file to last commit | git add missed.txt && git commit --amend --no-edit |
| Want to try something risky | git stash or create a temp branch first |
| Merge is a disaster, bail out | git merge --abort |
| Rebase is a disaster, bail out | git rebase --abort |
| Need to move commits to another branch | git cherry-pick the hashes |
| Hard reset wiped my work | git reflog then git checkout the hash |