Skip to main content

Linediff

PyPI version Python 3.8+ License: MIT CI Downloads

A lightweight, syntax-aware diff tool for Python with tree-sitter integration. Linediff understands code structure using tree-sitter parsers, providing more meaningful diffs than traditional line-based tools.

Downloads

  • Install with PyPI: pip install linediff
  • Tracking via pepy

Table of Contents

Features

  • Syntax-aware diffing: Uses tree-sitter parsers to understand code structure
  • Multiple display modes: unified, side-by-side, and inline views
  • Git integration: Can be used as external diff tool
  • Multi-language support: Python, JavaScript, JSON, HTML, CSS, Rust, Go, Java
  • Automatic language detection: Based on file extensions
  • Robust fallbacks: Falls back to line-based diffing when needed
  • Check-only mode: For CI/CD pipelines (exit code indicates if files differ)
  • Color output: ANSI color codes for better readability
  • Stdin support: Can read diff input from stdin

🚀 Quick Start

Get Linediff in Seconds

From PyPI (Easiest Way):

pip install linediff

From Source (For Contributors):

git clone https://github.com/OthmaneBlial/linediff.git
cd linediff
pip install -e .

Supercharge with Syntax Parsers

Unlock the full power of syntax-aware diffing:

# Get all language parsers
pip install linediff[tree-sitter]

# Or pick your favorites
pip install tree-sitter-python tree-sitter-javascript tree-sitter-json

🎯 Usage

Basic Diff Magic

Compare any two files with intelligent diffing:

linediff file1.py file2.py

See the difference? It's not just lines – it's code structure! 🎉

Git Integration

Linediff can be used as Git's external diff tool:

Global Setup

git config --global diff.external linediff

Per-Language Configuration

git config diff.python.command "linediff --language python"
git config diff.python.binary false

Project Configuration

Add to .gitattributes:

*.py diff=python
*.js diff=javascript
*.json diff=json

Check-Only Mode

For CI/CD pipelines, use --check-only to check if files differ without output:

linediff --check-only file1.py file2.py
echo $?  # 0 = identical, 1 = different

Language Override

Override automatic language detection:

linediff --language javascript file1.txt file2.txt

Display Modes

Choose how your diffs are displayed:

Unified (default) - Traditional diff format:

linediff file1.py file2.py

Side-by-side - See changes next to each other with color coding:

linediff --display side-by-side file1.py file2.py

Inline - Changes highlighted with ANSI colors:

linediff --display inline file1.py file2.py

Reading from Stdin

Pipe diff output through linediff:

git diff | linediff

Or provide content via stdin with separator:

cat > /tmp/diff_input << 'EOF'
old content here
---
new content here
EOF

linediff < /tmp/diff_input

🌍 Language Support

Supported Languages:

Language Extensions
Python .py, .pyw, .pyi
JavaScript .js, .jsx, .ts, .tsx, .mjs
JSON .json, .jsonc
HTML .html, .htm, .xml
CSS .css, .scss, .sass, .less
Rust .rs
Go .go
Java .java

For unsupported languages, Linediff falls back to line-based diffing using Python's difflib.

🎨 See Linediff in Action

Python Function Evolution

Before:

def calculate_total(items):
    total = 0
    for item in items:
        total += item.price
    return total

After:

def calculate_total(items, tax_rate=0.08):
    """Calculate total with tax."""
    subtotal = sum(item.price for item in items)
    tax = subtotal * tax_rate
    return subtotal + tax

Diff output:

--- old.py
+++ new.py
@@ -1,5 +1,6 @@
-def calculate_total(items):
-    total = 0
-    for item in items:
-        total += item.price
-    return total
+def calculate_total(items, tax_rate=0.08):
+    """Calculate total with tax."""
+    subtotal = sum(item.price for item in items)
+    tax = subtotal * tax_rate
+    return subtotal + tax

JavaScript Object Makeover

Before:

const config = {
  apiUrl: "https://api.example.com",
  timeout: 5000,
  retries: 3
};

After:

const config = {
  apiUrl: "https://api.example.com",
  timeout: 10000,
  retries: 3,
  headers: {
    "Authorization": "Bearer token"
  }
};

Diff output:

--- config.js
+++ config.js
@@ -1,5 +1,8 @@
 const config = {
   apiUrl: "https://api.example.com",
-  timeout: 5000,
+  timeout: 10000,
   retries: 3,
+  headers: {
+    "Authorization": "Bearer token"
+  }
 };

