What Is Git? The Version Control System Every Developer Must Master
The first time you run git push, it feels like magic. Then you hit your first merge conflict and the magic turns into panic.
Git is the distributed version control system used by millions of developers worldwide. But most developers never truly learn Git — they get stuck in the add, commit, push, pull loop and panic when things go wrong. This article builds the mental model you need to break out of that loop.
Why Does Git Exist?
Imagine five developers working on the same project. Everyone is editing the same files. One is adding a feature, another is fixing a critical bug, a third is trying to refactor everything.
Without Git, this is chaos. You save files as project_final_v2_REALFINAL.zip. You can't tell who changed what. When something breaks, you can't find what changed.
Git solves this with three core ideas: recording every change, allowing parallel work, and making it possible to go back in time.
Repository, Commit, Branch: The Core Concepts
A repository (repo) contains all your project files and their complete change history. It lives as a .git folder on your local machine. Created with git init or copied from somewhere with git clone.
A commit is a snapshot of a moment in time. A record that says "this is exactly what the code looked like at this point." Every commit has a unique hash — something like a3f8c2d. Commit messages matter for this reason; a good commit message explains not what was changed, but why.
# Bad commit message git commit -m "fix" # Good commit message git commit -m "fix: resolve crash when user submits login form with empty email"
A branch is an independent line of development. An isolated space where you can make your own changes without touching the main codebase. The main branch typically represents production code, while branches like feature/user-auth hold work in progress.
git branch feature/payment-integration # create branch git checkout feature/payment-integration # switch to branch # or in one command: git checkout -b feature/payment-integration
The Staging Area: The Concept Most People Skip
One of the hardest Git concepts to grasp is the staging area (also called the index). It's the intermediate zone between your working directory and your commit history.
Working Directory → Staging Area → Repository
(changes) (git add) (git commit)
Why does this intermediate layer exist? Because sometimes you make multiple changes but want to split them into separate commits. The staging area lets you choose exactly which changes belong in this commit.
git add src/auth/login.js # stage only this file git add src/auth/ # stage the entire folder git add -p # stage changes piece by piece (very powerful) git status # see what's staged and what's changed
Merge vs Rebase: The Most Confusing Topic
There are two ways to combine branches: merge and rebase. Both achieve the same result but the history looks different.
Merge combines the histories of two branches and creates a new "merge commit." History is preserved exactly as it happened — you can see clearly when things occurred.
git checkout main git merge feature/payment-integration
Rebase takes your branch's commits and replays them as if they were written on top of the latest version of main. History looks cleaner and more linear.
git checkout feature/payment-integration git rebase main
Which to use? General rule: merge on shared branches, rebase on personal feature branches. Rebase rewrites history, so rebasing a branch others are using causes problems.
Merge Conflicts: Solving Them Without Panicking
A merge conflict happens when two branches have modified the same line of the same file in different ways. Git can't decide which version is correct and asks you to resolve it.
Conflict markers look like this:
<<<<<<< HEAD
const timeout = 3000;
=======
const timeout = 5000;
>>>>>>> feature/performance-improvements
HEAD is the version in your current branch, below is the version from the branch you're trying to merge. You pick one, combine them, delete the markers, and commit.
git add src/config.js # mark conflict as resolved git commit # complete the merge
Commands That Make Daily Life Easier
git stash — Temporarily saves your unfinished changes. Ideal when you need to fix an urgent bug before switching branches.
git stash # save changes git stash pop # bring them back git stash list # list saved stashes
git log — Shows commit history. --oneline for a summary view, --graph for a visual tree.
git log --oneline --graph --all
git diff — Shows what changed. Critical for reviewing what you did before committing.
git diff # unstaged changes git diff --staged # staged changes git diff main..feature/x # difference between two branches
git reset and git revert — For undoing mistakes. reset rewrites history (use carefully), revert undoes a change with a new commit (safe).
git revert a3f8c2d # creates a new commit that undoes that commit git reset --soft HEAD~1 # undo last commit, keep changes
Branch Strategies: How Teams Work Together
Git Flow — A structured strategy with main, develop, feature/*, release/*, hotfix/* branches. Good for large teams and planned release cycles.
GitHub Flow — Simpler. Just main and feature branches. Each feature is developed in a branch, a Pull Request is opened, reviewed, then merged into main. Ideal for teams doing continuous deployment.
Trunk-Based Development — Everyone commits small changes directly to main. Feature flags hide features that aren't ready yet. Provides the fastest delivery when combined with CI/CD.
Which strategy is right? Depends on your team size, release frequency, and maturity. GitHub Flow is enough for a five-person startup; Git Flow provides more control for products with hundreds of developers.
.gitignore: What You Should Never Commit
Every project has files that shouldn't be committed. node_modules, build outputs, .env files, IDE settings. The .gitignore file keeps these out of Git's radar.
# .gitignore example
node_modules/
dist/
.env
.env.local
*.log
.DS_Store
.idea/
.vscode/
The most important rule: never commit .env files. API keys, database passwords, secrets — these live in .env and stay in .gitignore.
Git Never Stops Giving
git bisect lets you binary-search through commits to find exactly which one introduced a bug. git cherry-pick lets you grab a single commit from another branch. git reflog lets you recover everything you thought you deleted.
Git is a tool that gets more powerful the deeper you go. But to go deeper, you need a solid foundation. Once you move beyond the add, commit, push loop, Git stops being a version control tool and becomes a colleague that understands your code.