14 Days to GitHub

From a folder on your laptop to a repository you can branch, review, and recover.

If git and GitHub blur together in your head, you’re not behind — most people who use a computer every day have never opened either one. This module assumes you’ve never made a commit and aren’t sure what one is. That’s the starting line.

Finishing 30 Days to Claude Code first helps — you’ll already be comfortable in a terminal and pasting commands you didn’t write. But it isn’t required. If you can open a terminal and you’re willing to type things you don’t fully understand yet, you can start here.

Git is your memory. GitHub is your meeting place. Claude is your guide. The command line is the country you are learning to navigate.

Git is a travel journal for a folder. Every commit is a dated entry: this is what the project looked like at this moment, and here’s why it changed. GitHub is base camp — where that journal moves once it’s ready to live online, get backed up, and be read by someone other than you.

The shape of the two weeks. Three phases, each a small step up:

  • Days 1–5 · Your Own Journal. Everything stays on your laptop. You learn to keep a record of your own work.
  • Days 6–9 · Base Camp. The journal goes online — safely, and on purpose.
  • Days 10–14 · Side Roads. Branches, pull requests, review, and a merge conflict you cause on purpose so you stop being afraid of one.

Ten minutes a day. Some days are shorter.

Day 00

Base Camp Setup

There are two tools here, and their names are confusingly similar. It is worth thirty seconds to separate them, because almost every muddle later starts with mixing these up.

Git runs on your machine. It keeps the journal — the record of what your files looked like at each point in time. It works with no internet connection and no account. GitHub is a website that stores copies of those journals online. And gh is a small program that lets your terminal talk to that website without opening a browser.

Git is the notebook. GitHub is the filing cabinet in another building. gh is the courier. Today you install all three and prove they work. You will not track a single file today.

Before you start
You need a free GitHub account. Sign up at github.com first. The username you pick will be visible on anything you publish, so pick one you would put on a CV.

First, check whether you already have git. Most Macs do. Open your terminal and run:

git --version

You should see a line like git version 2.50.1. The exact number does not matter much — anything 2.23 or newer covers everything in these two weeks. If you see a version, git is installed and you are done with this step.

If instead a dialog box pops up offering to install the command line developer tools, click Install and wait. That is macOS installing git for you. When it finishes, run git --version again.

Now install gh. This is the piece that talks to GitHub:

brew install gh
If it says “command not found: brew”
Homebrew is a separate tool for installing things, and it does not come with a Mac. You have two ways forward. The simpler one: go to cli.github.com, download the macOS installer, and double-click it — no terminal involved. The other: install Homebrew from brew.sh first, then come back and run the line above. Either gets you to the same place.
Windows
Use this line instead of the brew one. Everything after this point is identical on Windows and Mac.
winget install --id GitHub.cli

Connect gh to your GitHub account. Run:

gh auth login
First time only
This asks you a short series of questions with arrow-key answers. Choose GitHub.com, then HTTPS when it asks about protocol, then yes to authenticating with your GitHub credentials. It shows you a one-time code, then opens a browser where you paste it. Approve it and come back to the terminal. You will not do this again on this computer.

Check that it took:

gh auth status

You want a line with a checkmark saying Logged in to github.com account followed by your username. If you see that, the courier knows where to go.

One last thing: sign your work. Every journal entry you make gets your name on it, so git needs to know your name. Run these two lines, with your own name and the email address on your GitHub account:

git config --global user.name "Your Name"
git config --global user.email "you@example.com"

Skip this and git will invent something from your computer’s hostname, and every entry you write will be signed by a stranger. Two lines now saves untangling it later. The --global means you are setting it once for every project on this machine, not just this one.

Optional: guardrails for the next two weeks
If you came through 30 Days to Claude Code and ran the AI 101 starter, you already have this and can move on. If not: the one-line installer at ai101.health/install adds safe-start to Claude Code — a coaching skill whose hooks ask before risky git operations, offer a reviewed savepoint before destructive work, explain half-finished merges and detached HEAD states instead of leaving you stranded in one, and default a new GitHub repository to private. Much of that is what the next two weeks are about. It is guardrails, not a sandbox and not a backup.
curl -fsSL https://ai101.health/install | bash
Nothing in these fourteen days depends on it, and piping a script into your shell is a real trust decision. In its favor: the script is short enough to open at that URL and read first, and all it does is fetch one pinned file, check it against a SHA-256 hash written into the script, and install nothing if the hash does not match. macOS only.
What you just learned
Git keeps the journal on your machine. GitHub stores it online. gh is the phone line between them. Nothing is tracked yet.
Phase One

Your Own Journal

Days 1 to 5 · Everything stays on your laptop.

Nothing leaves your machine this week. No account, no audience, nothing to be embarrassed about. You are just learning to keep a journal of your own work — what changed, when, and why.

Day 01

Start the Journal

A repository is the whole expedition folder — the maps, the journal, the supply list, and the record of the trip so far. People shorten it to repo. It sounds like a piece of software. It is a folder.

Today you tell git to start watching one.

If you finished 30 Days to Claude Code, use the tool you built on Day 21 or Day 27. Open your terminal and cd into that folder.

