Git Without the Panic - Commits, Branches and Merges
Git has a reputation for being confusing, and some of that is earned. The commands are named inconsistently, the error messages assume you already know what went wrong, and there are about four ways to do everything. But the core idea is simple, and you only need a handful of commands to get real value out of it. This is the version I'd hand to someone on their first day.
The one idea that matters
Git takes snapshots of your project. Each snapshot is called a commit. A commit records what every tracked file looked like at that moment, who made it, when, and a short message explaining why. Commits link back to the one before them, so your project's history becomes a chain you can walk back through.
That's really it. Branches and merges are just ways of managing more than one chain at a time.
Your first repository
Inside a project folder:
git init
That creates a hidden .git folder where Git keeps all the history. Nothing is tracked yet. Check what Git sees:
git status
You'll get a list of untracked files. Before you add anything, make a .gitignore file listing things that shouldn't be in history: dependency folders, build output, log files, and anything with passwords or API keys in it, like a .env file.
node_modules/
vendor/
*.log
.env
Getting secrets out of Git history after the fact is a real pain, so do this first.
Making a commit
Committing is two steps. First you choose which changes go into the snapshot (this is called staging), then you take the snapshot.
git add index.php style.css
git commit -m "Add homepage layout"
git add . stages everything in the current folder, which is handy but worth a git status first so you don't sweep in something you didn't mean to.
Why the two steps? Because you'll often change five files and realize they're really two separate pieces of work. Staging lets you commit them separately, which makes your history much easier to read later.
To see what you've done:
git log --oneline
Each line is a commit, with a short ID at the front. That ID is how you refer to a specific snapshot.
A word on commit messages. Write them for the person reading the log in six months, which is usually you. "Fix bug" tells them nothing. "Fix crash when cart is empty" tells them everything.
Branches
A branch is just a movable label pointing at a commit. When you make a new commit, the label for the branch you're on moves forward to it. Your default branch is usually called main (older projects often use master).
The point of branches is to work on something without messing up the version that works. Start a new feature:
git switch -c contact-form
That creates a branch called contact-form and moves you onto it. (On older guides you'll see git checkout -b contact-form, which does the same thing.) Now commit as much as you like. main doesn't move. If you need to jump back and fix something urgent on main:
git switch main
Your files change on disk to match main. Switch back to contact-form and your work in progress reappears. Git will refuse to switch if you have uncommitted changes that would get overwritten, which feels annoying but is protecting you. Commit them first, or look up git stash when you're ready for it.
Merging
When the feature is done, you bring it into main:
git switch main
git merge contact-form
If nothing happened on main while you were away, Git just slides the main label forward to where contact-form is. That's called a fast-forward and there's nothing to resolve. If both branches got new commits, Git creates a merge commit that combines them. Most of the time it figures out the combination on its own, because the two branches touched different files or different parts of the same file.
Once it's merged, you can delete the branch label:
git branch -d contact-form
The commits stay. Only the label goes.
Conflicts, and why they aren't scary
Sometimes both branches changed the same lines, and Git won't guess which version you want. It stops and marks the file:
<<<<<<< HEAD
<h1>Welcome</h1>
=======
<h1>Welcome to the shop</h1>
>>>>>>> contact-form
The part above ======= is what's on your current branch. The part below is what's coming in. Edit the file so it says what it should say, delete the marker lines, then:
git add index.php
git commit
That's a resolved conflict. Most editors, including VS Code, highlight these and give you buttons for "accept current" or "accept incoming," which makes it even less dramatic. If you get in over your head mid-merge, git merge --abort puts everything back the way it was before you started.
Where to go from here
Push your repository to GitHub, GitLab, or a self-hosted Gitea or Forgejo instance so it lives somewhere other than your laptop. That's where git push and git pull come in, and it's also where Git starts doubling as a backup.
My advice for the first few weeks: commit small and often, make a branch for anything you're not sure about, and run git status constantly. It's the closest thing Git has to a "where am I?" button, and almost every panic moment gets easier once you read what it says.

Comments