unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#26028: 26.0.50; epatch for multifile patches
@ 2017-03-08 17:29 Arseny Sher
  2017-03-08 19:17 ` Eli Zaretskii
  0 siblings, 1 reply; 16+ messages in thread
From: Arseny Sher @ 2017-03-08 17:29 UTC (permalink / raw)
  To: 26028

Hello,

I am having trouble using epatch while applying multifile patches.
Consider the following simple example:

ars@ars-thinkpad ~/tmp $ mkdir -p old/src
ars@ars-thinkpad ~/tmp $ echo "void main() { }" > old/src/hello.c
ars@ars-thinkpad ~/tmp $ mkdir -p new/src
ars@ars-thinkpad ~/tmp $ echo "int main() { return 0; }" > new/src/hello.c
ars@ars-thinkpad ~/tmp $ diff -cr old/ new/ > tmp.patch
ars@ars-thinkpad ~/tmp $ cat tmp.patch
diff -cr old/src/hello.c new/src/hello.c
*** old/src/hello.c	2017-03-08 14:29:24.743846995 +0300
--- new/src/hello.c	2017-03-08 14:29:32.399846824 +0300
***************
*** 1 ****
! void main() { }
--- 1 ----
! int main() { return 0; }


Now I start emacs and do M-x epatch. First of all, it asks me for
patch buffer or file, I point to '~/tmp/tmp.patch'. Then it asks for
directory to patch, I say '~/tmp/old' and ediff complains

Ediff has inferred that
	/home/ars/tmp/old/hello.c
is assumed to be the target for this patch.  However, this file does not exist.

Please enter an alternative patch target ...


because it ignores the path ('src' directory in this case) to the file.
Ediff manual says: "Ediff can recognize multi-file patches only if they
are in the context format or GNU unified format. All other patches are
treated as 1-file patches. Ediff is [hopefully] using the same algorithm
as patch to determine which files need to be patched." If I understand
this correctly, my scenario should work, so it seems like a bug.

I tried it on GNU Emacs versions 25.1, 25.2 RC2 and daily build
26.0.50.2, sometimes on clean Emacs (emacs -q), and sometimes with
unified diff format (diff -u), the result is the same.

I use GNU/Linux, Linux Mint 17 (based on Ubuntu 14.04).

Below goes autogenerated info from report-emacs-bug.

In GNU Emacs 26.0.50.2 (x86_64-pc-linux-gnu, GTK+ Version 3.10.8)
 of 2016-10-26 built on lgw01-50
Windowing system distributor 'The X.Org Foundation', version 11.0.11501000
System Description:	Linux Mint 17 Qiana

Recent messages:
my-misc loaded
Loading /home/ars/.emacs.d/recentf...done
Cleaning up the recentf list...done (0 removed)
my-text-processing loaded
Loading font-lock...done
my-windows-frames-funcs loaded
coding styles loaded
my-gdb-stuff loaded
Loading quail/cyrillic...done
For information about GNU Emacs and the GNU system, type C-h C-a.

Configured using:
 'configure --build=x86_64-linux-gnu --prefix=/usr
 '--includedir=${prefix}/include' '--mandir=${prefix}/share/man'
 '--infodir=${prefix}/share/info' --sysconfdir=/etc --localstatedir=/var
 '--libdir=${prefix}/lib/x86_64-linux-gnu'
 '--libexecdir=${prefix}/lib/x86_64-linux-gnu' --disable-maintainer-mode
 --disable-dependency-tracking --prefix=/usr --sharedstatedir=/var/lib
 --program-suffix=-snapshot --with-modules=yes --with-x=yes
 --with-x-toolkit=gtk3 --with-xwidgets=yes 'CFLAGS=-g -O2
 -fstack-protector --param=ssp-buffer-size=4 -Wformat
 -Werror=format-security' CPPFLAGS=-D_FORTIFY_SOURCE=2
 'LDFLAGS=-Wl,-Bsymbolic-functions -Wl,-z,relro''

Configured features:
XPM JPEG TIFF GIF PNG RSVG IMAGEMAGICK SOUND GPM DBUS GCONF GSETTINGS
NOTIFY LIBSELINUX GNUTLS LIBXML2 FREETYPE M17N_FLT LIBOTF XFT ZLIB
TOOLKIT_SCROLL_BARS GTK3 X11 MODULES XWIDGETS

Important settings:
  value of $LC_ALL: en_US.UTF-8
  value of $LC_MONETARY: ru_RU.UTF-8
  value of $LC_NUMERIC: ru_RU.UTF-8
  value of $LC_TIME: ru_RU.UTF-8
  value of $LANG: en_US.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Fundamental

Minor modes in effect:
  gud-tooltip-mode: t
  diff-hl-flydiff-mode: t
  global-diff-hl-mode: t
  diff-auto-refine-mode: t
  global-flycheck-mode: t
  projectile-mode: t
  ivy-mode: t
  show-paren-mode: t
  my-global-fci-mode: t
  global-linum-mode: t
  dired-omit-mode: t
  delete-selection-mode: t
  cua-mode: t
  recentf-mode: t
  savehist-mode: t
  global-undo-tree-mode: t
  global-auto-revert-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  blink-cursor-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  buffer-read-only: t
  column-number-mode: t
  line-number-mode: t
  transient-mark-mode: t

Load-path shadows:
~/.emacs.d/static_packages/all-the-icons.el/all-the-icons hides ~/.emacs.d/static_packages/all-the-icons
/usr/share/emacs/site-lisp/dictionaries-common/ispell hides /usr/share/emacs/26.0.50/lisp/textmodes/ispell
/usr/share/emacs/site-lisp/dictionaries-common/flyspell hides /usr/share/emacs/26.0.50/lisp/textmodes/flyspell
/usr/share/emacs/site-lisp/latex-cjk-thai/thai-word hides /usr/share/emacs/26.0.50/lisp/language/thai-word

Features:
(shadow sort mail-extr emacsbug message puny format-spec rfc822 mml
mml-sec epa derived epg gnus-util rmail rmail-loaddefs mm-decode
mm-bodies mm-encode mail-parse rfc2231 mailabbrev gmm-utils mailheader
sendmail rfc2047 rfc2045 ietf-drums mm-util mail-prsvr mail-utils colir
color ggtags etags xref project windmove quail gud my-gdb-stuff
diff-hl-flydiff diff-hl edmacro kmacro face-remap vc-hg vc-git vc-dir
ewoc vc vc-dispatcher diff-mode flycheck json map find-func rx subr-x
coding-styles cc-styles cc-align cc-engine cc-vars cc-defs projectile
advice grep compile comint ansi-color ibuf-ext ibuffer ibuffer-loaddefs
ivy ivy-overlay ffap thingatpt xcscope ring paren disp-table
fill-column-indicator easy-mmode linum dired-x dired dired-loaddefs
my-windows-frames-funcs all-the-icons all-the-icons-faces
data-weathericons data-octicons data-fileicons data-faicons
data-alltheicons font-lock+ cl dash my-text-processing drag-stuff delsel
cua-base recentf tree-widget wid-edit savehist undo-tree diff autorevert
filenotify wombat-theme my-misc finder-inf tex-site info package
epg-config url-handlers url-parse auth-source cl-seq eieio eieio-core
cl-macs eieio-loaddefs password-cache url-vars seq byte-opt gv bytecomp
byte-compile cl-extra help-mode easymenu cconv cl-loaddefs pcase cl-lib
time-date mule-util tooltip eldoc electric uniquify ediff-hook vc-hooks
lisp-float-type mwheel term/x-win x-win term/common-win x-dnd tool-bar
dnd fontset image regexp-opt fringe tabulated-list newcomment elisp-mode
lisp-mode prog-mode register page menu-bar rfn-eshadow timer select
scroll-bar mouse jit-lock font-lock syntax facemenu font-core
term/tty-colors frame cl-generic cham georgian utf-8-lang misc-lang
vietnamese tibetan thai tai-viet lao korean japanese eucjp-ms cp51932
hebrew greek romanian slovak czech european ethiopic indian cyrillic
chinese charscript case-table epa-hook jka-cmpr-hook help simple abbrev
obarray minibuffer cl-preloaded nadvice loaddefs button faces cus-face
macroexp files text-properties overlay sha1 md5 base64 format env
code-pages mule custom widget hashtable-print-readable backquote
dbusbind inotify dynamic-setting system-font-setting font-render-setting
xwidget-internal move-toolbar gtk x-toolkit x multi-tty
make-network-process emacs)

Memory information:
((conses 16 288745 21478)
 (symbols 48 35267 1)
 (miscs 40 517 1228)
 (strings 32 74748 9913)
 (string-bytes 1 2122820)
 (vectors 16 35442)
 (vector-slots 8 683178 9673)
 (floats 8 665 276)
 (intervals 56 444 300)
 (buffers 976 13)
 (heap 1024 41802 1607))





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-03-08 17:29 bug#26028: 26.0.50; epatch for multifile patches Arseny Sher
@ 2017-03-08 19:17 ` Eli Zaretskii
  2017-03-08 21:53   ` Arseny Sher
  0 siblings, 1 reply; 16+ messages in thread
From: Eli Zaretskii @ 2017-03-08 19:17 UTC (permalink / raw)
  To: Arseny Sher; +Cc: 26028

> From: Arseny Sher <sher-ars@yandex.ru>
> Date: Wed, 08 Mar 2017 20:29:52 +0300
> 
> ars@ars-thinkpad ~/tmp $ mkdir -p old/src
> ars@ars-thinkpad ~/tmp $ echo "void main() { }" > old/src/hello.c
> ars@ars-thinkpad ~/tmp $ mkdir -p new/src
> ars@ars-thinkpad ~/tmp $ echo "int main() { return 0; }" > new/src/hello.c
> ars@ars-thinkpad ~/tmp $ diff -cr old/ new/ > tmp.patch
> ars@ars-thinkpad ~/tmp $ cat tmp.patch
> diff -cr old/src/hello.c new/src/hello.c
> *** old/src/hello.c	2017-03-08 14:29:24.743846995 +0300
> --- new/src/hello.c	2017-03-08 14:29:32.399846824 +0300
> ***************
> *** 1 ****
> ! void main() { }
> --- 1 ----
> ! int main() { return 0; }
> 
> 
> Now I start emacs and do M-x epatch. First of all, it asks me for
> patch buffer or file, I point to '~/tmp/tmp.patch'. Then it asks for
> directory to patch, I say '~/tmp/old' and ediff complains
> 
> Ediff has inferred that
> 	/home/ars/tmp/old/hello.c
> is assumed to be the target for this patch.  However, this file does not exist.

I think you should point it to ~/tmp, not ~/tmp/old.  IOW, the
directory should be the one relative to which the file names in the
patch file will correctly point to the files.





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-03-08 19:17 ` Eli Zaretskii
@ 2017-03-08 21:53   ` Arseny Sher
  2017-03-09  1:41     ` Tino Calancha
  2017-05-23 11:26     ` Tino Calancha
  0 siblings, 2 replies; 16+ messages in thread
From: Arseny Sher @ 2017-03-08 21:53 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 26028

Eli Zaretskii <eliz@gnu.org> writes:

> I think you should point it to ~/tmp, not ~/tmp/old.  IOW, the
> directory should be the one relative to which the file names in the
> patch file will correctly point to the files.

Okay, it might be not very convenient (as you might have several
versions of project inside ~/tmp, and ediff will ask you which one is to
be patched), but it works. However, how then I am expected to apply
patches generated by VCS, where paths are prefixed with a/ and b/?
Again, let's consider some simple example:

mkdir -p proj/src
cd proj
echo "void main() {}" > src/hello.c
git init
git add src/ && git commit -m "commit"
echo "int main() { return 0; }" > src/hello.c
git diff > ../tmp.patch
git reset --hard HEAD

cat ../tmp.patch
diff --git a/src/hello.c b/src/hello.c
index ab73b3a..76e8197 100644
--- a/src/hello.c
+++ b/src/hello.c
@@ -1 +1 @@
-void main() {}
+int main() { return 0; }

How should I apply tmp.patch to proj?

Yet am not still sure that this is not a bug; some other guy said that
this behaviour doesn't make sense, and similar bug was fixed recently:
http://lists.gnu.org/archive/html/help-gnu-emacs/2017-03/msg00064.html
http://emacs.1067599.n8.nabble.com/bug-25010-26-0-50-epatch-might-parse-wrongly-a-file-name-td413747.html

And while I am here, I would like to express my gratitude to ediff
developers; it is in overall a great tool, and it is a pleasure to read
its manual. Thank you guys!





^ permalink raw reply related	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-03-08 21:53   ` Arseny Sher
@ 2017-03-09  1:41     ` Tino Calancha
  2017-03-09 11:44       ` Arseny Sher
  2017-05-23 11:26     ` Tino Calancha
  1 sibling, 1 reply; 16+ messages in thread
From: Tino Calancha @ 2017-03-09  1:41 UTC (permalink / raw)
  To: Arseny Sher; +Cc: Michael Heerdegen, 26028, tino.calancha

Arseny Sher <sher-ars@yandex.ru> writes:

> How then I am expected to apply
> patches generated by VCS, where paths are prefixed with a/ and b/?
Ediff will skip those 'a/', 'b/' automatically.
> Again, let's consider some simple example:
>
> mkdir -p proj/src
> cd proj
> echo "void main() {}" > src/hello.c
> git init
> git add src/ && git commit -m "commit"
> echo "int main() { return 0; }" > src/hello.c
> git diff > ../tmp.patch
> git reset --hard HEAD
>
> cat ../tmp.patch
> diff --git a/src/hello.c b/src/hello.c
> index ab73b3a..76e8197 100644
> --- a/src/hello.c
> +++ b/src/hello.c
> @@ -1 +1 @@
> -void main() {}
> +int main() { return 0; }
>
> How should I apply tmp.patch to proj?
Ediff uses the `default-directory' for the patch buffer as a hint.
When i apply patches generated with VCS i _always_ set the
`default-directory' of the patch buffer equal as the root directory of
the project.  That makes multi patches work OK.
This is how i would do in your exmple:
M-: (dired "/tmp/proj") RET
C-x b *p* RET ; Now copy the patch here in your favourite way.
C-x 4 r /tmp/tmp.patch RET
C-x h M-w C-x o C-y
M-x epatch RET y *p* RET /tmp/proj/src RET

> Yet am not still sure that this is not a bug; some other guy said that
> this behaviour doesn't make sense, and similar bug was fixed recently:
> http://lists.gnu.org/archive/html/help-gnu-emacs/2017-03/msg00064.html
> http://emacs.1067599.n8.nabble.com/bug-25010-26-0-50-epatch-might-parse-wrongly-a-file-name-td413747.html
I tend to agree that it might be possible to improve how Ediff
handle these things.  That said, as a user i haven't being disturbed
so much by this inconvenience.  I am also worry if i could break other
important feature while trying to improve this; that's essentially why
i didn't try hard to work in this issue.

If you have some an idea about how to prepare a patch improving
Ediff on this area i am welcome to support you in that task.

Regards,
Tino





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-03-09  1:41     ` Tino Calancha
@ 2017-03-09 11:44       ` Arseny Sher
  0 siblings, 0 replies; 16+ messages in thread
From: Arseny Sher @ 2017-03-09 11:44 UTC (permalink / raw)
  To: Tino Calancha; +Cc: Michael Heerdegen, 26028

Tino Calancha <tino.calancha@gmail.com> writes:

>> How should I apply tmp.patch to proj?
> Ediff uses the `default-directory' for the patch buffer as a hint.
> When i apply patches generated with VCS i _always_ set the
> `default-directory' of the patch buffer equal as the root directory of
> the project.  That makes multi patches work OK.
> This is how i would do in your exmple:
> M-: (dired "/tmp/proj") RET
> C-x b *p* RET ; Now copy the patch here in your favourite way.
> C-x 4 r /tmp/tmp.patch RET
> C-x h M-w C-x o C-y
> M-x epatch RET y *p* RET /tmp/proj/src RET

Honestly, I didn't understand your answer.
1) I don't see any effects of changing the default directory. I set
   default directory of buffer with patch to ~/tmp/proj as you say, then
   run epatch, firstly point it to this buffer, then specify ~/tmp/proj
   (because this is project root, and I want to patch the whole project!)
   as target directory and it fails again, exactly as before.
2) Interpreting your instructions literally, I should specify
   ~/tmp/proj/src as target directory to ediff (answer to its last
   question). Well, then it works, but this is precisely the behaviour
   which doesn't make any sense to me: ediff just ignores the path to
   file ('src' directory here). And it happens totally independent of
   default directory value, I tried with different ones.
   I suppose that the whole point of applying multifile patch is to allow
   ediff deduce which files (with full paths) it needs to patch
   automatically, without manually specifying dirs like 'src'. Imagine
   we have also hello.h file under proj/include; then, if I tell ediff
   to use ~/tmp/proj/src dir as target directory as you say, it will
   successfully patch hello.c, but it will fail again attempting to
   patch hello.h, because there is no such file proj/src/hello.h.

So, it is hard for me to suggest better epatch behaviour because I
actually don't comprehend its current one. I don't know why other people
don't encounter these problems. Probably they just apply patches via git
and only then look at the diff via ediff, and I should go the same
way...






^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-03-08 21:53   ` Arseny Sher
  2017-03-09  1:41     ` Tino Calancha
@ 2017-05-23 11:26     ` Tino Calancha
  2017-05-23 22:45       ` Michael Heerdegen
  1 sibling, 1 reply; 16+ messages in thread
From: Tino Calancha @ 2017-05-23 11:26 UTC (permalink / raw)
  To: Arseny Sher; +Cc: Michael Heerdegen, 26028, Kaushal Modi, tino.calancha

Arseny Sher <sher-ars@yandex.ru> writes:

> Eli Zaretskii <eliz@gnu.org> writes:
>
>> I think you should point it to ~/tmp, not ~/tmp/old.  IOW, the
>> directory should be the one relative to which the file names in the
>> patch file will correctly point to the files.
>
> Okay, it might be not very convenient (as you might have several
> versions of project inside ~/tmp, and ediff will ask you which one is to
> be patched), but it works. However, how then I am expected to apply
> patches generated by VCS, where paths are prefixed with a/ and b/?
> Again, let's consider some simple example:
>
> mkdir -p proj/src
> cd proj
> echo "void main() {}" > src/hello.c
> git init
> git add src/ && git commit -m "commit"
> echo "int main() { return 0; }" > src/hello.c
> git diff > ../tmp.patch
> git reset --hard HEAD
>
> cat ../tmp.patch
> diff --git a/src/hello.c b/src/hello.c
> index ab73b3a..76e8197 100644
> --- a/src/hello.c
> +++ b/src/hello.c
> @@ -1 +1 @@
> -void main() {}
> +int main() { return 0; }
>
> How should I apply tmp.patch to proj?
Recently i am not using `epatch', but maybe following patch
is of interest for someone:
--8<-----------------------------cut here---------------start------------->8---
commit 3f4561b160f2cc9e2b47d16e7f27fbb9b86c3f40
Author: Tino Calancha <tino.calancha@gmail.com>
Date:   Tue May 23 19:21:42 2017 +0900

    epatch: multi-patch enhancement
    
    Handle multi-patches with files belonging to different
    subdirectories.
    (ediff-fixup-patch-map): For a multi-patch, set base-dir1 to a/
    and base-dir2 to b/ if it is a Git patch; otherwise, set both to .
    (ediff-map-patch-buffer): Use buffer-substring-no-properties.
    * lisp/vc/ediff.el (ediff-patch-file): Show differente prompt
    for multi-patches.  For single patches, use the patch header to guess the
    file to patch, and ensure the input matches an existing file.
    * test/lisp/vc/ediff-ptch-tests.el (ediff-ptch-test-bug26028): Add test.

