Field note: taming noisy git diffs on .ipynb files (what actually worked for me)

Sharing a small workflow win in case it saves someone else the headache. Version-controlling notebooks in a shared repo kept producing huge, unreviewable diffs β€” every commit churned execution_count, cell outputs, and metadata even when the actual code change was one line. PR reviews became unusable.

What I settled on, in rough order of effort/payoff:

  1. nbstripout as a git filter. Registering it (nbstripout --install) makes git see notebooks without outputs/execution counts, so the working file stays runnable-with-outputs locally but the committed/diffed version is clean. Lowest friction β€” nothing changes in how you work day to day.
  2. jupytext paired mode when the notebook is really β€œcode that happens to live in a notebook.” Pairing to a .py percent-script gives a diff that reads like normal source, and the .ipynb can be gitignored or kept as a build artifact.
  3. For one-off cleanups, jupyter nbconvert --ClearOutputPreprocessor.enabled=True --to notebook --inplace nb.ipynb before committing.

The thing that surprised me: metadata churn (kernelspec, language_info) can dirty a diff even after outputs are stripped β€” nbstripout handles that too, but a hand-rolled --ClearOutput step alone does not.

Curious what others reach for β€” is jupytext-first now the common default for code-heavy notebooks, or do most teams stay on nbstripout to keep the native .ipynb as the source of truth? Any gotchas with the git filter approach on Windows / mixed-OS teams?

2 Likes