Starting here?
If you did not do the Claude Code module, make yourself a practice folder now. This one line at a time, or paste the whole block:
mkdir -p ~/github-lab
cd ~/github-lab
echo "Things I want to remember" > notes.md
echo "Half-formed ideas" > ideas.md
echo "Things to do" > todo.md
That makes a folder called github-lab in your home directory with three small text files in it. Every day from here uses this folder.

Now, from inside that folder, the command that starts everything:

git init

Git answers with one line: Initialized empty Git repository in … and the path to your folder. That is the whole ceremony.

Look at what it made:

ls -a

The -a means show me everything, including hidden things. Your three files are there, and so is something new: a folder called .git. The leading dot is why you never noticed folders like this before — it is how a Mac hides a file from ordinary view.

That .git folder is the journal. Everything git ever remembers about this project lives in there. Do not open it, do not edit it, and do not delete it unless you mean to throw the entire history away. You will never need to touch it directly.

If you want to hear it explained a second way, start Claude in that folder and ask:

I just ran git init in this folder. In plain English, what did that actually do?
What you just learned
A repository is just a folder git has agreed to watch. Making one changes nothing about your files — and nothing has been saved yet.
Day 02

Where Am I?

The working directory is what is spread out on the table right now — the actual files as they exist this second, including the half-finished edit you made ten minutes ago.

One command asks two questions at once: where am I, and what have I changed?

git status

You will see something close to this:

On branch main

No commits yet

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	ideas.md
	notes.md
	todo.md

nothing added to commit but untracked files present (use "git add" to track)

Read it in three pieces.

  • On branch main. Branches are Day 10. For now it is the name of the single path you are on. If yours says master instead, nothing is wrong — that is just a different default name, which depends on your version of git and on a setting called init.defaultBranch. Use whichever name you see.
  • No commits yet. You have never saved an entry. The journal is open to a blank page.
  • Untracked files. Git can see these three files sitting in the folder, but it has not been told to care about them. It is not watching them. It is just noticing them.

Now change something. Add a line to one of the files:

echo "Blood pressure cuff sizes are a menace" >> notes.md

The >> means add to the end, as opposed to a single >, which would wipe the file and start over. Now ask again:

git status

It looks exactly the same. That is not a bug, and it is the most useful thing you will see today. Git is not ignoring your edit — it never agreed to watch notes.md in the first place. To git, that file is one undifferentiated new thing sitting on the table. It has no earlier version to compare against, so it has nothing to say about a line you added.

Once a file is tracked, this same command starts reporting your edits line by line. Getting files tracked is tomorrow.

If any part of that output is still opaque, paste it to Claude and ask:

Explain this git status output to me line by line, like I've never seen it before.
What you just learned
git status is the question you will ask more than any other. When you are lost, it is always the right first move.
Day 03

Packing the Bag

Between the table and the journal there is a third place, and it is the part of git that confuses everyone. It is called the staging area. Think of it as a bag you pack before you leave.

You do not have to take everything on the table. You choose what goes in this trip and what waits for the next one.

Put one file in the bag:

git add notes.md

Git says nothing back. Silence means it worked — that is a habit of these tools worth getting used to. Ask what changed:

git status

Now there are two groups where there was one. Under Changes to be committed: notes.md. Under Untracked files: ideas.md and todo.md.

That split is the whole day. One file is in the bag. Two are still on the table. Nothing has been saved yet — packing is not leaving.

Sit with that for a second, because it is the piece people skip. Git did not decide what goes in. You did, by naming one file and not the other two.

Now pack the bag for real. All three files:

git add .

The . means this folder and everything in it. Run git status one more time and all three are listed under Changes to be committed. The bag is packed. Leave it packed overnight.

Why not always git add .?
Because a commit should be one idea. If you fixed a typo and rewrote a whole section, staging them separately gives you two clean entries instead of one confusing one. Today it does not matter — three new files, one idea. On a real project it will.
What you just learned
Staging is a choice, not a formality. It is the difference between “everything I touched” and “the part I meant.”
Day 04

The Dated Entry

The commit is the dated journal entry. This is what the project looked like at this moment, and here is what I was doing. It is the point of everything so far.

Your bag has been packed since yesterday. Write the entry:

git commit -m "Start tracking notes, ideas, and todo"

The -m is for message — the note you are writing on this entry. Git answers with something like:

[main (root-commit) 9c1306e] Start tracking notes, ideas, and todo and then 3 files changed, 4 insertions(+).

root-commit means this is the first one, the bottom of the stack. That short jumble of letters and numbers is the commit’s name. Every commit gets one, and no two are alike. You will never have to type one from memory.

Read your history back:

git log

One entry, with your name, the email you set on Day 0, the date and time, and your message. That is the journal. It has one page.

If the output fills the screen and your prompt does not come back, git has handed you a pager. Press q to get out. This will catch you at least once.

The same history, compressed:

git log --oneline

One line per commit — short name, then message. With one entry the difference is unimpressive. At forty entries this is the one you will use.

On messages
Write the message for the version of you who reads it in six months with no memory of today. “Fixed stuff” tells that person nothing. “Fix wrong cuff size in notes” tells them everything. It takes the same four seconds.

One more thing, now that it will work. Yesterday you packed the bag. Sooner or later you will put something in it you did not mean to, and you will want it back out before you leave. You could not practice that yesterday, because taking something out of the bag means putting it back the way it was in the last entry — and there was no last entry. Now there is.

