Last active
November 22, 2023 06:02
-
-
Save PlasmaPower/795af590f88cfb5e21c5ad9b8a32afdf to your computer and use it in GitHub Desktop.
Git diff intersection finder
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/usr/bin/env python3 | |
import argparse | |
import sys | |
from pathlib import PurePath | |
from unidiff import PatchSet | |
from unidiff.constants import LINE_TYPE_CONTEXT | |
parser = argparse.ArgumentParser(prog="diff-intersection") | |
parser.add_argument("base_patch", help="The base patch to be filtered") | |
parser.add_argument("filter_patches", nargs="*", help="Filter the base patch down to common modifications with these patches") | |
parser.add_argument("--only-hunks", action='store_true', help="Don't filter lines; only filter hunks") | |
parser.add_argument("--ignore-files", default=[], nargs="*", help="Filter out any diffs in these file globs") | |
args = parser.parse_args() | |
with open(args.base_patch) as f: | |
base_patch = PatchSet(f.read()) | |
filter_patches = [] | |
for path in args.filter_patches: | |
with open(path) as f: | |
filter_patches.append(PatchSet(f.read())) | |
def extract_lines(patch, output=set()): | |
for file in patch: | |
for hunk in file: | |
current_line = None | |
for line in hunk: | |
if line.target_line_no is not None: | |
current_line = line.target_line_no | |
if not line.is_context: | |
output.add((file.target_file or file.source_file, current_line)) | |
return output | |
filter_lines = set() | |
for patch in filter_patches: | |
extract_lines(patch, filter_lines) | |
remove_files = [] | |
for file in base_patch: | |
file_ignored = False | |
for ignore_file in args.ignore_files: | |
if PurePath(file.target_file[2:]).match(ignore_file) or PurePath(file.source_file[2:]).match(ignore_file): | |
remove_files.append(file) | |
file_ignored = True | |
if file_ignored: | |
continue | |
found_file_match = False | |
remove_hunks = [] | |
for hunk in file: | |
found_hunk_match = False | |
current_line = None | |
remove_lines = [] | |
for line in hunk: | |
if line.target_line_no is not None: | |
current_line = line.target_line_no | |
if not line.is_context: | |
matches = (file.target_file or file.source_file, current_line) in filter_lines | |
if matches: | |
found_hunk_match = True | |
found_file_match = True | |
elif args.only_hunks: | |
pass | |
elif line.is_added: | |
line.line_type = LINE_TYPE_CONTEXT | |
elif line.is_removed: | |
remove_lines.append(line) | |
if not found_hunk_match: | |
remove_hunks.append(hunk) | |
else: | |
for line in remove_lines: | |
hunk.remove(line) | |
if not found_file_match: | |
remove_files.append(file) | |
else: | |
for hunk in remove_hunks: | |
file.remove(hunk) | |
for file in remove_files: | |
base_patch.remove(file) | |
files_changed = 0 | |
insertions = 0 | |
deletions = 0 | |
for file in base_patch: | |
files_changed += 1 | |
for hunk in file: | |
for line in hunk: | |
if line.is_added: | |
insertions += 1 | |
elif line.is_removed: | |
deletions += 1 | |
sys.stderr.write(f" {files_changed} files changed, {insertions} insertions(+), {deletions} deletions(-)\n") | |
print(base_patch) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment