Skip to content

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:

winget install --id Git.Git -e
xcode-select --install     # installs Git with Apple's command-line tools
# or: brew install git

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:

git config --global core.autocrlf true
git config --global core.autocrlf input

Check your settings any time with git config --global --list.


Step 2 — Get a repository

Start tracking a new project:

cd my-project
git init

Or copy an existing one from GitHub:

git clone https://github.com/<user>/<repo>.git
cd <repo>

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:

git switch feature/pdf-upload
git fetch
git merge origin/main

Safe and easy to understand; adds a "merge commit" to your history.

git switch feature/pdf-upload
git fetch
git rebase origin/main
git push --force-with-lease     # your branch's history changed, so a normal push is rejected

Replays your commits on top of the latest main for a straight-line 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:

<<<<<<< HEAD
chunk_size = 500
=======
chunk_size = 800
>>>>>>> origin/main
  1. Run git status — conflicted files are listed under "both modified".
  2. 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.
  3. Stage the fixed files and continue:

    git add config.py
    git commit              # if you were merging
    git rebase --continue   # if you were rebasing
    

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

git config --global alias.lg "log --oneline --graph --all --decorate"
Now git 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:

.gitignore
# 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):

git rm --cached .env
git commit -m "Stop tracking .env"

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
  1. 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.)
  2. Address review comments with more commits on the same branch and git push again — the PR updates automatically.
  3. After it's merged, clean up:

    git switch main
    git pull
    git branch -d fix/empty-pdf-crash
    

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.