patch-fixer 0.5.1
Fixes erroneous git apply patches to the best of its ability.
Requires Python: >=3.10

# patch-fixer
So you asked an LLM to generate a code diff, tried to apply it with `git apply`, and got a bunch of malformed patch errors? Well fear no more, `patch-fixer` is here to save the day... more or less.

This tool can also split patches into separate files based on file lists, making it easy to selectively apply changes.

## Installation
```bash
# Make sure you're using at least python 3.10
python -m venv .venv/
source .venv/bin/activate
pip install patch-fixer
```

## Usage

### Command Line Interface

After installation, `patch-fixer` provides a unified command-line interface:

#### Fixing broken patches:
```bash
patch-fixer fix original broken.patch fixed.patch
```
where:
- `original` is the file or directory you were trying to patch
- `broken.patch` is the malformed patch generated by the LLM
- `fixed.patch` is the output file containing the (hopefully) fixed patch

Options:
- `--fuzzy`: enable fuzzy string matching for better context matching (experimental)
- `--add-newline`: add final newlines when processing "No newline at end of file" markers


#### Splitting patches by file:
```bash
# Split with files specified on command line
patch-fixer split input.patch included.patch excluded.patch -f file1.py file2.py

# Split using a file list
patch-fixer split input.patch included.patch excluded.patch -i files_to_include.txt
```
where:
- `input.patch` is the patch file to split
- `included.patch` will contain changes for the specified files
- `excluded.patch` will contain changes for all other files
- `-f` allows specifying files directly on the command line
- `-i` reads the file list from a text file (one file per line)

### Python API

#### Fixing patches:
```python
from patch_fixer import fix_patch

patch_file = "/path/to/broken.patch"
original = "/path/to/original/state"    # file or directory being patched
with open(patch_file, encoding="utf-8") as f:
    patch_lines = f.readlines()
    
# basic usage
fixed_lines = fix_patch(patch_lines, original)

# with fuzzy matching enabled
fixed_lines = fix_patch(patch_lines, original, fuzzy=True)

# with final newline addition
fixed_lines = fix_patch(patch_lines, original, add_newline=True)

output_file = "/path/to/fixed.patch"
with open(output_file, 'w', encoding='utf-8') as f:
    f.writelines(fixed_lines)
```

#### Splitting patches:
```python
from patch_fixer import split_patch

with open("input.patch", encoding="utf-8") as f:
    patch_lines = f.readlines()

# split to include only specific files
files_to_include = ["./src/main.py", "./src/utils.py"]
included, excluded = split_patch(patch_lines, files_to_include)

# write the split patches
with open("included.patch", 'w', encoding='utf-8') as f:
    f.writelines(included)
    
with open("excluded.patch", 'w', encoding='utf-8') as f:
    f.writelines(excluded)
```

## Advanced Features

### Patch Analysis

Analyze patches without modifying them:

```python
from patch_fixer.analyzer import analyze_patch, get_patch_summary

with open("patch.diff") as f:
    lines = f.readlines()

# Get detailed information
info = analyze_patch(lines)
print(f"Files: {info.total_files}, Added: {info.total_additions}, Removed: {info.total_deletions}")

# Get human-readable summary
print(get_patch_summary(lines))
```

### Custom Processing Hooks

Add custom logic to patch processing:

```python
def log_hunks(hunk_lines, context):
    print(f"Processing hunk in {context['current_file']}")
    return hunk_lines

fixed = fix_patch(patch_lines, original, pre_hunk_hook=log_hunks)
```

## Known Limitations

- When fixing patches with missing `index` lines, the tool requires the files to be in a git repository to regenerate the index. This is only needed for file deletions and renames.
- `patch-fixer` assumes the patch follows git's unified diff format.
- Current implementation is not very robust to corrupted hunk content
  - Much more comprehensive fuzzy string matching is planned

## Local Testing
```bash
git clone https://github.com/ajcm474/patch-fixer.git
cd patch-fixer
pip install -e .[test]
pytest
```
From version `0.3.0` onward (at least until version `1.0`), some test failures are expected
in bugfix versions as I like to use test-driven development to build out new features. 
Please only report test failures if the same test existed and passed in the most recent `0.x.0` version.

## License

This is free and open source software, released under the Apache 2.0 License. See `LICENSE` for details.