Skip to content

Diff two commits

Compare the filesystem snapshots of two commits:

neogit diff <commit-hash-1> <commit-hash-2>

Both refs are commit SHA-1 hashes (the value printed by neogit commit and used by neogit branch). The first is the old commit, the second the new one.

Each line of output is an FSDiffObject:

field meaning
status NEW, DEL, or MOD
is_dir whether the entry is a directory (Tree) or a file (Blob)
path absolute path within the snapshot
old_sha1sum / new_sha1sum content hashes on each side (None where absent)

The diff is recursive by default: an added or removed directory is expanded to all of its descendants, and modified directories are descended into. Because neogit stores directories as nodes, empty directories also appear (unlike git, which cannot track them). A path that switches between file and directory is reported as a DEL of the old node plus a NEW of the new one.

Internally this walks the two Tree graphs keyed by the HAS_CHILD_TREE / HAS_CHILD_BLOB relationship name, so it is genuinely path-aware — moved or duplicated blobs are not confused. Refs that do not resolve to a commit raise a ValueError.

Using it as a library

from neogit.service import Neogit

git = Neogit()
for change in git.diff(old_hash, new_hash):
    print(change.status.name, change.path)

Neogit.diff() returns an iterator of FSDiffObject; pass recursive=False to get only the top-level entries.

Try it on your own git repository

A neat way to see neogit in action is to replay a real git project's history and check that neogit diff reproduces git diff. The end-to-end test tests/e2e/test_diff.py does exactly that — point it at any repository with --repo (it needs running Neo4j + MinIO containers):

poetry run pytest tests/e2e/test_diff.py::test_real_repo \
    -k "FakeObjectStorage and workers-1" \
    --persistdb --repo=/path/to/your/repo --repo-commits=30

Each consecutive pair of (oldest-first) commits is an independent test, labelled with the commit's short hash and summary, so the report reads like a replay of the project's history. Every pair archives both trees, captures them with neogit, and asserts that every add/delete/modify git reports is reproduced by neogit. Without --repo, test_real_repo is skipped; the always-on test_synthetic_repo exercises the same logic on a small built-in repository.

See also