First, make a change you will pretend to regret. This adds the word oops to the end of ideas.md:

echo "oops" >> ideas.md

Now put it in the bag, the same way you packed files yesterday:

git add ideas.md

Ask where things stand:

git status

ideas.md is listed under Changes to be committed. It is packed. Notice that git is also telling you how to undo it, in the line just underneath: use “git restore --staged <file>...” to unstage.

Take it at its word. This lifts the file back out of the bag:

git restore --staged ideas.md

Ask again:

git status

ideas.md has moved to Changes not staged for commit. Out of the bag, back on the table. Read that carefully, because it is the reassuring part: your edit is not gone. The word oops is still sitting in the file. Unstaging only unpacks the bag — it never touches your actual work.

Tidy up before tomorrow, so you start Day 5 with a clean table. You know this one from Day 2: a single > wipes the file and starts over, so this puts ideas.md back exactly as it was:

echo "Half-formed ideas" > ideas.md

One last check:

git status

Git says nothing to commit, working tree clean. Nothing on the table, nothing in the bag, one entry in the journal. That sentence is what a finished day looks like.

What you just learned
A commit is a dated entry with your name on it, and the history is only as useful as the messages you write. Staging is also reversible: git restore --staged takes a file back out of the bag without touching a word of your work.
Day 05

What Changed

You have a saved entry now, which means for the first time git can answer a genuinely useful question: what is different between the map on my table and the last one I filed?

Make a change worth looking at:

echo "Ask about the vaccine fridge alarm" >> todo.md

Now ask:

git diff

Past a few lines of bookkeeping, the part that matters:

 Things to do
+Ask about the vaccine fridge alarm

A line with + in front of it is a line you added. A line with - would be one you removed. Lines with a space in front are unchanged, shown so you can see where the addition landed. That is the whole notation, and it is the same notation GitHub uses on every page you will look at from Day 6 onward.

Now the part that trips everyone. Pack the change into the bag:

git add todo.md

And ask the same question again:

git diff

Nothing. Empty. Your change did not go anywhere — but the command that showed it to you ten seconds ago now has nothing to say.

Ask a different way:

git diff --staged

There it is again.

Why it went blank
git diff compares the table to the bag. git diff --staged compares the bag to the last entry. Once you pack something, it is no longer on the table — so the first question has a truthful and entirely useless answer: nothing out here has changed since you packed.

File the entry:

git commit -am "Add fridge alarm to todo"

That -a is new. It means pack everything git already tracks, then commit, saving you a separate git add. Convenient, and worth knowing exactly what it does not do: it ignores untracked files entirely. A brand-new file will sit there unnoticed no matter how many times you run it. And it sweeps up every tracked file you edited, including ones you had not thought about — which is how tidy histories turn muddy. Had you run this yesterday without tidying up, the word oops would now be filed under an entry about a fridge alarm. Use it when you know what is on the table.

Run git log --oneline and you have two entries. That is a history.

What you just learned
git diff and git diff --staged answer two different questions. Knowing which one you are asking is most of the confusion, gone.
Phase Two

Base Camp

Days 6 to 9 · The work goes online.

This week your journal gets a home that isn’t your hard drive. We start by deciding what should never leave the laptop, then send everything else somewhere safe.

Day 06

What Stays Behind

Every expedition has things that never go in the journal. Scratch paper, receipts, the key to the cabin. You carry them and you use them, and you would not write any of them down for a stranger to read.

Git keeps a list for exactly this. It is a plain text file called .gitignore, and anything named in it becomes invisible to git — not deleted, not hidden from you, just never offered up to be committed.

You are doing this the day before anything goes online, on purpose. Once something has been committed and pushed, taking it back is not a matter of deleting it. Far easier to decide what stays behind while the journal is still only on your laptop.

Start by making the exact kind of file you never want to send anywhere. This writes a pretend API key into a file called secrets.txt:

echo "sk-live-51H8xQ2ExampleKeyDoNotUse" > secrets.txt

Now ask git what it can see:

git status

There it is, under Untracked files, with git cheerfully explaining how to include it in what will be committed. That is the whole danger in one line. Git is not being careless — it has no idea what a key is. It sees a file.

So tell it. First make the list:

touch .gitignore

touch creates an empty file. Now put four lines in it. You know this shape from Day 2 — >> adds a line to the end of a file:

echo ".DS_Store" >> .gitignore
echo "*.log" >> .gitignore
echo ".env" >> .gitignore
echo "secrets.txt" >> .gitignore

Those four lines are four separate decisions:

  • .DS_Store — a junk file macOS drops into any folder you open in Finder. It has nothing to do with your work.
  • *.log — the * is a wildcard meaning anything, so this covers every file ending in .log. Log files are usually noise, and occasionally they are noise with a password in the middle of them.
  • .env — the conventional name for the file that holds keys and passwords. If you ever build something that talks to an API, this is where its credentials will live.
  • secrets.txt — the file you just made.

Ask again:

git status

secrets.txt has vanished from the output. In its place is .gitignore itself, listed as untracked, because it is a new file too. Your pretend key has not moved — it is still sitting on your disk exactly where you left it. Git has simply stopped offering to publish it.

