Problem
Some versions of diff
(notably, busybox diff
in every build of BusyBox I've seen) only produce "unified" diff format output, they don't produce the output that GNU diff
produces by default.
When I try to run diff-refine-hunk
in a vc-diff
buffer (git
repository, if that matters), it fails, and prints the following error in the minibuffer:
Unexpected patch hunk header: --- /tmp/diff*
(Where the asterisk is some number of randomly generated characters, obviously.)
Question
Is there a way I can get diff-refine-hunk
to handle a diff-command
whose output is always just unified diffs?
Detailed Example
Steps
I start with this dummy file, version-controlled in git:
00 00
11 11
22 22
33 33
55 55
66 66
77 77
88 88
99 99
bb bb
cc cc
dd dd
ee ee
ff ff
I edit/replace it with this, and save it to disk (but don't stage/commit in git):
00 00
22 22
33 33
44 44
55 55
66 66
7x 77
88 x8
99 99
aa aa
bx bb
dd dd
ex ee
ff ff
Run vc-diff
with C-x v =
, so far so good.
Run diff-refine-hunk
with C-c C-b
. And if the invoked diff command is BusyBox diff
(or GNU diff
with the -u
option) it fails with the "Unexpected patch hunk header" error. If the diff command is just regular GNU diff
without the default output, it works fine.
Example Output emacs
sees from various diff
s
From what I've gathered since I first posted this, diff-refine-hunk
runs diff-command
on several temporary files (one for each hunk?), and parses the output from each to refine the hunks. What follows are the output diff-command
produces for one of those hunks (the one with the "77" and "88" 'words'). In the case of the failure cases, this hunk is the one diff-refine-hunk
stops at (in the success case, it keeps calling diff-command
on other hunks).
GNU diff
output
When called by diff-refine-hunk
in the setup above, this isthe output of GNU diff
(normal, successful case):
2,3c2,3
< 77
< 77
---
> 7
> x
12,13c12,13
< 88
< 88
---
> x
> 8
GNU diff -u
output
When called by diff-refine-hunk
in the setup above, this is the output of GNU diff -u
(failure case):
--- /tmp/diff122272NoN 2016-11-26 00:43:54.796744201 +0000
+++ /tmp/diff222272ayT 2016-11-26 00:43:54.832744689 +0000
@@ -1,6 +1,6 @@
-
-77
-77
+7
+x
77
77
@@ -9,6 +9,6 @@
88
88
-88
-88
+x
+8
BusyBox diff
output
When called by diff-refine-hunk
in the setup above, this is the output of Busybox diff
(failure case - same output except no datetime in header):
--- /tmp/diff122272bly
+++ /tmp/diff222272NvB
@@ -1,6 +1,6 @@
-
-77
-77
+7
+x
77
77
@@ -9,6 +9,6 @@
88
88
-88
-88
+x
+8
How I captured the above outputs
I created a script like this and pointed diff-command
in emacs to it to test each variant and capture the outputs of each background run of diff (unlike a prior edit to this question, this script captures each diff-command
run in a separate file, since diff-refine-hunk
can call the diff backend program multiple times.
#!/bin/sh
exec busybox diff "$@" | tee "`mktemp /tmp/busybox_diff.XXXXXX`"
#exec diff -u "$@" | tee "`mktemp /tmp/gnu_diff-u.XXXXXX`"
#exec diff "$@" | tee "`mktemp /tmp/gnu_diff.XXXXXX`"
Question - Re-Worded
Is there a way to get diff-refine-hunk
to work correctly when the underlying diff command that it calls in the background on temporary files always produces output in that unified format?
M-x report-emacs-bug
and give an actual problematic diff sample. The author of diff-mode (yours truly) mostly uses unified diffs, so this format should actually work better rather than worse.diff-refine-hunk
shows that it tries to detect the diff format by callingdiff-hunk-style
which in turn uses a fairly complex regexpdiff-hunk-header-re
. Is that not detecting the hunks from the busybox diff output as being in the unified style?Unexpected patch hunk header: --- /tmp/diff*
appears to come from eithersmerge-refine-subst
(seems likely) orsmerge-apply-resolution-patch
(identical code, presumably not the trigger in this case). A grep says no other elisp produces that error message. It is indeed parsing the output of a call todiff-command
with no-u
argument, and consequently expecting non-unified output, as you ascertained. I think there was some confusion over where the error was coming from, though.