diff --git a/lisp/vc/ediff-ptch.el b/lisp/vc/ediff-ptch.el
index 0340672da2..0d53b2aff4 100644
--- a/lisp/vc/ediff-ptch.el
+++ b/lisp/vc/ediff-ptch.el
@@ -222,10 +222,10 @@ ediff-map-patch-buffer
 	    ;;     (filename-from-1st-header-line . filename-from-2nd-line)
 	    (setq possible-file-names
 		  (cons (if (and beg1 end1)
-			    (buffer-substring beg1 end1)
+			    (buffer-substring-no-properties beg1 end1)
 			  "/dev/null")
 			(if (and beg2 end2)
-			    (buffer-substring beg2 end2)
+			    (buffer-substring-no-properties beg2 end2)
 			  "/dev/null")))
             ;; Remove file junk (Bug#26084).
             (while (re-search-backward
@@ -290,18 +290,25 @@ ediff-fixup-patch-map
 		    (or (file-name-directory (cdr proposed-file-names))
 			""))
 		   )
-	      ;; If both base-dir1 and base-dir2 are relative and exist,
-	      ;; assume that
-	      ;; these dirs lead to the actual files starting at the present
-	      ;; directory. So, we don't strip these relative dirs from the
-	      ;; file names. This is a heuristic intended to improve guessing
 	      (let ((default-directory (file-name-directory filename)))
-		(unless (or (file-name-absolute-p base-dir1)
-			    (file-name-absolute-p base-dir2)
-			    (not (file-exists-p base-dir1))
-			    (not (file-exists-p base-dir2)))
-		  (setq base-dir1 ""
-			base-dir2 "")))
+                (cond (multi-patch-p
+                       ;; Git diffs appends 'a/' '/b' to the files.
+                       (if (and (string-match-p "\\`a/" base-dir1)
+                                (string-match-p "\\`b/" base-dir2))
+                           (setq base-dir1 "a/" base-dir2 "b/")
+                         (setq base-dir1 "" base-dir2 "")))
+                      (t
+                       ;; If both base-dir1 and base-dir2 are relative and exist,
+                       ;; assume that
+                       ;; these dirs lead to the actual files starting at the present
+                       ;; directory. So, we don't strip these relative dirs from the
+                       ;; file names. This is a heuristic intended to improve guessing
+                       (unless (or (file-name-absolute-p base-dir1)
+                                   (file-name-absolute-p base-dir2)
+                                   (not (file-exists-p base-dir1))
+                                   (not (file-exists-p base-dir2)))
+                         (setq base-dir1 ""
+                               base-dir2 "")))))
 	      (or (string= (car proposed-file-names) "/dev/null")
 		  (setcar proposed-file-names
 			  (ediff-file-name-sans-prefix
diff --git a/lisp/vc/ediff.el b/lisp/vc/ediff.el
index 4751bb6ddc..e41839e968 100644
--- a/lisp/vc/ediff.el
+++ b/lisp/vc/ediff.el
@@ -121,6 +121,7 @@ ediff-date
 
 (require 'ediff-init)
 (require 'ediff-mult)  ; required because of the registry stuff
+(require 'diff-mode) ; diff-hunk-file-names
 
 (defgroup ediff nil
   "Comprehensive visual interface to `diff' and `patch'."
@@ -1355,6 +1356,7 @@ ediff-patch-default-directory
 (declare-function ediff-dispatch-file-patching-job "ediff-ptch"
                   (patch-buf filename &optional startup-hooks))
 
+(defvar ediff-patch-map)
 ;;;###autoload
 (defun ediff-patch-file (&optional arg patch-buf)
   "Query for a file name, and then run Ediff by patching that file.
@@ -1376,11 +1378,26 @@ ediff-patch-file
 			     (expand-file-name
 			      (buffer-file-name patch-buf))))
 			   (t default-directory)))
-    (setq source-file
-	  (read-file-name
-	   "File to patch (directory, if multifile patch): "
-	   ;; use an explicit initial file
-	   source-dir nil nil (ediff-get-default-file-name)))
+    (let ((multi-patch-p (with-current-buffer patch-buf (cdr ediff-patch-map))))
+      (cond ((not multi-patch-p)
+             (let* ((files (with-current-buffer patch-buf
+                             (diff-hunk-file-names 'old-first)))
+                    (def (if (and (string-match "\\`a/" (car files))
+                                  (string-match "\\`b/" (cadr files)))
+                             (expand-file-name
+                              (substring-no-properties (car files) 2)
+                              default-directory)
+                           (car files))))
+               (setq source-file
+                     (read-file-name
+                      "Single file to patch: "
+                      ;; use an explicit initial file
+                      source-dir nil 'mustmatch def))))
+            (t ; multi-patch
+             (setq source-file
+                   (read-file-name
+                    "Directory to patch, use root project dir: "
+                    source-dir)))))
     (ediff-dispatch-file-patching-job patch-buf source-file)))
 
 (declare-function ediff-patch-buffer-internal "ediff-ptch"
diff --git a/test/lisp/vc/ediff-ptch-tests.el b/test/lisp/vc/ediff-ptch-tests.el
index 387786ced0..74db053c97 100644
--- a/test/lisp/vc/ediff-ptch-tests.el
+++ b/test/lisp/vc/ediff-ptch-tests.el
@@ -21,6 +21,8 @@
 
 (require 'ert)
 (require 'ediff-ptch)
+(require 'ediff-diff) ; For `ediff-diff-program'.
+(eval-when-compile (require 'cl-lib))
 
 (ert-deftest ediff-ptch-test-bug25010 ()
   "Test for http://debbugs.gnu.org/25010 ."
@@ -104,6 +106,151 @@
           (delete-directory tmpdir 'recursive)
           (delete-file patch)))))
 
+(ert-deftest ediff-ptch-test-bug26028 ()
+  "Test for http://debbugs.gnu.org/26028 ."
+  (skip-unless (executable-find "git"))
+  (skip-unless (executable-find ediff-patch-program))
+  (skip-unless (executable-find ediff-diff-program))
+  (let ((git-program (executable-find "git"))
+        (default-dir default-directory)
+        tmpdir buffers)
+    ;;; Simple patch: old/src/hello.c /new/src/hello.c
+    (unwind-protect
+        (let* ((dir (make-temp-file "multipatch-test" t))
+               (file1 (expand-file-name "old/src/hello.c" dir))
+               (file2 (expand-file-name "new/src/hello.c" dir))
+               (patch (expand-file-name "tmp.patch" dir))
+               (default-directory (file-name-as-directory dir)))
+          (setq tmpdir dir)
+          (make-directory (expand-file-name "old/src/" dir) 'parents)
+          (make-directory (expand-file-name "new/src/" dir) 'parents)
+          (with-temp-buffer
+            (insert "void main() { }\n")
+            (write-region nil nil file1 nil 'silent)
+            (erase-buffer)
+            (insert "int main() { return 0; }\n")
+            (write-region nil nil file2 nil 'silent)
+            (erase-buffer)
+            (call-process ediff-diff-program nil t nil "-cr" "old" "new")
+            (write-region nil nil patch nil 'silent)
+            (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) nil))
+                      ((symbol-function 'ediff-prompt-for-patch-file)
+                       (lambda (&rest x) (find-file-noselect patch)))
+                      ((symbol-function 'read-file-name) (lambda (x1 x2 x3 x4 x5) x5))
+                      ((symbol-function 'ediff-dispatch-file-patching-job)
+                       (lambda (x y) y)))
+              (should (equal (file-relative-name file1) (epatch nil patch)))
+              (push (get-file-buffer patch) buffers))))
+      (when (file-exists-p tmpdir)
+        (setq default-directory default-dir)
+        (delete-directory tmpdir 'recursive))
+      (mapc (lambda (b)
+              (when (buffer-live-p b) (kill-buffer b)))
+            buffers)
+      (setq buffers nil))
+    ;;; Simple Git patch: proj/src/hello.c
+    (unwind-protect
+        (let* ((dir (make-temp-file "multipatch-test" t))
+               (rootdir (expand-file-name "proj/src/" dir))
+               (file (expand-file-name "hello.c" rootdir))
+               (patch (expand-file-name "tmp.patch" dir))
+               (default-directory (file-name-as-directory rootdir)))
+          (make-directory rootdir 'parents)
+          (setq tmpdir dir)
+          (with-temp-buffer
+            (insert "void main() { }\n")
+            (write-region nil nil file nil 'silent)
+            (call-process git-program nil nil nil "init")
+            (call-process git-program nil nil nil "add" ".")
+            (call-process git-program nil nil nil "commit" "-m" "test repository.")
+            (erase-buffer)
+            (insert "int main() { return 0; }\n")
+            (write-region nil nil file nil 'silent)
+            (call-process git-program nil `(:file ,patch) nil "diff")
+            (call-process git-program nil nil nil "reset" "--hard" "head")
+            (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) nil))
+                      ((symbol-function 'ediff-prompt-for-patch-file)
+                       (lambda (&rest x) (find-file-noselect patch)))
+                      ((symbol-function 'read-file-name) (lambda (&rest x) file))
+                      ((symbol-function 'read-file-name) (lambda (x1 x2 x3 x4 x5) x5))
+                      ((symbol-function 'ediff-dispatch-file-patching-job)
+                       (lambda (x y) y)))
+              (should (equal file (epatch nil patch)))))
+          (push (get-file-buffer patch) buffers))
+      ;; clean up
+      (when (file-exists-p tmpdir)
+        (setq default-directory default-dir)
+        (delete-directory tmpdir 'recursive))
+      (mapc (lambda (b)
+              (when (buffer-live-p b) (kill-buffer b)))
+            buffers)
+      (setq buffers nil))
+    ;;; Git multipatch.
+    (unwind-protect
+        (let* ((dir (make-temp-file "multipatch-test" t))
+               (file1 (expand-file-name "proj/src/hello.c" dir))
+               (file2 (expand-file-name "proj/src/bye.c" dir))
+               (file3 (expand-file-name "proj/lisp/foo.el" dir))
+               (file4 (expand-file-name "proj/lisp/bar.el" dir))
+               (file5 (expand-file-name "proj/etc/news" dir))
+               (patch (expand-file-name "tmp.patch" dir))
+               (default-directory (expand-file-name "proj" dir)))
+          (setq tmpdir dir)
+          (dolist (d '("src" "lisp" "etc"))
+            (setq rootdir (expand-file-name (concat "proj/" d) dir))
+            (make-directory rootdir 'parents))
+          (with-temp-buffer
+            (insert "void main() { }\n")
+            (write-region nil nil file1 nil 'silent)
+            (write-region nil nil file2 nil 'silent)
+            (erase-buffer)
+            (insert "(defun foo () nil)\n")
+            (write-region nil nil file3 nil 'silent)
+            (erase-buffer)
+            (insert "(defun bar () nil)\n")
+            (write-region nil nil file4 nil 'silent)
+            (erase-buffer)
+            (insert "new functions 'foo' and 'bar'\n")
+            (write-region nil nil file5 nil 'silent)
+            (call-process git-program nil nil nil "init")
+            (call-process git-program nil nil nil "add" "src" "lisp" "etc")
+            (call-process git-program nil nil nil "commit" "-m" "test repository.");)
+            (erase-buffer)
+            (insert "int main() { return 0;}\n")
+            (write-region nil nil file1 nil 'silent)
+            (write-region nil nil file2 nil 'silent)
+            (erase-buffer)
+            (insert "(defun qux () nil)\n")
+            (write-region nil nil file3 nil 'silent)
+            (erase-buffer)
+            (insert "(defun quux () nil)\n")
+            (write-region nil nil file4 nil 'silent)
+            (erase-buffer)
+            (insert "new functions 'qux' and 'quux'\n")
+            (write-region nil nil file5 nil 'silent)
+            (call-process git-program nil `(:file ,patch) nil "diff")
+            (call-process git-program nil nil nil "reset" "--hard" "head"))
+          (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) nil))
+                    ((symbol-function 'ediff-get-patch-file) (lambda (&rest x) patch))
+                    ((symbol-function 'read-file-name) (lambda (&rest x) patch)))
+            (epatch nil patch)
+            (with-current-buffer "*Ediff Session Group Panel*"
+              (push (get-file-buffer patch) buffers)
+              (should (= 5 (length (cdr ediff-meta-list))))
+              ;; don't ask confirmation to exit.
+              (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) t)))
+                (ediff-quit-meta-buffer)))))
+      ;; clean up
+      (when (file-exists-p tmpdir)
+        (setq default-directory default-dir)
+        (delete-directory tmpdir 'recursive))
+      (when ediff-registry-buffer
+        (push ediff-registry-buffer buffers))
+      (mapc (lambda (b)
+              (when (buffer-live-p b) (kill-buffer b)))
+            buffers)
+      (setq buffers nil))))
+
 
 (provide 'ediff-ptch-tests)
 ;;; ediff-ptch-tests.el ends here
--8<-----------------------------cut here---------------end--------------->8---
In GNU Emacs 26.0.50 (build 1, x86_64-pc-linux-gnu, GTK+ Version 3.22.11)
 of 2017-05-23
Repository revision: 4a485410ce74cafd4e9c344e31f7575464a16113






^ permalink raw reply related	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-05-23 11:26     ` Tino Calancha
@ 2017-05-23 22:45       ` Michael Heerdegen
  2017-05-24  0:46         ` Tino Calancha
  0 siblings, 1 reply; 16+ messages in thread
From: Michael Heerdegen @ 2017-05-23 22:45 UTC (permalink / raw)
  To: Tino Calancha; +Cc: 26028, Arseny Sher, Kaushal Modi

Tino Calancha <tino.calancha@gmail.com> writes:

> Recently i am not using `epatch', but maybe following patch
> is of interest for someone:

FWIW, do you think you understood all of the code you changed (I ask
because I gave up after a while)?  If you did, do you think we should
install your fix, or are there open questions?


Thanks,

Michael.





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-05-23 22:45       ` Michael Heerdegen
@ 2017-05-24  0:46         ` Tino Calancha
  2020-08-11  7:34           ` Stefan Kangas
  0 siblings, 1 reply; 16+ messages in thread
From: Tino Calancha @ 2017-05-24  0:46 UTC (permalink / raw)
  To: Michael Heerdegen; +Cc: 26028, Tino Calancha, Arseny Sher, Kaushal Modi



On Wed, 24 May 2017, Michael Heerdegen wrote:

> Tino Calancha <tino.calancha@gmail.com> writes:
>
>> Recently i am not using `epatch', but maybe following patch
>> is of interest for someone:
>
> FWIW, do you think you understood all of the code you changed (I ask
> because I gave up after a while)?
I think so.  I you exclude from the patch the added tests in
test/lisp/vc/ediff-ptch-tests.el
then this patch just touch:
2 files changed, 42 insertions(+), 18 deletions(-)
The only it does:
* tweak the original heuristic part for guessing the file names
* change the suggested defaults in the prompts asking for the patch and
   dirs to patch.

>  If you did, do you think we should
> install your fix, or are there open questions?
I have tested with plain diffs and with Git diffs.  I didn't test it
with other VC than Git.
I don't think solves all the problems, but at least it makes 
`epatch' usable for the most common multipatch situations.

I)
You know, `epatch' has never parsed multipatches with files in different 
dirs, as:
foo/file1
bar/file2

II)
The only multipatches `epatch' worked before is when all the files belong
to the same dir, as:
foo/file1
foo/file2

This patch makes possible to handle I) as well.





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2017-05-24  0:46         ` Tino Calancha
@ 2020-08-11  7:34           ` Stefan Kangas
  2020-10-03 22:25             ` Michael Heerdegen
  0 siblings, 1 reply; 16+ messages in thread
From: Stefan Kangas @ 2020-08-11  7:34 UTC (permalink / raw)
  To: Tino Calancha; +Cc: Michael Heerdegen, 26028, Arseny Sher, Kaushal Modi

Tino Calancha <tino.calancha@gmail.com> writes:

> On Wed, 24 May 2017, Michael Heerdegen wrote:
>
>> Tino Calancha <tino.calancha@gmail.com> writes:
>>
>>> Recently i am not using `epatch', but maybe following patch
>>> is of interest for someone:
>>
>> FWIW, do you think you understood all of the code you changed (I ask
>> because I gave up after a while)?
> I think so.  I you exclude from the patch the added tests in
> test/lisp/vc/ediff-ptch-tests.el
> then this patch just touch:
> 2 files changed, 42 insertions(+), 18 deletions(-)
> The only it does:
> * tweak the original heuristic part for guessing the file names
> * change the suggested defaults in the prompts asking for the patch and
>   dirs to patch.
>
>>  If you did, do you think we should
>> install your fix, or are there open questions?
> I have tested with plain diffs and with Git diffs.  I didn't test it
> with other VC than Git.
> I don't think solves all the problems, but at least it makes `epatch' usable for
> the most common multipatch situations.

That was 3 years ago.

It looks like this patch was never installed. Should it be?

Best regards,
Stefan Kangas





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2020-08-11  7:34           ` Stefan Kangas
@ 2020-10-03 22:25             ` Michael Heerdegen
  2021-05-10 12:02               ` Lars Ingebrigtsen
  0 siblings, 1 reply; 16+ messages in thread
From: Michael Heerdegen @ 2020-10-03 22:25 UTC (permalink / raw)
  To: Stefan Kangas; +Cc: 26028, Tino Calancha, Arseny Sher, Kaushal Modi

Stefan Kangas <stefan@marxist.se> writes:

> It looks like this patch was never installed. Should it be?

AFAIR it solved my problem, but I can't judge whether it's ok to
install since I don't know the code.

Michael.





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2020-10-03 22:25             ` Michael Heerdegen
@ 2021-05-10 12:02               ` Lars Ingebrigtsen
  2021-05-12  9:27                 ` Michael Heerdegen
  2021-05-13 16:29                 ` Filipp Gunbin
  0 siblings, 2 replies; 16+ messages in thread
From: Lars Ingebrigtsen @ 2021-05-10 12:02 UTC (permalink / raw)
  To: Michael Heerdegen
  Cc: 26028, Kaushal Modi, Stefan Kangas, Arseny Sher, Tino Calancha

Michael Heerdegen <michael_heerdegen@web.de> writes:

> AFAIR it solved my problem, but I can't judge whether it's ok to
> install since I don't know the code.

The patch no longer applies cleanly -- do you have an up-to-date version
of Tino's patch in your tree, by any chance?  Or did you just try it
then, and then back it out again?

Looking at the commit history for ediff, it doesn't really look like we
have any ediff domain experts to evaluate the patch, which makes it hard
to decide whether to try to apply the patch or not.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2021-05-10 12:02               ` Lars Ingebrigtsen
@ 2021-05-12  9:27                 ` Michael Heerdegen
  2021-05-13 16:29                 ` Filipp Gunbin
  1 sibling, 0 replies; 16+ messages in thread
From: Michael Heerdegen @ 2021-05-12  9:27 UTC (permalink / raw)
  To: Lars Ingebrigtsen
  Cc: 26028, Tino Calancha, Stefan Kangas, Arseny Sher, Kaushal Modi

Lars Ingebrigtsen <larsi@gnus.org> writes:

> > AFAIR it solved my problem, but I can't judge whether it's ok to
> > install since I don't know the code.
>
> The patch no longer applies cleanly -- do you have an up-to-date version
> of Tino's patch in your tree, by any chance?  Or did you just try it
> then, and then back it out again?

I'm very sorry Lars, but I don't have anything better than Tino's
original patch.  I removed the patch from my tree when the collisions
started to occur.

Regards,

Michael.





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2021-05-10 12:02               ` Lars Ingebrigtsen
  2021-05-12  9:27                 ` Michael Heerdegen
@ 2021-05-13 16:29                 ` Filipp Gunbin
  2021-05-16 13:57                   ` Lars Ingebrigtsen
  1 sibling, 1 reply; 16+ messages in thread
From: Filipp Gunbin @ 2021-05-13 16:29 UTC (permalink / raw)
  To: Lars Ingebrigtsen
  Cc: 26028, Arseny Sher, Tino Calancha, Michael Heerdegen,
	Stefan Kangas, Kaushal Modi

On 10/05/2021 14:02 +0200, Lars Ingebrigtsen wrote:

> Michael Heerdegen <michael_heerdegen@web.de> writes:
>
>> AFAIR it solved my problem, but I can't judge whether it's ok to
>> install since I don't know the code.
>
> The patch no longer applies cleanly -- do you have an up-to-date version
> of Tino's patch in your tree, by any chance?  Or did you just try it
> then, and then back it out again?
>
> Looking at the commit history for ediff, it doesn't really look like we
> have any ediff domain experts to evaluate the patch, which makes it hard
> to decide whether to try to apply the patch or not.

I've looked into ediff some months ago (when writing a function which
opens new multifile ediff session for comparing vc commits; it's
unfinished yet).  So I could look at the patch if needed.

Filipp





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2021-05-13 16:29                 ` Filipp Gunbin
@ 2021-05-16 13:57                   ` Lars Ingebrigtsen
  2021-07-23 12:54                     ` Lars Ingebrigtsen
  0 siblings, 1 reply; 16+ messages in thread
From: Lars Ingebrigtsen @ 2021-05-16 13:57 UTC (permalink / raw)
  To: Filipp Gunbin
  Cc: 26028, Arseny Sher, Tino Calancha, Michael Heerdegen,
	Stefan Kangas, Kaushal Modi

Filipp Gunbin <fgunbin@fastmail.fm> writes:

> I've looked into ediff some months ago (when writing a function which
> opens new multifile ediff session for comparing vc commits; it's
> unfinished yet).  So I could look at the patch if needed.

Yes, that would be very helpful.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no





^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2021-05-16 13:57                   ` Lars Ingebrigtsen
@ 2021-07-23 12:54                     ` Lars Ingebrigtsen
  2022-03-24  8:34                       ` Lars Ingebrigtsen
  0 siblings, 1 reply; 16+ messages in thread
From: Lars Ingebrigtsen @ 2021-07-23 12:54 UTC (permalink / raw)
  To: Filipp Gunbin
  Cc: 26028, Arseny Sher, Tino Calancha, Michael Heerdegen,
	Stefan Kangas, Kaushal Modi

Lars Ingebrigtsen <larsi@gnus.org> writes:

> Filipp Gunbin <fgunbin@fastmail.fm> writes:
>
>> I've looked into ediff some months ago (when writing a function which
>> opens new multifile ediff session for comparing vc commits; it's
>> unfinished yet).  So I could look at the patch if needed.
>
> Yes, that would be very helpful.

Did you get any further here?






^ permalink raw reply	[flat|nested] 16+ messages in thread

* bug#26028: 26.0.50; epatch for multifile patches
  2021-07-23 12:54                     ` Lars Ingebrigtsen
@ 2022-03-24  8:34                       ` Lars Ingebrigtsen
  0 siblings, 0 replies; 16+ messages in thread
From: Lars Ingebrigtsen @ 2022-03-24  8:34 UTC (permalink / raw)
  To: Filipp Gunbin
  Cc: 26028, Arseny Sher, Tino Calancha, Michael Heerdegen,
	Stefan Kangas, Kaushal Modi

[-- Attachment #1: Type: text/plain, Size: 342 bytes --]

Lars Ingebrigtsen <larsi@gnus.org> writes:

> Did you get any further here?

I forgot that the patch no longer applied.  I've respun it now, but
somewhat unsure of how the changes to ediff-fixup-patch-map should be
adjusted to the patch.

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no

[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: ediff.patch --]
[-- Type: text/x-diff, Size: 14112 bytes --]

diff --git a/lisp/vc/ediff-ptch.el b/lisp/vc/ediff-ptch.el
index 17654f80ec..db39368397 100644
--- a/lisp/vc/ediff-ptch.el
+++ b/lisp/vc/ediff-ptch.el
@@ -217,10 +217,10 @@ ediff-map-patch-buffer
 	    ;;     (filename-from-1st-header-line . filename-from-2nd-line)
 	    (setq possible-file-names
 		  (cons (if (and beg1 end1)
-			    (buffer-substring beg1 end1)
+			    (buffer-substring-no-properties beg1 end1)
 			  null-device)
 			(if (and beg2 end2)
-			    (buffer-substring beg2 end2)
+			    (buffer-substring-no-properties beg2 end2)
 			  null-device)))
             ;; Remove file junk (Bug#26084).
             (while (re-search-backward
@@ -285,31 +285,42 @@ ediff-fixup-patch-map
 		    (or (file-name-directory (cdr proposed-file-names))
 			""))
 		   )
-	      ;; If both base-dir1 and base-dir2 are relative and exist,
-	      ;; assume that
-	      ;; these dirs lead to the actual files starting at the present
-	      ;; directory. So, we don't strip these relative dirs from the
-	      ;; file names. This is a heuristic intended to improve guessing
 	      (let ((default-directory (file-name-directory filename)))
-		(unless (or (file-name-absolute-p base-dir1)
-			    (file-name-absolute-p base-dir2))
-		  (if (and (file-exists-p base-dir1)
-			   (file-exists-p base-dir2))
-		      (setq base-dir1 ""
-			    base-dir2 "")
-		    ;; Strip possible source/destination prefixes
-		    ;; such as a/ and b/ from dir names.
-		    (save-match-data
-		      (let ((m1 (when (string-match "^[^/]+/" base-dir1)
-                                  (cons (substring base-dir1 0 (match-end 0))
-                                        (substring base-dir1 (match-end 0)))))
-			    (m2 (when (string-match "^[^/]+/" base-dir2)
-				  (cons (substring base-dir2 0 (match-end 0))
-                                        (substring base-dir2 (match-end 0))))))
-			(when (and (file-exists-p (cdr m1))
-				   (file-exists-p (cdr m2)))
-			  (setq base-dir1 (car m1)
-				base-dir2 (car m2))))))))
+                (cond
+                 (multi-patch-p
+                  ;; Git diffs appends 'a/' '/b' to the files.
+                  (if (and (string-match-p "\\`a/" base-dir1)
+                           (string-match-p "\\`b/" base-dir2))
+                      (setq base-dir1 "a/" base-dir2 "b/")
+                    (setq base-dir1 "" base-dir2 "")))
+                 (t
+                  ;; If both base-dir1 and base-dir2 are relative and
+                  ;; exist, assume that these dirs lead to the actual
+                  ;; files starting at the present directory. So, we
+                  ;; don't strip these relative dirs from the file
+                  ;; names. This is a heuristic intended to improve
+                  ;; guessing
+		  (unless (or (file-name-absolute-p base-dir1)
+			      (file-name-absolute-p base-dir2))
+		    (if (and (file-exists-p base-dir1)
+			     (file-exists-p base-dir2))
+		        (setq base-dir1 ""
+			      base-dir2 "")
+		      ;; Strip possible source/destination prefixes
+		      ;; such as a/ and b/ from dir names.
+		      (save-match-data
+		        (let ((m1
+                               (when (string-match "^[^/]+/" base-dir1)
+                                 (cons (substring base-dir1 0 (match-end 0))
+                                       (substring base-dir1 (match-end 0)))))
+			      (m2
+                               (when (string-match "^[^/]+/" base-dir2)
+				 (cons (substring base-dir2 0 (match-end 0))
+                                       (substring base-dir2 (match-end 0))))))
+			  (when (and (file-exists-p (cdr m1))
+				     (file-exists-p (cdr m2)))
+			    (setq base-dir1 (car m1)
+				  base-dir2 (car m2))))))))))
 	      (or (string= (car proposed-file-names) null-device)
 		  (setcar proposed-file-names
 			  (ediff-file-name-sans-prefix
diff --git a/lisp/vc/ediff.el b/lisp/vc/ediff.el
index 840ab8cf51..e7ee36eb10 100644
--- a/lisp/vc/ediff.el
+++ b/lisp/vc/ediff.el
@@ -111,6 +111,7 @@ ediff-version
 
 (require 'ediff-init)
 (require 'ediff-mult)  ; required because of the registry stuff
+(require 'diff-mode) ; diff-hunk-file-names
 
 (defgroup ediff nil
   "Comprehensive visual interface to `diff' and `patch'."
@@ -1412,6 +1413,7 @@ ediff-patch-default-directory
 (declare-function ediff-dispatch-file-patching-job "ediff-ptch"
                   (patch-buf filename &optional startup-hooks))
 
+(defvar ediff-patch-map)
 ;;;###autoload
 (defun ediff-patch-file (&optional arg patch-buf)
   "Query for a file name, and then run Ediff by patching that file.
@@ -1433,11 +1435,26 @@ ediff-patch-file
 			     (expand-file-name
 			      (buffer-file-name patch-buf))))
 			   (t default-directory)))
-    (setq source-file
-	  (read-file-name
-	   "File to patch (directory, if multifile patch): "
-	   ;; use an explicit initial file
-	   source-dir nil nil (ediff-get-default-file-name)))
+    (let ((multi-patch-p (with-current-buffer patch-buf (cdr ediff-patch-map))))
+      (cond ((not multi-patch-p)
+             (let* ((files (with-current-buffer patch-buf
+                             (diff-hunk-file-names 'old-first)))
+                    (def (if (and (string-match "\\`a/" (car files))
+                                  (string-match "\\`b/" (cadr files)))
+                             (expand-file-name
+                              (substring-no-properties (car files) 2)
+                              default-directory)
+                           (car files))))
+               (setq source-file
+                     (read-file-name
+                      "Single file to patch: "
+                      ;; use an explicit initial file
+                      source-dir nil 'mustmatch def))))
+            (t ; multi-patch
+             (setq source-file
+                   (read-file-name
+                    "Directory to patch, use root project dir: "
+                    source-dir)))))
     (ediff-dispatch-file-patching-job patch-buf source-file)))
 
 (declare-function ediff-patch-buffer-internal "ediff-ptch"
diff --git a/test/lisp/vc/ediff-ptch-tests.el b/test/lisp/vc/ediff-ptch-tests.el
index 935046198f..7f143fe139 100644
--- a/test/lisp/vc/ediff-ptch-tests.el
+++ b/test/lisp/vc/ediff-ptch-tests.el
@@ -24,6 +24,8 @@
 (require 'ert)
 (require 'ert-x)
 (require 'ediff-ptch)
+(require 'ediff-diff) ; For `ediff-diff-program'.
+(eval-when-compile (require 'cl-lib))
 
 (ert-deftest ediff-ptch-test-bug25010 ()
   "Test for https://debbugs.gnu.org/25010 ."
@@ -118,6 +120,151 @@ ediff-ptch-test-bug26084
                             (insert-file-contents backup)
                             (buffer-string))))))))))))
 
+(ert-deftest ediff-ptch-test-bug26028 ()
+  "Test for http://debbugs.gnu.org/26028 ."
+  (skip-unless (executable-find "git"))
+  (skip-unless (executable-find ediff-patch-program))
+  (skip-unless (executable-find ediff-diff-program))
+  (let ((git-program (executable-find "git"))
+        (default-dir default-directory)
+        tmpdir buffers)
+    ;;; Simple patch: old/src/hello.c /new/src/hello.c
+    (unwind-protect
+        (let* ((dir (make-temp-file "multipatch-test" t))
+               (file1 (expand-file-name "old/src/hello.c" dir))
+               (file2 (expand-file-name "new/src/hello.c" dir))
+               (patch (expand-file-name "tmp.patch" dir))
+               (default-directory (file-name-as-directory dir)))
+          (setq tmpdir dir)
+          (make-directory (expand-file-name "old/src/" dir) 'parents)
+          (make-directory (expand-file-name "new/src/" dir) 'parents)
+          (with-temp-buffer
+            (insert "void main() { }\n")
+            (write-region nil nil file1 nil 'silent)
+            (erase-buffer)
+            (insert "int main() { return 0; }\n")
+            (write-region nil nil file2 nil 'silent)
+            (erase-buffer)
+            (call-process ediff-diff-program nil t nil "-cr" "old" "new")
+            (write-region nil nil patch nil 'silent)
+            (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) nil))
+                      ((symbol-function 'ediff-prompt-for-patch-file)
+                       (lambda (&rest x) (find-file-noselect patch)))
+                      ((symbol-function 'read-file-name) (lambda (x1 x2 x3 x4 x5) x5))
+                      ((symbol-function 'ediff-dispatch-file-patching-job)
+                       (lambda (x y) y)))
+              (should (equal (file-relative-name file1) (epatch nil patch)))
+              (push (get-file-buffer patch) buffers))))
+      (when (file-exists-p tmpdir)
+        (setq default-directory default-dir)
+        (delete-directory tmpdir 'recursive))
+      (mapc (lambda (b)
+              (when (buffer-live-p b) (kill-buffer b)))
+            buffers)
+      (setq buffers nil))
+    ;;; Simple Git patch: proj/src/hello.c
+    (unwind-protect
+        (let* ((dir (make-temp-file "multipatch-test" t))
+               (rootdir (expand-file-name "proj/src/" dir))
+               (file (expand-file-name "hello.c" rootdir))
+               (patch (expand-file-name "tmp.patch" dir))
+               (default-directory (file-name-as-directory rootdir)))
+          (make-directory rootdir 'parents)
+          (setq tmpdir dir)
+          (with-temp-buffer
+            (insert "void main() { }\n")
+            (write-region nil nil file nil 'silent)
+            (call-process git-program nil nil nil "init")
+            (call-process git-program nil nil nil "add" ".")
+            (call-process git-program nil nil nil "commit" "-m" "test repository.")
+            (erase-buffer)
+            (insert "int main() { return 0; }\n")
+            (write-region nil nil file nil 'silent)
+            (call-process git-program nil `(:file ,patch) nil "diff")
+            (call-process git-program nil nil nil "reset" "--hard" "head")
+            (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) nil))
+                      ((symbol-function 'ediff-prompt-for-patch-file)
+                       (lambda (&rest x) (find-file-noselect patch)))
+                      ((symbol-function 'read-file-name) (lambda (&rest x) file))
+                      ((symbol-function 'read-file-name) (lambda (x1 x2 x3 x4 x5) x5))
+                      ((symbol-function 'ediff-dispatch-file-patching-job)
+                       (lambda (x y) y)))
+              (should (equal file (epatch nil patch)))))
+          (push (get-file-buffer patch) buffers))
+      ;; clean up
+      (when (file-exists-p tmpdir)
+        (setq default-directory default-dir)
+        (delete-directory tmpdir 'recursive))
+      (mapc (lambda (b)
+              (when (buffer-live-p b) (kill-buffer b)))
+            buffers)
+      (setq buffers nil))
+    ;;; Git multipatch.
+    (unwind-protect
+        (let* ((dir (make-temp-file "multipatch-test" t))
+               (file1 (expand-file-name "proj/src/hello.c" dir))
+               (file2 (expand-file-name "proj/src/bye.c" dir))
+               (file3 (expand-file-name "proj/lisp/foo.el" dir))
+               (file4 (expand-file-name "proj/lisp/bar.el" dir))
+               (file5 (expand-file-name "proj/etc/news" dir))
+               (patch (expand-file-name "tmp.patch" dir))
+               (default-directory (expand-file-name "proj" dir)))
+          (setq tmpdir dir)
+          (dolist (d '("src" "lisp" "etc"))
+            (setq rootdir (expand-file-name (concat "proj/" d) dir))
+            (make-directory rootdir 'parents))
+          (with-temp-buffer
+            (insert "void main() { }\n")
+            (write-region nil nil file1 nil 'silent)
+            (write-region nil nil file2 nil 'silent)
+            (erase-buffer)
+            (insert "(defun foo () nil)\n")
+            (write-region nil nil file3 nil 'silent)
+            (erase-buffer)
+            (insert "(defun bar () nil)\n")
+            (write-region nil nil file4 nil 'silent)
+            (erase-buffer)
+            (insert "new functions 'foo' and 'bar'\n")
+            (write-region nil nil file5 nil 'silent)
+            (call-process git-program nil nil nil "init")
+            (call-process git-program nil nil nil "add" "src" "lisp" "etc")
+            (call-process git-program nil nil nil "commit" "-m" "test repository.");)
+            (erase-buffer)
+            (insert "int main() { return 0;}\n")
+            (write-region nil nil file1 nil 'silent)
+            (write-region nil nil file2 nil 'silent)
+            (erase-buffer)
+            (insert "(defun qux () nil)\n")
+            (write-region nil nil file3 nil 'silent)
+            (erase-buffer)
+            (insert "(defun quux () nil)\n")
+            (write-region nil nil file4 nil 'silent)
+            (erase-buffer)
+            (insert "new functions 'qux' and 'quux'\n")
+            (write-region nil nil file5 nil 'silent)
+            (call-process git-program nil `(:file ,patch) nil "diff")
+            (call-process git-program nil nil nil "reset" "--hard" "head"))
+          (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) nil))
+                    ((symbol-function 'ediff-get-patch-file) (lambda (&rest x) patch))
+                    ((symbol-function 'read-file-name) (lambda (&rest x) patch)))
+            (epatch nil patch)
+            (with-current-buffer "*Ediff Session Group Panel*"
+              (push (get-file-buffer patch) buffers)
+              (should (= 5 (length (cdr ediff-meta-list))))
+              ;; don't ask confirmation to exit.
+              (cl-letf (((symbol-function 'y-or-n-p) (lambda (x) t)))
+                (ediff-quit-meta-buffer)))))
+      ;; clean up
+      (when (file-exists-p tmpdir)
+        (setq default-directory default-dir)
+        (delete-directory tmpdir 'recursive))
+      (when ediff-registry-buffer
+        (push ediff-registry-buffer buffers))
+      (mapc (lambda (b)
+              (when (buffer-live-p b) (kill-buffer b)))
+            buffers)
+      (setq buffers nil))))
+
 
 (provide 'ediff-ptch-tests)
 ;;; ediff-ptch-tests.el ends here

^ permalink raw reply related	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2022-03-24  8:34 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-03-08 17:29 bug#26028: 26.0.50; epatch for multifile patches Arseny Sher
2017-03-08 19:17 ` Eli Zaretskii
2017-03-08 21:53   ` Arseny Sher
2017-03-09  1:41     ` Tino Calancha
2017-03-09 11:44       ` Arseny Sher
2017-05-23 11:26     ` Tino Calancha
2017-05-23 22:45       ` Michael Heerdegen
2017-05-24  0:46         ` Tino Calancha
2020-08-11  7:34           ` Stefan Kangas
2020-10-03 22:25             ` Michael Heerdegen
2021-05-10 12:02               ` Lars Ingebrigtsen
2021-05-12  9:27                 ` Michael Heerdegen
2021-05-13 16:29                 ` Filipp Gunbin
2021-05-16 13:57                   ` Lars Ingebrigtsen
2021-07-23 12:54                     ` Lars Ingebrigtsen
2022-03-24  8:34                       ` Lars Ingebrigtsen

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).