You type git status, and the terminal answers with something like "You have unmerged paths." You have no idea which file to open, what to delete, or whether you just destroyed three days of work.
That moment — the first merge conflict — is where most beginners quietly give up on version control. Not because Git is hard. Git is a small, logical program. It's because almost every tutorial stops right before the part that hurts, then hands you a list of forty commands and wishes you luck.
I've walked a fair number of people through their first repository, and I've broken my own share of them. The fix is almost always the same: learn four concepts properly, and the commands stop being magic spells you copy from a search bar. Let's do that.
Key takeaways
- Version control tracks the history of a project, so any past state can be restored — not just "saved over".
- Git is distributed: every clone is a complete repository with its own history, which is why you can commit on a plane.
- Commits move through three zones: working directory, staging area, local repository. Confusion about Git is almost always confusion about which zone you're in.
- A branch is a movable label, not a copy of your files. Creating one costs nothing.
- Merge conflicts are normal, not a sign that you broke something. You resolve them by editing text and committing the result.
- Almost every mistake is recoverable with
git reflog, as long as the commit existed at some point.
What is version control, and why does it actually matter?
Version control is a system that records changes to a set of files over time, so you can recall a specific version later. That's the textbook sentence. Here's what it means in practice.
Before I used Git properly, my project folders looked like this: report_final.docx, report_final_v2.docx, report_final_ACTUAL_final.docx, and a folder called old backups don't touch. That's version control too. It's just manual, fragile, and completely useless the moment two people edit the same file.
The real problem version control solves
Three things happen on any project that lasts more than a week, and no folder-naming scheme survives them:
- You need to know what changed since last Tuesday, and why.
- Two people need to work on the same file at the same time without overwriting each other.
- Something that worked yesterday stopped working, and you need to go back.
Git answers all three. And it answers them locally, on your machine, without a server or an internet connection.
Distributed versus centralised: why it changes your day
Older tools like SVN were centralised. There was one server holding the real history. To commit, you needed to reach it. Lose the connection, lose the ability to record anything.
Git flipped that. When you clone a repository, you get the entire history — every commit ever made — copied onto your disk. The server becomes a convenience for sharing, not the source of truth.
What does that mean for a beginner, concretely? You can commit ten times on a train with no signal, then push all ten when you get home. You can inspect the full history of a project offline. And if the remote server disappears tomorrow, your clone still has everything.
That's the single most important structural fact about Git. Everything else follows from it.
Understanding the three zones inside Git
Here's the thing almost nobody explains clearly enough: Git doesn't just track your files. It maintains three separate areas, and every command you'll ever type moves something between them.
| Zone | What lives there | Command that moves files in |
|---|---|---|
| Working directory | The actual files you edit in your editor | git checkout / git switch |
| Staging area (index) | A draft of your next commit | git add |
| Local repository | Permanent, identified snapshots | git commit |
| Remote repository | A shared copy others can pull from | git push |
Why the staging area exists at all
New users often ask why they can't just commit straight from their editor. The staging area is the answer to a real annoyance: you don't always want everything in one commit.
Say you fixed a typo in the README and also rewrote a function. Those are two different changes with two different reasons. Staging lets you commit the typo fix on its own, then the function rewrite separately. Six months from now, when someone asks why that function changed, the history actually tells them — instead of burying it in a commit titled "stuff".
You can skip the staging step entirely with git commit -a -m "message", which stages and commits tracked files in one go. I use it for quick fixes. I regret it when I don't.
What a commit really is
A commit is not a diff. It's a snapshot of your whole project at a point in time, plus a pointer to its parent commit, plus an author, a timestamp, and a message. Git computes the differences on the fly when you ask for them.
That's why a repository with tens of thousands of commits stays small: identical file contents are stored once and referenced.
Each commit gets a hash — a long string like a3f9c21. You'll rarely type the full thing; the first seven characters are enough. That hash is how Git names any moment in your project's history.
Getting Git running: your first repository
Download Git from the official site — the installer bundles everything you need. Once it's in, open a terminal and check:
git --version If a version number comes back, you're done installing. Now tell Git who you are, because every commit carries an author and an anonymous commit is useless to your teammates:
git config --global user.name "Your Name"
git config --global user.email "[email protected]" The --global flag applies it to every repository on your machine. Inside a specific project, you can drop the flag to use a different identity — handy when you have a work account and a personal one.
Creating a repository and making your first commit
Two ways to start. Either you turn an existing folder into a repository:
git init Or you copy an existing project from somewhere else:
git clone https://github.com/someone/project.git The second one is what you'll use most often in practice, since most work starts from a repository that already exists.
After git init, run git status. It will list every file as untracked. Git sees them but isn't watching them yet. Add what you want, then commit:
git add .
git commit -m "Initial commit" The dot means "everything in this folder." It's convenient and occasionally a mistake. If you're about to commit something containing a password or an API key, that dot just published it into your history — and history is very hard to scrub clean afterward.
The .gitignore file
This is the file that prevents that disaster, and it's a one-line habit worth building on day one. Create a file called .gitignore at the root of your repository and list what Git should never track:
node_modules/
.env
*.log
build/ Anything matching those patterns gets ignored by git status and git add. Dependencies you can reinstall, secrets, generated files, logs — none of them belong in a repository.
One caveat that catches everyone once: .gitignore only affects files Git isn't already tracking. If you committed a secret before adding the rule, you have to remove it from tracking explicitly. The file stays on disk; Git just stops watching it.
Branches and conflicts: where beginners actually struggle
A branch in Git is a lightweight movable pointer to a commit. That's the whole definition. Creating one doesn't copy your files or take measurable time.
git switch -c feature/login You're now on a new line of work. Commit as much as you want. The main branch is untouched. When the feature is ready:
git switch main
git merge feature/login If nobody else touched the same lines, that merge completes silently in under a second. If someone did, you get the message that scares everyone.
Resolving a merge conflict, step by step
Here's what actually happens. Git stops the merge, marks the conflicting file, and waits for you. Nothing is broken. Nothing is lost.
- Run
git status. Under "Unmerged paths", you'll see which files conflict. - Open one. Git has inserted markers:
<<<<<<<starts your version,=======separates,>>>>>>>ends the incoming version. - Edit the file so it reads the way you want. Delete the markers. You are literally just choosing text.
git addthe file, thengit committo complete the merge.
Changed your mind mid-conflict? git merge --abort puts everything back exactly where it was. I've used that command more times than I'd like to admit, usually at 11pm.
The mental shift: a conflict isn't Git failing. Git is telling you it found two versions of the same line and refuses to guess. It's asking a question only you can answer.
Why pull requests matter more than branches
On a hosted platform like GitHub or GitLab, you push your branch and open a pull request. A teammate reviews the diff, leaves comments, requests changes. The merge happens on the platform after approval.
Plenty of tutorials treat this as an optional extra. I'd argue the opposite: on any team, the review is where most of the value lives. The branch is just the vehicle.
Undoing things without panicking
Every beginner eventually commits something they regret. The right command depends on one question: has that commit been pushed and shared?
| Situation | Command | Effect |
|---|---|---|
| Bad commit message, not pushed | git commit --amend | Rewrites the last commit |
| Wrong files staged, not committed | git restore --staged <file> | Removes them from staging, keeps edits |
| Discard local edits to a file | git restore <file> | Reverts the file to last committed state |
| Undo a pushed commit | git revert <hash> | Creates a new commit that reverses it |
| Lost a commit entirely | git reflog | Shows where HEAD has been, so you can go back |
git reflog deserves a special mention. It records every position your branch has pointed to, even ones you thought you'd thrown away. When someone tells me they "lost a commit", my answer is almost always the same: run git reflog, find the hash, check it out. I've recovered work this way that I was certain was gone.
The one rule: never rewrite history that others have already pulled. --amend and reset change commits in place. If a teammate based work on the old version, you've just handed them a mess. Once a commit is shared, use revert.
Where to learn more without drowning
The temptation is to read one comprehensive guide, memorise forty commands, and start. It doesn't work. Commands you've never used in anger slide right out of your head — mine included, and I've typed git stash pop more times than I can count without ever once remembering git stash list at the right moment.
A better order: create a throwaway repository, break it on purpose, and fix it. Add a file, commit it, delete it, restore it. Make a branch, commit on both branches, force a conflict, resolve it. Twenty minutes of deliberate breakage taught me more than any chapter I read. And the documentation — the free Pro Git book covers the internals properly once you want them; W3Schools-style references are fine for looking up a single command's syntax in a hurry.
What sticks is the model, not the cheat sheet. Once you genuinely understand that your working directory, staging area, and local history are three separate things — and that Git's job is to move content between them — the commands become almost predictable. You stop asking "what do I type" and start asking "where do I want this change to be".
The first merge conflict you resolve alone will feel like defusing something. It isn't. It's Git asking you a question in a syntax you hadn't learned yet.