JSON Config Transformation

Before:

{
  "database": {
    "host": "localhost",
    "port": 5432
  },
  "features": ["auth", "logging"]
}

After:

{
  "database": {
    "host": "prod-db.example.com",
    "port": 5432,
    "ssl": true
  },
  "features": ["auth", "logging", "metrics"]
}

Diff output:

--- config.json
+++ config.json
@@ -1,6 +1,8 @@
 {
   "database": {
-    "host": "localhost",
+    "host": "prod-db.example.com",
     "port": 5432,
+    "ssl": true
   },
   "features": ["auth", "logging", "metrics"]
 }

How It Works

Linediff uses a two-phase approach:

  1. Syntax Parsing: Tree-sitter parsers convert source code into abstract syntax trees (ASTs)
  2. Structural Diffing: A graph-based algorithm finds optimal matches between AST nodes
  3. Fallback: For large files or unsupported languages, falls back to standard difflib

The diff engine implements Dijkstra's algorithm on a graph where nodes represent code elements and edges represent possible matches, insertions, or deletions.

Performance

  • Small files: Syntax-aware diffing provides more meaningful results
  • Large files: Automatically falls back to line-based diffing for performance
  • Memory efficient: Tree parsing is done on-demand with caching
  • Fast startup: Parser instances are cached for repeated operations

🛠️ Development

Get Started Contributing

Set up your dev environment:

git clone https://github.com/OthmaneBlial/linediff.git
cd linediff
python -m venv venv
source venv/bin/activate  # Windows: venv\Scripts\activate
pip install -e ".[dev]"

Run the Test Suite

pytest  # Fast, comprehensive testing

Comprehensive Testing Suite

For an interactive testing experience with various scenarios and display modes:

./test_linediff.sh

This provides a menu-driven interface to test different file types, languages, and display modes with real example files.

Building Documentation

# Generate documentation (if applicable)

Code Quality

# Run linting
flake8 src/

# Run type checking
mypy src/

# Format code
black src/

📚 Documentation

Comprehensive documentation is available in the docs/ directory:

For online documentation, visit https://github.com/OthmaneBlial/linediff/blob/main/docs/index.md.

🤝 Contributing

Join the revolution! We love contributions from developers like you.

Check out our Contributing Guide for the full details.

Quick Contribution Flow

  1. 🍴 Fork the repository
  2. 🌿 Create a feature branch: git checkout -b feature/amazing-idea
  3. 💻 Make your awesome changes
  4. ✅ Add comprehensive tests
  5. 🚀 Ensure all tests pass: pytest
  6. 📤 Submit a pull request

Add a New Language

Expand Linediff's universe:

  1. Add tree-sitter parser to pyproject.toml
  2. Configure language support in parser.py
  3. Write tests for the new language
  4. Update this README with examples

License

This project is licensed under the MIT License - see the LICENSE file for details.

🙏 Acknowledgments

Standing on the shoulders of giants:

  • Powered by Tree-sitter for syntax parsing
  • Built with Python and cutting-edge algorithms
  • Community-driven development with ❤️

Metadata

Release files for linediff 0.1.3

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for linediff 0.1.3
File Size Uploaded
linediff-0.1.3.tar.gz 25.9 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for linediff 0.1.3
File Interpreter ABI Platform
linediff-0.1.3-py3-none-any.whl Python 3 none any Details

Total release size: 41.1 kB

Release files / linediff-0.1.3.tar.gz

Download URL linediff-0.1.3.tar.gz
Size 25.9 kB
Tags Source
SHA-256 checksum
How to use checksums
c3f0ad2e6102f0a4c9d990f2db2cda3eecc3fe75b4a27e9ed5c7b774145dd687
BLAKE2b-256 checksum
How to use checksums
fe5bdf60ac53026b17f5ef24e2df1ed06d9640bc0c5658e455b4802f8e527305
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.7

Release files / linediff-0.1.3-py3-none-any.whl

Download URL linediff-0.1.3-py3-none-any.whl
Size 15.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
b0583d83525328d1df24798a822b3d062ea0af054cb129c82e36963fc103f4b6
BLAKE2b-256 checksum
How to use checksums
5c059827c978ee5c2a6c84c8becd89aa693d48f3cbdb3a8972833fc17b4836c1
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.2.0 CPython/3.12.7

Release history Release notifications | RSS feed

This release

0.1.3 This release

2 release files

0.1.2

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page