Add support for rewriting hunks if only specific lines match

This commit is contained in:
Sven van Heugten 2026-03-05 20:22:59 +01:00
parent 56d1f03c83
commit 8ebcba623e
No known key found for this signature in database
GPG key ID: D612F88666F4F660
2 changed files with 87 additions and 22 deletions

View file

@ -4,9 +4,9 @@ import re
import sys import sys
def should_include_change( def filter_change_block(
change_lines: list[str], search: str, replace: str change_lines: list[str], search: str, replace: str
) -> bool: ) -> tuple[list[str] | None, bool]:
removed_lines = [] removed_lines = []
added_lines = [] added_lines = []
phase = "minus" phase = "minus"
@ -23,33 +23,66 @@ def should_include_change(
added_lines.append(line[1:]) added_lines.append(line[1:])
continue continue
raise ValueError("Unexpected non-change line in change block.") raise ValueError("Unexpected non-change line in change block.")
transformed_removed = [ transformed_removed = [
re.sub(search, replace, line) for line in removed_lines re.sub(search, replace, line) for line in removed_lines
] ]
return transformed_removed == added_lines if transformed_removed == added_lines:
return change_lines, False
if len(removed_lines) != len(added_lines):
return None, True
kept_lines = []
for removed, added in zip(removed_lines, added_lines):
if re.sub(search, replace, removed) == added:
kept_lines.append(f"-{removed}")
kept_lines.append(f"+{added}")
else:
kept_lines.append(f" {removed}")
if not kept_lines:
return None, True
return kept_lines, True
def should_include_hunk(hunk_text: str, search: str, replace: str) -> bool: def filter_hunk(
lines = hunk_text.splitlines() hunk_lines: list[str], search: str, replace: str
if not lines: ) -> tuple[list[str] | None, bool]:
return True if not hunk_lines:
return None, False
change = [] header = hunk_lines[0]
for line in lines[1:]: body = hunk_lines[1:]
output_body: list[str] = []
left_out = False
i = 0
while i < len(body):
line = body[i]
if line.startswith(("+", "-")): if line.startswith(("+", "-")):
change.append(line) block = []
while i < len(body) and body[i].startswith(("+", "-")):
block.append(body[i])
i += 1
kept_block, block_left_out = filter_change_block(
block, search, replace
)
if kept_block:
output_body.extend(kept_block)
else:
left_out = True
if block_left_out:
left_out = True
continue continue
output_body.append(line)
i += 1
if change: if not any(line.startswith(("+", "-")) for line in output_body):
if not should_include_change(change, search, replace): return None, True
return False
change = []
if change: return [header] + output_body, left_out
if not should_include_change(change, search, replace):
return False
return True
def main() -> None: def main() -> None:
@ -97,11 +130,15 @@ def main() -> None:
kept_hunks = [] kept_hunks = []
for hunk_lines in section["hunks"]: for hunk_lines in section["hunks"]:
hunk_text = "".join(hunk_lines) filtered, hunk_left_out = filter_hunk(
if should_include_hunk(hunk_text, search, replace): hunk_lines, search, replace
kept_hunks.append(hunk_lines) )
if filtered:
kept_hunks.append(filtered)
else: else:
left_out = True left_out = True
if hunk_left_out:
left_out = True
if not kept_hunks: if not kept_hunks:
continue continue

View file

@ -128,3 +128,31 @@ setup() {
assert_success assert_success
assert_output "$(cat "$BATS_TEST_TMPDIR/diff")" assert_output "$(cat "$BATS_TEST_TMPDIR/diff")"
} }
@test "keeps only matching line in a consecutive change block" {
cat >"$BATS_TEST_TMPDIR/old" <<-'EOF'
foo
bar
baz
EOF
sed \
-e 's/foo/foo_changed/' \
-e 's/bar/bar_changed/' \
"$BATS_TEST_TMPDIR/old" >"$BATS_TEST_TMPDIR/new"
sed 's/foo/foo_changed/' "$BATS_TEST_TMPDIR/old" \
>"$BATS_TEST_TMPDIR/new_kept"
git diff --no-index "$BATS_TEST_TMPDIR/old" "$BATS_TEST_TMPDIR/new" \
>"$BATS_TEST_TMPDIR/diff_full" || true
git diff --no-index "$BATS_TEST_TMPDIR/old" "$BATS_TEST_TMPDIR/new_kept" \
>"$BATS_TEST_TMPDIR/diff_expected" || true
run mechanicaldiff.py foo foo_changed <"$BATS_TEST_TMPDIR/diff_full"
assert_failure
expected="$(sed \
-e 's/new_kept/new/g' \
-e '/^index /d' \
"$BATS_TEST_TMPDIR/diff_expected")"
output_normalized="$(printf '%s\n' "$output" | sed -e '/^index /d')"
assert_equal "$expected" "$output_normalized"
}