Skip to main content

Compare environment-level changes between two git branches or commits

Project description

GitAudit

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.

gitaudit 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. gitaudit reads both refs directly from git (no checkout required) and reports only what's environment-relevant, in one pass.

Installation

pip install gitaudit

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

CLI usage

gitaudit <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
gitaudit main feature/new-auth

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

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

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

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

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

Exit codes

gitaudit 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, gitaudit 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 gitaudit 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 gitaudit import compare
from gitaudit.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=gitaudit --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/ gitaudit

# 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.0.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.0-py3-none-any.whl (21.3 kB view details)

Uploaded Python 3

File details

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

File metadata

  • Download URL: gitinspect-0.1.0.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.0.tar.gz
Algorithm Hash digest
SHA256 3d82a931e9ac818539798991f19fa72b8460f2f9e090e4a46b61bced0c5f948d
MD5 800d83bdc5433779ccc5bf2c674d4dcc
BLAKE2b-256 f7422b8c3742bd233bd4792e7f05003f84306b40519502e101397a16513e35b7

See more details on using hashes here.

File details

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

File metadata

  • Download URL: gitinspect-0.1.0-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.0-py3-none-any.whl
Algorithm Hash digest
SHA256 b768dbfa865a671ec4f978e31af9fa6d4533cada320b90753341286e498a75a5
MD5 28a48c82ac702ea1cc2bb11a1774694c
BLAKE2b-256 6226dae357d8131d93bdbcb4f5026c3bf925d571d6ea65f2a20214ac9e06fab6

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