[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
bug#26028: 26.0.50; epatch for multifile patches
From: |
Tino Calancha |
Subject: |
bug#26028: 26.0.50; epatch for multifile patches |
Date: |
Tue, 23 May 2017 20:26:55 +0900 |
User-agent: |
Gnus/5.13 (Gnus v5.13) Emacs/26.0.50 (gnu/linux) |
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
- bug#26028: 26.0.50; epatch for multifile patches,
Tino Calancha <=