Version control, diffs and reproducible output
Why notebook JSON produces unreadable commits, how to strip outputs automatically, and how to make a notebook that renders identically on another machine.
Why notebooks diff badly
An .ipynb is JSON containing source, execution counts, outputs, metadata and base64 images. Changing one number in a plot regenerates a large blob, so a one-line edit can produce a thousand-line diff.
- Execution counters like
[17]change even when the code does not. - Outputs embed PNG data, which no review tool can read usefully.
- Cell ids and metadata order vary between Jupyter versions.
- Two branches touching the same cell almost always conflict.
Stripping outputs automatically
pip install nbstripout
nbstripout --install # configures the git filter for this repository
# check it is wired up
git config --get filter.nbstripout.clean
# strip a single file manually
nbstripout analysis.ipynb# .gitattributes
*.ipynb filter=nbstripout
*.ipynb diff=jupyternotebook
*.ipynb merge=jupyternotebookThe clean filter runs on the way into the index, so outputs are removed from what you commit while your working file keeps them. The diff and merge drivers give you a cell-aware view instead of raw JSON.
Deterministic output
import random, numpy as np
SEED = 42
random.seed(SEED)
np.random.seed(SEED)
# set display options that affect output but not results
np.set_printoptions(precision=4, suppress=True)| Source of difference | Fix |
|---|---|
| Library versions | Pin with pip freeze, a lockfile, or an environment file |
| Randomness | Seed every generator, including in library calls you pass a seed to |
| Floating point ordering | Avoid relying on last-digit equality across machines |
| Timestamps in output | Do not print now() inside a report cell |
| Locale-dependent formatting | Format numbers explicitly rather than relying on defaults |
Combine the strip filter with pinned dependencies and the notebook becomes reviewable code plus a reproducible document.
FAQ
Can I keep outputs for the final report and strip them elsewhere?
nbconvert --execute as a build artefact instead of committing outputs into the notebook.What if a teammate already committed a notebook with a secret in the output?
Related
Notebooks vs scripts: jupytext, nbconvert and papermill A notebook workflow that scales
Last refreshed 2026-09-18.