This one matters
The most common way people get burned by git is committing a password or an API key and then pushing it somewhere public. Git never forgets, so deleting the file afterward does not help — the key is still sitting in the history, readable by anyone who knows where to look. If it happens to you, the fix is not to delete the file. The fix is to go to whoever issued the key and revoke it. Assume it is compromised the moment it is pushed, because it is.

Now commit the list itself. Unlike the things it names, .gitignore belongs in the journal — it is a decision about the project, and anyone who touches this folder later should inherit it. Pack it:

git add .gitignore

And file the entry:

git commit -m "Add gitignore"

If you want a second opinion about what else should stay behind, start Claude in the folder and ask:

Look at the files in this folder and tell me if any of them contain something that should never be committed to git.
What you just learned
.gitignore is the list of what stays out of the journal. Write it before your first push, not after — a key that has been pushed is a key that has to be revoked.
Day 07

A Note for the Next Traveler

A README is the note you leave for whoever arrives cold. Where they are, what this is for, how to start. The name is shouted because it is meant to be the file nobody can claim they missed.

It also has a specific job on GitHub: it is rendered on the repository’s front page, above everything else. Tomorrow this folder goes online, and this file becomes the page. Writing it today is not busywork — it is writing tomorrow’s landing page.

You could write it from scratch. It is faster to have Claude draft one and then correct what it gets wrong. Start Claude in your folder and ask:

Write a short README.md for this folder. Explain what it is, who it's for, and how someone would start using it. Keep it under 200 words and don't make it sound like a corporate press release.

Then read what it hands you and change it until it sounds like you. It has never met you and it does not know why this folder exists, so it will guess — usually a little grandly. Cut the parts that overclaim. A README that says a practice repository I am using to learn git is more useful than one that says a robust framework for version control workflows, because the first one is true.

Not using Claude?
Make the file yourself with the same move you have used all week, then open it in any text editor and write a few sentences.
echo "# github-lab" > README.md
The # makes that line a heading. That is Markdown, the same plain-text formatting your .md files have been using since Day 1.

When it says what you want it to say, pack it:

git add README.md

And file it:

git commit -m "Add README"
Why bother, if it’s just you?
Because in four months you will be the stranger. You will open this folder with no memory of what you were doing or why, and the README is the fastest note-to-self you will ever write. Everyone who skips it learns this the same way.
What you just learned
A README is the difference between a folder someone can use and a folder someone abandons. On GitHub it is also the front page, so it is the first thing anyone sees.
Day 08

Base Camp

A remote is another known location where a copy of the expedition lives. Your laptop holds the copy you work in. The remote holds the copy that survives your laptop.

origin is git’s default nickname for the remote you started from. It is not a keyword and it carries no special power — it is a name, the way you might call a shared drive “the server.” You will see it constantly from here on.

Today the journal stops being only on your laptop.

One command creates the repository on GitHub and sends your history up to it:

gh repo create github-lab --private --source=. --remote=origin --push

Four flags, and you should be able to name what each one did:

  • --private — only you can see it.
  • --source=. — use this folder. The . means here, the same as it did on Day 3.
  • --remote=origin — name the connection origin.
  • --push — send the commits up right now, instead of creating an empty repository and leaving you to work out the rest.
Private on purpose
We are starting private because a practice folder is exactly where a stray phone number or a patient’s initials ends up. When you have a project you genuinely want to share, you can flip it in the repository settings, or create the next one with --public. Never flip a repository public without reading its history first — remember that what is in the history stays in the history.
No PHI, ever
GitHub is a public-internet service and you have no BAA with it. Nothing that could identify a patient belongs in a repository, private or not. Private is a permission setting, not a safeguard.

Confirm the connection exists:

git remote -v

Two lines come back, both beginning with origin — one marked (fetch) and one marked (push). Git records where it reads from and where it writes to as separate settings. For everything you do here they will be the same address.

Now go and look at the thing you just made:

gh repo view --web

Your browser opens on a real GitHub repository, with your history in it and yesterday’s README rendered on the front page. Notice what is not there: secrets.txt. It was never committed, so it had nothing to travel with. That is Day 6 paying for itself.

One more thing, so that pushing is something you have done on its own. That single gh command did a lot at once, and it is worth separating the ordinary daily motion out of it. Add a line to your notes:

echo "Base camp established" >> notes.md

File the entry, using the shortcut from Day 5:

git commit -am "Note that the repo is live"

And send it up:

git push

Git reports two commit names with .. between them — where the remote was, and where it is now. Refresh the browser tab and your new line is there. That three-step rhythm — change, commit, push — is what most days actually look like from here on.

If push complains about an upstream branch
You may see fatal: The current branch has no upstream branch. It means git knows where the remote is but has not been told that this particular branch belongs there. Git prints the exact command to fix it on the next line, already filled in with your branch name — run that, and plain git push will work every time after.
What you just learned
Your journal now exists in two places. If the laptop dies tonight, the work does not. origin is just the nickname for the other place.
Day 09

Bringing It Home

Cloning is making a complete local copy of what is at base camp — not just the files as they stand today, but the entire journal that got them there.

Pulling is bringing back whatever changed while you were away.

