Skip to main content

Compare environment-level changes between two git branches or commits

Project description

GitInspect

Compare environment-level changes between two git branches or commits — dependencies, environment variables, and Python runtime version — in one unified view. No more manually diffing requirements.txt, .env.example, and .python-version across branches by hand.

gitinspect main feature/new-auth
Dependencies
- flask-session
+ requests==2.32.0 (was 2.28.0)

Environment Variables
+ JWT_SECRET
+ REDIS_URL

Python Runtime
3.11 → 3.12

Why

When reviewing a PR or preparing to merge a branch, "what changed in the environment" is usually scattered across several files and requires manual cross-referencing. gitinspect reads both refs directly from git (no checkout required) and reports only what's environment-relevant, in one pass.

Installation

pip install gitinspect

Requires Python 3.10+ and git installed and available on PATH.

CLI usage

gitinspect <ref_old> <ref_new> [OPTIONS]
Option Description Default
--format, -f Output format: color, text, or json color
--repo-path, -C Path to the git repository . (current directory)
--auto-fetch Fetch missing refs from origin automatically, no prompt off
--verbose, -v Print debug logs to stderr off

Examples

# Compare two branches with colored terminal output
gitinspect main feature/new-auth

# Plain text — safe for piping/redirecting
gitinspect main feature/new-auth --format text > changes.txt

# JSON — for CI pipelines or scripting
gitinspect main feature/new-auth --format json | jq '.dependencies'

# Compare commits or tags directly
gitinspect v1.2.0 v1.3.0

# Run against a repo elsewhere on disk
gitinspect main develop --repo-path ~/projects/my-app

# Non-interactive: auto-fetch missing refs instead of prompting
gitinspect main origin-only-branch --auto-fetch

Exit codes

gitinspect is CI-friendly: it exits 0 when no environment changes are found, and 1 when changes are detected — useful for gating a build on "did the environment change without sign-off."

Code Meaning
0 No environment-level changes found
1 Changes were found
2 Error (bad ref, not a git repo, bad arguments, parse failure)

Missing branches

If a ref doesn't exist locally, gitinspect will:

  1. Check whether it exists on the origin remote.
  2. Prompt you to confirm fetching it (unless --auto-fetch is set).
  3. Fetch it and proceed, or fail with a clear message if it can't be found anywhere.

No raw git errors are ever shown — only clean, actionable messages.

Python SDK

from gitinspect import compare

# Get a structured result
result = compare("main", "feature/new-auth")
for change in result.dep_changes:
    print(change.name, change.old_version, "->", change.new_version)

# Or get pre-rendered output directly
text = compare("main", "feature/new-auth", output_format="text")
json_str = compare("main", "feature/new-auth", output_format="json")

By default, the SDK auto-fetches missing refs (auto_fetch=True) since scripted use can't respond to an interactive prompt. Pass auto_fetch=False to raise RefNotFoundError instead.

from gitinspect import compare
from gitinspect.exceptions import RefNotFoundError, NotAGitRepoError, ParseError

try:
    result = compare("main", "feature/x", repo_path="/path/to/repo")
except RefNotFoundError as e:
    print(f"Ref not found: {e.ref}")
except NotAGitRepoError:
    print("Not a git repository")
except ParseError as e:
    print(f"Could not parse {e.filename}")

What it tracks

File What's compared
requirements.txt Package additions, removals, and == version pins
pyproject.toml [project.dependencies] (PEP 621), requires-python
.env.example Declared environment variable keys (values are not compared)
.python-version Exact pinned Python version

Dependency names are matched case-insensitively (PyPI convention). Environment variable keys are matched case-sensitively (OS convention).

Not yet supported (natural extension points for a future parser):

  • Poetry's [tool.poetry.dependencies]
  • Pipfile / Pipfile.lock
  • package.json (Node environments)
  • Lockfile-level diffing (poetry.lock, requirements-lock.txt)

Architecture

git ref ──▶ git_layer ──▶ parser_layer ──▶ diff_layer ──▶ formatter_layer ──▶ output
            (fetch file    (parse text      (compare two    (render as
             content,       into typed       ParsedSnapshots  text/json/color)
             no checkout)   models)          → DiffResult)
  • git_layer — reads file content at any ref via git show, never touches your working tree or index. Handles missing-branch detection and remote fetching.
  • parser_layer — one function per file type, registered independently. Adding a new file type means writing one parser function — no other layer changes.
  • diff_layer — pure data comparison between two parsed snapshots, no I/O.
  • formatter_layer — one class per output format (text, json, color), all swappable via dependency injection through get_formatter(name).

Everything is fully offline — no network calls except an optional git fetch when a ref is missing locally, and no AI/external APIs are used anywhere.

Development

git clone https://github.com/namanbhola1888/diffenv
cd diffenv
pip install -e ".[dev]"

Running tests

pytest

Tests are split into fast unit tests (test_models.py, test_parser_layer.py, test_diff_layer.py, test_formatter_layer.py, test_exceptions.py — no I/O, no git) and integration tests (test_git_layer.py, test_api.py) that build real temporary git repositories via fixtures in conftest.py rather than mocking subprocess calls, since git_layer's correctness depends on actually invoking git.

# Run with coverage
pytest --cov=gitinspect --cov-report=term-missing

# Run only fast unit tests, skipping integration tests that shell out to git
pytest tests/test_models.py tests/test_parser_layer.py tests/test_diff_layer.py tests/test_formatter_layer.py tests/test_exceptions.py

Building and publishing to PyPI

# 1. Install build tooling
pip install --upgrade build twine

# 2. Build the distribution
python -m build

# 3. Check the build for issues
twine check dist/*

# 4. Upload to TestPyPI first (recommended)
twine upload --repository testpypi dist/*
pip install --index-url https://test.pypi.org/simple/ gitinspect

# 5. Upload to the real PyPI
twine upload dist/*

You'll need a PyPI account and an API token (set via ~/.pypirc or the TWINE_PASSWORD environment variable with __token__ as the username).

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

gitinspect-0.1.1.tar.gz (28.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

gitinspect-0.1.1-py3-none-any.whl (21.3 kB view details)

Uploaded Python 3

File details

Details for the file gitinspect-0.1.1.tar.gz.

File metadata

  • Download URL: gitinspect-0.1.1.tar.gz
  • Upload date:
  • Size: 28.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.5

File hashes

Hashes for gitinspect-0.1.1.tar.gz
Algorithm Hash digest
SHA256 1206da629d1f94ba69c5005408d7c52cb8ff1b004b494856b65c7062e427aadd
MD5 15d9e6752b1aff80b824820db36cef06
BLAKE2b-256 2a4f8eeaa2ef5c446a24aa17bc2789079e3e46c1481d6a044c22fe960ccf2a74

See more details on using hashes here.

File details

Details for the file gitinspect-0.1.1-py3-none-any.whl.

File metadata

  • Download URL: gitinspect-0.1.1-py3-none-any.whl
  • Upload date:
  • Size: 21.3 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.12.5

File hashes

Hashes for gitinspect-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 8eba8430249ce505f358731d1799a4f64751c7eab3727a0d186f178a88758f08
MD5 57ea28b9c25eefdf18ee950174065a09
BLAKE2b-256 88fcf4e6b104cdbf0fd4515fd9594eb069d908135b0dbd43324cc4e08fba3eda

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page