You’ve definitely heard of GitHub, and you’ve probably heard of GitLab — but where are they, exactly?

GitHub and GitLab are services that let you use Git (two completely different companies, by the way). Git itself is the thing doing the real work: it’s version control, which means it exists to preserve the different versions of a project as you build it.

You’ve probably made something small, broken it, and then mashed Ctrl-Z over and over trying to crawl back to the version that actually worked. What if you didn’t have to? What if you could take a copy of the whole project, make your edits over there, check whether they work, and then merge them back in?

That’s Git. Most tutorials stop at git add, git commit, git push — enough to save your code, but not how teams actually work. Let’s go further.

First, some terminology

A few words get thrown around constantly. Here’s what they actually mean:

  • Repository (repo): the project itself — where your main code lives, along with all the offshoots you’re working on.
  • Local repository: the copy on your own machine. Changes you make here don’t show up anywhere else until you run a few commands.
  • Remote repository: the centralized copy — the one you’ve seen on GitHub. Other people can work on it with you, and you can pull from it yourself.
  • Branch: think of it like a tree. The main (or master) branch is the trunk — the code you know works. You copy everything from the trunk and branch off to try new code, without touching what already works.

Branches: the trunk and the offshoots

Imagine two developers editing the same file at the same time, both pushing straight to main. Every push would risk overwriting the other’s work. Chaos.

Branches fix that. A branch is an isolated copy of the codebase where you can work freely, without affecting anyone else. The typical loop looks like this:

  1. Start from main (the stable, deployed branch)
  2. Create a feature branch: git checkout -b feature/my-change
  3. Make commits on that branch
  4. Open a Pull Request to merge it back into main
  5. Someone reviews the code
  6. It gets merged

Try it

The widget below walks through those steps — create a branch, commit, open a PR, merge:

Git Branch Visualizer
main
C0
Start by creating a feature branch off main.

The commands you’ll actually run

Every Git command starts with git, so they’re easy enough to spot. Here are the ones you’ll reach for daily.

git clone

Whether it was an open-source tool or a Minecraft mod, many a young dev has stared at a GitHub page asking “now where the hell is the download button?” There isn’t one — there’s git clone.

Run git clone <url> in your CLI (command line interface) and it copies the entire repository — all the code and its history — down into a folder on your machine. Clone a repo you own, and that folder stays linked to the remote, so you can push your changes back up later.

git add .

This is usually the first command in your workflow. It adds new or changed files to the index — the staging area for what you’re about to commit. So every time you add, you’re lining changes up for the next step.

The . stages everything you’ve changed. Want to be picky? Pass a path instead:

git add .                # stage everything
git add src/auth.ts      # stage just one file

git rm

Need to get rid of a file? git rm <file> removes it from your working tree and the index in one move.

git commit

git commit takes everything sitting in your index and records it as a new commit at the tip of your current branch. (Technically: it creates a new commit whose parent is wherever HEAD points, then moves the branch forward to it.)

Good commit messages matter:

# Bad
git commit -m "fix stuff"
git commit -m "wip"

# Good
git commit -m "fix: handle null user in auth middleware"
git commit -m "feat: add email validation to signup form"

A useful convention is Conventional Commits: feat:, fix:, chore:, docs:, refactor:, test:. Future-you (and your teammates) will thank you.

Pull requests: merging, with a conversation

The phrasing is a little odd, but it clicks once you’ve done it. You’ve committed your changes to your branch — now you want to pull them into another branch (usually main) so the rest of the project gets them. Do this regularly and the code you’ve changed stays present going forward.

So why a request? On your own repo, you can merge as many pull requests as you want until you go blank in the face — go nuts. But the moment you’re working with other people (especially your boss), they’re going to review the request, see if it’s up to snuff, and kick it back to you with notes.

A Pull Request isn’t just a way to merge code — it’s a structured code review conversation. A good description documents:

  • What changed
  • Why it changed
  • How to test it

It prevents wasted review time and makes your git history actually useful months later.

Merge conflicts: not scary, just math

Git keeps us from silently clobbering each other’s work — but there’s a catch. When two people change the same lines of the same file and both want to merge, Git can’t guess who’s right. It stops and makes you figure out who’s doing what, and to what.

When you open the file, you’ll see this:

<<<<<<< HEAD
const greeting = "Hello";
=======
const greeting = "Hi there";
>>>>>>> feature/new-greeting
  • Everything between <<<<<<< HEAD and ======= is your current branch’s version
  • Everything between ======= and >>>>>>> is the incoming branch’s version
  • Delete the markers and keep the version you want (or combine them)

Then git add the resolved file and git commit. Done — not so scary.

Your everyday cheat sheet

# Create and switch to a new branch
git checkout -b feature/my-thing

# See what branch you're on
git branch

# See uncommitted changes
git status
git diff

# Stage and commit
git add .
git commit -m "feat: add dark mode toggle"

# Push branch to remote and set upstream
git push -u origin feature/my-thing

# Pull latest changes from main
git pull origin main

# Merge main into your branch (stay up to date)
git merge main

# See log of commits
git log --oneline

# Undo last commit (keep changes)
git reset HEAD~1

What happens when you push to main directly at a job

You’ll probably be working on a team with branch protection rules. These are GitHub/GitLab settings that:

  • Prevent direct pushes to main
  • Require a PR with at least one approval before merging
  • Require CI checks to pass before merging

This isn’t bureaucracy — it’s how production code stays stable.

The mental model

Think of main as a production train. Every feature is built in its own station (a branch). When it’s ready and reviewed, it gets loaded onto the train (merged). The train never stops for half-finished work.

That’s the whole workflow. Once this clicks, you can work on any team in the world.