Git commands for everyday coding¶
Beginner-friendly · 15 min · Windows & macOS
You don't need hundreds of Git commands — about 20 cover almost every working day. This guide teaches them in the order you actually use them: set up once, then save → share → branch → sync → undo.
How Git thinks: four places your code lives¶
flowchart LR
W["Working folder<br/>(files you edit)"] -- "git add" --> S["Staging area<br/>(next commit)"]
S -- "git commit" --> L["Local repo<br/>(your history)"]
L -- "git push" --> R["Remote<br/>(GitHub)"]
R -- "git pull" --> W
| Place | What it is | Command that moves code forward |
|---|---|---|
| Working folder | The files you're editing right now | git add |
| Staging area | The changes you've picked for the next commit | git commit |
| Local repository | Saved snapshots (commits) on your machine | git push |
| Remote | The shared copy on GitHub/GitLab | git pull brings others' work back |
The one habit that prevents most mistakes
Run git status before and after every Git command. It tells you which branch you're on,
what's changed, and usually suggests the next command.
Step 1 — Install and configure Git (once per machine)¶
Download Git for Windows from git-scm.com and keep the default options. Or with winget:
Check it works, then tell Git who you are (this name and email appear on every commit):
git --version
git config --global user.name "Your Name"
git config --global user.email "you@example.com"
git config --global init.defaultBranch main # new repos start on "main"
Line endings differ between Windows and macOS/Linux. Set this once so files don't show as "changed" for no reason:
Check your settings any time with git config --global --list.
Step 2 — Get a repository¶
Start tracking a new project:
Or copy an existing one from GitHub:
git clone downloads the full history and connects the remote (named origin) for you.
To connect a project you created with git init to a new, empty GitHub repo:
git remote add origin https://github.com/<user>/<repo>.git
git push -u origin main # -u: remember origin/main for future push/pull
Step 3 — The daily loop: status → add → commit → push¶
This is 80% of your Git usage.
git status # what changed?
git diff # exactly which lines changed (not yet staged)
git add app.py utils.py # stage specific files
git diff --staged # review what's about to be committed
git commit -m "Add retry logic to the LLM client"
git push # send your commits to GitHub
Useful variations:
| You want to… | Command |
|---|---|
| Stage every change in the folder | git add . |
| Stage only some changes inside a file | git add -p app.py (Git asks about each block: y/n) |
| Stage and commit already-tracked files in one go | git commit -am "message" (doesn't include new files) |
| Write a longer message in your editor | git commit (no -m) |
Check before git add .
git add . stages everything, including .env files with API keys if they aren't in
.gitignore. Run git status first, and set up .gitignore (step 9) on day one.
Writing good commit messages¶
A good message says what changed and finishes the sentence "This commit will…":
| ❌ Avoid | ✅ Prefer |
|---|---|
fix |
Fix crash when the PDF has no text layer |
changes |
Add chunk-overlap setting to the ingestion pipeline |
final final v2 |
Switch embeddings to text-embedding-3-small |
Keep the first line under ~72 characters. Commit small, related changes together — one idea per commit makes history easy to read and easy to undo.
Step 4 — Work on a branch¶
Never build features directly on main. A branch is a separate line of work you can merge
back when it's ready.
git switch -c feature/pdf-upload # create a branch and move onto it
# ...edit, add, commit as usual...
git push -u origin feature/pdf-upload # first push of a new branch
| You want to… | Command |
|---|---|
List branches (* marks the current one) |
git branch |
| Include branches on GitHub | git branch -a |
| Move to another branch | git switch main |
| Rename the current branch | git branch -m new-name |
| Delete a merged branch | git branch -d feature/pdf-upload |
| Delete a branch on GitHub | git push origin --delete feature/pdf-upload |
switch vs checkout
Older tutorials use git checkout -b name and git checkout name. They still work —
git switch (and git restore, below) are the newer, clearer replacements for the two jobs
checkout used to do.
Branch names: use short, descriptive prefixes like feature/…, fix/…, docs/….
Step 5 — Stay in sync with your team¶
git fetch # download new commits from GitHub, change nothing locally
git pull # fetch + merge them into your current branch
When main has moved on while you were working on your branch, bring those changes into your branch.
There are two ways:
Safe and easy to understand; adds a "merge commit" to your history.
The golden rule of rebasing
Only rebase your own branch. Never rebase or force-push main or any branch other people
are working on — it rewrites history they already have. Use --force-with-lease, never plain
--force: it refuses to push if someone else has pushed in the meantime.
Fixing a merge conflict¶
A conflict means two people changed the same lines. Git stops and marks the file:
- Run
git status— conflicted files are listed under "both modified". - Open each file, keep the right code, and delete all three marker lines (
<<<<<<<,=======,>>>>>>>). VS Code shows Accept Current / Accept Incoming / Accept Both buttons above each conflict. -
Stage the fixed files and continue:
Want to give up and get back to where you were? git merge --abort or git rebase --abort.
Step 6 — Undo mistakes (safely)¶
Pick the row that matches your situation:
| Situation | Command | Safe? |
|---|---|---|
| Throw away edits to a file (not staged) | git restore app.py |
⚠️ the edits are gone |
| Unstage a file but keep the edits | git restore --staged app.py |
✅ |
| Fix the last commit's message, or add a forgotten file | git add forgotten.py then git commit --amend |
✅ if not pushed yet |
| Undo the last commit but keep the changes | git reset --soft HEAD~1 |
✅ if not pushed yet |
| Undo a commit that's already pushed | git revert <commit-id> |
✅ always — it adds a new "undo" commit |
| Wipe everything back to the last commit | git reset --hard |
⚠️ uncommitted work is gone |
Pushed already? Use revert, not reset.
git revert creates a new commit that cancels the old one, so nobody else's history breaks.
reset and --amend rewrite history — fine locally, harmful once others have pulled your commits.
The safety net: git reflog. Git remembers every place your branch has pointed to for weeks. If a
reset or rebase went wrong:
git reflog # find the entry from before the mistake, e.g. a1b2c3d
git reset --hard a1b2c3d # go back to it
Step 7 — Park unfinished work with stash¶
You're halfway through something and need to switch branches (urgent bug, quick review):
git stash push -m "half-done PDF parser" # save and clear your uncommitted changes
git switch main # do the urgent work...
git switch feature/pdf-upload
git stash pop # bring the changes back
| You want to… | Command |
|---|---|
| Include new (untracked) files too | git stash push -u -m "message" |
| See saved stashes | git stash list |
| Apply a stash but keep it in the list | git stash apply stash@{1} |
| Delete a stash | git stash drop stash@{1} |
Step 8 — Read the history¶
git log --oneline --graph --all # compact history with branch lines
git log -5 # the last 5 commits in detail
git log -p app.py # every change ever made to one file
git show a1b2c3d # what one commit changed
git blame app.py # who last changed each line (and in which commit)
git diff main..feature/pdf-upload # how two branches differ
Make a short alias
Nowgit lg shows the whole branch graph.
Step 9 — Keep secrets and junk out with .gitignore¶
Create a .gitignore file in the project root before your first commit:
# secrets
.env
*.pem
# Python
.venv/
__pycache__/
*.pyc
# Node / Next.js
node_modules/
.next/
# editor and OS files
.vscode/
.DS_Store
Already committed a file that should be ignored? Add it to .gitignore, then stop tracking it
(the file stays on your disk):
Pushed an API key by accident?
Removing it in a new commit is not enough — it's still in the history and bots scan GitHub for keys within minutes. Revoke the key immediately in your provider's dashboard and create a new one. Then remove it from your code.
Step 10 — The pull-request workflow¶
This is how most teams (and open-source projects) ship code:
git switch main
git pull # 1. start from the latest main
git switch -c fix/empty-pdf-crash # 2. branch
# ...edit, test...
git add .
git commit -m "Fix crash on PDFs with no text layer" # 3. commit
git push -u origin fix/empty-pdf-crash # 4. push the branch
- Open GitHub — it shows a Compare & pull request button. Describe what and why, then request
a review. (With the GitHub CLI:
gh pr create --fill.) - Address review comments with more commits on the same branch and
git pushagain — the PR updates automatically. -
After it's merged, clean up:
A typical day, start to finish¶
# morning — start from fresh main
git switch main
git pull
git switch -c feature/streaming-responses
# during the day — small commits as each piece works
git status
git add -p
git commit -m "Stream tokens from the chat endpoint"
# main moved on while you worked? catch up
git fetch
git rebase origin/main # or: git merge origin/main
# end of day — back it up on GitHub, open a PR when it's ready
git push -u origin feature/streaming-responses
Everyday Git cheat sheet¶
| Task | Command |
|---|---|
| What's going on? | git status |
| See unstaged / staged changes | git diff · git diff --staged |
| Stage files | git add <file> · git add . · git add -p |
| Commit | git commit -m "message" |
| Send to GitHub | git push (first time on a branch: git push -u origin <branch>) |
| Get others' work | git pull · git fetch |
| New branch | git switch -c <branch> |
| Change branch | git switch <branch> |
| Merge a branch into the current one | git merge <branch> |
| Update your branch with main | git rebase origin/main |
| Discard edits to a file | git restore <file> |
| Unstage a file | git restore --staged <file> |
| Fix the last commit | git commit --amend |
| Undo a pushed commit | git revert <commit-id> |
| Park / restore work | git stash push -m "msg" · git stash pop |
| History | git log --oneline --graph --all |
| Recover "lost" commits | git reflog |
| Stop tracking a file | git rm --cached <file> |
Troubleshooting¶
| You see… | Fix |
|---|---|
fatal: not a git repository |
You're in the wrong folder — cd into the project, or run git init |
Please tell me who you are |
Run the two git config --global user.… commands in step 1 |
! [rejected] … (fetch first) or non-fast-forward on push |
GitHub has commits you don't. Run git pull (or git pull --rebase), fix any conflicts, then push |
Your local changes would be overwritten by merge/checkout |
Commit your changes first, or git stash, then repeat the command and git stash pop |
fatal: The current branch has no upstream branch |
First push of a new branch — use git push -u origin <branch> |
Authentication failed / Support for password authentication was removed |
GitHub doesn't accept account passwords. Sign in through Git Credential Manager (installed with Git for Windows), use a personal access token as the password, or switch to SSH |
You are in 'detached HEAD' state |
You checked out a commit, not a branch. Run git switch -c new-branch to keep any work, or git switch main to go back |
warning: LF will be replaced by CRLF |
Harmless line-ending notice on Windows — the core.autocrlf setting in step 1 handles it |
refusing to merge unrelated histories |
The local repo and GitHub repo were created separately (e.g. GitHub added a README). Run git pull origin main --allow-unrelated-histories once |
Next: put this into practice by keeping your uv-managed GenAI project
under Git — commit pyproject.toml and uv.lock, and add .venv/ and .env to .gitignore.