How to use this page
Find the job you're trying to do, and copy the command. Words in capitals, like FILE, NAME or COMMIT, are yours to replace. COMMIT can be a commit ID from git log --oneline, like 88be1c0, or a position, like HEAD~1 (one commit before where you are). Every row links to the lesson that explains it. Use the print button at the top to keep a copy next to you.
Two habits that make every command here easier:
- Run
git statusbefore and after anything. It tells you which branch you're on, what's changed, and usually what to do next. - Read the whole message. When Git refuses, the fix is almost always printed right there, often in brackets.
When Git says no
Start here when a command fails. Find the line Git printed, and it tells you where you are.
| Git says | It means | Do this | Lesson |
|---|---|---|---|
Your local changes ... would be overwritten by checkout | You have unsaved changes that switching branches would destroy | Commit them, or set them aside with git stash, switch, then git stash pop when you're back | 6 |
CONFLICT (content): Merge conflict in FILE | Both branches changed the same lines, and Git won't guess which wins | Edit the file, delete the <<<<<<<, ======= and >>>>>>> lines, then git add FILE and git commit --no-edit. Or back out with git merge --abort | 4, 6 |
! [rejected] main -> main (fetch first) | GitHub has commits you don't have yet | git pull, then git push again | 5 |
Need to specify how to reconcile divergent branches | Both you and GitHub have new commits, and Git wants to know how to combine them | git pull --no-rebase --no-edit, or set it once with git config --global pull.rebase false | 5 |
the branch 'NAME' is not fully merged | Git can't prove the branch's commits are on your branch. After a squash merge, they're there but look different | Check its changes are really on main, then git branch -D NAME | 4 |
You are in 'detached HEAD' state | You're looking at an old commit, not a branch. Nothing is broken | Just looking: git switch main. Made commits to keep: git switch -c NAME first | 6 |
you are leaving 1 commit behind | You left detached HEAD with a commit no branch points to | git branch NAME COMMIT, with the ID from the warning | 6 |
Which undo do you need?
The answer depends on one question: has it been committed, and has anyone else seen that commit? Rewrite only what nobody else has seen. Once it's pushed, revert.
| Where the change is | To | Run | Lesson |
|---|---|---|---|
| Not committed yet | Throw away a change in a file (gone for good) | git restore FILE | 3 |
| Not committed yet | Unstage a file, but keep the change | git restore --staged FILE | 3 |
| Committed, not pushed | Fix the last commit's message, or add a forgotten file | git add FILE, then git commit --amend -m "MESSAGE" | 3 |
| Committed, not pushed | Undo the last commit, keep its changes in your files | git reset HEAD~1 | 3 |
| Committed, not pushed | Undo the last two commits and their changes | git reset --hard HEAD~2 | 3 |
| Committed and pushed | Undo a commit with a new commit | git revert --no-edit COMMIT | 3 |
| Anywhere | Find where you were before a reset | git reflog -3 | 3 |
| Anywhere | Undo a reset you regret | git reset --hard 'HEAD@{1}' | 3 |
Setting up, once
| To | Run | Lesson |
|---|---|---|
| Check Git is installed | git --version | 1 |
| Set your name for commits | git config --global user.name "Your Name" | 1 |
| Set your email for commits | git config --global user.email "you@example.com" | 1 |
Name new repositories' first branch main | git config --global init.defaultBranch main | 1 |
Make git pull merge, not rebase | git config --global pull.rebase false | 5 |
| See all your settings | git config --global --list | 1 |
| Sign the GitHub command line in | gh auth login | 5 |
Saving your work
A change goes from your folder, to the staging area (what goes in the next commit), to a commit.
| To | Run | Lesson |
|---|---|---|
| Start a repository in this folder | git init | 2 |
| See what's changed and what's staged | git status | 2 |
| The same, one line per file | git status --short | 2 |
| See the changes you haven't staged | git diff | 2 |
| Stage one file | git add FILE | 2 |
Stage everything (check git status first) | git add . | 2 |
| Commit what's staged | git commit -m "MESSAGE" | 2 |
| Stage every tracked file you changed, and commit | git commit -am "MESSAGE" | 3 |
| Stop tracking a file, but keep it on disk | git rm --cached FILE, then add it to .gitignore | 2 |
| Keep files out of Git | List them in a file called .gitignore, like .env or *.log | 2 |
A good commit message finishes the sentence "This commit will...": Add a to-do list, not stuff or fixed it.
Reading the history
| To | Run | Lesson |
|---|---|---|
| List commits, one line each | git log --oneline | 2 |
| Only the last few | git log --oneline -3 | 3 |
| See branches joining, as a drawing | git log --oneline --graph -5 | 4 |
| What the last commit changed | git show | 2 |
| Which files a commit changed | git show --stat COMMIT | 3 |
| Everywhere HEAD has been, even after a reset | git reflog | 3 |
| Look at an old version of everything | git checkout COMMIT, then git switch main to come back | 6 |
Branches
| To | Run | Lesson |
|---|---|---|
List branches (* marks the one you're on) | git branch | 4 |
| Make a branch and switch to it | git switch -c NAME | 4 |
| Switch to a branch | git switch NAME | 4 |
| Make a branch, but stay where you are | git branch NAME | 6 |
| Bring a branch's work into the one you're on | git merge NAME | 4 |
| Cancel a merge that went wrong | git merge --abort | 6 |
| Delete a merged branch (safe: refuses if it isn't merged) | git branch -d NAME | 4 |
| Delete a branch anyway | git branch -D NAME | 4 |
GitHub and pull requests
| To | Run | Lesson |
|---|---|---|
| Put this repository on GitHub | gh repo create NAME --private --source=. --remote=origin --push | 5 |
| Download a repository, with its full history | gh repo clone NAME | 5 |
| See where this repository pushes to | git remote -v | 5 |
| Push a new branch, the first time | git push -u origin NAME | 5 |
| Push, after that | git push | 5 |
| Bring in GitHub's new commits | git pull | 5 |
| Check GitHub without changing anything | git fetch, then git status | 5 |
| Clear records of branches deleted on GitHub | git fetch --prune | 5 |
| See your laptop's record of GitHub's branches | git branch -r | 5 |
| Open a pull request | gh pr create --fill --base main | 5 |
| Open it in the browser | gh pr view --web | 5 |
| Squash and merge it, and delete the branch | gh pr merge --squash --delete-branch | 5 |
The whole loop, for every change: git switch main and git pull, then git switch -c NAME, commit, git push -u origin NAME, gh pr create, merge, and back to git switch main and git pull.
| Merge method | What lands on main | Good for |
|---|---|---|
| Merge commit | Every commit from the branch, plus a commit joining them | Keeping the full history of how a change was built |
| Squash | One new commit with all the branch's changes | A clean history: one commit per pull request |
| Rebase | The branch's commits, copied onto the end of main | A straight line that keeps every commit |
Getting out of trouble
| To | Run | Lesson |
|---|---|---|
| Set unfinished work aside | git stash | 6 |
| The same, including new files Git isn't tracking yet | git stash -u | 6 |
| Bring the work back | git stash pop | 6 |
| See everything you've set aside | git stash list | 6 |
| Throw away the latest stash | git stash drop | 6 |
Committed on main by mistake (not pushed): move it to a branch | git branch NAME, then git reset --hard HEAD~1. Branch first, then reset | 6 |
| Keep commits made in detached HEAD | git switch -c NAME | 6 |
| Rescue a commit you left behind | git branch NAME COMMIT | 6 |
If you commit a secret, like a password or API key:
- Treat it as leaked, and change it. Make a new one and turn the old one off. This is the step that actually protects you.
- Not pushed yet? Take it out of the file, add the file to
.gitignore, andgit commit --amend. - Already pushed? Removing it from history takes tools like
git filter-repo, and rewrites shared history. That's why step 1 comes first.