X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/4ae69eaccfa11c54516ad25942d123e0b320a914..cd30a521aa838f6bcf08ee6ffb62986cd2bf8daa:/lisp/ediff.el diff --git a/lisp/ediff.el b/lisp/ediff.el index d629a3e249..4e6cd8667f 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 Free Software Foundation, Inc. +;; Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc. ;; Author: Michael Kifer ;; Created: February 2, 1994 ;; Keywords: comparing, merging, patching, version control. -(defconst ediff-version "2.54" "The current version of Ediff") -(defconst ediff-date "February 14, 1996" "Date of last update") +(defconst ediff-version "2.70.2" "The current version of Ediff") +(defconst ediff-date "May 21, 1998" "Date of last update") + ;; This file is part of GNU Emacs. @@ -29,7 +30,7 @@ ;;; Commentary: ;; Never read that diff output again! -;; Apply patch selectively, like a pro! +;; Apply patch interactively! ;; Merge with ease! ;; This package provides a convenient way of simultaneous browsing through @@ -58,7 +59,7 @@ ;; files with their older versions. Ediff can also work with remote and ;; compressed files. Details are given below. -;; Finally, Ediff supports directory-level comparison and merging operations. +;; 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 @@ -105,165 +106,60 @@ ;;; Code: +(provide 'ediff) + +;; Compiler pacifier +(defvar cvs-cookie-handle) +(defvar ediff-last-dir-patch) +(defvar ediff-patch-default-directory) + +(and noninteractive + (eval-when-compile + (load-library "dired") + (load-library "info") + (load "pcl-cvs" 'noerror))) +(eval-when-compile + (let ((load-path (cons (expand-file-name ".") load-path))) + (or (featurep 'ediff-init) + (load "ediff-init.el" nil nil 'nosuffix)) + (or (featurep 'ediff-mult) + (load "ediff-mult.el" nil nil 'nosuffix)) + (or (featurep 'ediff-ptch) + (load "ediff-ptch.el" nil nil 'nosuffix)) + (or (featurep 'ediff-vers) + (load "ediff-vers.el" nil nil 'nosuffix)) + )) +;; end pacifier + (require 'ediff-init) -(require 'ediff-mult) +(require 'ediff-mult) ; required because of the registry stuff + +(defgroup ediff nil + "A comprehensive visual interface to diff & patch" + :tag "Ediff" + :group 'tools) + + +(defcustom ediff-use-last-dir nil + "*If t, Ediff will use previous directory as default when reading file name." + :type 'boolean + :group 'ediff) + +;; Last directory used by an Ediff command for file-A. +(defvar ediff-last-dir-A nil) +;; Last directory used by an Ediff command for file-B. +(defvar ediff-last-dir-B nil) +;; Last directory used by an Ediff command for file-C. +(defvar ediff-last-dir-C nil) +;; 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-use-last-dir nil - "*If t, Ediff uses previous directory as default when reading file name.") - -(defvar ediff-last-dir-A nil - "Last directory used by an Ediff command for file-A.") -(defvar ediff-last-dir-B nil - "Last directory used by an Ediff command for file-B.") -(defvar ediff-last-dir-C nil - "Last directory used by an Ediff command for file-C.") -(defvar ediff-last-dir-ancestor nil - "Last directory used by an Ediff command for the ancestor file.") -(defvar ediff-last-dir-patch nil - "Last directory used by an Ediff command for file to patch.") - -;;; Patching - -(defvar ediff-backup-extension - (if (memq system-type '(vax-vms axp-vms emx ms-dos windows-nt windows-95)) - "_orig" ".orig") - "Default backup extension for the patch program.") -;;;###autoload -(defun ediff-patch-file (source-filename &optional startup-hooks job-name) - "Run Ediff by patching SOURCE-FILENAME." - ;; This now returns the control buffer - (interactive - (list (ediff-read-file-name - "File to patch" - (if ediff-use-last-dir - ediff-last-dir-patch - default-directory) - (ediff-get-default-file-name)))) - - (setq source-filename (expand-file-name source-filename)) - (ediff-get-patch-buffer - (if (eq job-name 'ediff-patch-buffer) - (ediff-eval-in-buffer (get-file-buffer source-filename) - default-directory) - (file-name-directory source-filename))) - - (let* ((backup-extension - ;; if the user specified a -b option, extract the backup - ;; extension from there; else use ediff-backup-extension - (substring ediff-patch-options - (if (string-match "-b[ \t]+" ediff-patch-options) - (match-end 0) 0) - (if (string-match "-b[ \t]+[^ \t]+" ediff-patch-options) - (match-end 0) 0))) - (shell-file-name ediff-shell) - ;; ediff-find-file may use a temp file to do the patch - ;; so, we save source-filename and true-source-filename as a var - ;; that initially is source-filename but may be changed to a temp - ;; file for the purpose of patching. - (true-source-filename source-filename) - (target-filename source-filename) - target-buf buf-to-patch file-name-magic-p ctl-buf backup-style) - - ;; if the user didn't specify a backup extension, use - ;; ediff-backup-extension - (if (string= backup-extension "") - (setq backup-extension ediff-backup-extension)) - (if (string-match "-V" ediff-patch-options) - (error - "Ediff doesn't take the -V option in `ediff-patch-options'--sorry")) - - ;; Make a temp file, if source-filename has a magic file handler (or if - ;; it is handled via auto-mode-alist and similar magic). - ;; Check if there is a buffer visiting source-filename and if they are in - ;; sync; arrange for the deletion of temp file. - (ediff-find-file 'true-source-filename 'buf-to-patch - 'ediff-last-dir-patch 'startup-hooks) - - ;; Check if source file name has triggered black magic, such as file name - ;; handlers or auto mode alist, and make a note of it. - ;; true-source-filename should be either the original name or a - ;; temporary file where we put the after-product of the file handler. - (setq file-name-magic-p (not (equal (file-truename true-source-filename) - (file-truename source-filename)))) - - ;; Checkout orig file, if necessary, so that the patched file could be - ;; checked back in. - (if (ediff-file-checked-in-p (buffer-file-name buf-to-patch)) - (ediff-toggle-read-only buf-to-patch)) - - (ediff-eval-in-buffer ediff-patch-diagnostics - (message "Applying patch ... ") - ;; fix environment for gnu patch, so it won't make numbered extensions - (setq backup-style (getenv "VERSION_CONTROL")) - (setenv "VERSION_CONTROL" nil) - ;; always pass patch the -f option, so it won't ask any questions - (shell-command-on-region - (point-min) (point-max) - (format "%s -f %s -b %s %s" - ediff-patch-program ediff-patch-options - backup-extension - (expand-file-name true-source-filename)) - t) - ;; restore environment for gnu patch - (setenv "VERSION_CONTROL" backup-style)) - ;;(message "Applying patch ... done")(sit-for 0) - (switch-to-buffer ediff-patch-diagnostics) - (sit-for 0) ; synchronize - let the user see diagnostics - - (or (file-exists-p (concat true-source-filename backup-extension)) - (error "Patch failed or didn't modify the original file")) - - ;; If black magic is involved, apply patch to a temp copy of the - ;; file. Otherwise, apply patch to the orig copy. If patch is applied - ;; to temp copy, we name the result old-name_patched for local files - ;; and temp-copy_patched for remote files. The orig file name isn't - ;; changed, and the temp copy of the original is later deleted. - ;; Without magic, the original file is renamed (usually into - ;; old-name_orig) and the result of patching will have the same name as - ;; the original. - (if (not file-name-magic-p) - (ediff-eval-in-buffer buf-to-patch - (set-visited-file-name (concat source-filename backup-extension)) - (set-buffer-modified-p nil)) - - ;; Black magic in effect. - ;; If orig file was remote, put the patched file in the temp directory. - ;; If orig file is local, put the patched file in the directory of - ;; the orig file. - (setq target-filename - (concat - (if (ediff-file-remote-p (file-truename source-filename)) - true-source-filename - source-filename) - "_patched")) - - (rename-file true-source-filename target-filename t) - - ;; arrange that the temp copy of orig will be deleted - (rename-file (concat true-source-filename backup-extension) - true-source-filename t)) - - ;; make orig buffer read-only - (setq startup-hooks - (cons 'ediff-set-read-only-in-buf-A startup-hooks)) - - ;; set up a buf for the patched file - (setq target-buf (find-file-noselect target-filename)) - - (setq ctl-buf - (ediff-buffers-internal - buf-to-patch target-buf nil - startup-hooks (or job-name 'ediff-patch-file))) - - (bury-buffer ediff-patch-diagnostics) - (message "Patch diagnostics are available in buffer %s" - (buffer-name ediff-patch-diagnostics)) - ctl-buf)) - ;; Used as a startup hook to set `_orig' patch file read-only. (defun ediff-set-read-only-in-buf-A () - (ediff-eval-in-buffer ediff-buffer-A + (ediff-with-current-buffer ediff-buffer-A (toggle-read-only 1))) ;; Return a plausible default for ediff's first file: @@ -278,11 +174,6 @@ (file-name-nondirectory (buffer-file-name (current-buffer)))) )) -;;;###autoload -(defalias 'epatch 'ediff-patch-file) -;;;###autoload -(defalias 'epatch-buffer 'ediff-patch-buffer) - ;;; Compare files/buffers ;;;###autoload @@ -384,7 +275,7 @@ ;; 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)) @@ -401,10 +292,10 @@ ;; Setup the buffer (set buffer-name (find-file-noselect file)) - (ediff-eval-in-buffer (symbol-value buffer-name) + (ediff-with-current-buffer (symbol-value buffer-name) (widen) ; Make sure the entire file is seen - (cond (file-magic ;; file has handler, such as jka-compr-handler or - ;; ange-ftp-hook-function--arrange for temp file + (cond (file-magic ; file has a handler, such as jka-compr-handler or + ;;; ange-ftp-hook-function--arrange for temp file (ediff-verify-file-buffer 'magic) (setq file (ediff-make-temp-file @@ -423,7 +314,9 @@ (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) (message "Reading file %s ... " file-A) ;;(sit-for 0) @@ -444,7 +337,8 @@ 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 @@ -465,9 +359,12 @@ (save-window-excursion (other-window 1)) (ediff-other-buffer bf)) t)))) - (or job-name (setq job-name 'ediff-buffers)) (ediff-buffers-internal buffer-A buffer-B nil startup-hooks job-name)) + +;;;###autoload +(defalias 'ebuffers 'ediff-buffers) + ;;;###autoload (defun ediff-buffers3 (buffer-A buffer-B buffer-C @@ -492,13 +389,17 @@ (ediff-other-buffer (list bf bff))) t) ))) - (or job-name (setq job-name 'ediff-buffers3)) (ediff-buffers-internal buffer-A buffer-B buffer-C startup-hooks job-name)) + +;;;###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)) @@ -536,7 +437,7 @@ )) startup-hooks) (list (cons 'ediff-job-name job-name)) - ))) + merge-buffer-file))) ;;; Directory and file group operations @@ -559,18 +460,18 @@ (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 -further filters the file names." +can be used to filter out certain file names." (interactive (let ((dir-A (ediff-get-default-directory-name)) f) - (list (setq f (ediff-read-file-name "Directory A to compare" dir-A nil)) - (ediff-read-file-name "Directory B to compare" + (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil)) + (ediff-read-file-name "Directory B to compare:" (if ediff-use-last-dir ediff-last-dir-B (ediff-strip-last-dir f)) nil) (read-string "Filter through regular expression: " - nil ediff-filtering-regexp-history) + nil 'ediff-filtering-regexp-history) ))) (ediff-directories-internal dir1 dir2 nil regexp 'ediff-files 'ediff-directories @@ -588,9 +489,9 @@ names. Only the files that are under revision control are taken into account." (interactive (let ((dir-A (ediff-get-default-directory-name))) (list (ediff-read-file-name - "Directory to compare with revision" dir-A nil) + "Directory to compare with revision:" dir-A nil) (read-string "Filter through regular expression: " - nil ediff-filtering-regexp-history) + nil 'ediff-filtering-regexp-history) ))) (ediff-directory-revisions-internal dir1 regexp 'ediff-revision 'ediff-directory-revisions @@ -604,23 +505,23 @@ names. Only the files that are under revision control are taken into account." (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 further filters the file names." +expression that can be used to filter out certain file names." (interactive (let ((dir-A (ediff-get-default-directory-name)) 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" + (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil)) + (setq f (ediff-read-file-name "Directory B to compare:" (if ediff-use-last-dir 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-strip-last-dir f)) nil) (read-string "Filter through regular expression: " - nil ediff-filtering-regexp-history) + nil 'ediff-filtering-regexp-history) ))) (ediff-directories-internal dir1 dir2 dir3 regexp 'ediff-files3 'ediff-directories3 @@ -633,18 +534,18 @@ expression that further filters the file names." (defun ediff-merge-directories (dir1 dir2 regexp) "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 -further filters the file names." +can be used to filter out certain file names." (interactive (let ((dir-A (ediff-get-default-directory-name)) f) - (list (setq f (ediff-read-file-name "Directory A to merge" dir-A nil)) - (ediff-read-file-name "Directory B to merge" + (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil)) + (ediff-read-file-name "Directory B to merge:" (if ediff-use-last-dir ediff-last-dir-B (ediff-strip-last-dir f)) nil) (read-string "Filter through regular expression: " - nil ediff-filtering-regexp-history) + nil 'ediff-filtering-regexp-history) ))) (ediff-directories-internal dir1 dir2 nil regexp 'ediff-merge-files 'ediff-merge-directories @@ -654,29 +555,31 @@ further filters the file names." (defalias 'edirs-merge 'ediff-merge-directories) ;;;###autoload -(defun ediff-merge-directories-with-ancestor (dir1 dir2 dir3 regexp) - "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 -further filters the file names." +(defun ediff-merge-directories-with-ancestor (dir1 dir2 ancestor-dir regexp) + "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 +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." (interactive (let ((dir-A (ediff-get-default-directory-name)) 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" + (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil)) + (setq f (ediff-read-file-name "Directory B to merge:" (if ediff-use-last-dir ediff-last-dir-B (ediff-strip-last-dir f)) nil)) - (ediff-read-file-name "Ancestor directory: " + (ediff-read-file-name "Ancestor directory:" (if ediff-use-last-dir ediff-last-dir-C (ediff-strip-last-dir f)) nil) (read-string "Filter through regular expression: " - nil ediff-filtering-regexp-history) + nil 'ediff-filtering-regexp-history) ))) (ediff-directories-internal - dir1 dir2 dir3 regexp + dir1 dir2 ancestor-dir regexp 'ediff-merge-files-with-ancestor 'ediff-merge-directories-with-ancestor )) @@ -688,9 +591,9 @@ names. Only the files that are under revision control are taken into account." (interactive (let ((dir-A (ediff-get-default-directory-name))) (list (ediff-read-file-name - "Directory to merge with revisions" dir-A nil) + "Directory to merge with revisions:" dir-A nil) (read-string "Filter through regular expression: " - nil ediff-filtering-regexp-history) + nil 'ediff-filtering-regexp-history) ))) (ediff-directory-revisions-internal dir1 regexp 'ediff-merge-revisions 'ediff-merge-directory-revisions @@ -707,9 +610,9 @@ names. Only the files that are under revision control are taken into account." (interactive (let ((dir-A (ediff-get-default-directory-name))) (list (ediff-read-file-name - "Directory to merge with revisions and ancestors" dir-A nil) + "Directory to merge with revisions and ancestors:" dir-A nil) (read-string "Filter through regular expression: " - nil ediff-filtering-regexp-history) + nil 'ediff-filtering-regexp-history) ))) (ediff-directory-revisions-internal dir1 regexp 'ediff-merge-revisions-with-ancestor @@ -726,11 +629,11 @@ names. Only the files that are under revision control are taken into account." ;; 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 further filters the -;; file names. +;; The third argument, REGEXP, is a regular expression that can be used to +;; filter out certain file names. ;; JOBNAME is the symbol indicating the meta-job to be performed. -(defun ediff-directories-internal (dir1 dir2 dir3 regexp - action jobname +;; MERGE-DIR is the directory in which to store merged files. +(defun ediff-directories-internal (dir1 dir2 dir3 regexp action jobname &optional startup-hooks) ;; 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. @@ -750,9 +653,31 @@ names. Only the files that are under revision control are taken into account." (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 + ediff-last-merge-autostore-dir + (ediff-strip-last-dir dir1)) + nil))) + ;; verify we are not merging into an orig directory + (if (stringp merge-autostore-dir) + (cond ((and (stringp dir1) (string= merge-autostore-dir dir1)) + (or (y-or-n-p "Merge directory same as 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? ") + (error "Directory merge aborted"))) + ((and (stringp dir3) (string= merge-autostore-dir dir3)) + (or (y-or-n-p + "Merge directory same as ancestor directory, sure? ") + (error "Directory merge aborted"))))) + (setq file-list (ediff-intersect-directories - jobname 'diffs regexp dir1 dir2 dir3)) + jobname 'diffs + regexp dir1 dir2 dir3 merge-autostore-dir)) (setq startup-hooks ;; this sets various vars in the meta buffer inside ;; ediff-prepare-meta-buffer @@ -763,7 +688,7 @@ names. Only the files that are under revision control are taken into account." (setq ediff-dir-difference-list (quote (, diffs))))) startup-hooks)) (setq meta-buf (ediff-prepare-meta-buffer - 'ediff-dir-action + 'ediff-filegroup-action file-list "*Ediff Session Group Panel" 'ediff-redraw-directory-group-buffer @@ -775,9 +700,26 @@ names. Only the files that are under revision control are taken into account." (defun ediff-directory-revisions-internal (dir1 regexp action jobname &optional startup-hooks) (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1))) - (let (file-list meta-buf) + + (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))) + ;; verify merge-autostore-dir != dir1 + (if (and (stringp 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? ") + (error "Merge of directory revisions aborted"))) + (setq file-list - (ediff-get-directory-files-under-revision jobname regexp dir1)) + (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 @@ -787,7 +729,7 @@ names. Only the files that are under revision control are taken into account." )) startup-hooks)) (setq meta-buf (ediff-prepare-meta-buffer - 'ediff-dir-action + 'ediff-filegroup-action file-list "*Ediff Session Group Panel" 'ediff-redraw-directory-group-buffer @@ -848,7 +790,7 @@ If WIND-B is nil, use window next to WIND-A." end-B (window-end)))) (ediff-regions-internal buffer-A beg-A end-A buffer-B beg-B end-B - startup-hooks job-name word-mode))) + startup-hooks job-name word-mode nil))) ;;;###autoload (defun ediff-regions-wordwise (buffer-A buffer-B &optional startup-hooks) @@ -885,7 +827,7 @@ lines. For large regions, use `ediff-regions-linewise'." (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))) + startup-hooks 'ediff-regions-wordwise 'word-mode nil))) ;;;###autoload (defun ediff-regions-linewise (buffer-A buffer-B &optional startup-hooks) @@ -928,7 +870,6 @@ lines. For small regions, use `ediff-regions-wordwise'." (setq reg-B-beg (region-beginning) reg-B-end (region-end)) ;; enlarge the region to hold full lines - (goto-char reg-A-beg) (goto-char reg-B-beg) (beginning-of-line) (setq reg-B-beg (point)) @@ -941,21 +882,22 @@ lines. For small regions, use `ediff-regions-wordwise'." (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))) ; no word mode + 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. (defun ediff-regions-internal (buffer-A beg-A end-A buffer-B beg-B end-B - startup-hooks job-name word-mode) + 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-eval-in-buffer buffer-A + (ediff-with-current-buffer buffer-A (setq beg-A (move-marker (make-marker) beg-A) end-A (move-marker (make-marker) end-A))) - (ediff-eval-in-buffer buffer-B + (ediff-with-current-buffer buffer-B (setq beg-B (move-marker (make-marker) beg-B) end-B (move-marker (make-marker) end-B))) @@ -997,10 +939,11 @@ Continue anyway? (y/n) ")) (delete-file (, file-A)) (delete-file (, file-B)))) startup-hooks) - (list (cons 'ediff-word-mode word-mode) - (cons 'ediff-narrow-bounds (list overl-A overl-B)) - (cons 'ediff-job-name job-name)) - ) + (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)) )) @@ -1011,11 +954,14 @@ Continue anyway? (y/n) ")) (defsubst ediff-merge-on-startup () (ediff-do-merge 0) - (ediff-eval-in-buffer ediff-buffer-C + (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 @@ -1047,11 +993,17 @@ Continue anyway? (y/n) ")) 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 @@ -1097,13 +1049,18 @@ Continue anyway? (y/n) ")) 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 (let (bf) @@ -1120,12 +1077,17 @@ Continue anyway? (y/n) ")) (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 (let (bf bff) @@ -1150,11 +1112,12 @@ Continue anyway? (y/n) ")) (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." @@ -1176,12 +1139,17 @@ buffer." (ediff-load-version-control) ;; ancestor-revision=nil (funcall - (intern (format "%S-ediff-merge-internal" ediff-version-control-package)) - rev1 rev2 nil startup-hooks))) + (intern (format "ediff-%S-merge-internal" ediff-version-control-package)) + 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 buffer." @@ -1208,13 +1176,13 @@ buffer." (file-name-nondirectory file) "current buffer")))) (ediff-load-version-control) (funcall - (intern (format "%S-ediff-merge-internal" ediff-version-control-package)) - rev1 rev2 ancestor-rev startup-hooks))) + (intern (format "ediff-%S-merge-internal" ediff-version-control-package)) + rev1 rev2 ancestor-rev startup-hooks merge-buffer-file))) ;;;###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 lide 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) @@ -1225,68 +1193,49 @@ file and then run `run-ediff-from-cvs-buffer'." ;;; Apply patch - -;;;###autoload -(defun ediff-patch-buffer (buffer-name &optional startup-hooks) - "Run Ediff by patching BUFFER-NAME." - (interactive "bBuffer to patch: ") - - (let* ((buf-to-patch (get-buffer buffer-name)) - (file-name-ok (if buf-to-patch (buffer-file-name buf-to-patch))) - (buf-mod-status (buffer-modified-p buf-to-patch)) - default-dir file-name ctl-buf) - (if file-name-ok - (setq file-name file-name-ok) - (ediff-eval-in-buffer buffer-name - (setq default-dir default-directory) - (setq file-name (ediff-make-temp-file buffer-name)) - (set-visited-file-name file-name) - (setq buffer-auto-save-file-name nil) ; don't create auto-save file - (rename-buffer buffer-name) ; don't confuse the user with new buf name - (set-buffer-modified-p nil) - (set-visited-file-modtime) ; sync buffer and temp file - (setq default-directory default-dir) - )) - - (setq ctl-buf - (ediff-patch-file file-name startup-hooks 'ediff-patch-buffer)) - - (if file-name-ok - () - (ediff-eval-in-buffer ctl-buf - (delete-file (buffer-file-name ediff-buffer-A)) - (delete-file (buffer-file-name ediff-buffer-B)) - (ediff-eval-in-buffer ediff-buffer-A - (if default-dir (setq default-directory default-dir)) - (set-visited-file-name nil) - (rename-buffer buffer-name) - (set-buffer-modified-p buf-mod-status)) - (ediff-eval-in-buffer ediff-buffer-B - (setq buffer-auto-save-file-name nil) ; don't create auto-save file - (if default-dir (setq default-directory default-dir)) - (set-visited-file-name nil) - (rename-buffer (ediff-unique-buffer-name - (concat buffer-name "_patched") "")) - (set-buffer-modified-p t)))) - )) +;;;###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) + (require 'ediff-ptch) + (setq patch-buf (ediff-get-patch-buffer)) + (setq source-dir (cond (ediff-use-last-dir ediff-last-dir-patch) + ((and (not ediff-patch-default-directory) + (buffer-file-name patch-buf)) + (file-name-directory + (expand-file-name + (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 (ediff-get-default-file-name))) + (ediff-dispatch-file-patching-job patch-buf source-file))) -(defun ediff-get-patch-buffer (dir) - "Obtain patch buffer. If patch is already in a buffer---use it. -Else, read patch file into a new buffer." - (if (y-or-n-p "Is the patch file already in a buffer? ") - (setq ediff-patch-buf - (get-buffer (read-buffer "Patch buffer name: " nil t))) ;must match - (setq ediff-patch-buf - (find-file-noselect (read-file-name "Patch file name: " dir)))) +;;;###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)) + (ediff-other-buffer (current-buffer))) + (t (current-buffer))) + 'must-match)))) - (setq ediff-patch-diagnostics - (get-buffer-create "*ediff patch diagnostics*")) - (ediff-eval-in-buffer ediff-patch-diagnostics - (insert-buffer ediff-patch-buf))) +;;;###autoload +(defalias 'epatch 'ediff-patch-file) +;;;###autoload +(defalias 'epatch-buffer 'ediff-patch-buffer) - ;;; Versions Control functions @@ -1312,9 +1261,13 @@ buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'." (file-name-nondirectory file) "current buffer")))) (ediff-load-version-control) (funcall - (intern (format "%S-ediff-internal" ediff-version-control-package)) + (intern (format "ediff-%S-internal" ediff-version-control-package)) rev1 rev2 startup-hooks) )) + + +;;;###autoload +(defalias 'erevision 'ediff-revision) ;; Test if version control package is loaded and load if not @@ -1342,8 +1295,9 @@ When called interactively, displays the version." ;;;###autoload -(defun ediff-documentation () - "Jump to Ediff's Info file." +(defun ediff-documentation (&optional node) + "Display Ediff's manual. +With optional NODE, goes to that node." (interactive) (let ((ctl-window ediff-control-window) (ctl-buf ediff-control-buffer)) @@ -1353,15 +1307,13 @@ When called interactively, displays the version." (progn (pop-to-buffer (get-buffer-create "*info*")) (info (if ediff-xemacs-p "ediff.info" "ediff")) - (message "Type `i' to search for a specific topic")) + (if node + (Info-goto-node node) + (message "Type `i' to search for a specific topic")) + (raise-frame (selected-frame))) (error (beep 1) - (with-output-to-temp-buffer " *ediff-info*" - (princ (format " -The Info file for Ediff does not seem to be installed. - -This file is part of the distribution of %sEmacs. -Please contact your system administrator. " - (if ediff-xemacs-p "X" "")))) + (with-output-to-temp-buffer ediff-msg-buffer + (princ ediff-BAD-INFO)) (if (window-live-p ctl-window) (progn (select-window ctl-window) @@ -1372,10 +1324,10 @@ Please contact your system administrator. " ;;; Local Variables: ;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun) -;;; eval: (put 'ediff-eval-in-buffer 'lisp-indent-hook 1) +;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1) +;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body)) ;;; End: -(provide 'ediff) (require 'ediff-util) ;;; ediff.el ends here