Those two motions are the whole of working with other people. Today you rehearse both with nobody else involved, by being both people yourself. Start from your home directory:

cd ~

Now make a second copy of the repository, in a new folder called github-lab-copy. Replace YOUR-USERNAME with your actual GitHub username:

git clone https://github.com/YOUR-USERNAME/github-lab.git github-lab-copy
Not sure of your username, or tired of typing it
gh already knows who you are, so it can fill that part in for you. This does the same thing as the line above.
gh repo clone github-lab github-lab-copy
Worth seeing the full address at least once, though. That URL is what a clone actually is: a folder, fetched from a location.

Move into the copy:

cd github-lab-copy

Look at what came down:

ls

All four of your files. .gitignore arrived too — ls hides it because of the leading dot, so add -a if you want to see it, the way you did on Day 1. And once again, no secrets.txt. It never left your laptop and it never will.

The history came with it, which is the part that makes a clone different from a downloaded folder:

git log --oneline

Every entry you have written, in a folder that is four minutes old. A clone is not a snapshot of the current files. It is the whole journal.

Now be the other person. Make a change here, in the copy:

echo "Edited from the clone" >> notes.md

Commit it:

git commit -am "Edit from the clone"

And send it to base camp. You do not need to set anything up first — a cloned folder already knows where it came from:

git push

Now go back to the original folder, which has no idea any of that happened:

cd ~/github-lab

Bring down what changed:

git pull

Git reports a Fast-forward and tells you notes.md changed by one line. Read the file and see it for yourself:

cat notes.md

The line you wrote in the other folder is sitting there.

What just happened
You edited a file in one folder and it appeared in another, because both are talking to the same base camp. That is the entire idea behind working with other people, rehearsed with only yourself involved. Replace “the other folder” with “a colleague’s laptop” and nothing about the mechanism changes.
If git refuses to pull
If you made a commit in both folders before pulling, git stops with Need to specify how to reconcile divergent branches and a block of hints. It is not broken and nothing is lost — git is asking you to choose how to combine two histories, and it will not guess. Run the line below once, then run git pull again.
git config pull.rebase false
That tells git to combine them by merging rather than replaying one history on top of the other — the behaviour most people want, and the same choice you will make again on Day 13.
What you just learned
Clone makes a copy, history and all. Pull brings changes home. That is the whole mechanism behind collaboration, and you have now done both halves.
Phase Three

Side Roads

Days 10 to 14 · Trying things without breaking things.

The last five days are the ones people think are hard: branches, pull requests, review, and the dreaded merge conflict. You are going to cause a merge conflict on purpose and fix it, which is the fastest way to stop being afraid of one.

Day 10

The Side Road

A branch is a side road. You turn off the main route, drive down it, make whatever mess you like — and the main route sits there untouched the entire time, exactly as you left it.

That is the whole idea, and it is why people who are good at this are not afraid to try things. They are never trying them on the main road.

Start where you left off:

cd ~/github-lab

Before you build a side road, find out what the main one is called:

git branch --show-current
Whichever name you see
Almost certainly this says main, and you can carry on. If it says master, nothing is wrong — you met that on Day 2, and it just means your git was set up with the older default name. Everywhere the next five days say main, type master instead. It is the only substitution you will need.

Now make the side road:

git switch -c experiment

Git answers Switched to a new branch ‘experiment’. The -c is for create — make this branch, then move onto it. Without it, git switch only moves to a branch that already exists.

Ask where you are:

git status

Two lines: On branch experiment, and nothing to commit, working tree clean. Read the second one carefully. Making a branch changed nothing about your files. Not one byte moved. All that changed is which road you are standing on.

Now make a mess, safely. This adds a deliberately alarming line to your notes:

echo "REWRITE: this whole file is different now" >> notes.md

File it as an entry, the same way you have all week:

git commit -am "Try a different approach in notes"

