From one snapshot to a daily habit
Last session you made history by hand: init, add, commit, log. Today that becomes a loop you run all day, connected to a copy of your repo living on GitHub. The loop is only five commands, but the confidence comes from two things wrapped around it - keeping the wrong files out (.gitignore) and knowing exactly how to undo when something goes sideways. By the end you will push to GitHub, ignore a secret, and pick the safe undo tool for any "oh no" without guessing.
The loop: status, add, commit, push, pull 5 min live
Engineers do not think about these commands individually any more than you think about individual keystrokes when typing. They run one loop: check what changed, stage it, snapshot it, send it up, and pull teammates' work down. The new idea versus Session 1 is the remote - a copy of your repo that lives on GitHub, nicknamed origin. Push sends your commits up; pull brings other people's commits down.
LiveWhy pull before push is the golden habit3 min▶
- Local repo vs GitHub copy. Your commits are yours until you push.
originis the nickname for the GitHub copy - the shared source of truth your teammates also push to. - Push sends up, pull brings down.
git pushuploads your local commits to origin.git pulldownloads commits others pushed and merges them into your branch. - Pull before you push. If a teammate pushed while you were working, pushing straight away gets rejected (your branch has diverged). Pulling first merges their work into yours so your push is a clean fast-forward. Make it reflex: pull, work, add, commit, pull again, push.
A two-person data team both edit the same pipeline config on a Monday. The one who forgets to pull first gets "Updates were rejected because the remote contains work you do not have locally." The one who pulls first sees the teammate's change merge in cleanly and pushes in seconds. Same commands, one habit apart.
Self-studyFetch vs pull, and where origin comes from2 min read▶
git fetch downloads the remote's commits but does not merge them - it just updates your knowledge of what is on origin. git pull is fetch plus merge in one step. When you want to look before you leap, fetch and inspect; when you just want to sync, pull.
origin is set the first time you connect a repo to GitHub - either by gh repo create ... --source . or by git remote add origin <url>. See it any time with git remote -v. It is just a nickname; you can have several remotes, but origin is the conventional name for "the main one".
.gitignore and commit hygiene 4 min live
Not everything belongs in history. Secrets, build junk, and giant dependency folders should never be committed - and .gitignore is how you tell git to pretend they are not there. The stakes are real: once a secret is committed, it lives in history forever, even if you delete the file later. Prevention beats cleanup every time.
LiveIgnore the junk, write messages future-you can grep3 min▶
- .gitignore keeps history clean and safe. Patterns match files git should never stage: secrets (
.env), dependencies (node_modules/), build output (dist/), OS cruft (.DS_Store). GitHub'sgitignorerepo has a ready-made template for every language. - Good commit messages are imperative and explain why. "Add retry to the loader" beats "fixed stuff". The subject line finishes the sentence "If applied, this commit will...". Put the why in the body when it is not obvious - future-you debugging at 2am will thank you.
- Conventional commits (optional but tidy). Prefixes like
feat:,fix:,docs:make history skimmable and drive automatic changelogs (Session 6). Even solo,fix: handle empty CSVreads beautifully ingit log. - Atomic commits. One logical change per commit. If your message needs the word "and", it is probably two commits. Atomic commits make revert and review surgical instead of scary.
The classic incident: a developer commits a .env holding live AWS keys, pushes to a public repo, and within minutes automated scanners find the keys and spin up crypto-mining bills in the thousands. Deleting the file in a later commit does not help - the keys are still sitting in history for anyone to git log. A single .env line in .gitignore prevents the whole story. .gitignore is a security control, not tidiness.
Self-studyAlready tracked? .gitignore will not save you2 min read▶
.gitignore only stops untracked files from being staged. If you already committed config.env, adding it to .gitignore does nothing - git is still tracking it. You have to stop tracking it explicitly:
That removes it from future commits but it is still in past history. For a truly leaked secret, the only real fix is to rotate the secret (make the old one useless) and, if needed, rewrite history - which we flag as an advanced, careful operation. The lesson stands: ignore first, commit never.
The undo toolbox in safe tiers 6 min live
Three tools that people constantly confuse, sorted by how public the mistake is. The whole trick is matching the situation to the tool: have you committed yet? Have you pushed yet? Answer those two questions and the safe tool is obvious. The one rule that keeps you out of trouble: revert in public, reset only locally.
LiveRestore, revert, reset - what each really does4 min▶
git restore- discard working-tree changes, before you commit. Edited a file and want the last committed version back?git restore <file>. Staged something by mistake?git restore --staged <file>unstages it. Nothing committed, nothing risky.git revert- make a NEW commit that undoes a public commit. Safe because it never rewrites history: the bad commit stays, and a new one on top cancels it out. This is the only correct undo for anything you have already pushed and teammates may have pulled.git reset- move the branch pointer, rewriting local history. Powerful and dangerous on shared branches. Three flavors:--softkeeps your changes staged,--mixed(default) keeps them unstaged,--hardthrows the changes away entirely. Reset only commits that are still local and unpushed.
revert it. If it is still local and unpushed, reset is fine. When unsure whether something was pushed, treat it as public and revert.
Self-studyThe safety net: reflog undoes almost anything2 min read▶
Terrified of reset --hard? Git keeps a private log of everywhere HEAD has been - the reflog - for about 90 days. Even after a hard reset that "deleted" commits, they are still reachable:
The reflog is why experienced engineers are calm about local mistakes: almost nothing is truly gone for 90 days. It is local-only, though - it will not recover work you never committed, which is why the habit is: commit early, commit often.
Wire the loop to GitHub ★ 7 min · everyone connects
Open your my-project from Session 1 (or start fresh: mkdir my-project && cd my-project && git init). Confirm with git status.
Create an origin. Easiest: gh repo create my-project --public --source . --push. No gh? Make an empty repo on github.com, then git remote add origin <url> and git push -u origin main.
Edit the README (add a line), then run the full loop once: git status → git add . → git commit -m "Add project description" → git pull → git push.
Open the repo on github.com and confirm your commit is there. That round trip - local edit to visible on GitHub - is the loop you will run all week.
git push -u origin main the first time sets the upstream so that afterwards plain git push and git pull just work. You only need -u once per new branch.
Add a .gitignore and stop a secret ★ 6 min · everyone types
Create a fake secret file: echo "AWS_KEY=totally-fake-12345" > .env. Run git status - git wants to track .env (it shows up as untracked). That is the danger.
Create a .gitignore containing one line, .env: echo ".env" > .gitignore. Run git status again - .env has vanished from the list. Git is now pretending it is not there.
Commit the .gitignore itself: git add .gitignore → git commit -m "Add .gitignore for secrets and junk" → git push. The .gitignore belongs in history; the .env never does.
Discuss: without that one line, the next git add . would have staged your keys and git push would have published them. Deleting .env later would not remove them from history. Prevention is the whole game.
Pick the safe undo, every time ★ 8 min · everyone sorts
You have three undo tools and six "oh no" moments. Drag each situation to the tool that undoes it safely. Green means you matched the tier correctly - the same two-question logic from the decision strip.
On a shared main branch, an engineer who reflexively reaches for reset --hard to "clean up" a pushed commit rewrites history everyone else already has - and every teammate's next pull explodes with conflicts. The engineer who reaches for revert ships a clean undo commit that CI re-checks and everyone pulls painlessly. The habit you build here is the difference between a five-second fix and a team-wide mess.
This week ◐ 25 min total
- Run the full loop daily in your my-project repo: status → add → commit → pull → push. Goal: the loop should feel like one motion by Session 3, not five decisions.
- Write a real .gitignore for the language you actually use - grab GitHub's template (
Node,Python, whatever fits) from github.com/github/gitignore and commit it. - Practice one safe revert: make a throwaway commit, push it, then
git revertit. Watch a new commit appear that undoes it, with history intact. - Read the self-study cards: fetch vs pull, already-tracked files, and the reflog safety net.
Three questions before you go 🎯 ◐ 90 seconds
1 · Why pull before you push?
When the remote has commits you do not, your branch has diverged and the push is rejected. Pulling first merges those commits into yours, so the push becomes a clean fast-forward.
2 · You committed a secret, then deleted the file in a later commit and pushed. Is the secret gone?
History is append-only by default. Deleting a file in a new commit leaves the old commit - and the secret - fully readable via git log. The real fix is to rotate the key so the leaked one is useless.
3 · A bad commit is on a shared branch everyone has pulled. Revert or reset?
Reset rewrites history, which breaks every teammate who already has the old commits. Revert is safe in public: the bad commit stays and a new commit cancels it out. Revert in public, reset only locally.
Official GitHub Skills coverage
This session completes the working content of GitHub's Introduction to Git exercise and adds the hygiene and undo skills every builder needs but the official course leaves thin.
Session 2 cheat sheet · pin this
status → add . → commit -m → pull → push. Run it all day.gh repo create --source . --push, or git remote add origin <url> + push -u origin main.node_modules/, build output, .DS_Store. A security control - a secret once committed lives in history forever.restore. Committed + pushed → revert. Committed, local only → reset. Revert in public, reset only locally.git reflog tracks every HEAD move for ~90 days - most local mistakes are recoverable.