Subject: bug#62731: 29.0.60; diff-apply-hunk doesn't work for creating new files

From: Dmitry Gutov <[email-masked]>
Date: Wed, 2 Oct 2024 21:57:48 +0300

On 02/10/2024 09:58, Eli Zaretskii wrote:
>> Date: Wed, 2 Oct 2024 03:41:05 +0300
>> From: Dmitry Gutov <[email-masked]>
>>
>>> Observed behavior: diff-mode prompts for the location of "b/foo", and
>>> doesn't allow specifying the location as a non-existent file, meaning
>>> the file can't actaully be created.
>>
>> This is annoying indeed.
>>
>> The attached patch should handle this:
>>
>> * When OLD equals to /dev/null, allow reading non-existing file name.
>> * When NEW starts with b/ or /a, slice that off if such dir does not exist.
>> * Bonus: when the diff is applied in reverse, the checked file names are
>> switched. That helps undo deletions as well. Or renames.
> 
> I think Git uses other prefixes as well (something like i/ and r/,
> perhaps?)

Interesting, I don't remember seeing 'i/' or 'r/' in use.

OT2H, apparently the prefixes are configurable both through command line 
and Git config: 
https://stackoverflow.com/questions/6764953/what-is-the-reason-for-the-a-b-prefixes-of-git-diff

I think that can mean two things: a/ and b/ could be different, and also 
- if a/ and b/ conflict with some dir, the repository could have 
settings to change the diff prefixes. Although not every user will know 
that, perhaps.

> And relying on b/ being an existing directory can cause
> false positives.  How about relying on the "--git" part in the
> "diff --git" header instead, and in the Git case _always_ removing one
> leading directory?

I guess that's an option too.

> (And if this also happens with Hg, include that in
> the test as well.)

With Hg, the format look like this:

   diff -r df0ef194120b -r 2039b18843da accessible/aom/AccessibleNode.cpp

No mention of 'Hg', that is. Could we match "\`diff -r" and

And I suppose this is theoretically a problem for most VC backends, 
although of we only support Git and Hg, it might be fine. That piece of 
code only affect the default file name input anyway.

-r is a real 'diff' option, by the way. When I try it, here's how the 
header looks:

diff '--color=auto' -u -r emacs/aclocal.m4 emacs-master/aclocal.m4
--- emacs/aclocal.m4    2024-08-19 04:06:06.237502671 +0300
+++ emacs-master/aclocal.m4     2024-09-14 05:42:16.907845058 +0300

> Also, what about the opposite case, when NEW is /dev/null? does that
> work correctly?

Not currently or with the proposed patch. It could be fixed along 
similar lines, but I'm not clear on the ideal behavior here. Delete the 
"old" file and kill its buffer? And say that with 'message'? This would 
differ from the regular diff-apply-hunk behavior in that nothing else 
might appear on screen if the buffer was not displayed, and if it was 
displayed - that would kill a visible buffer. Unusual for Emacs behavior 
either way.

Deleting files is something that one can do manually, though, so solving 
this seems lower priority.