{"course":{"slug":"ai-careers","title":"Working in AI","url":"https://addaly.com/learn/ai-careers","level":"beginner","track":"work"},"lesson":{"slug":"version-control-in-public","title":"Git, and what a reviewer sees before your code","url":"https://addaly.com/learn/ai-careers/version-control-in-public","position":25,"of":85,"minutes":8,"answer":"Your repository is read as evidence that you can work with other people, and the commit history is read before the code is.","keyIdea":"Your repository is read as evidence that you can work with other people, and the commit history is read before the code is."},"body":{"format":"markdown","text":"## The first thing opened, and what is looked for\n\nA hiring manager clicks your GitHub link before they read your CV properly. They are not counting stars. In about forty seconds they are answering one question: *does this look like someone who has worked with others?*\n\nWhat answers it:\n\n- A README at the top of the repository that says what this is and how to run it.\n- A commit history with more than one commit, and messages that are sentences.\n- A `.gitignore`, so the repository is code and not a `.venv` directory and a 200MB CSV.\n- No credentials anywhere.\n- Branches and pull requests, even on a solo project: it shows you know the shape of team work.\n\nWhat answers it badly: one commit called `final`, then `final2`, then `actually final`.\n\n## The commands that are ninety-five per cent of it\n\n```bash\ngit status                 # what have I changed\ngit add -p                 # stage changes hunk by hunk, reading them\ngit commit -m \"Fix timezone handling in the nightly load\"\ngit push\ngit switch -c fix/timezones\ngit pull --rebase\n```\n\n`git add -p` is the one to adopt deliberately. It walks you through your own changes before you commit them, and it catches the debug print, the hard-coded path and the accidental password more often than any tool.\n\nCommit messages: imperative mood, saying *why* where the *what* is not obvious. `Fix double-counting in the weekly report — orders were joined to items before summing` is worth more than `bug fix`, because the next person searching the history is you.\n\n## The secret that cannot be deleted\n\nYou commit a `.env` with an API key, notice, delete the file, commit again. The key is still in the repository, because git stores snapshots and every past snapshot is still there. On a public repository, automated scrapers index new commits within minutes, and several providers now scan public code themselves and revoke keys they find.\n\nThe order of the fix matters and people get it backwards:\n\n1. **Revoke and rotate the key first.** Immediately. Assume it is already copied.\n2. *Then* clean the history, with `git filter-repo` or the provider's support process.\n\nRewriting history first and rotating later leaves a live key in circulation while you do the harder task. Rotating first makes the exposed key worthless in a minute.\n\nPrevent it: put `.env` in `.gitignore` on the first commit of every project, and keep a `.env.example` with the names of the variables and no values.\n\n## Pull requests as writing, not ceremony\n\nA pull request description is the most-read thing you will write at work. The shape that works:\n\n```\nWhat this changes: the nightly load now retries on a\ntimeout instead of failing the whole run.\n\nWhy: the run died three times last week, always on the\nsame slow endpoint, and a rerun always succeeded.\n\nHow I checked: forced a timeout locally and confirmed\ntwo retries then success; ran against last Tuesday's\ndata and the row counts match.\n\nWhat I did not do: no backoff between retries yet —\nworth adding if this recurs.\n```\n\nThat takes four minutes and it is the difference between a review that takes an hour and one that takes five minutes. It is also, not incidentally, an audition for every promotion conversation you will ever have.\n\n## What to do with a large file\n\nDo not commit the 400MB dataset. The repository keeps it forever, clones become painful, and some hosts refuse pushes over a size limit. Commit a small sample (a hundred rows) plus the script that fetches the real thing, and say in the README where the full data lives.\n\n## The free path\n\nGit itself is free and always was. GitHub's free tier includes unlimited private repositories and CI minutes; GitLab and Codeberg are alternatives if you prefer. Nothing in this lesson needs a paid plan. GitHub Copilot and team plans are the paid products; neither affects how your history reads.\n\n## Why it feels arbitrary for a year\n\nGit's commands feel inconsistent because they expose an underlying model rather than hiding it: git stores complete snapshots of your files, addressed by a hash of their content, and branches are just labels pointing at one of those snapshots. Once that clicks, `rebase` and `reset` stop being magic spells. Until it does, you will follow recipes and occasionally break things, and that is the normal experience of everyone who uses it, including people who have used it for a decade.\n\nKeep one recipe handy for the situation that frightens beginners most: you have committed to the wrong branch, or you want to undo the last commit but keep the work.\n\n```bash\ngit reset --soft HEAD~1     # undo the commit, keep the changes staged\n```\n\nNothing is lost. Almost nothing in git is ever lost, which is worth knowing at 23:00 when it appears to be."},"check":{"question":"You realise you committed and pushed a `.env` containing a live API key to a public repository twenty minutes ago. What is the correct first action?","options":[{"text":"Delete the file and push a new commit removing it.","correct":false,"tempting":"The intuitive fix, and the one that does nothing: git keeps every past snapshot, so the key remains readable in the history."},{"text":"Make the repository private, then decide what to do.","correct":false,"tempting":"Reduces further exposure but leaves a key that was public and very likely already harvested, still valid."},{"text":"Rewrite the history with `git filter-repo` to erase the file from every commit.","correct":false,"tempting":"The right eventual step, done in the wrong order: it takes time while the exposed key is still live and usable."},{"text":"Revoke and rotate the key immediately, on the assumption it has already been copied, and only then clean the history.","correct":true}]},"prev":{"slug":"statistics-you-will-be-asked-about","title":"The statistics that come up, and the ones that never do","url":"https://addaly.com/learn/ai-careers/statistics-you-will-be-asked-about"},"next":{"slug":"the-command-line-and-the-free-tier","title":"The terminal, the container, and a cloud bill of zero","url":"https://addaly.com/learn/ai-careers/the-command-line-and-the-free-tier"},"authorship":"Founder and series editor: Dr Mustafa Mun. Written by AI models to a written standard. Exam keys checked blind by a second model: 3,665 of 3,666 agreed. Academic review invited.","review":{"status":"Draft, open for academic review"},"cite":"Addaly (2026) 'Git, and what a reviewer sees before your code', in Working in AI. Edited by M. Mun. Addaly. Available at: https://addaly.com/learn/ai-careers/version-control-in-public (Accessed: 13 October 2026).","licence":{"name":"CC BY-NC-SA 4.0","url":"https://creativecommons.org/licenses/by-nc-sa/4.0/"},"reader":{"schema":1,"title":"Git, and what a reviewer sees before your code","courseTitle":"Working in AI","module":{"index":3,"slug":"the-evidence","title":"The evidence: what gets tested, and how to prove you have it"},"answer":null,"keyIdea":"Your repository is read as evidence that you can work with other people, and the commit history is read before the code is.","hero":{"kind":"plate","image":null,"alt":null,"caption":null},"blocks":[{"t":"h2","id":"the-first-thing-opened-and-what-is-looked-for","runs":[{"text":"The first thing opened, and what is looked for"}]},{"t":"p","runs":[{"text":"A hiring manager clicks your GitHub link before they read your CV properly. They are not counting stars. In about forty seconds they are answering one question: "},{"text":"does this look like someone who has worked with others?","i":true}]},{"t":"p","runs":[{"text":"What answers it:"}]},{"t":"ul","items":[[{"text":"A README at the top of the repository that says what this is and how to run it."}],[{"text":"A commit history with more than one commit, and messages that are sentences."}],[{"text":"A "},{"text":".gitignore","code":true},{"text":", so the repository is code and not a "},{"text":".venv","code":true},{"text":" directory and a 200MB CSV."}],[{"text":"No credentials anywhere."}],[{"text":"Branches and pull requests, even on a solo project: it shows you know the shape of team work."}]]},{"t":"p","runs":[{"text":"What answers it badly: one commit called "},{"text":"final","code":true},{"text":", then "},{"text":"final2","code":true},{"text":", then "},{"text":"actually final","code":true},{"text":"."}]},{"t":"h2","id":"the-commands-that-are-ninety-five-per-cent-of-it","runs":[{"text":"The commands that are ninety-five per cent of it"}]},{"t":"code","lang":"bash","label":"Bash","text":"git status                 # what have I changed\ngit add -p                 # stage changes hunk by hunk, reading them\ngit commit -m \"Fix timezone handling in the nightly load\"\ngit push\ngit switch -c fix/timezones\ngit pull --rebase"},{"t":"p","runs":[{"text":"git add -p","code":true},{"text":" is the one to adopt deliberately. It walks you through your own changes before you commit them, and it catches the debug print, the hard-coded path and the accidental password more often than any tool."}]},{"t":"p","runs":[{"text":"Commit messages: imperative mood, saying "},{"text":"why","i":true},{"text":" where the "},{"text":"what","i":true},{"text":" is not obvious. "},{"text":"Fix double-counting in the weekly report — orders were joined to items before summing","code":true},{"text":" is worth more than "},{"text":"bug fix","code":true},{"text":", because the next person searching the history is you."}]},{"t":"h2","id":"the-secret-that-cannot-be-deleted","runs":[{"text":"The secret that cannot be deleted"}]},{"t":"p","runs":[{"text":"You commit a "},{"text":".env","code":true},{"text":" with an API key, notice, delete the file, commit again. The key is still in the repository, because git stores snapshots and every past snapshot is still there. On a public repository, automated scrapers index new commits within minutes, and several providers now scan public code themselves and revoke keys they find."}]},{"t":"p","runs":[{"text":"The order of the fix matters and people get it backwards:"}]},{"t":"ol","start":1,"items":[[{"text":"Revoke and rotate the key first.","b":true},{"text":" Immediately. Assume it is already copied."}],[{"text":"Then","i":true},{"text":" clean the history, with "},{"text":"git filter-repo","code":true},{"text":" or the provider’s support process."}]]},{"t":"p","runs":[{"text":"Rewriting history first and rotating later leaves a live key in circulation while you do the harder task. Rotating first makes the exposed key worthless in a minute."}]},{"t":"p","runs":[{"text":"Prevent it: put "},{"text":".env","code":true},{"text":" in "},{"text":".gitignore","code":true},{"text":" on the first commit of every project, and keep a "},{"text":".env.example","code":true},{"text":" with the names of the variables and no values."}]},{"t":"h2","id":"pull-requests-as-writing-not-ceremony","runs":[{"text":"Pull requests as writing, not ceremony"}]},{"t":"p","runs":[{"text":"A pull request description is the most-read thing you will write at work. The shape that works:"}]},{"t":"code","lang":"","label":"Text","text":"What this changes: the nightly load now retries on a\ntimeout instead of failing the whole run.\n\nWhy: the run died three times last week, always on the\nsame slow endpoint, and a rerun always succeeded.\n\nHow I checked: forced a timeout locally and confirmed\ntwo retries then success; ran against last Tuesday's\ndata and the row counts match.\n\nWhat I did not do: no backoff between retries yet —\nworth adding if this recurs."},{"t":"p","runs":[{"text":"That takes four minutes and it is the difference between a review that takes an hour and one that takes five minutes. It is also, not incidentally, an audition for every promotion conversation you will ever have."}]},{"t":"h2","id":"what-to-do-with-a-large-file","runs":[{"text":"What to do with a large file"}]},{"t":"p","runs":[{"text":"Do not commit the 400MB dataset. The repository keeps it forever, clones become painful, and some hosts refuse pushes over a size limit. Commit a small sample (a hundred rows) plus the script that fetches the real thing, and say in the README where the full data lives."}]},{"t":"h2","id":"the-free-path","runs":[{"text":"The free path"}],"role":"free"},{"t":"p","runs":[{"text":"Git itself is free and always was. GitHub’s free tier includes unlimited private repositories and CI minutes; GitLab and Codeberg are alternatives if you prefer. Nothing in this lesson needs a paid plan. GitHub Copilot and team plans are the paid products; neither affects how your history reads."}]},{"t":"h2","id":"why-it-feels-arbitrary-for-a-year","runs":[{"text":"Why it feels arbitrary for a year"}]},{"t":"p","runs":[{"text":"Git’s commands feel inconsistent because they expose an underlying model rather than hiding it: git stores complete snapshots of your files, addressed by a hash of their content, and branches are just labels pointing at one of those snapshots. Once that clicks, "},{"text":"rebase","code":true},{"text":" and "},{"text":"reset","code":true},{"text":" stop being magic spells. Until it does, you will follow recipes and occasionally break things, and that is the normal experience of everyone who uses it, including people who have used it for a decade."}]},{"t":"p","runs":[{"text":"Keep one recipe handy for the situation that frightens beginners most: you have committed to the wrong branch, or you want to undo the last commit but keep the work."}]},{"t":"code","lang":"bash","label":"Bash","text":"git reset --soft HEAD~1     # undo the commit, keep the changes staged"},{"t":"p","runs":[{"text":"Nothing is lost. Almost nothing in git is ever lost, which is worth knowing at 23:00 when it appears to be."}]}],"check":{"question":[{"text":"You realise you committed and pushed a "},{"text":".env","code":true},{"text":" containing a live API key to a public repository twenty minutes ago. What is the correct first action?"}],"options":[{"text":[{"text":"Delete the file and push a new commit removing it."}],"correct":false,"tempting":[{"text":"The intuitive fix, and the one that does nothing: git keeps every past snapshot, so the key remains readable in the history."}]},{"text":[{"text":"Make the repository private, then decide what to do."}],"correct":false,"tempting":[{"text":"Reduces further exposure but leaves a key that was public and very likely already harvested, still valid."}]},{"text":[{"text":"Rewrite the history with "},{"text":"git filter-repo","code":true},{"text":" to erase the file from every commit."}],"correct":false,"tempting":[{"text":"The right eventual step, done in the wrong order: it takes time while the exposed key is still live and usable."}]},{"text":[{"text":"Revoke and rotate the key immediately, on the assumption it has already been copied, and only then clean the history."}],"correct":true,"tempting":null}],"order":[1,0,2,3],"why":null},"prev":{"slug":"statistics-you-will-be-asked-about","title":"The statistics that come up, and the ones that never do","minutes":10},"next":{"slug":"the-command-line-and-the-free-tier","title":"The terminal, the container, and a cloud bill of zero","minutes":10}}}