Git Basics
init, add, commit, log, branch, merge — the daily 80%.
Git looks like 200 commands. It's actually 4 ideas and a dozen verbs. Once the mental model clicks, every command stops being magic and starts being obvious. Let's build that model.
The mental model in two sentences
A commit is a snapshot of every tracked file at a moment in time. A branch is a sticky-note pointing at one of those snapshots.
That's it. Every operation in git is some variation on "take a snapshot," "move a sticky note," or "compare two snapshots." If a command ever confuses you, ask which of those three it's doing.
Starting a repo
mkdir my-project
cd my-project
git init
# Initialized empty Git repository in /Users/ada/my-project/.git/
# git stores everything in the hidden .git directory
ls -la
# . .. .gitThat hidden .gitfolder is the whole repository. Delete it and you're back to a plain folder. Copy it and you have a full history clone.
The three areas: working dir, staging, repo
Every file lives in one of three places at any moment:
- Working directory: the files you edit in your editor.
- Staging area (the index): a draft of the next commit. Files you've marked with
git add. - Repository (.git): the permanent record of past commits.
git add moves changes from working dir to staging. git commit moves staging to repo. git status shows you what's where.
status, add, commit
echo "# my project" > README.md
git status
# On branch main
#
# No commits yet
#
# Untracked files:
# (use "git add <file>..." to include in what will be committed)
# README.md
#
# nothing added to commit but untracked files present
git add README.md
git status
# Changes to be committed:
# (use "git rm --cached <file>..." to unstage)
# new file: README.md
git commit -m "Initial commit"
# [main (root-commit) a1b2c3d] Initial commit
# 1 file changed, 1 insertion(+)
# create mode 100644 README.mdEvery commit has a SHA: a 40-character hash like a1b2c3d4.... The first 7 characters are usually unique enough to refer to it. SHAs never change. They are the commit's permanent ID.
git add .stages everything. That's often what you want, but it's also how secrets and big binaries sneak in. Get in the habit of git status before every commit, and prefergit add <files> when you can.Seeing history: log and diff
git log
# commit b4f8a91c7e2... (HEAD -> main)
# Author: Ada <ada@example.com>
# Date: Fri May 24 10:14:02 2024
#
# Add hero section
#
# commit a1b2c3d4e5f...
# Author: Ada <ada@example.com>
# Date: Fri May 24 09:58:17 2024
#
# Initial commit
# more compact view (the alias every dev sets up)
git log --oneline --graph --decorate
# * b4f8a91 (HEAD -> main) Add hero section
# * a1b2c3d Initial commitgit diff shows changes. It has three useful flavors:
git diff # working dir vs staging (unstaged changes)
git diff --staged # staging vs last commit (what you'd commit)
git diff main feature # all changes between two branches
git diff a1b2c3d b4f8a91 # between any two commitsgit diff
# diff --git a/README.md b/README.md
# index e69de29..d1f0e8c 100644
# --- a/README.md
# +++ b/README.md
# @@ -1 +1,3 @@
# -# my project
# +# My Project
# +
# +A small experiment.Branches: cheap, instant, everywhere
A branch is just a movable pointer to a commit. Creating one is instant. There's no copying, no fork in the road, just a new sticky note. The default branch is usually called main (older repos: master).
git branch # list branches
# * main
git branch feature/login # create one
git switch feature/login # move HEAD to it (modern syntax)
# Switched to branch 'feature/login'
# the old way that still works
git checkout feature/login
# create and switch in one step
git switch -c feature/loginHEADis git's name for "the commit I'm currently on." When you commit, HEAD moves forward and drags whichever branch was pointing at it along too.
Merging
Once your branch has the changes you want, merge them back into main. Git tries to be smart and most merges just work.
git switch main
git merge feature/login
# Updating a1b2c3d..b4f8a91
# Fast-forward
# src/auth.ts | 42 ++++++++++++++++++++++++++++++++++++++++++
# 1 file changed, 42 insertions(+)A fast-forward merge happens when main hasn't moved since you branched. Git just slides the sticky note forward. If main has its own new commits, you get a real merge commit that joins the two histories.
# when both branches have moved, you'll see something like:
# Merge made by the 'ort' strategy.
# src/auth.ts | 12 ++++++++++++
# src/home.ts | 4 +---
# 2 files changed, 13 insertions(+), 3 deletions(-)<<<<<<< markers showing each side. Edit, save, git add, then git committo complete the merge. Don't panic, this is routine.Working with a remote: push, pull, clone
A remote is another copy of your repo, usually on GitHub or similar. origin is the conventional name for the one you cloned from.
# start from someone else's repo
git clone https://github.com/vercel/next.js.git
cd next.js
# see your remotes
git remote -v
# origin https://github.com/vercel/next.js.git (fetch)
# origin https://github.com/vercel/next.js.git (push)
# get the latest commits from origin
git pull
# Already up to date.
# upload your local commits to origin
git push
# To github.com:ada/my-project.git
# a1b2c3d..b4f8a91 main -> main
# pushing a new branch for the first time
git push -u origin feature/login
# -u sets it as the upstream so future 'git push' is enoughpull is really two steps: fetch (download commits) followed by merge(apply them). You'll learn to use fetch alone when you want to see what changed before merging.
A daily workflow
This loop happens dozens of times a day:
git switch main
git pull # sync with the world
git switch -c feature/cool-thing # branch off
# ... edit files ...
git status # what changed?
git diff # what exactly changed?
git add src/cool.ts # stage
git commit -m "Add cool thing" # snapshot
git push -u origin feature/cool-thing
# ... open a Pull Request on GitHub ...Quick quiz
What does a git commit actually store?
Recap
- A commit is a snapshot. A branch is a pointer. HEADis "where I am."
- Three areas: working dir → (
git add) → staging → (git commit) → repo. git statusandgit diffanswer "what changed?" before you commit.git switch -c namecreates and moves to a new branch.git merge namebrings it back.git pushandgit pullsync your local history with a remote like GitHub.