17 Git Commands I Wish Someone Had Explained to Me Properly

Let me be upfront about something: most Git tutorials are terrible.

They show you `git add`, `git commit`, `git push` — congratulations, you can now commit to a repo! — and then completely abandon you the moment things get complicated. Which they will. Fast.

I've watched junior devs freeze up during code reviews because they didn't know the difference between `revert` and `reset`. I've seen people nuke a shared branch with `--hard`. I've done some of those things myself.

So this isn't a cheat sheet. It's the explanation I wanted when I was learning — covering 17 commands with real context, real examples, and the "when do I actually use this?" answer that most posts skip entirely.

 

Before we dive in — a note on the order

I've deliberately ordered these by when you'd encounter them in a real project, not alphabetically. Config before init. Init before clone. That kind of thing. Makes more sense when you're learning.

 

Table of Contents

 

1. git config

Nobody talks about this one enough. It's boring, it's setup, it feels like homework — and yet skipping it means every commit you make has the wrong name attached to it forever.

`git config` sets your identity and preferences. Git stamps your name and email onto every single commit you make. It's permanent. Get it right first.

# Run these two before you do literally anything else
git config --global user.name "Your Name"
git config --global user.email "[email protected]"

# Tell Git to use VS Code for anything that needs an editor
git config --global core.editor "code --wait"

# I always set this — saves an annoying warning on every new repo
git config --global init.defaultBranch main

# Coloured output in the terminal (why is this not the default?)
git config --global color.ui auto

# See everything currently configured
git config --list

# Check a specific value
git config user.email

The `--global` vs `--local` thing: There are three scope levels and they nest. `--system` applies to every user on the machine and lives in `/etc/gitconfig`. `--global` applies to every repo under your user account and lives in `~/.gitconfig`. `--local` applies only to the current repository, stored in `.git/config`, and overrides everything above it.

That last one is useful when you contribute to open source with a personal email but need your work email on company repos:

# Inside a work project:
git config --local user.email "[email protected]"

 

2. git init

One command, one job: turns any folder into a Git repository.

cd my-project
git init

# Or skip the cd — create the folder and init at once
git init my-new-project

Git creates a hidden `.git` folder in your project root. That folder *is* the repository — the full history, all branches, all config. If you delete it, you lose everything. Don't delete it.

`git init` vs `git clone` — people sometimes confuse these. Use `git init` when you're starting a brand-new project from scratch and there's no existing remote repo to copy. Use `git clone` when the project already exists somewhere and you need a local copy. The other practical difference: after `git init` you have an empty repo with no remote configured — you'll need to add that yourself with `git remote add`. After `git clone`, the remote is already set up and pointing back to where you cloned from.

3. git clone

This is how you get an existing project onto your machine. It copies everything — files, full history, all branches.

# The everyday version
git clone https://github.com/facebook/react.git

# Clone into a different folder name
git clone https://github.com/facebook/react.git my-react-copy

# Shallow clone — only the latest snapshot, no history
# Much faster for large repos when you just need the code
git clone --depth 1 https://github.com/facebook/react.git

# Clone a specific branch and nothing else
git clone --branch develop --single-branch https://github.com/user/repo.git

# SSH clone (needs key setup, but preferred once you've done it)
git clone [email protected]:facebook/react.git

`--depth 1` is underused. If you're cloning something big just to run it or read the code, skip the 10-year commit history. Clones in seconds instead of minutes.

4. git remote

A "remote" is just a named shortcut to a URL. When you clone a repo, Git automatically creates one called `origin` pointing back to where you cloned from.

# See what remotes you have configured
git remote -v

# Add a remote (what you'd do after git init + creating a GitHub repo)
git remote add origin https://github.com/username/my-project.git

# Update the URL — e.g. after renaming the repo on GitHub
git remote set-url origin https://github.com/username/new-name.git

# Rename a remote
git remote rename origin old-origin

# Remove one you don't need
git remote remove upstream

The fork workflow — if you're contributing to open source, you'll typically have two remotes. `origin` points to your personal fork — this is where you push. `upstream` points to the original project — this is where you pull updates from.

git remote add upstream https://github.com/original-owner/original-repo.git

# Sync your fork with the original project
git fetch upstream
git merge upstream/main

5. git branch

Branches are one of Git's best features. They let you work on something in isolation without touching the main codebase — which means you can experiment freely, mess things up, and throw the branch away if needed.

git branch               # List local branches (* = where you are)
git branch -a            # List local + remote-tracking branches
git branch feature/login # Create a new branch
git branch -d feature/login  # Delete (only if merged — safe)
git branch -D feature/login  # Force-delete regardless
git branch -m main           # Rename current branch to 'main'

Good branch names make PRs and `git log` dramatically easier to follow. The pattern most teams use: a type prefix, a slash, then a short description. Something like `feature/add-login-page`, `fix/navbar-overflow`, `hotfix/payment-null-error`, `release/v2.1.0`, or `chore/update-dependencies`. Simple, readable, instantly communicates what the branch is for.

6. git checkout / git switch

`git checkout` is one of those commands that does too many things. Switching branches, creating branches, restoring files — all the same command. Git 2.23 finally split this into `git switch` (branches) and `git restore` (files), which is much clearer.

Both still work. You'll see `checkout` everywhere in older docs and Stack Overflow answers, so know both:

# Switch to an existing branch
git checkout main
git switch main          # same thing, clearer

# Create AND switch to a new branch
git checkout -b feature/shopping-cart
git switch -c feature/shopping-cart    # same thing, clearer

# Discard all unsaved changes to a file
git checkout -- src/Header.js
git restore src/Header.js              # same thing, clearer

# Restore a file to how it looked at a specific commit
git checkout a3f8c21 -- config/settings.py

One thing to watch out for: `git checkout ` overwrites your file with no confirmation, no warning. The newer `git restore` is at least a bit more explicit about what it's doing. If you're on a recent enough Git version, just use `switch` and `restore` going forward.

7. git status

Run this constantly. Seriously. Before `git add`. Before `git commit`. Whenever you're not sure what's happening. It costs nothing and has saved me from embarrassing commits more times than I can count.

git