Python for Actuarial Students · Course 0W
Module 5

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, and diff
  • Write a .gitignore that keeps junk and secrets out
45 min
Concepts & demos
45 min
Version your uv project
Part 1

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 project

Who, when, why

Each has an author, a timestamp, and a message

Differences

See exactly which lines changed, and when

Time travel

Return to any snapshot, any time

An 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.

Part 2

Setting Up

A GitHub profile, Git for Windows, and your identity.

GitHubCreate your GitHub profile

  1. Go to github.com → Sign up
  2. Choose a professional username — future employers will see it (jane-doe-actuary, not xXdragonslayerXx)
  3. Verify your email address
  4. 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 editorPick Notepad or VS Code — not Vim
Initial branch nameOverride → main
PATHGit from the command line and also from 3rd-party software
Line endingsCheckout 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:

WindowsCR LF (\r\n)
Linux / macOSLF (\n)

Mix them and every line of a file looks “changed” in Git — even though nothing visibly did.

git config --global core.autocrlf true
Your working filesCRLF — Windows-friendly
↓ commit converts to LF ↓   ↑ checkout converts to CRLF ↑
RepositoryLF — portable for everyone

ConfigTell 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.

Part 3

The Git Lifecycle

Working tree → staging area → commit history.

Core modelThree places your changes live

Working treeThe files you see and edit in your folder
→git add
Staging areaChanges selected for the next snapshot
→git commit
Commit historyPermanent snapshots in .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))
CommandShows
git statusWhat’s changed / staged / untracked
git diffUnstaged line changes
git diff --stagedWhat the next commit will contain
git log --onelineCompact history
git show 9a7b6f0One commit in full

Red - lines were removed; green + lines were added. Press q to exit long output.

Safety netUndoing things

SituationCommandRisk
Staged something by mistakegit restore --staged fileSafe — edits kept
Want to throw away edits to a filegit restore fileUncommitted edits are lost
Typo in the last commit message (not pushed yet)git commit --amend -m "…"Rewrites last commit
A committed change was wronggit revert <commit>Safe — adds an “undo” commit
See an old version of a filegit show a0b3c11:main.pyRead-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 code

Large raw .csv

Git keeps every version forever; repos balloon. Often confidential, too

Credentials

API keys, passwords, .env files. Once pushed, assume they’re compromised

Policyholder 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 .gitignore itself

.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

  1. On GitHub: New repository → name it lapse_analytics → Private → don’t add a README
  2. 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

  1. Create your GitHub account (with private email) and install Git for Windows
  2. Configure name, email, autocrlf, and default branch
  3. Initialize a repository in C:\actuarial_work\lapse_analytics (Module 4)
  4. Extend .gitignore for Python/Jupyter and prove it works
  5. Make a series of small, atomic commits
  6. 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

  1. Edit check_env.py: add a force-of-mortality column mu (see the diff slide)
  2. git status → git diff → read the red/green lines
  3. git add check_env.py → git diff --staged
  4. Commit with a clear message; check git log --oneline
  5. 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 --version works; name, email, autocrlf are set
  • I can explain working tree → staging → history
  • I can init, add, commit, and write a good message
  • I use status, log --oneline, and diff to inspect state
  • My .gitignore excludes .venv, data, pickles, and secrets
  • I know how to undo safely
  • My lapse_analytics project has at least 4 commits
Up next
Module 6

Markdown, pandoc & Jupyter Notebooks

Plain-text documents that render beautifully — and notebooks that mix code, results, and prose.