X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/34317da2d4673bd4861a5858c1fa64a19832eecc..0f0b350b8863bb74455b4e07f4f3372657acd22d:/lisp/ediff.el diff --git a/lisp/ediff.el b/lisp/ediff.el index ea7d747a70..35b28a3e55 100644 --- a/lisp/ediff.el +++ b/lisp/ediff.el @@ -1,13 +1,14 @@ ;;; ediff.el --- a comprehensive visual interface to diff & patch -;; Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc. +;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +;; 2003, 2004, 2005 Free Software Foundation, Inc. -;; Author: Michael Kifer +;; Author: Michael Kifer ;; Created: February 2, 1994 -;; Keywords: comparing, merging, patching, version control. +;; Keywords: comparing, merging, patching, tools, unix -(defconst ediff-version "2.67" "The current version of Ediff") -(defconst ediff-date "August 7, 1997" "Date of last update") +(defconst ediff-version "2.80" "The current version of Ediff") +(defconst ediff-date "July 8, 2005" "Date of last update") ;; This file is part of GNU Emacs. @@ -24,8 +25,8 @@ ;; You should have received a copy of the GNU General Public License ;; along with GNU Emacs; see the file COPYING. If not, write to the -;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, -;; Boston, MA 02111-1307, USA. +;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, +;; Boston, MA 02110-1301, USA. ;;; Commentary: @@ -42,7 +43,7 @@ ;; another (and recover old differences if you change your mind). ;; Ediff also supports merging operations on files and buffers, including -;; merging using ancestor versions. Both comparison and merging operations can +;; merging using ancestor versions. Both comparison and merging operations can ;; be performed on directories, i.e., by pairwise comparison of files in those ;; directories. @@ -56,18 +57,18 @@ ;; you don't like). ;; Ediff is aware of version control, which lets the user compare -;; files with their older versions. Ediff can also work with remote and -;; compressed files. Details are given below. +;; files with their older versions. Ediff can also work with remote and +;; compressed files. Details are given below. ;; Finally, Ediff supports directory-level comparison, merging and patching. ;; See the on-line manual for details. ;; This package builds upon the ideas borrowed from emerge.el and several -;; Ediff's functions are adaptations from emerge.el. Much of the functionality +;; Ediff's functions are adaptations from emerge.el. Much of the functionality ;; Ediff provides is also influenced by emerge.el. -;; The present version of Ediff supersedes Emerge. It provides a superior user -;; interface and has numerous major features not found in Emerge. In +;; The present version of Ediff supersedes Emerge. It provides a superior user +;; interface and has numerous major features not found in Emerge. In ;; particular, it can do patching, and 2-way and 3-way file comparison, ;; merging, and directory operations. @@ -75,7 +76,7 @@ ;;; Bugs: -;; 1. The undo command doesn't restore deleted regions well. That is, if +;; 1. The undo command doesn't restore deleted regions well. That is, if ;; you delete all characters in a difference region and then invoke ;; `undo', the reinstated text will most likely be inserted outside of ;; what Ediff thinks is the current difference region. (This problem @@ -85,13 +86,13 @@ ;; you can hit '!' to recompute the differences. ;; 2. On a monochrome display, the repertoire of faces with which to -;; highlight fine differences is limited. By default, Ediff is using -;; underlining. However, if the region is already underlined by some other +;; highlight fine differences is limited. By default, Ediff is using +;; underlining. However, if the region is already underlined by some other ;; overlays, there is no simple way to temporarily remove that residual -;; underlining. This problem occurs when a buffer is highlighted with -;; hilit19.el or font-lock.el packages. If this residual highlighting gets -;; in the way, you can do the following. Both font-lock.el and hilit19.el -;; provide commands for unhighlighting buffers. You can either place these +;; underlining. This problem occurs when a buffer is highlighted with +;; hilit19.el or font-lock.el packages. If this residual highlighting gets +;; in the way, you can do the following. Both font-lock.el and hilit19.el +;; provide commands for unhighlighting buffers. You can either place these ;; commands in `ediff-prepare-buffer-hook' (which will unhighlight every ;; buffer used by Ediff) or you can execute them interactively, at any time ;; and on any buffer. @@ -101,8 +102,8 @@ ;; Ediff was inspired by Dale R. Worley's emerge.el. ;; Ediff would not have been possible without the help and encouragement of -;; its many users. See Ediff on-line Info for the full list of those who -;; helped. Improved defaults in Ediff file-name reading commands. +;; its many users. See Ediff on-line Info for the full list of those who +;; helped. Improved defaults in Ediff file-name reading commands. ;;; Code: @@ -135,7 +136,7 @@ (require 'ediff-mult) ; required because of the registry stuff (defgroup ediff nil - "A comprehensive visual interface to diff & patch" + "A comprehensive visual interface to diff & patch." :tag "Ediff" :group 'tools) @@ -154,7 +155,7 @@ ;; Last directory used by an Ediff command for the ancestor file. (defvar ediff-last-dir-ancestor nil) ;; Last directory used by an Ediff command as the output directory for merge. -(defvar ediff-last-merge-autostore-dir) +(defvar ediff-last-merge-autostore-dir nil) ;; Used as a startup hook to set `_orig' patch file read-only. @@ -163,13 +164,27 @@ (toggle-read-only 1))) ;; Return a plausible default for ediff's first file: -;; In dired, return the file name under the point, unless it is a directory -;; If the buffer has a file name, return that file name. -(defun ediff-get-default-file-name () +;; In dired, return the file number FILENO (or 0) in the list +;; (all-selected-files, filename under the cursor), where directories are +;; ignored. Otherwise, return DEFAULT file name, if non-nil. Else, +;; if the buffer is visiting a file, return that file name. +(defun ediff-get-default-file-name (&optional default fileno) (cond ((eq major-mode 'dired-mode) - (let ((f (dired-get-filename nil 'no-error))) - (if (and (stringp f) (not (file-directory-p f))) - f))) + (let ((current (dired-get-filename nil 'no-error)) + (marked (condition-case nil + (dired-get-marked-files 'no-dir) + (error nil))) + aux-list choices result) + (or (integerp fileno) (setq fileno 0)) + (if (stringp default) + (setq aux-list (cons default aux-list))) + (if (and (stringp current) (not (file-directory-p current))) + (setq aux-list (cons current aux-list))) + (setq choices (nconc marked aux-list)) + (setq result (elt choices fileno)) + (or result + default))) + ((stringp default) default) ((buffer-file-name (current-buffer)) (file-name-nondirectory (buffer-file-name (current-buffer)))) )) @@ -185,12 +200,14 @@ default-directory)) dir-B f) (list (setq f (ediff-read-file-name - "File A to compare" dir-A - (ediff-get-default-file-name))) - (ediff-read-file-name "File B to compare" + "File A to compare" + dir-A + (ediff-get-default-file-name) + 'no-dirs)) + (ediff-read-file-name "File B to compare" (setq dir-B (if ediff-use-last-dir - ediff-last-dir-B + ediff-last-dir-B (file-name-directory f))) (progn (setq file-name-history @@ -199,9 +216,9 @@ (file-name-nondirectory f) dir-B)) file-name-history)) - f)) + (ediff-get-default-file-name f 1))) ))) - (ediff-files-internal file-A + (ediff-files-internal file-A (if (file-directory-p file-B) (expand-file-name (file-name-nondirectory file-A) file-B) @@ -209,7 +226,7 @@ nil ; file-C startup-hooks 'ediff-files)) - + ;;;###autoload (defun ediff-files3 (file-A file-B file-C &optional startup-hooks) "Run Ediff on three files, FILE-A, FILE-B, and FILE-C." @@ -219,9 +236,11 @@ default-directory)) dir-B dir-C f ff) (list (setq f (ediff-read-file-name - "File A to compare" dir-A - (ediff-get-default-file-name))) - (setq ff (ediff-read-file-name "File B to compare" + "File A to compare" + dir-A + (ediff-get-default-file-name) + 'no-dirs)) + (setq ff (ediff-read-file-name "File B to compare" (setq dir-B (if ediff-use-last-dir ediff-last-dir-B @@ -234,8 +253,8 @@ (file-name-nondirectory f) dir-B)) file-name-history)) - f))) - (ediff-read-file-name "File C to compare" + (ediff-get-default-file-name f 1)))) + (ediff-read-file-name "File C to compare" (setq dir-C (if ediff-use-last-dir ediff-last-dir-C (file-name-directory ff))) @@ -246,9 +265,9 @@ (file-name-nondirectory ff) dir-C)) file-name-history)) - ff)) + (ediff-get-default-file-name ff 2))) ))) - (ediff-files-internal file-A + (ediff-files-internal file-A (if (file-directory-p file-B) (expand-file-name (file-name-nondirectory file-A) file-B) @@ -264,34 +283,34 @@ (defalias 'ediff3 'ediff-files3) -;; Visit FILE and arrange its buffer to Ediff's liking. +;; Visit FILE and arrange its buffer to Ediff's liking. ;; FILE is actually a variable symbol that must contain a true file name. ;; BUFFER-NAME is a variable symbol, which will get the buffer object into ;; which FILE is read. ;; LAST-DIR is the directory variable symbol where FILE's -;; directory name should be returned. HOOKS-VAR is a variable symbol that will +;; directory name should be returned. HOOKS-VAR is a variable symbol that will ;; be assigned the hook to be executed after `ediff-startup' is finished. ;; `ediff-find-file' arranges that the temp files it might create will be ;; deleted. (defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var) (let* ((file (symbol-value file-var)) - (file-magic (find-file-name-handler file 'find-file-noselect)) + (file-magic (ediff-filename-magic-p file)) (temp-file-name-prefix (file-name-nondirectory file))) (cond ((not (file-readable-p file)) (error "File `%s' does not exist or is not readable" file)) ((file-directory-p file) (error "File `%s' is a directory" file))) - + ;; some of the commands, below, require full file name (setq file (expand-file-name file)) - + ;; Record the directory of the file (if last-dir (set last-dir (expand-file-name (file-name-directory file)))) - + ;; Setup the buffer (set buffer-name (find-file-noselect file)) - + (ediff-with-current-buffer (symbol-value buffer-name) (widen) ; Make sure the entire file is seen (cond (file-magic ; file has a handler, such as jka-compr-handler or @@ -300,7 +319,7 @@ (setq file (ediff-make-temp-file (current-buffer) temp-file-name-prefix)) - (set hooks-var (cons (` (lambda () (delete-file (, file)))) + (set hooks-var (cons `(lambda () (delete-file ,file)) (symbol-value hooks-var)))) ;; file processed via auto-mode-alist, a la uncompress.el ((not (equal (file-truename file) @@ -308,14 +327,21 @@ (setq file (ediff-make-temp-file (current-buffer) temp-file-name-prefix)) - (set hooks-var (cons (` (lambda () (delete-file (, file)))) + (set hooks-var (cons `(lambda () (delete-file ,file)) (symbol-value hooks-var)))) (t ;; plain file---just check that the file matches the buffer (ediff-verify-file-buffer)))) (set file-var file))) -(defun ediff-files-internal (file-A file-B file-C startup-hooks job-name) +;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer +(defun ediff-files-internal (file-A file-B file-C startup-hooks job-name + &optional merge-buffer-file) (let (buf-A buf-B buf-C) + (if (string= file-A file-B) + (error "Files A and B are the same")) + (if (stringp file-C) + (or (and (string= file-A file-C) (error "Files A and C are the same")) + (and (string= file-B file-C) (error "Files B and C are the same")))) (message "Reading file %s ... " file-A) ;;(sit-for 0) (ediff-find-file 'file-A 'buf-A 'ediff-last-dir-A 'startup-hooks) @@ -335,17 +361,34 @@ buf-B file-B buf-C file-C startup-hooks - (list (cons 'ediff-job-name job-name))))) - + (list (cons 'ediff-job-name job-name)) + merge-buffer-file))) + ;;;###autoload (defalias 'ediff 'ediff-files) +;;;###autoload +(defun ediff-backup (file) + "Run Ediff on FILE and its backup file. +Uses the latest backup, if there are several numerical backups. +If this file is a backup, `ediff' it with its original." + (interactive (list (read-file-name "Ediff (file with backup): "))) + ;; The code is taken from `diff-backup'. + (require 'diff) + (let (bak ori) + (if (backup-file-name-p file) + (setq bak file + ori (file-name-sans-versions file)) + (setq bak (or (diff-latest-backup-file file) + (error "No backup found for %s" file)) + ori file)) + (ediff-files bak ori))) ;;;###autoload (defun ediff-buffers (buffer-A buffer-B &optional startup-hooks job-name) "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B." - (interactive + (interactive (let (bf) (list (setq bf (read-buffer "Buffer A to compare: " (ediff-other-buffer "") t)) @@ -362,12 +405,12 @@ ;;;###autoload (defalias 'ebuffers 'ediff-buffers) - + ;;;###autoload (defun ediff-buffers3 (buffer-A buffer-B buffer-C &optional startup-hooks job-name) "Run Ediff on three buffers, BUFFER-A, BUFFER-B, and BUFFER-C." - (interactive + (interactive (let (bf bff) (list (setq bf (read-buffer "Buffer A to compare: " (ediff-other-buffer "") t)) @@ -391,10 +434,12 @@ ;;;###autoload (defalias 'ebuffers3 'ediff-buffers3) - - -(defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name) + + +;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer +(defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name + &optional merge-buffer-file) (let* ((buf-A-file-name (buffer-file-name (get-buffer buf-A))) (buf-B-file-name (buffer-file-name (get-buffer buf-B))) (buf-C-is-alive (ediff-buffer-live-p buf-C)) @@ -415,24 +460,23 @@ (setq buf-B-file-name (file-name-nondirectory buf-B-file-name))) (if (stringp buf-C-file-name) (setq buf-C-file-name (file-name-nondirectory buf-C-file-name))) - + (setq file-A (ediff-make-temp-file buf-A buf-A-file-name) file-B (ediff-make-temp-file buf-B buf-B-file-name)) (if buf-C-is-alive (setq file-C (ediff-make-temp-file buf-C buf-C-file-name))) - + (ediff-setup (get-buffer buf-A) file-A (get-buffer buf-B) file-B (if buf-C-is-alive (get-buffer buf-C)) file-C - (cons (` (lambda () - (delete-file (, file-A)) - (delete-file (, file-B)) - (if (stringp (, file-C)) (delete-file (, file-C))) - )) + (cons `(lambda () + (delete-file ,file-A) + (delete-file ,file-B) + (if (stringp ,file-C) (delete-file ,file-C))) startup-hooks) (list (cons 'ediff-job-name job-name)) - ))) + merge-buffer-file))) ;;; Directory and file group operations @@ -454,19 +498,26 @@ ;;;###autoload (defun ediff-directories (dir1 dir2 regexp) "Run Ediff on a pair of directories, DIR1 and DIR2, comparing files that have -the same name in both. The third argument, REGEXP, is a regular expression that -can be used to filter out certain file names." +the same name in both. The third argument, REGEXP, is nil or a regular +expression; only file names that match the regexp are considered." (interactive (let ((dir-A (ediff-get-default-directory-name)) + (default-regexp (eval ediff-default-filtering-regexp)) f) (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil)) - (ediff-read-file-name "Directory B to compare:" + (ediff-read-file-name "Directory B to compare:" (if ediff-use-last-dir - ediff-last-dir-B + ediff-last-dir-B (ediff-strip-last-dir f)) nil) - (read-string "Filter through regular expression: " - nil 'ediff-filtering-regexp-history) + (read-string + (if (stringp default-regexp) + (format "Filter through regular expression (default %s): " + default-regexp) + "Filter through regular expression: ") + nil + 'ediff-filtering-regexp-history + (eval ediff-default-filtering-regexp)) ))) (ediff-directories-internal dir1 dir2 nil regexp 'ediff-files 'ediff-directories @@ -480,13 +531,21 @@ can be used to filter out certain file names." (defun ediff-directory-revisions (dir1 regexp) "Run Ediff on a directory, DIR1, comparing its files with their revisions. The second argument, REGEXP, is a regular expression that filters the file -names. Only the files that are under revision control are taken into account." +names. Only the files that are under revision control are taken into account." (interactive - (let ((dir-A (ediff-get-default-directory-name))) + (let ((dir-A (ediff-get-default-directory-name)) + (default-regexp (eval ediff-default-filtering-regexp)) + ) (list (ediff-read-file-name "Directory to compare with revision:" dir-A nil) - (read-string "Filter through regular expression: " - nil 'ediff-filtering-regexp-history) + (read-string + (if (stringp default-regexp) + (format "Filter through regular expression (default %s): " + default-regexp) + "Filter through regular expression: ") + nil + 'ediff-filtering-regexp-history + (eval ediff-default-filtering-regexp)) ))) (ediff-directory-revisions-internal dir1 regexp 'ediff-revision 'ediff-directory-revisions @@ -499,24 +558,32 @@ names. Only the files that are under revision control are taken into account." ;;;###autoload (defun ediff-directories3 (dir1 dir2 dir3 regexp) "Run Ediff on three directories, DIR1, DIR2, and DIR3, comparing files that -have the same name in all three. The last argument, REGEXP, is a regular -expression that can be used to filter out certain file names." +have the same name in all three. The last argument, REGEXP, is nil or a +regular expression; only file names that match the regexp are considered." + (interactive (let ((dir-A (ediff-get-default-directory-name)) + (default-regexp (eval ediff-default-filtering-regexp)) f) (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil)) - (setq f (ediff-read-file-name "Directory B to compare:" + (setq f (ediff-read-file-name "Directory B to compare:" (if ediff-use-last-dir - ediff-last-dir-B + ediff-last-dir-B (ediff-strip-last-dir f)) nil)) - (ediff-read-file-name "Directory C to compare:" + (ediff-read-file-name "Directory C to compare:" (if ediff-use-last-dir - ediff-last-dir-C + ediff-last-dir-C (ediff-strip-last-dir f)) nil) - (read-string "Filter through regular expression: " - nil 'ediff-filtering-regexp-history) + (read-string + (if (stringp default-regexp) + (format "Filter through regular expression (default %s): " + default-regexp) + "Filter through regular expression: ") + nil + 'ediff-filtering-regexp-history + (eval ediff-default-filtering-regexp)) ))) (ediff-directories-internal dir1 dir2 dir3 regexp 'ediff-files3 'ediff-directories3 @@ -526,112 +593,152 @@ expression that can be used to filter out certain file names." (defalias 'edirs3 'ediff-directories3) ;;;###autoload -(defun ediff-merge-directories (dir1 dir2 regexp) +(defun ediff-merge-directories (dir1 dir2 regexp &optional merge-autostore-dir) "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have -the same name in both. The third argument, REGEXP, is a regular expression that -can be used to filter out certain file names." +the same name in both. The third argument, REGEXP, is nil or a regular +expression; only file names that match the regexp are considered." (interactive (let ((dir-A (ediff-get-default-directory-name)) + (default-regexp (eval ediff-default-filtering-regexp)) f) (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil)) - (ediff-read-file-name "Directory B to merge:" + (ediff-read-file-name "Directory B to merge:" (if ediff-use-last-dir - ediff-last-dir-B + ediff-last-dir-B (ediff-strip-last-dir f)) nil) - (read-string "Filter through regular expression: " - nil 'ediff-filtering-regexp-history) + (read-string + (if (stringp default-regexp) + (format "Filter through regular expression (default %s): " + default-regexp) + "Filter through regular expression: ") + nil + 'ediff-filtering-regexp-history + (eval ediff-default-filtering-regexp)) ))) (ediff-directories-internal dir1 dir2 nil regexp 'ediff-merge-files 'ediff-merge-directories + nil merge-autostore-dir )) ;;;###autoload (defalias 'edirs-merge 'ediff-merge-directories) ;;;###autoload -(defun ediff-merge-directories-with-ancestor (dir1 dir2 ancestor-dir regexp) +(defun ediff-merge-directories-with-ancestor (dir1 dir2 ancestor-dir regexp + &optional + merge-autostore-dir) "Merge files in directories DIR1 and DIR2 using files in ANCESTOR-DIR as ancestors. -Ediff merges files that have identical names in DIR1, DIR2. If a pair of files +Ediff merges files that have identical names in DIR1, DIR2. If a pair of files in DIR1 and DIR2 doesn't have an ancestor in ANCESTOR-DIR, Ediff will merge -without ancestor. The fourth argument, REGEXP, is a regular expression that -can be used to filter out certain file names." +without ancestor. The fourth argument, REGEXP, is nil or a regular expression; +only file names that match the regexp are considered." (interactive (let ((dir-A (ediff-get-default-directory-name)) + (default-regexp (eval ediff-default-filtering-regexp)) f) (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil)) - (setq f (ediff-read-file-name "Directory B to merge:" + (setq f (ediff-read-file-name "Directory B to merge:" (if ediff-use-last-dir - ediff-last-dir-B + ediff-last-dir-B (ediff-strip-last-dir f)) nil)) (ediff-read-file-name "Ancestor directory:" (if ediff-use-last-dir - ediff-last-dir-C + ediff-last-dir-C (ediff-strip-last-dir f)) nil) - (read-string "Filter through regular expression: " - nil 'ediff-filtering-regexp-history) + (read-string + (if (stringp default-regexp) + (format "Filter through regular expression (default %s): " + default-regexp) + "Filter through regular expression: ") + nil + 'ediff-filtering-regexp-history + (eval ediff-default-filtering-regexp)) ))) (ediff-directories-internal dir1 dir2 ancestor-dir regexp 'ediff-merge-files-with-ancestor 'ediff-merge-directories-with-ancestor + nil merge-autostore-dir )) ;;;###autoload -(defun ediff-merge-directory-revisions (dir1 regexp) +(defun ediff-merge-directory-revisions (dir1 regexp + &optional merge-autostore-dir) "Run Ediff on a directory, DIR1, merging its files with their revisions. The second argument, REGEXP, is a regular expression that filters the file -names. Only the files that are under revision control are taken into account." +names. Only the files that are under revision control are taken into account." (interactive - (let ((dir-A (ediff-get-default-directory-name))) + (let ((dir-A (ediff-get-default-directory-name)) + (default-regexp (eval ediff-default-filtering-regexp)) + ) (list (ediff-read-file-name "Directory to merge with revisions:" dir-A nil) - (read-string "Filter through regular expression: " - nil 'ediff-filtering-regexp-history) + (read-string + (if (stringp default-regexp) + (format "Filter through regular expression (default %s): " + default-regexp) + "Filter through regular expression: ") + nil + 'ediff-filtering-regexp-history + (eval ediff-default-filtering-regexp)) ))) (ediff-directory-revisions-internal dir1 regexp 'ediff-merge-revisions 'ediff-merge-directory-revisions + nil merge-autostore-dir )) ;;;###autoload (defalias 'edir-merge-revisions 'ediff-merge-directory-revisions) ;;;###autoload -(defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp) +(defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp + &optional + merge-autostore-dir) "Run Ediff on a directory, DIR1, merging its files with their revisions and ancestors. The second argument, REGEXP, is a regular expression that filters the file -names. Only the files that are under revision control are taken into account." +names. Only the files that are under revision control are taken into account." (interactive - (let ((dir-A (ediff-get-default-directory-name))) + (let ((dir-A (ediff-get-default-directory-name)) + (default-regexp (eval ediff-default-filtering-regexp)) + ) (list (ediff-read-file-name "Directory to merge with revisions and ancestors:" dir-A nil) - (read-string "Filter through regular expression: " - nil 'ediff-filtering-regexp-history) + (read-string + (if (stringp default-regexp) + (format "Filter through regular expression (default %s): " + default-regexp) + "Filter through regular expression: ") + nil + 'ediff-filtering-regexp-history + (eval ediff-default-filtering-regexp)) ))) (ediff-directory-revisions-internal dir1 regexp 'ediff-merge-revisions-with-ancestor 'ediff-merge-directory-revisions-with-ancestor + nil merge-autostore-dir )) ;;;###autoload (defalias 'edir-merge-revisions-with-ancestor - 'ediff-merge-directory-revisions-with-ancestor) + 'ediff-merge-directory-revisions-with-ancestor) ;;;###autoload (defalias 'edirs-merge-with-ancestor 'ediff-merge-directories-with-ancestor) ;; Run ediff-action (ediff-files, ediff-merge, ediff-merge-with-ancestors) ;; on a pair of directories (three directories, in case of ancestor). -;; The third argument, REGEXP, is a regular expression that can be used to -;; filter out certain file names. +;; The third argument, REGEXP, is nil or a regular expression; +;; only file names that match the regexp are considered. ;; JOBNAME is the symbol indicating the meta-job to be performed. -;; MERGE-DIR is the directory in which to store merged files. -(defun ediff-directories-internal (dir1 dir2 dir3 regexp action jobname - &optional startup-hooks) +;; MERGE-AUTOSTORE-DIR is the directory in which to store merged files. +(defun ediff-directories-internal (dir1 dir2 dir3 regexp action jobname + &optional startup-hooks + merge-autostore-dir) ;; ediff-read-file-name is set to attach a previously entered file name if - ;; the currently entered file is a directory. This code takes care of that. + ;; the currently entered file is a directory. This code takes care of that. (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1)) dir2 (if (file-directory-p dir2) dir2 (file-name-directory dir2))) @@ -647,44 +754,56 @@ names. Only the files that are under revision control are taken into account." (string= dir2 dir3)) (error "Directories B and C are the same: %s" dir1))) - (let (diffs ; var where ediff-intersect-directories returns the diff list - merge-autostore-dir - file-list meta-buf) - (if (and ediff-autostore-merges (ediff-merge-metajob jobname)) - (setq merge-autostore-dir - (ediff-read-file-name "Directory to save merged files:" - (if ediff-use-last-dir + (if merge-autostore-dir + (or (stringp merge-autostore-dir) + (error "%s: Directory for storing merged files must be a string" + jobname))) + (let (;; dir-diff-struct is of the form (common-list diff-list) + ;; It is a structure where ediff-intersect-directories returns + ;; commonalities and differences among directories + dir-diff-struct + meta-buf) + (if (and ediff-autostore-merges + (ediff-merge-metajob jobname) + (not merge-autostore-dir)) + (setq merge-autostore-dir + (read-file-name "Save merged files in directory: " + (if ediff-use-last-dir ediff-last-merge-autostore-dir (ediff-strip-last-dir dir1)) - nil))) + nil + 'must-match))) ;; verify we are not merging into an orig directory - (if (stringp merge-autostore-dir) + (if merge-autostore-dir (cond ((and (stringp dir1) (string= merge-autostore-dir dir1)) - (or (y-or-n-p "Merge directory same as directory A, sure? ") + (or (y-or-n-p + "Directory for saving merged files = Directory A. Sure? ") (error "Directory merge aborted"))) ((and (stringp dir2) (string= merge-autostore-dir dir2)) - (or (y-or-n-p "Merge directory same as directory B, sure? ") + (or (y-or-n-p + "Directory for saving merged files = Directory B. Sure? ") (error "Directory merge aborted"))) ((and (stringp dir3) (string= merge-autostore-dir dir3)) (or (y-or-n-p - "Merge directory same as ancestor directory, sure? ") + "Directory for saving merged files = Ancestor Directory. Sure? ") (error "Directory merge aborted"))))) - - (setq file-list (ediff-intersect-directories - jobname 'diffs - regexp dir1 dir2 dir3 merge-autostore-dir)) + + (setq dir-diff-struct (ediff-intersect-directories + jobname + regexp dir1 dir2 dir3 merge-autostore-dir)) (setq startup-hooks ;; this sets various vars in the meta buffer inside ;; ediff-prepare-meta-buffer - (cons (` (lambda () - ;; tell what to do if the user clicks on a session record - (setq ediff-session-action-function (quote (, action))) - ;; set ediff-dir-difference-list - (setq ediff-dir-difference-list (quote (, diffs))))) + (cons `(lambda () + ;; tell what to do if the user clicks on a session record + (setq ediff-session-action-function (quote ,action)) + ;; set ediff-dir-difference-list + (setq ediff-dir-difference-list + (cdr (quote ,dir-diff-struct)))) startup-hooks)) - (setq meta-buf (ediff-prepare-meta-buffer + (setq meta-buf (ediff-prepare-meta-buffer 'ediff-filegroup-action - file-list + (car dir-diff-struct) "*Ediff Session Group Panel" 'ediff-redraw-directory-group-buffer jobname @@ -692,38 +811,47 @@ names. Only the files that are under revision control are taken into account." (ediff-show-meta-buffer meta-buf) )) -(defun ediff-directory-revisions-internal (dir1 regexp action jobname - &optional startup-hooks) +;; MERGE-AUTOSTORE-DIR can be given to tell ediff where to store the merged +;; files +(defun ediff-directory-revisions-internal (dir1 regexp action jobname + &optional startup-hooks + merge-autostore-dir) (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1))) - (let (file-list meta-buf merge-autostore-dir) - (if (and ediff-autostore-merges (ediff-merge-metajob jobname)) - (setq merge-autostore-dir - (ediff-read-file-name "Directory to save merged files:" - (if ediff-use-last-dir - ediff-last-merge-autostore-dir - (ediff-strip-last-dir dir1)) - nil))) + (if merge-autostore-dir + (or (stringp merge-autostore-dir) + (error "%S: Directory for storing merged files must be a string" + jobname))) + (let (file-list meta-buf) + (if (and ediff-autostore-merges + (ediff-merge-metajob jobname) + (not merge-autostore-dir)) + (setq merge-autostore-dir + (read-file-name "Save merged files in directory: " + (if ediff-use-last-dir + ediff-last-merge-autostore-dir + (ediff-strip-last-dir dir1)) + nil + 'must-match))) ;; verify merge-autostore-dir != dir1 - (if (and (stringp merge-autostore-dir) + (if (and merge-autostore-dir (stringp dir1) (string= merge-autostore-dir dir1)) (or (y-or-n-p - "Directory for saving merges is the same as directory A. Sure? ") + "Directory for saving merged file = directory A. Sure? ") (error "Merge of directory revisions aborted"))) - + (setq file-list (ediff-get-directory-files-under-revision jobname regexp dir1 merge-autostore-dir)) (setq startup-hooks ;; this sets various vars in the meta buffer inside ;; ediff-prepare-meta-buffer - (cons (` (lambda () - ;; tell what to do if the user clicks on a session record - (setq ediff-session-action-function (quote (, action))) - )) + (cons `(lambda () + ;; tell what to do if the user clicks on a session record + (setq ediff-session-action-function (quote ,action))) startup-hooks)) - (setq meta-buf (ediff-prepare-meta-buffer + (setq meta-buf (ediff-prepare-meta-buffer 'ediff-filegroup-action file-list "*Ediff Session Group Panel" @@ -746,7 +874,7 @@ If WIND-B is nil, use window next to WIND-A." (interactive "P") (ediff-windows dumb-mode wind-A wind-B startup-hooks 'ediff-windows-wordwise 'word-mode)) - + ;;;###autoload (defun ediff-windows-linewise (dumb-mode &optional wind-A wind-B startup-hooks) "Compare WIND-A and WIND-B, which are selected by clicking, linewise. @@ -757,7 +885,7 @@ If WIND-B is nil, use window next to WIND-A." (interactive "P") (ediff-windows dumb-mode wind-A wind-B startup-hooks 'ediff-windows-linewise nil)) - + ;; Compare WIND-A and WIND-B, which are selected by clicking. ;; With prefix argument, DUMB-MODE, or on a non-windowing display, ;; works as follows: @@ -769,11 +897,11 @@ If WIND-B is nil, use window next to WIND-A." wind-B (ediff-get-next-window wind-B wind-A)) (setq wind-A (ediff-get-window-by-clicking wind-A nil 1) wind-B (ediff-get-window-by-clicking wind-B wind-A 2))) - + (let ((buffer-A (window-buffer wind-A)) (buffer-B (window-buffer wind-B)) beg-A end-A beg-B end-B) - + (save-excursion (save-window-excursion (sit-for 0) ; sync before using window-start/end -- a precaution @@ -783,17 +911,27 @@ If WIND-B is nil, use window next to WIND-A." (select-window wind-B) (setq beg-B (window-start) end-B (window-end)))) + (setq buffer-A + (ediff-clone-buffer-for-window-comparison + buffer-A wind-A "-Window.A-") + buffer-B + (ediff-clone-buffer-for-window-comparison + buffer-B wind-B "-Window.B-")) (ediff-regions-internal buffer-A beg-A end-A buffer-B beg-B end-B startup-hooks job-name word-mode nil))) - + + ;;;###autoload (defun ediff-regions-wordwise (buffer-A buffer-B &optional startup-hooks) - "Run Ediff on a pair of regions in two different buffers. -Regions \(i.e., point and mark\) are assumed to be set in advance. + "Run Ediff on a pair of regions in specified buffers. +Regions \(i.e., point and mark\) are assumed to be set in advance except +for the second region in the case both regions are from the same buffer. +In such a case the user is asked to interactively establish the second +region. This function is effective only for relatively small regions, up to 200 -lines. For large regions, use `ediff-regions-linewise'." - (interactive +lines. For large regions, use `ediff-regions-linewise'." + (interactive (let (bf) (list (setq bf (read-buffer "Region's A buffer: " (ediff-other-buffer "") t)) @@ -808,9 +946,13 @@ lines. For large regions, use `ediff-regions-linewise'." (error "Buffer %S doesn't exist" buffer-A)) (if (not (ediff-buffer-live-p buffer-B)) (error "Buffer %S doesn't exist" buffer-B)) - - - (let (reg-A-beg reg-A-end reg-B-beg reg-B-end) + + + (let ((buffer-A + (ediff-clone-buffer-for-region-comparison buffer-A "-Region.A-")) + (buffer-B + (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-")) + reg-A-beg reg-A-end reg-B-beg reg-B-end) (save-excursion (set-buffer buffer-A) (setq reg-A-beg (region-beginning) @@ -818,20 +960,23 @@ lines. For large regions, use `ediff-regions-linewise'." (set-buffer buffer-B) (setq reg-B-beg (region-beginning) reg-B-end (region-end))) - + (ediff-regions-internal (get-buffer buffer-A) reg-A-beg reg-A-end (get-buffer buffer-B) reg-B-beg reg-B-end startup-hooks 'ediff-regions-wordwise 'word-mode nil))) - + ;;;###autoload (defun ediff-regions-linewise (buffer-A buffer-B &optional startup-hooks) - "Run Ediff on a pair of regions in two different buffers. -Regions \(i.e., point and mark\) are assumed to be set in advance. + "Run Ediff on a pair of regions in specified buffers. +Regions \(i.e., point and mark\) are assumed to be set in advance except +for the second region in the case both regions are from the same buffer. +In such a case the user is asked to interactively establish the second +region. Each region is enlarged to contain full lines. This function is effective for large regions, over 100-200 -lines. For small regions, use `ediff-regions-wordwise'." - (interactive +lines. For small regions, use `ediff-regions-wordwise'." + (interactive (let (bf) (list (setq bf (read-buffer "Region A's buffer: " (ediff-other-buffer "") t)) @@ -846,48 +991,52 @@ lines. For small regions, use `ediff-regions-wordwise'." (error "Buffer %S doesn't exist" buffer-A)) (if (not (ediff-buffer-live-p buffer-B)) (error "Buffer %S doesn't exist" buffer-B)) - - (let (reg-A-beg reg-A-end reg-B-beg reg-B-end) + + (let ((buffer-A + (ediff-clone-buffer-for-region-comparison buffer-A "-Region.A-")) + (buffer-B + (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-")) + reg-A-beg reg-A-end reg-B-beg reg-B-end) (save-excursion (set-buffer buffer-A) (setq reg-A-beg (region-beginning) reg-A-end (region-end)) ;; enlarge the region to hold full lines - (goto-char reg-A-beg) + (goto-char reg-A-beg) (beginning-of-line) (setq reg-A-beg (point)) - (goto-char reg-A-end) + (goto-char reg-A-end) (end-of-line) (or (eobp) (forward-char)) ; include the newline char (setq reg-A-end (point)) - + (set-buffer buffer-B) (setq reg-B-beg (region-beginning) reg-B-end (region-end)) ;; enlarge the region to hold full lines - (goto-char reg-B-beg) + (goto-char reg-B-beg) (beginning-of-line) (setq reg-B-beg (point)) - (goto-char reg-B-end) + (goto-char reg-B-end) (end-of-line) (or (eobp) (forward-char)) ; include the newline char (setq reg-B-end (point)) ) ; save excursion - + (ediff-regions-internal (get-buffer buffer-A) reg-A-beg reg-A-end (get-buffer buffer-B) reg-B-beg reg-B-end startup-hooks 'ediff-regions-linewise nil nil))) ; no word mode - + ;; compare region beg-A to end-A of buffer-A -;; to regions beg-B -- end-B in buffer-B. +;; to regions beg-B -- end-B in buffer-B. (defun ediff-regions-internal (buffer-A beg-A end-A buffer-B beg-B end-B startup-hooks job-name word-mode setup-parameters) (let ((tmp-buffer (get-buffer-create ediff-tmp-buffer)) overl-A overl-B file-A file-B) - + ;; in case beg/end-A/B aren't markers--make them into markers (ediff-with-current-buffer buffer-A (setq beg-A (move-marker (make-marker) beg-A) @@ -895,66 +1044,54 @@ lines. For small regions, use `ediff-regions-wordwise'." (ediff-with-current-buffer buffer-B (setq beg-B (move-marker (make-marker) beg-B) end-B (move-marker (make-marker) end-B))) - - (if (and (eq buffer-A buffer-B) - (or (and (< beg-A end-B) (<= beg-B beg-A)) ; b-B b-A e-B - (and (< beg-B end-A) (<= end-A end-B)))) ; b-B e-A e-B - (progn - (with-output-to-temp-buffer ediff-msg-buffer - (princ " -You have requested to compare overlapping regions of the same buffer. - -In this case, Ediff's highlighting may be confusing---in the same window, -you may see highlighted regions that belong to different regions. -Continue anyway? (y/n) ")) - - (if (y-or-n-p "Continue anyway? ") - () - (error "%S aborted" job-name)))) - ;; make file-A (if word-mode (ediff-wordify beg-A end-A buffer-A tmp-buffer) (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer)) (setq file-A (ediff-make-temp-file tmp-buffer "regA")) - + ;; make file-B (if word-mode (ediff-wordify beg-B end-B buffer-B tmp-buffer) (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer)) (setq file-B (ediff-make-temp-file tmp-buffer "regB")) - + (setq overl-A (ediff-make-bullet-proof-overlay beg-A end-A buffer-A)) (setq overl-B (ediff-make-bullet-proof-overlay beg-B end-B buffer-B)) (ediff-setup buffer-A file-A buffer-B file-B nil nil ; buffer & file C - (cons (` (lambda () - (delete-file (, file-A)) - (delete-file (, file-B)))) + (cons `(lambda () + (delete-file ,file-A) + (delete-file ,file-B)) startup-hooks) (append (list (cons 'ediff-word-mode word-mode) (cons 'ediff-narrow-bounds (list overl-A overl-B)) (cons 'ediff-job-name job-name)) - setup-parameters) - ) + setup-parameters)) )) - - + + ;;; Merge files and buffers - + ;;;###autoload (defalias 'ediff-merge 'ediff-merge-files) - + (defsubst ediff-merge-on-startup () (ediff-do-merge 0) - (ediff-with-current-buffer ediff-buffer-C - (set-buffer-modified-p nil))) + ;; Can't remember why this is here, but it may cause the automatically merged + ;; buffer to be lost. So, keep the buffer modified. + ;;(ediff-with-current-buffer ediff-buffer-C + ;; (set-buffer-modified-p nil)) + ) ;;;###autoload -(defun ediff-merge-files (file-A file-B &optional startup-hooks) +(defun ediff-merge-files (file-A file-B + ;; MERGE-BUFFER-FILE is the file to be + ;; associated with the merge buffer + &optional startup-hooks merge-buffer-file) "Merge two files without ancestor." (interactive (let ((dir-A (if ediff-use-last-dir @@ -962,12 +1099,14 @@ Continue anyway? (y/n) ")) default-directory)) dir-B f) (list (setq f (ediff-read-file-name - "File A to merge" dir-A - (ediff-get-default-file-name))) - (ediff-read-file-name "File B to merge" + "File A to merge" + dir-A + (ediff-get-default-file-name) + 'no-dirs)) + (ediff-read-file-name "File B to merge" (setq dir-B (if ediff-use-last-dir - ediff-last-dir-B + ediff-last-dir-B (file-name-directory f))) (progn (setq file-name-history @@ -976,21 +1115,27 @@ Continue anyway? (y/n) ")) (file-name-nondirectory f) dir-B)) file-name-history)) - f)) + (ediff-get-default-file-name f 1))) ))) (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks)) - (ediff-files-internal file-A + (ediff-files-internal file-A (if (file-directory-p file-B) (expand-file-name (file-name-nondirectory file-A) file-B) file-B) nil ; file-C startup-hooks - 'ediff-merge-files)) - + 'ediff-merge-files + merge-buffer-file)) + ;;;###autoload (defun ediff-merge-files-with-ancestor (file-A file-B file-ancestor - &optional startup-hooks) + &optional + startup-hooks + ;; MERGE-BUFFER-FILE is the file + ;; to be associated with the + ;; merge buffer + merge-buffer-file) "Merge two files with ancestor." (interactive (let ((dir-A (if ediff-use-last-dir @@ -998,12 +1143,14 @@ Continue anyway? (y/n) ")) default-directory)) dir-B dir-ancestor f ff) (list (setq f (ediff-read-file-name - "File A to merge" dir-A - (ediff-get-default-file-name))) - (setq ff (ediff-read-file-name "File B to merge" + "File A to merge" + dir-A + (ediff-get-default-file-name) + 'no-dirs)) + (setq ff (ediff-read-file-name "File B to merge" (setq dir-B (if ediff-use-last-dir - ediff-last-dir-B + ediff-last-dir-B (file-name-directory f))) (progn (setq file-name-history @@ -1013,8 +1160,8 @@ Continue anyway? (y/n) ")) (file-name-nondirectory f) dir-B)) file-name-history)) - f))) - (ediff-read-file-name "Ancestor file" + (ediff-get-default-file-name f 1)))) + (ediff-read-file-name "Ancestor file" (setq dir-ancestor (if ediff-use-last-dir ediff-last-dir-ancestor @@ -1026,25 +1173,30 @@ Continue anyway? (y/n) ")) (file-name-nondirectory ff) dir-ancestor)) file-name-history)) - ff)) + (ediff-get-default-file-name ff 2))) ))) (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks)) - (ediff-files-internal file-A + (ediff-files-internal file-A (if (file-directory-p file-B) (expand-file-name (file-name-nondirectory file-A) file-B) file-B) file-ancestor startup-hooks - 'ediff-merge-files-with-ancestor)) - + 'ediff-merge-files-with-ancestor + merge-buffer-file)) + ;;;###autoload (defalias 'ediff-merge-with-ancestor 'ediff-merge-files-with-ancestor) - + ;;;###autoload -(defun ediff-merge-buffers (buffer-A buffer-B &optional startup-hooks job-name) +(defun ediff-merge-buffers (buffer-A buffer-B + &optional + ;; MERGE-BUFFER-FILE is the file to be + ;; associated with the merge buffer + startup-hooks job-name merge-buffer-file) "Merge buffers without ancestor." - (interactive + (interactive (let (bf) (list (setq bf (read-buffer "Buffer A to merge: " (ediff-other-buffer "") t)) @@ -1055,18 +1207,23 @@ Continue anyway? (y/n) ")) (save-window-excursion (other-window 1)) (ediff-other-buffer bf)) t)))) - + (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks)) (or job-name (setq job-name 'ediff-merge-buffers)) (ediff-buffers-internal - buffer-A buffer-B nil startup-hooks job-name)) - + buffer-A buffer-B nil startup-hooks job-name merge-buffer-file)) + ;;;###autoload -(defun ediff-merge-buffers-with-ancestor (buffer-A - buffer-B buffer-ancestor - &optional startup-hooks job-name) +(defun ediff-merge-buffers-with-ancestor (buffer-A buffer-B buffer-ancestor + &optional + startup-hooks + job-name + ;; MERGE-BUFFER-FILE is the + ;; file to be associated + ;; with the merge buffer + merge-buffer-file) "Merge buffers with ancestor." - (interactive + (interactive (let (bf bff) (list (setq bf (read-buffer "Buffer A to merge: " (ediff-other-buffer "") t)) @@ -1085,15 +1242,16 @@ Continue anyway? (y/n) ")) (ediff-other-buffer (list bf bff))) t) ))) - + (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks)) (or job-name (setq job-name 'ediff-merge-buffers-with-ancestor)) (ediff-buffers-internal - buffer-A buffer-B buffer-ancestor startup-hooks job-name)) - + buffer-A buffer-B buffer-ancestor startup-hooks job-name merge-buffer-file)) + ;;;###autoload -(defun ediff-merge-revisions (&optional file startup-hooks) +(defun ediff-merge-revisions (&optional file startup-hooks merge-buffer-file) + ;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer "Run Ediff by merging two revisions of a file. The file is the optional FILE argument or the file visited by the current buffer." @@ -1103,7 +1261,7 @@ buffer." (setq rev1 (read-string (format - "Version 1 to merge (default: %s's latest version): " + "Version 1 to merge (default: %s's working version): " (if (stringp file) (file-name-nondirectory file) "current buffer"))) rev2 @@ -1116,13 +1274,18 @@ buffer." ;; ancestor-revision=nil (funcall (intern (format "ediff-%S-merge-internal" ediff-version-control-package)) - rev1 rev2 nil startup-hooks))) - + rev1 rev2 nil startup-hooks merge-buffer-file))) + ;;;###autoload -(defun ediff-merge-revisions-with-ancestor (&optional file startup-hooks) +(defun ediff-merge-revisions-with-ancestor (&optional + file startup-hooks + ;; MERGE-BUFFER-FILE is the file to + ;; be associated with the merge + ;; buffer + merge-buffer-file) "Run Ediff by merging two revisions of a file with a common ancestor. -The file is the the optional FILE argument or the file visited by the current +The file is the optional FILE argument or the file visited by the current buffer." (interactive) (if (stringp file) (find-file file)) @@ -1130,7 +1293,7 @@ buffer." (setq rev1 (read-string (format - "Version 1 to merge (default: %s's latest version): " + "Version 1 to merge (default: %s's working version): " (if (stringp file) (file-name-nondirectory file) "current buffer"))) rev2 @@ -1142,18 +1305,19 @@ buffer." ancestor-rev (read-string (format - "Ancestor version (default: %s): " + "Ancestor version (default: %s's base revision): " (if (stringp file) (file-name-nondirectory file) "current buffer")))) (ediff-load-version-control) (funcall (intern (format "ediff-%S-merge-internal" ediff-version-control-package)) - rev1 rev2 ancestor-rev startup-hooks))) + rev1 rev2 ancestor-rev startup-hooks merge-buffer-file))) +;; MK: Check. This function doesn't seem to be used any more by pcvs or pcl-cvs ;;;###autoload (defun run-ediff-from-cvs-buffer (pos) "Run Ediff-merge on appropriate revisions of the selected file. -First run after `M-x cvs-update'. Then place the cursor on a line describing a +First run after `M-x cvs-update'. Then place the cursor on a line describing a file and then run `run-ediff-from-cvs-buffer'." (interactive "d") (ediff-load-version-control) @@ -1161,18 +1325,23 @@ file and then run `run-ediff-from-cvs-buffer'." (if tin (cvs-run-ediff-on-file-descriptor tin) (error "There is no file to merge")))) - - + + ;;; Apply patch ;;;###autoload -(defun ediff-patch-file () - "Run Ediff by patching SOURCE-FILENAME." - ;; This now returns the control buffer - (interactive) - (let (source-dir source-file patch-buf) +(defun ediff-patch-file (&optional arg patch-buf) + "Run Ediff by patching SOURCE-FILENAME. +If optional PATCH-BUF is given, use the patch in that buffer +and don't ask the user. +If prefix argument, then: if even argument, assume that the patch is in a +buffer. If odd -- assume it is in a file." + (interactive "P") + (let (source-dir source-file) (require 'ediff-ptch) - (setq patch-buf (ediff-get-patch-buffer)) + (setq patch-buf + (ediff-get-patch-buffer + (if arg (prefix-numeric-value arg)) patch-buf)) (setq source-dir (cond (ediff-use-last-dir ediff-last-dir-patch) ((and (not ediff-patch-default-directory) (buffer-file-name patch-buf)) @@ -1181,25 +1350,33 @@ file and then run `run-ediff-from-cvs-buffer'." (buffer-file-name patch-buf)))) (t default-directory))) (setq source-file - ;; the default is the directory, not the visited file name - (ediff-read-file-name "Which file to patch? " source-dir source-dir)) + (read-file-name + "File to patch (directory, if multifile patch): " + ;; use an explicit initial file + source-dir nil nil (ediff-get-default-file-name))) (ediff-dispatch-file-patching-job patch-buf source-file))) ;;;###autoload -(defun ediff-patch-buffer () - "Run Ediff by patching BUFFER-NAME." - (interactive) - (let (patch-buf) - (require 'ediff-ptch) - (setq patch-buf (ediff-get-patch-buffer)) - (ediff-patch-buffer-internal - patch-buf - (read-buffer "Which buffer to patch? " - (cond ((eq patch-buf (current-buffer)) - (window-buffer (other-window 1))) - (t (current-buffer))) - 'must-match)))) - +(defun ediff-patch-buffer (&optional arg patch-buf) + "Run Ediff by patching the buffer specified at prompt. +Without the optional prefix ARG, asks if the patch is in some buffer and +prompts for the buffer or a file, depending on the answer. +With ARG=1, assumes the patch is in a file and prompts for the file. +With ARG=2, assumes the patch is in a buffer and prompts for the buffer. +PATCH-BUF is an optional argument, which specifies the buffer that contains the +patch. If not given, the user is prompted according to the prefix argument." + (interactive "P") + (require 'ediff-ptch) + (setq patch-buf + (ediff-get-patch-buffer + (if arg (prefix-numeric-value arg)) patch-buf)) + (ediff-patch-buffer-internal + patch-buf + (read-buffer + "Which buffer to patch? " + (current-buffer)))) + + ;;;###autoload (defalias 'epatch 'ediff-patch-file) ;;;###autoload @@ -1208,27 +1385,38 @@ file and then run `run-ediff-from-cvs-buffer'." -;;; Versions Control functions - +;;; Versions Control functions + ;;;###autoload (defun ediff-revision (&optional file startup-hooks) "Run Ediff by comparing versions of a file. -The file is an optional FILE argument or the file visited by the current -buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'." +The file is an optional FILE argument or the file entered at the prompt. +Default: the file visited by the current buffer. +Uses `vc.el' or `rcs.el' depending on `ediff-version-control-package'." ;; if buffer is non-nil, use that buffer instead of the current buffer (interactive "P") - (if (stringp file) (find-file file)) + (if (not (stringp file)) + (setq file + (ediff-read-file-name "Compare revisions for file" + (if ediff-use-last-dir + ediff-last-dir-A + default-directory) + (ediff-get-default-file-name) + 'no-dirs))) + (find-file file) + (if (and (buffer-modified-p) + (y-or-n-p (message "Buffer %s is modified. Save buffer? " + (buffer-name)))) + (save-buffer (current-buffer))) (let (rev1 rev2) (setq rev1 (read-string - (format "Version 1 to compare (default: %s's latest version): " - (if (stringp file) - (file-name-nondirectory file) "current buffer"))) + (format "Revision 1 to compare (default: %s's latest revision): " + (file-name-nondirectory file))) rev2 - (read-string - (format "Version 2 to compare (default: %s): " - (if (stringp file) - (file-name-nondirectory file) "current buffer")))) + (read-string + (format "Revision 2 to compare (default: %s's current state): " + (file-name-nondirectory file)))) (ediff-load-version-control) (funcall (intern (format "ediff-%S-internal" ediff-version-control-package)) @@ -1238,8 +1426,8 @@ buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'." ;;;###autoload (defalias 'erevision 'ediff-revision) - - + + ;; Test if version control package is loaded and load if not ;; Is SILENT is non-nil, don't report error if package is not found. (defun ediff-load-version-control (&optional silent) @@ -1250,7 +1438,7 @@ buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'." (message "") ; kill the message from `locate-library' (require ediff-version-control-package)) (or silent - (error "Version control package %S.el not found. Use vc.el instead" + (error "Version control package %S.el not found. Use vc.el instead" ediff-version-control-package))))) @@ -1283,14 +1471,67 @@ With optional NODE, goes to that node." (raise-frame (selected-frame))) (error (beep 1) (with-output-to-temp-buffer ediff-msg-buffer + (ediff-with-current-buffer standard-output + (fundamental-mode)) (princ ediff-BAD-INFO)) (if (window-live-p ctl-window) (progn (select-window ctl-window) (set-window-buffer ctl-window ctl-buf))))))) - +(dolist (mess '("^Errors in diff output. Diff output is in " + "^Hmm... I don't see an Ediff command around here...$" + "^Undocumented command! Type `G' in Ediff Control Panel to drop a note to the Ediff maintainer$" + ": This command runs in Ediff Control Buffer only!$" + ": Invalid op in ediff-check-version$" + "^ediff-shrink-window-C can be used only for merging jobs$" + "^Lost difference info on these directories$" + "^This command is inapplicable in the present context$" + "^This session group has no parent$" + "^Can't hide active session, $" + "^Ediff: something wrong--no multiple diffs buffer$" + "^Can't make context diff for Session $" + "^The patch buffer wasn't found$" + "^Aborted$" + "^This Ediff session is not part of a session group$" + "^No active Ediff sessions or corrupted session registry$" + "^No session info in this line$" + "^`.*' is not an ordinary file$" + "^Patch appears to have failed$" + "^Recomputation of differences cancelled$" + "^No fine differences in this mode$" + "^Lost connection to ancestor buffer...sorry$" + "^Not merging with ancestor$" + "^Don't know how to toggle read-only in buffer " + "Emacs is not running as a window application$" + "^This command makes sense only when merging with an ancestor$" + "^At end of the difference list$" + "^At beginning of the difference list$" + "^Nothing saved for diff .* in buffer " + "^Buffer is out of sync for file " + "^Buffer out of sync for file " + "^Output from `diff' not found$" + "^You forgot to specify a region in buffer " + "^All right. Make up your mind and come back...$" + "^Current buffer is not visiting any file$" + "^Failed to retrieve revision: $" + "^Can't determine display width.$" + "^File `.*' does not exist or is not readable$" + "^File `.*' is a directory$" + "^Buffer .* doesn't exist$" + "^Directories . and . are the same: " + "^Directory merge aborted$" + "^Merge of directory revisions aborted$" + "^Buffer .* doesn't exist$" + "^There is no file to merge$" + "^Version control package .*.el not found. Use vc.el instead$")) + (add-to-list 'debug-ignored-errors mess)) + + +(require 'ediff-util) + +(run-hooks 'ediff-load-hook) ;;; Local Variables: ;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun) @@ -1298,6 +1539,5 @@ With optional NODE, goes to that node." ;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body)) ;;; End: -(require 'ediff-util) - +;;; arch-tag: 97c71396-db02-4f41-8b48-6a51c3348fcc ;;; ediff.el ends here