Source Control with Git
Snapshots, history, and an infinite undo button — the professional way to keep track of your work.
Module 5 · OverviewBy the end of this module you can…
- Explain what source control is and why folder copies don’t cut it
- Tell Git (the tool) from GitHub (the website)
- Install and configure Git for Windows correctly
- Move changes through the lifecycle: working tree → staging → history
- Inspect your work with
status,log, anddiff - Write a
.gitignorethat keeps junk and secrets out
What Is Source Control?
And why project_v2_final_FINAL is a disaster.
The problemWe’ve all done this
Q3_reserving\
├── reserve_model.xlsx
├── reserve_model_v2.xlsx
├── reserve_model_v2_JS_comments.xlsx
├── reserve_model_v3.xlsx
├── reserve_model_v3_final.xlsx
├── reserve_model_v3_final_FINAL.xlsx
├── reserve_model_v3_final_FINAL_fixed.xlsx
└── reserve_model_USE_THIS_ONE.xlsx
- Which one went to the board?
- What changed between v2 and v3?
- Who changed it, and why?
- What if two people edit “FINAL” at once?
- How do I get back last Tuesday’s version?
Folder-copy versioning answers none of these reliably.
The solutionSource control = an infinite undo button
e41c9a2 (HEAD -> main) Use 2025 lapse assumptions in projection
7b0d315 Fix off-by-one in policy-year calculation
c93a8f0 Add IBNR chain-ladder method
5f12e7b Load claims extract from data/raw
a0b3c11 Initial project structure
Snapshots
Every commit is a complete snapshot of the projectWho, when, why
Each has an author, a timestamp, and a messageDifferences
See exactly which lines changed, and whenTime travel
Return to any snapshot, any timeAn audit trail for models: “show me the code that produced the Q3 numbers” becomes a one-line command.
VocabularyGit vs. GitHub
Git
- A program on your computer
- Tracks history in a hidden
.git\folder inside your project - Works completely offline
- Free, open source, created in 2005 for the Linux kernel
GitHub
- A website that hosts Git repositories
- Off-site backup + sharing + collaboration
- Your public profile doubles as a portfolio
- Alternatives: GitLab, Bitbucket, Azure DevOps
Analogy: Git is like Word’s Track Changes engine; GitHub is like SharePoint for storing and sharing the tracked files.
Setting Up
A GitHub profile, Git for Windows, and your identity.
GitHubCreate your GitHub profile
- Go to github.com → Sign up
- Choose a professional username — future employers will see it (
jane-doe-actuary, notxXdragonslayerXx) - Verify your email address
- Turn on two-factor authentication (Settings → Password and authentication)
Keep your email private: Settings → Emails → tick Keep my email addresses private. GitHub gives you a …@users.noreply.github.com address to use in Git instead.
Work projects with company data belong in your employer’s private organization — never a personal public repo.
InstallGit for Windows
winget install --id Git.Git -e --source winget
…or download from git-scm.com. The installer has many screens; defaults are fine except:
| Default editor | Pick Notepad or VS Code — not Vim |
| Initial branch name | Override → main |
| PATH | Git from the command line and also from 3rd-party software |
| Line endings | Checkout Windows-style, commit Unix-style |
Then, in a new terminal (the installer changed your PATH):
PS> git --version
git version 2.51.0.windows.1
Git for Windows also installs Git Bash (a mini UNIX shell) and Git Credential Manager, which handles GitHub sign-in through your browser.
ConfigLine endings: autocrlf = true
Text files mark the end of each line with invisible characters:
| Windows | CR LF (\r\n) |
| Linux / macOS | LF (\n) |
Mix them and every line of a file looks “changed” in Git — even though nothing visibly did.
git config --global core.autocrlf true
CRLF — Windows-friendlyLF — portable for everyoneConfigTell Git who you are
git config --global user.name "Jane Doe"
git config --global user.email "12345678+jane-doe-actuary@users.noreply.github.com"
git config --global init.defaultBranch main
git config --global core.autocrlf true
git config --global --list # check everything
Your name and email are stamped on every commit you make. Use the same email as your GitHub account (or its noreply address) so commits link to your profile.
--global settings live in C:\Users\Jane\.gitconfig — a plain text file. Open it with notepad ~\.gitconfig.
The Git Lifecycle
Working tree → staging area → commit history.
Core modelThree places your changes live
.git\Why a staging area? You edited three files, but only two belong together. Stage those two, commit them with one clear message, then deal with the third separately.
Like preparing a journal entry: you choose which adjustments go into this posting, review them, then post it with a description.
In practicegit init and git status
PS C:\actuarial_work\lapse_analytics> git init
Initialized empty Git repository in C:/actuarial_work/lapse_analytics/.git/
PS C:\actuarial_work\lapse_analytics> git status
On branch main
No commits yet
Untracked files:
(use "git add <file>..." to include in what will be committed)
.gitignore
.python-version
README.md
check_env.py
main.py
pyproject.toml
uv.lock
git status is your dashboard. Run it constantly — it always tells you what to do next. (Notice .venv is missing: uv’s .gitignore already excludes it.)
In practicegit add and git commit
git add pyproject.toml uv.lock .python-version
git commit -m "Set up uv project with pandas and numpy"
git add main.py check_env.py
git commit -m "Add environment check script"
git add . # stage everything that changed
git commit -m "Describe project in README"
Good commit messages
| ✗ | stuff, fix, asdf, final |
| ✓ | Add chain-ladder IBNR method |
| ✓ | Fix policy-year off-by-one in lapse rates |
Make atomic commits: one logical change per commit. Write the message as a command: “Add…”, “Fix…”, “Remove…”.
In practiceInspecting state: log and diff
PS> git log --oneline
3c1e2d4 (HEAD -> main) Describe project in README
9a7b6f0 Add environment check script
51d0e8a Set up uv project with pandas and numpy
PS> git diff
diff --git a/check_env.py b/check_env.py
@@ -12,3 +12,4 @@
df["px"] = 1 - df["qx"]
-print(df)
+df["mu"] = -np.log(df["px"]) # constant force
+print(df.round(5))
| Command | Shows |
|---|---|
git status | What’s changed / staged / untracked |
git diff | Unstaged line changes |
git diff --staged | What the next commit will contain |
git log --oneline | Compact history |
git show 9a7b6f0 | One commit in full |
Red - lines were removed; green + lines were added. Press q to exit long output.
Safety netUndoing things
| Situation | Command | Risk |
|---|---|---|
| Staged something by mistake | git restore --staged file | Safe — edits kept |
| Want to throw away edits to a file | git restore file | Uncommitted edits are lost |
| Typo in the last commit message (not pushed yet) | git commit --amend -m "…" | Rewrites last commit |
| A committed change was wrong | git revert <commit> | Safe — adds an “undo” commit |
| See an old version of a file | git show a0b3c11:main.py | Read-only |
Once something is committed, it is very hard to lose. Commit early, commit often.
.gitignoreWhat must never be committed
.venv\
Hundreds of MB, machine-specific, rebuilt in seconds from uv.lock*.pkl
Binary, bulky, derived from code — and loading an untrusted pickle can execute codeLarge raw .csv
Git keeps every version forever; repos balloon. Often confidential, tooCredentials
API keys, passwords,.env files. Once pushed, assume they’re compromisedPolicyholder and claims data is personal data. Committing it — especially to a public repo — can be a regulatory breach. Keep data out; commit the code that reads it.
.gitignoreA .gitignore tuned for Python & Jupyter
# Python / uv
__pycache__/
*.py[oc]
.venv/
build/
dist/
*.egg-info
# Jupyter
.ipynb_checkpoints/
# Data & derived artefacts
data/raw/
*.pkl
*.parquet
output/
# Secrets
.env
*.key
credentials*.json
# OS & Office clutter
Thumbs.db
desktop.ini
~$*
- One pattern per line;
#starts a comment folder/ignores a whole directory*is a wildcard- Commit the
.gitignoreitself
.gitignore only affects untracked files. If you already committed something, remove it from tracking with git rm --cached file.
Check it works: git status --ignored
OptionalBacking up to GitHub
- On GitHub: New repository → name it
lapse_analytics→ Private → don’t add a README - Copy the URL it shows you, then connect and push:
git remote add origin https://github.com/jane-doe-actuary/lapse_analytics.git
git push -u origin main # first time: a browser window opens so you can sign in
git push # every time after that
commit saves a snapshot locally. push copies your snapshots to GitHub. They are separate steps.
Hands-on Lab · 45 minVersion your uv project
- Create your GitHub account (with private email) and install Git for Windows
- Configure name, email,
autocrlf, and default branch - Initialize a repository in
C:\actuarial_work\lapse_analytics(Module 4) - Extend
.gitignorefor Python/Jupyter and prove it works - Make a series of small, atomic commits
- Inspect your history and diffs in the terminal — and in VS Code
Lab · Steps 3–5Initialize, ignore, commit
cd C:\actuarial_work\lapse_analytics
git init # "Reinitialized…" is fine if uv already did it
notepad .gitignore # add the Jupyter / data / secrets sections
mkdir data\raw; Set-Content data\raw\claims.csv "id,amount`n1,1200"
Set-Content .env "API_KEY=not-a-real-key"
git status # claims.csv and .env must NOT appear
git status --ignored # …but they show up here
git add .gitignore
git commit -m "Ignore venv, data, notebook checkpoints and secrets"
git add pyproject.toml uv.lock .python-version
git commit -m "Set up uv project with pandas, numpy, openpyxl"
git add main.py check_env.py README.md
git commit -m "Add starter scripts and README"
git log --oneline
Lab · Step 6Change, inspect, commit — then look in VS Code
- Edit
check_env.py: add a force-of-mortality columnmu(see the diff slide) git status→git diff→ read the red/green linesgit add check_env.py→git diff --staged- Commit with a clear message; check
git log --oneline - Break something on purpose, then rescue it with
git restore
Diffs in VS Code (preview of Module 7)
winget install --id Microsoft.VisualStudioCode -e
# new terminal, then:
code .
Open the Source Control panel (Ctrl+Shift+G) and click a changed file for a side-by-side diff.
RecapModule 5 checklist
- I have a GitHub account with 2FA and a private email
git --versionworks; name, email,autocrlfare set- I can explain working tree → staging → history
- I can
init,add,commit, and write a good message
- I use
status,log --oneline, anddiffto inspect state - My
.gitignoreexcludes.venv, data, pickles, and secrets - I know how to undo safely
- My
lapse_analyticsproject has at least 4 commits
Markdown, pandoc & Jupyter Notebooks
Plain-text documents that render beautifully — and notebooks that mix code, results, and prose.