Git reports [experiment and a commit name, then 1 file changed, 1 insertion(+). Notice the branch name in those brackets. That entry lives on the side road, and nowhere else.

Here is the part that teaches it. Go back to the main road:

git switch main

Now read the file:

cat notes.md

The REWRITE line is gone. Not hidden, not commented out — the file on your disk does not contain it. If you opened notes.md in a text editor right now, it would not be there either.

Go back to the side road:

git switch experiment

And read it again:

cat notes.md

It is back.

Do that twice more. Switch, read, switch, read. This is the one thing worth being slightly repetitive about, because branches stop being confusing the moment you watch a file change under your own hands. It is a physical fact about your folder, not a concept to hold in your head.

Nothing is lost
Switching branches never deletes anything. Both versions of notes.md exist the whole time, safe in the .git folder from Day 1. Git is not editing your work when you switch — it is putting one version on the table and filing the other one away. You are choosing which one is in front of you.
If git refuses to switch
If you have uncommitted edits that would be overwritten, git stops and says so rather than losing them. That is the tool protecting you. Commit what you have, and switch again.
What you just learned
A branch lets you try something risky while the working version stays exactly where it was. This is what makes experimenting safe.
Day 11

Pin a Note to the Board

An issue is a note pinned to the planning board. A bug, a question, an idea, a task you keep meaning to get to. It lives on GitHub next to the code, and it stays there until someone takes it down.

It is not bureaucracy. It is the thing you would otherwise forget by Thursday.

Today is short. Make sure you are in your folder:

cd ~/github-lab

Now file one. This one is about your README, which you wrote on Day 7 and which almost certainly does not tell a stranger what to actually do first:

gh issue create --title "README doesn't say how to actually start" --body "A stranger reading this still wouldn't know the first command to run."

Git and GitHub have very little to say back. You get a URL, and the last part of it is a number — /issues/1, or /issues/4, or whatever your repository is up to. Write that number down. You need it tomorrow, and it will not be the same as anyone else’s.

See what is on the board:

gh issue list

Your issue, with its number in the first column. This command shows open issues only, which is a detail that will matter on Day 13.

Now go and look at it properly. Replace NUMBER with your issue’s number — the same way you replaced YOUR-USERNAME on Day 9:

gh issue view NUMBER --web

Your browser opens on the issue: a title, your description, a comment box, and a sidebar full of things you can safely ignore for now. That is the board.

Make it a real one
Do not invent a fake issue for the exercise. Pick something that genuinely bothers you about this folder — the README, a file with a bad name, a note you never finished. On Day 13 you are going to close it, and closing a real one feels different from closing a prop.
Still no PHI
An issue is text on GitHub, exactly like a commit message. Day 8’s rule has not moved: nothing that could identify a patient, in a private repository or any other kind.
What you just learned
An issue is a note to your future self that other people can also read. Note the number it was given — you will need it on Day 12.
Day 12

Presenting the Route

You have been down the side road and you think it is a good route. A pull request is coming back and presenting it to the group before the official map gets redrawn.

The name is unhelpful and everyone agrees it is unhelpful. People say PR. Read it as here is what I did, have a look before it becomes real.

Get back on the side road:

cd ~/github-lab

And onto the branch:

git switch experiment

Base camp has never heard of this branch — everything you did on Day 10 stayed on your laptop. Send it up:

git push -u origin experiment

Git reports * [new branch] and then a line saying the branch is set up to track origin/experiment. That is the -u doing its work: it tells git that this local branch and that remote branch are the same road, so from now on a plain git push on this branch knows where to go with no arguments. You only ever need -u the first time you push a new branch.

Now open the pull request. Put your issue number from yesterday in place of NUMBER:

gh pr create --title "Rework the notes file" --body "Closes #NUMBER"

You do not have to tell it what to merge into. GitHub uses your repository’s default branch, which is the main road you checked the name of on Day 10.

That Closes line is not decoration
Put the word Closes and an issue number in a pull request description and GitHub will close that issue automatically the moment this is merged. Fixes works the same way. It is a small thing that keeps two records in sync for free, and it is the reason you wrote the number down yesterday.

Go and look at what you made:

gh pr view --web

Today is the day you leave the terminal, on purpose. Take a minute in the browser and find three things.

  • The Conversation tab. Your title, your description, and a note that this pull request will close your issue. That is the Closes line already working.
  • The Files changed tab. Click it. Your REWRITE line, in green, with a + in front of it. This is the same notation you learned on Day 5 — green and + for added, red and - for removed. GitHub is showing you a git diff with better lighting.
  • The comment box. At the bottom of the conversation, and beside any individual line in Files changed. That is where review actually happens.

It is worth saying plainly why this day is in a browser at all. Reading a diff is a visual act. Colour, side-by-side columns, and the ability to click one line and say this one, here are genuinely better than a terminal at this particular job. Insisting otherwise would be dogma. Use the tool that fits the task.

Nothing has moved yet
Look at your main branch on GitHub while the pull request is open. The REWRITE line is not there. A pull request does not change anything — it is a proposal sitting next to the repository, waiting for someone to say yes.
What you just learned
A pull request is a proposal, not a change. Nothing has moved yet. That gap between proposing and merging is where review happens.
Day 13

The Other Travelers

On a real expedition, the other travelers look at your proposed route before anyone commits to it. Where are the cliffs, what did you miss, is there a shorter way round. That is review.

You are on your own here, so today Claude plays that part. This is one of the places where that substitution is genuinely useful rather than a consolation prize — a diff is a small, bounded, self-contained thing, which is exactly what a model reads well.

Back to the folder:

cd ~/github-lab

First, look at the change yourself. This asks what is different between the main road and the side road:

git diff main..experiment

Past the usual few lines of bookkeeping, you get your REWRITE line with a + in front of it — the same thing the Files changed tab showed you yesterday, in the same notation, without the colour. The two dots mean from here to there. If your main branch is called master, use that name instead, as you have been doing since Day 10.

Now get the second opinion. Start Claude in this folder and ask:

You are reviewing a pull request. Run `git diff main..experiment` and read the change. You are the other traveler checking my route: where are the cliffs, what did I miss, and is there a simpler way to do this? Be direct.

Read what comes back with the same scepticism you used on the README draft on Day 7. Some of it will be worth doing. Some of it will be a confident suggestion about a file it does not understand. You are still the one deciding — a reviewer advises, they do not approve on your behalf.

Why review your own work?
Because a diff is a different view than the file. Things that looked fine while you were writing them look obviously wrong in red and green, stripped of everything around them. Solo developers open pull requests on their own repositories for exactly this reason, and it is not a ritual — it is a second pair of eyes made out of a change of format.

Now merge it. This is the moment the main route officially changes:

gh pr merge --merge --delete-branch

Two flags. --merge combines the two histories and keeps both, joining them with a single new commit — the same choice you made on Day 9 when you told git pull.rebase false, rather than replaying one history on top of the other. --delete-branch removes the side road once its contents have arrived, both on GitHub and on your laptop. The road existed to get the work somewhere. It is finished now.

Make sure you are back on the main road:

git switch main

And bring down the merge that happened up at base camp:

git pull

If either of those says you are already there and already up to date, nothing has gone wrong — gh tidies up after itself and may have moved you already. Run cat notes.md and your REWRITE line is now on the main road, where it was not yesterday.

One last thing, and it is the satisfying part:

gh issue list

Your issue from Day 11 is not there. Nothing broke — remember from Day 11 that this command lists open issues only. The note came off the board because the work is done, and you never had to go and take it down. That is the Closes line from yesterday, paying out.

What you just learned
Review is reading your change as a stranger would. Merge is the moment the main route officially changes.
Day 14

Two Maps, One Disagreement

Two travelers came back having edited the same stretch of the map differently. Git can show you the disagreement precisely. It cannot tell you who is right, and it will not pretend to.

That is a merge conflict, and it is the thing that frightens people off git entirely. So we are going to cause one on purpose, right now, and fix it.

In your folder:

cd ~/github-lab

Make a new side road:

git switch -c route-a

Add a line to your notes proposing one way round:

echo "Route A: go around the lake" >> notes.md

Commit it:

git commit -am "Route A"

Now go back to the main road, which knows nothing about Route A:

git switch main

And add a different answer to the very same spot — the end of the same file:

echo "Route B: go over the ridge" >> notes.md

Commit that too:

git commit -am "Route B"

Two branches have now written different things in the same place. Force them together:

git merge route-a

It fails, and the failure is the entire lesson:

Auto-merging notes.md
CONFLICT (content): Merge conflict in notes.md
Automatic merge failed; fix conflicts and then commit the result.

Open the file and look at what git did to it:

cat notes.md

At the bottom, underneath your ordinary lines, you will find this:

<<<<<<< HEAD
Route B: go over the ridge
=======
Route A: go around the lake
>>>>>>> route-a

Those three rows of symbols are seven characters each, and they are just fences. Read them like this:

  • <<<<<<< HEAD — everything below this line is what is on the branch you are standing on. HEAD means where you are now, which is main.
  • ======= — the divider. Nothing more than that.
  • >>>>>>> route-a — everything above this line, back to the divider, is what is coming in from the branch named on the end.

Your job is to make the file say what you want it to say, and delete all three fence lines. That is it. There is no special command and no correct answer — keep Route A, keep Route B, keep both, or write a third thing. Git genuinely does not care, which is precisely why it stopped and asked.

If you want it explained against your actual file rather than this example, ask Claude in that folder:

I have a merge conflict in notes.md. Explain what each part of the conflict is showing me, then help me decide which version to keep.

Now edit the file. Open notes.md in a text editor — the same way you opened your README on Day 7 — delete the three fence lines and the route you do not want, and save. If you keep Route B, the last line of the file should simply read Route B: go over the ridge, with no symbols anywhere near it.

Opening a file in an editor from the terminal
On a Mac, this opens it in TextEdit:
open -e notes.md
On Windows, use this instead:
notepad notes.md
If you already use VS Code or another editor, open it however you normally would. Nothing here depends on which editor you pick.

When the file reads the way you want, tell git you have settled it:

git add notes.md

Check that git agrees:

git status

You want to see All conflicts fixed but you are still merging. If instead it still lists notes.md under Unmerged paths, a fence line is still in the file somewhere — open it again and look.

Finish the merge:

git commit -m "Resolve conflict between routes"

And send it to base camp:

git push

Tidy up the side road, which has served its purpose:

git branch -d route-a
The thing nobody tells you
A merge conflict is not an error, and you have not broken anything. Git is telling you it found a decision it is not allowed to make on your behalf. That is all a conflict has ever been. The reason it looks frightening is that the symbols are ugly and nobody explains them — not because anything is wrong.
The escape hatch
At any point during a conflict, this puts everything back exactly as it was before you typed git merge:
git merge --abort
Git even prints it in the git status output while a conflict is open. You can walk away from a merge at any time, which is worth knowing before the first one you did not schedule.

That is the two weeks. You started with a folder that git had never heard of. You now have a repository with a history, a backup that survives your laptop, a README, a list of things that never leave the building, a branch you took and merged through a reviewed pull request, and an issue that closed itself when the work was done.

And you finished on the part everyone is most afraid of. You caused a merge conflict deliberately and resolved it in about ten minutes. The next one will arrive unannounced, in the middle of something else, and it will be the same five lines of symbols meaning the same simple thing.

You will not remember every command here, and you are not supposed to. What you have now is the shape of the thing — enough to know what to ask for, and enough to recognise the answer when you get it.

What you just learned
Git shows the disagreement. A person decides. You are now past the thing that stops most people.

After the 14 Days

You’ve built:

  • A repository with a history. Every change dated, signed, and readable in one screen.
  • A copy that outlives the laptop. Base camp holds the journal whether or not the machine does.
  • A line between what travels and what stays. The .gitignore keeps secrets.txt at home, permanently.
  • A full round trip. Branch, push, issue, pull request, review, merge, close.
  • A merge conflict you are no longer afraid of, because you caused one on purpose and it took ten minutes.

One piece of housekeeping. You finished with two folders, and only one of them matters. ~/github-lab is the real repository — that is the one to open, the one connected to base camp, the one to keep. ~/github-lab-copy was the Day 9 rehearsal, standing in for a colleague’s laptop so you could push from one place and pull into another. Everything that was ever in it is already at base camp. Drag it to the Trash whenever you like, or leave it sitting there — neither choice costs you anything.

Where to go next, when you’re ready:

  • Run Days 1 through 8 again on a folder of real work — a talk, a dataset, a set of notes. Private repo, README and .gitignore before the first push.
  • Clone a public repository you already use and read its git log --oneline. Someone else’s journal is the fastest way to see what a good entry looks like.
  • Add one other person to a private repo and do Day 9 for real — two laptops instead of two folders. Nothing about the mechanism changes.
  • Put git around the tools you built in 30 Days to Claude Code, so the things you wrote have a history too.

The commands, in one place

cd ~/github-lab
Go to the folder with the journal in it
ls
What’s here
ls -a
What’s here, including the hidden files
cat notes.md
Read a file without opening anything
git status
Where am I, and what have I changed?
git branch --show-current
Which road am I standing on?
git log --oneline
The journal, one line per entry
git diff
What have I changed and not yet packed?
git diff --staged
What is about to go into the next entry?
git diff main..experiment
How do two roads differ?
git add notes.md
Put one file in the bag
git add .
Put everything you changed in the bag
git commit -m "what changed"
Write the dated entry
git commit -am "what changed"
Pack and write in one move. Tracked files only
git push
Send the new entries to base camp
git pull
Bring back whatever changed there
git restore --staged ideas.md
Take a file back out of the bag
git merge --abort
Put everything back the way it was before the merge
git switch -c experiment
Start a side road and step onto it
git switch main
Go back to the main road
git merge route-a
Bring a side road into the road you are on
git branch -d route-a
Delete a road you are finished with
gh auth status
Is this terminal still logged in?
git remote -v
Which base camp does this folder talk to?
gh repo view --web
Open the repository in a browser
git push -u origin experiment
Send a new branch up for the first time
gh issue create --title "..."
Pin a note to the board
gh issue list
What is still open?
gh pr create --title "..."
Propose the side road
gh pr merge --merge --delete-branch
Accept it and tidy up after
git clone URL folder-name
Copy a whole repository, journal and all
“Explain this git status output line by line.”
“What is this error actually telling me?”
“Is this safe to run in this folder?”
“Help me undo my last commit. I have not pushed it.”
“What would happen if I ran this?”

When things go weird

  • It says “fatal: not a git repository.”
    You are standing in the wrong folder. pwd prints where you currently are; cd ~/github-lab takes you back to the one with the journal in it.
  • It says “Please tell me who you are.”
    Git will not sign an entry it cannot attribute. Run the two git config --global lines from Day 0, then commit again.
  • It asked me for a username and password and rejected them.
    GitHub stopped accepting passwords for this in 2021. Run gh auth login again; that replaces the password entirely.
  • My push was rejected.
    Someone pushed first — probably you, from another folder. Run git pull, deal with anything it flags, then push again.
  • It says the current branch has no upstream branch.
    Git knows where base camp is, but has not been told this new branch belongs there. It prints the fix on the next line, already filled in with your branch name, spelled out the long way as git push --set-upstream origin experiment. That is the same flag as the -u in the cheat sheet above. Run whichever git handed you, once, and plain git push works from then on.
  • It refuses to pull and says something about divergent branches.
    You committed in two places before pulling, and git will not guess how to combine them. Run git config pull.rebase false once, then pull again. Nothing is lost while you decide.
  • I committed something I shouldn’t have.
    If it has not been pushed, ask Claude to help you undo the last commit. If it has been pushed and it was a key or a password, go revoke the key first. Deleting the file in a later commit does not remove it from the history.
  • I committed, and my new file wasn’t in it.
    git commit -am only packs files git already tracks. A brand-new file needs git add first. git status will show it sitting there, untracked.
  • I’m on a branch and I don’t know how I got here.
    git status tells you which branch you are on. git switch main takes you home, and nothing about your files changes on the way.
  • I have two folders and I don’t know which one is real.
    ~/github-lab is the real one. ~/github-lab-copy was the Day 9 rehearsal, standing in for someone else’s laptop. Everything in it is already at base camp, so dragging it to the Trash is safe, and leaving it there is fine too.
  • It opened a text editor I don’t recognise and I can’t get out.
    That is vim. Type :q! and press Enter. It shows up when you run git commit without -m, which is why every command here includes one. This happens to everyone exactly once.
  • My question isn’t on this list.
    Ask Echs — the chat button on this page. Or ask Claude in the terminal, in the folder where it happened: it can read the error and the repository at the same time, which is more than this page can do.

The journal is yours, base camp is somewhere you can find it again, and the point was never that you would remember the commands. It is that you can now try the risky thing, because you know how to get back.