Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/step/check_parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ use std::collections::HashSet;
use std::path::PathBuf;
use xx::file::display_path;

use super::strip_orig_suffix;
use super::types::Step;

/// Attempt to canonicalize a path, falling back to the original if it fails.
Expand Down Expand Up @@ -89,6 +90,8 @@ impl Step {
original_files: &[PathBuf],
stdout: &str,
) -> (Vec<PathBuf>, Vec<PathBuf>) {
let stdout = strip_orig_suffix(stdout);

// Parse unified diff format to extract file names from --- and +++ lines
let mut listed: HashSet<PathBuf> = HashSet::new();

Expand Down
6 changes: 5 additions & 1 deletion src/step/diff.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
use crate::Result;
use std::io::Write;

use super::strip_orig_suffix;
use super::types::Step;

impl Step {
Expand All @@ -19,6 +20,9 @@ impl Step {
/// Automatically detects whether the diff uses `a/` and `b/` prefixes (git-style)
/// and sets the appropriate strip level (`-p1` or `-p0`).
///
/// Also handles Go-style diffs where the `---` line has a `.orig` suffix
/// (e.g., `--- file.go.orig` instead of `--- file.go`).
///
/// # Arguments
///
/// * `stdout` - The unified diff output from the check_diff command
Expand All @@ -33,7 +37,7 @@ impl Step {
debug!("{}: no diff content to apply", self.name);
return Ok(false);
}
let diff_content = stdout;
let diff_content = strip_orig_suffix(stdout);

// Detect if this diff uses a/ and b/ prefixes (git-style)
// Use -p1 to strip prefixes if present, -p0 otherwise
Expand Down
19 changes: 19 additions & 0 deletions src/step/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,22 @@ pub use types::{OutputSummary, Pattern, RunType, Script, Step};
// Re-export for potential external use (currently only used internally)
#[allow(unused_imports)]
pub use filtering::{is_binary_file, is_symlink_file};

/// Strip ".orig" from "---" lines when the next "+++" line doesn't have it.
/// Go tools like gofmt add ".orig" only to the "---" line.
pub(crate) fn strip_orig_suffix(diff: &str) -> String {
let mut result: Vec<String> = Vec::new();
let mut lines = diff.lines().peekable();
while let Some(line) = lines.next() {
if line.starts_with("--- ") && line.contains(".orig") {
if let Some(next) = lines.peek() {
if next.starts_with("+++ ") && !next.contains(".orig") {
result.push(line.replace(".orig", ""));
continue;
}
}
}
result.push(line.to_string());
}
result.join("\n") + "\n"
}
117 changes: 99 additions & 18 deletions test/apply_check_diff.bats
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,12 @@ EOF
# Create a file without trailing newline
printf "hello" > test.txt

git add .
git commit -m "initial"

# Run fix
run hk fix test.txt
assert_success

# The file should have a newline added (from the diff), NOT "FIXED:" prefix
# If git apply worked, content is "hello\n"
# If the apply worked, content is "hello\n"
# If fixer ran, content would be "FIXED:hello"
run cat test.txt
assert_output "hello" # With newline from diff
Expand All @@ -78,7 +75,7 @@ EOF
assert_failure
}

@test "check_diff falls back to fixer when git apply fails" {
@test "check_diff falls back to fixer when apply fails" {
# Create a "formatter" that outputs invalid diff
cat <<'SCRIPT' > formatter.sh
#!/bin/bash
Expand Down Expand Up @@ -114,9 +111,6 @@ EOF

echo "hello" > test.txt

git add .
git commit -m "initial"

# Run fix - should fall back to fixer since diff is invalid
run hk fix test.txt
assert_success
Expand Down Expand Up @@ -171,8 +165,6 @@ hooks {
EOF

echo "original" > test.txt
git add .
git commit -m "initial"

run hk fix test.txt
assert_success
Expand Down Expand Up @@ -222,8 +214,6 @@ hooks {
EOF

echo "original" > test.txt
git add .
git commit -m "initial"

# Run CHECK (not fix) - should fail but NOT modify the file
run hk check test.txt
Expand Down Expand Up @@ -281,8 +271,6 @@ hooks {
EOF

echo "original" > test.txt
git add .
git commit -m "initial"

run hk fix test.txt
assert_success
Expand All @@ -293,7 +281,7 @@ EOF
}

@test "check_diff handles diffs without a/b prefixes" {
# Some tools output diffs without the git-style a/ and b/ prefixes
# Some tools output diffs without the a/ and b/ prefixes
# e.g., "--- src/file.py" instead of "--- a/src/file.py"
cat <<'SCRIPT' > formatter.sh
#!/bin/bash
Expand Down Expand Up @@ -333,13 +321,106 @@ hooks {
EOF

echo "original" > test.txt
git add .
git commit -m "initial"

run hk fix test.txt
assert_success

# Verify the diff was applied (uses -p0 since no a/b prefixes)
run cat test.txt
assert_output "no_prefix_diff"
}

@test "check_diff handles diffs with .orig suffix on --- line" {
# Go tools like gofmt output diffs with .orig suffix on the --- line
# e.g., "--- file.go.orig" instead of "--- file.go"
cat <<'SCRIPT' > formatter.sh
#!/bin/bash
file="$1"
if [ -f "$file" ]; then
# Output diff with .orig suffix (like gofmt -d)
echo "--- $file.orig"
echo "+++ $file"
echo "@@ -1 +1 @@"
echo "-$(cat "$file")"
echo "+gofmt_fixed"
fi
exit 1
SCRIPT
chmod +x formatter.sh

cat <<'SCRIPT' > fixer.sh
#!/bin/bash
echo "FIXER_RAN" > "$1"
SCRIPT
chmod +x fixer.sh

cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
["fix"] {
fix = true
steps {
["fmt"] {
glob = List("*.go")
check_diff = "./formatter.sh {{files}}"
fix = "./fixer.sh {{files}}"
}
}
}
}
EOF

echo "original" > test.go

run hk fix test.go
assert_success

# Verify the diff was applied (should strip .orig suffix)
run cat test.go
assert_output "gofmt_fixed"
}

@test "check_diff works if the file has .orig suffix" {
cat <<'SCRIPT' > formatter.sh
#!/bin/bash
file="$1"
if [ -f "$file" ]; then
echo "--- $file"
echo "+++ $file"
echo "@@ -1 +1 @@"
echo "-$(cat "$file")"
echo "+diffed"
fi
exit 1
SCRIPT
chmod +x formatter.sh

cat <<'SCRIPT' > fixer.sh
#!/bin/bash
echo "FIXER_RAN" > "$1"
SCRIPT
chmod +x fixer.sh

cat <<EOF > hk.pkl
amends "$PKL_PATH/Config.pkl"
hooks {
["fix"] {
fix = true
steps {
["fmt"] {
glob = List("*.orig")
check_diff = "./formatter.sh {{files}}"
fix = "./fixer.sh {{files}}"
}
}
}
}
EOF

echo "original" > test.orig

run hk fix test.orig
assert_success

run cat test.orig
assert_output "diffed"
}
Loading