]> code.delx.au - gnu-emacs/blobdiff - lisp/ediff.el
Fix a typo in a comment. Reported by Michel de Ruiter <Michel@smr.nl>.
[gnu-emacs] / lisp / ediff.el
index 571cd65981c28b85ba54fbd7c70deaef7da56cbf..efab0dec0d1af2c9f2c0589e10a3e5a6e11acbde 100644 (file)
@@ -1,12 +1,14 @@
 ;;; ediff.el --- a comprehensive visual interface to diff & patch
-;;; Copyright (C) 1994, 1995 Free Software Foundation, Inc.
+
+;; Copyright (C) 1994, 95, 96, 97, 98, 99, 2000 Free Software Foundation, Inc.
 
 ;; Author: Michael Kifer <kifer@cs.sunysb.edu>
 ;; Created: February 2, 1994
 ;; Keywords: comparing, merging, patching, version control.
 
-(defconst ediff-version "2.47" "The current version of Ediff")
-(defconst ediff-date "October 11, 1995" "Date of last update")  
+(defconst ediff-version "2.74" "The current version of Ediff")
+(defconst ediff-date "October 31, 1999" "Date of last update")  
+
 
 ;; This file is part of GNU Emacs.
 
 ;; GNU General Public License for more details.
 
 ;; 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, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; 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.
 
 ;;; 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
@@ -40,7 +42,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.
 
 ;; 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 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
-;; 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.
 
 
 
 ;;; 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
 ;;  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 underlied 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.
-;;
 
 
 ;;; Acknowledgements:
 
 ;; Ediff was inspired by Dale R. Worley's <drw@math.mit.edu> 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:
 
+(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
 
-(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.")
+(defgroup ediff nil
+  "A comprehensive visual interface to diff & patch"
+  :tag "Ediff"
+  :group 'tools)
 
-;;;###autoload
-(defun ediff-patch-file (source-filename &optional startup-hooks job-name)
-  "Run Ediff by patching FILE-TP-PATCH."
-  ;; 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)
-         
-    ;; if the user didn't specify a backup extension, use
-    ;; ediff-backup-extension 
-    (if (string= backup-extension "")
-       (setq backup-extension ediff-backup-extension))
-                                       
-    ;; 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
-    ;; synch; 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 ... ")
-      ;;(sit-for 0)
-      ;; 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))
-    ;;(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))
-  
+
+(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)
+
+
+;; Used as a startup hook to set `_orig' patch file read-only.
 (defun ediff-set-read-only-in-buf-A ()
-  "Used as a startup hook to set `_orig' patch file read-only."
-  (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:
         (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
 (defalias 'ediff3 'ediff-files3)
 
 
+;; 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
+;; 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)
-  "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 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."
   (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 command, below, require full file name
+    ;; some of the commands, below, require full file name
     (setq file (expand-file-name file))
   
     ;; Record the directory of the file
@@ -395,15 +292,15 @@ deleted."
     ;; 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
                    (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)
@@ -411,13 +308,15 @@ deleted."
             (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)
     (message "Reading file %s ... " file-A)
     ;;(sit-for 0)
@@ -438,7 +337,8 @@ deleted."
                 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
@@ -459,9 +359,12 @@ deleted."
                          (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
@@ -486,13 +389,17 @@ deleted."
                                    (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))
@@ -514,8 +421,8 @@ deleted."
     (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))
-    (setq file-B (ediff-make-temp-file buf-B buf-B-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)))
          
@@ -523,14 +430,13 @@ deleted."
                 (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
@@ -552,19 +458,19 @@ deleted."
 ;;;###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
-further filters the file names."
+the same name in both.  The third 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 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
@@ -578,13 +484,13 @@ further filters the 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)))
      (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
@@ -597,24 +503,24 @@ 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 further filters the file names."
+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."
   (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
@@ -624,90 +530,101 @@ expression that further filters the 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
-further filters the file names."
+the same name in both.  The third 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))
-          (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
+   nil merge-autostore-dir
    ))
 
 ;;;###autoload
 (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
+                                                  &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
+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
+   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)))
      (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
+   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)))
      (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
    'ediff-merge-directory-revisions-with-ancestor
+   nil merge-autostore-dir
    ))
 
 ;;;###autoload
@@ -720,14 +637,15 @@ 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 
-                                       &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)))
 
@@ -743,21 +661,51 @@ 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)))
 
+  (if merge-autostore-dir
+      (or (stringp merge-autostore-dir)
+         (error "%s: Directory for storing merged files must be a string"
+                jobname)))
   (let (diffs ; var where ediff-intersect-directories returns the diff list
        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 we are not merging into an orig directory
+    (if merge-autostore-dir
+       (cond ((and (stringp dir1) (string= merge-autostore-dir dir1))
+              (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
+                   "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
+                   "Directory for saving merged files = 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
-         (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 (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
@@ -766,22 +714,48 @@ names. Only the files that are under revision control are taken into account."
     (ediff-show-meta-buffer meta-buf)
     ))
 
+;; 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)
+                                               &optional startup-hooks
+                                               merge-autostore-dir)
   (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1)))
+
+  (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 merge-autostore-dir
+            (stringp dir1)
+            (string= merge-autostore-dir dir1))
+       (or (y-or-n-p
+            "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))
+         (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 
-                   'ediff-dir-action
+                   'ediff-filegroup-action
                    file-list
                    "*Ediff Session Group Panel"
                    'ediff-redraw-directory-group-buffer
@@ -833,7 +807,7 @@ If WIND-B is nil, use window next to WIND-A."
     
     (save-excursion
       (save-window-excursion
-       (sit-for 0) ; synch before using window-start/end -- a precaution
+       (sit-for 0) ; sync before using window-start/end -- a precaution
        (select-window wind-A)
        (setq beg-A (window-start)
              end-A (window-end))
@@ -842,14 +816,14 @@ 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)
   "Run Ediff on a pair of regions in two different buffers.
 Regions \(i.e., point and mark\) are assumed to be set in advance.
 This function is effective only for relatively small regions, up to 200
-lines. For large regions, use `ediff-regions-linewise'."
+lines.  For large regions, use `ediff-regions-linewise'."
   (interactive 
    (let (bf)
      (list (setq bf (read-buffer "Region's A buffer: "
@@ -879,7 +853,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)
@@ -887,7 +861,7 @@ lines. For large regions, use `ediff-regions-linewise'."
 Regions \(i.e., point and mark\) are assumed to be set in advance.
 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'."
+lines.  For small regions, use `ediff-regions-wordwise'."
   (interactive 
    (let (bf)
      (list (setq bf (read-buffer "Region A's buffer: "
@@ -922,7 +896,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))
@@ -935,21 +908,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)))
        
@@ -987,14 +961,15 @@ Continue anyway? (y/n) "))
     (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)
-                (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))
     ))
     
  
@@ -1005,11 +980,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
@@ -1041,11 +1019,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
@@ -1091,13 +1075,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)
@@ -1114,12 +1103,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)
@@ -1144,18 +1138,18 @@ 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."
   (interactive)
-  (ediff-load-version-control)
   (if (stringp file) (find-file file))
-  (let (rev1 rev2 buf1 buf2)
+  (let (rev1 rev2)
     (setq rev1
          (read-string
           (format
@@ -1168,49 +1162,26 @@ buffer."
            "Version 2 to merge (default: %s): "
            (if (stringp file)
                (file-name-nondirectory file) "current buffer"))))
-    (cond ((eq ediff-version-control-package 'vc)
-          (save-excursion
-            (vc-version-other-window rev1)
-            (setq buf1 (current-buffer)))
-          (save-excursion
-            (or (string= rev2 "")
-                (vc-version-other-window rev2))
-            (setq buf2 (current-buffer)))
-          (setq startup-hooks 
-                (cons 
-                 (` (lambda () 
-                      (delete-file (, (buffer-file-name buf1)))
-                      (or (, (string= rev2 ""))
-                          (delete-file (, (buffer-file-name buf2))))))
-                 startup-hooks)))
-         ((eq ediff-version-control-package 'rcs)
-          (setq buf1 (rcs-ediff-view-revision rev1)
-                buf2 (if (string= rev2 "")
-                         (current-buffer)
-                       (rcs-ediff-view-revision rev2))))
-         ((eq ediff-version-control-package 'generic-sc)
-          (save-excursion
-            (if (string= rev1 "")
-                (setq rev1 (generic-sc-get-latest-rev)))
-            (sc-visit-previous-revision rev1)
-            (setq buf1 (current-buffer)))
-          (save-excursion
-            (or (string= rev2 "")
-                (sc-visit-previous-revision rev2))
-            (setq buf2 (current-buffer))))
-         ) ; cond
-    (ediff-merge-buffers buf1 buf2 startup-hooks 'ediff-merge-revisions)))
+    (ediff-load-version-control)
+    ;; ancestor-revision=nil
+    (funcall
+     (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."
   (interactive)
-  (ediff-load-version-control)
   (if (stringp file) (find-file file))
-  (let (rev1 rev2 ancestor-rev buf1 buf2 ancestor-buf)
+  (let (rev1 rev2 ancestor-rev)
     (setq rev1
          (read-string
           (format
@@ -1226,121 +1197,77 @@ 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"))))
-    (cond ((eq ediff-version-control-package 'vc)
-          (save-excursion
-            (vc-version-other-window rev1)
-            (setq buf1 (current-buffer)))
-          (save-excursion
-            (or (string= rev2 "")
-                (vc-version-other-window rev2))
-            (setq buf2 (current-buffer)))
-          (save-excursion
-            (or (string= ancestor-rev "")
-                (vc-version-other-window ancestor-rev))
-            (setq ancestor-buf (current-buffer)))
-          (setq startup-hooks 
-                (cons
-                 (` (lambda () 
-                      (delete-file (, (buffer-file-name buf1)))
-                      (or (, (string= rev2 ""))
-                          (delete-file (, (buffer-file-name buf2))))
-                      (or (, (string= ancestor-rev ""))
-                          (delete-file (, (buffer-file-name ancestor-buf))))))
-                 startup-hooks)))
-         ((eq ediff-version-control-package 'rcs)
-          (setq buf1 (rcs-ediff-view-revision rev1)
-                buf2 (if (string= rev2 "")
-                         (current-buffer)
-                       (rcs-ediff-view-revision rev2))
-                ancestor-buf (if (string= ancestor-rev "")
-                                 (current-buffer)
-                               (rcs-ediff-view-revision ancestor-rev))))
-         ((eq ediff-version-control-package 'generic-sc)
-          (save-excursion
-            (if (string= rev1 "")
-                (setq rev1 (generic-sc-get-latest-rev)))
-            (sc-visit-previous-revision rev1)
-            (setq buf1 (current-buffer)))
-          (save-excursion
-            (or (string= rev2 "")
-                (sc-visit-previous-revision rev2))
-            (setq buf2 (current-buffer)))
-          (save-excursion
-            (or (string= ancestor-rev "")
-                (sc-visit-previous-revision ancestor-rev))
-            (setq ancestor-buf (current-buffer))))
-         ) ; cond
-    (ediff-merge-buffers-with-ancestor
-     buf1 buf2 ancestor-buf
-     startup-hooks 'ediff-merge-revisions-with-ancestor)))
+    (ediff-load-version-control)
+    (funcall
+     (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 line describing a
+file and then run `run-ediff-from-cvs-buffer'."
+  (interactive "d")
+  (ediff-load-version-control)
+  (let ((tin (tin-locate cvs-cookie-handle pos)))
+    (if tin
+       (cvs-run-ediff-on-file-descriptor tin)
+      (error "There is no file to merge"))))
      
      
 ;;; Apply patch
-    
+
+;;;###autoload
+(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
+          (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))
+                           (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
+         (read-file-name 
+          "File to patch (directory, if multifile patch): "
+          source-dir (ediff-get-default-file-name)))
+    (ediff-dispatch-file-patching-job patch-buf source-file)))
+
 ;;;###autoload
-(defun ediff-patch-buffer (buffer-name &optional startup-hooks)                  
+(defun ediff-patch-buffer (&optional arg patch-buf)
   "Run Ediff by patching BUFFER-NAME."
-  (interactive "bBuffer to patch: ")
+  (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? "
+    (ediff-prompt-for-patch-buffer))))
   
-  (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))))
-    ))
 
-
-(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))))
-  
-  (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)
 
 
-      
 
 \f
 ;;; Versions Control functions      
@@ -1349,7 +1276,7 @@ Else, read patch file into a new buffer."
 (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'."
+buffer.  Use `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))
@@ -1366,122 +1293,28 @@ 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
 ;; Is SILENT is non-nil, don't report error if package is not found.
 (defun ediff-load-version-control (&optional silent)
+  (require 'ediff-vers)
   (or (featurep ediff-version-control-package)
       (if (locate-library (symbol-name ediff-version-control-package))
          (progn
            (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)))))
 
-      
-(defun vc-ediff-internal (rev1 rev2 &optional startup-hooks)
-  "Run Ediff on versions of the current buffer.
-If REV2 is \"\" then compare current buffer with REV1.
-If the current buffer is named `F', the version is named `F.~REV~'.
-If `F.~REV~' already exists, it is used instead of being re-created."
-  (let (file1 file2 rev1buf rev2buf)
-    (save-excursion
-      (vc-version-other-window rev1)
-      (setq rev1buf (current-buffer)
-           file1 (buffer-file-name)))
-    (save-excursion
-      (or (string= rev2 "")            ; use current buffer
-         (vc-version-other-window rev2))
-      (setq rev2buf (current-buffer)
-           file2 (buffer-file-name)))
-    (setq startup-hooks
-         (cons (` (lambda ()
-                    (delete-file (, file1))
-                    (or (, (string= rev2 "")) (delete-file (, file2)))
-                    ))
-               startup-hooks))
-    (ediff-buffers
-     rev1buf rev2buf
-     startup-hooks
-     'ediff-revision)))
-    
-(defun rcs-ediff-view-revision (&optional rev)
-  "View previous RCS revision of current file.
-With prefix argument, prompts for a revision name." 
-  (interactive (list (if current-prefix-arg 
-                        (read-string "Revision: "))))
-  (let* ((filename (buffer-file-name (current-buffer)))
-        (switches (append '("-p")
-                          (if rev (list (concat "-r" rev)) nil)))
-        (buff (concat (file-name-nondirectory filename) ".~" rev "~")))
-    (message "Working ...")
-    (setq filename (expand-file-name filename))
-    (with-output-to-temp-buffer buff
-      (let ((output-buffer (ediff-rcs-get-output-buffer filename buff)))
-       (delete-windows-on output-buffer)
-       (save-excursion
-         (set-buffer output-buffer)
-         (apply 'call-process "co" nil t nil
-                ;; -q: quiet (no diagnostics)
-                (append switches rcs-default-co-switches
-                        (list "-q" filename))))) 
-      (message "")
-      buff)))    
-      
-(defun ediff-rcs-get-output-buffer (file name)
-  ;; Get a buffer for RCS output for FILE, make it writable and clean it up.
-  ;; Optional NAME is name to use instead of `*RCS-output*'.
-  ;; This is a modified version from rcs.el v1.1. I use it here to make
-  ;; Ediff immune to changes in rcs.el
-  (let* ((default-major-mode 'fundamental-mode) ; no frills!
-        (buf (get-buffer-create name)))
-    (save-excursion
-      (set-buffer buf)
-      (setq buffer-read-only nil
-           default-directory (file-name-directory (expand-file-name file)))
-      (erase-buffer))
-    buf))
-
-(defun rcs-ediff-internal (rev1 rev2 &optional startup-hooks)
-  "Run Ediff on versions of the current buffer.
-If REV2 is \"\" then use current buffer."
-  (let ((rev2buf (if (string= rev2 "")
-                    (current-buffer)
-                  (rcs-ediff-view-revision rev2)))
-       (rev1buf (rcs-ediff-view-revision rev1)))
-       
-    ;; rcs.el doesn't create temp version files, so we don't have to delete
-    ;; anything in startup hooks to ediff-buffers
-    (ediff-buffers rev1buf rev2buf startup-hooks 'ediff-revision)
-    ))
-
-(defun generic-sc-get-latest-rev ()
-  (cond ((eq sc-mode 'CCASE)
-        (eval "main/LATEST"))
-       (t (eval "")))
-  )
-
-(defun generic-sc-ediff-internal (rev1 rev2 &optional startup-hooks)
-  "Run Ediff on versions of the current buffer.
-If REV2 is \"\" then compare current buffer with REV1.
-If the current buffer is named `F', the version is named `F.~REV~'.
-If `F.~REV~' already exists, it is used instead of being re-created."
-  (let (rev1buf rev2buf)
-    (save-excursion
-      (if (or (not rev1) (string= rev1 ""))
-         (setq rev1 (generic-sc-get-latest-rev)))
-      (sc-visit-previous-revision rev1)
-      (setq rev1buf (current-buffer)))
-    (save-excursion
-      (or (string= rev2 "")            ; use current buffer
-         (sc-visit-previous-revision rev2))
-      (setq rev2buf (current-buffer)))
-    (ediff-buffers rev1buf rev2buf startup-hooks 'ediff-revision)))
 
 ;;;###autoload
 (defun ediff-version ()
@@ -1493,7 +1326,42 @@ When called interactively, displays the version."
     (format "Ediff %s of %s" ediff-version ediff-date)))
 
 
-(provide 'ediff)
+;;;###autoload
+(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))
+
+    (ediff-skip-unsuitable-frames)
+    (condition-case nil
+       (progn
+         (pop-to-buffer (get-buffer-create "*info*"))
+         (info (if ediff-xemacs-p "ediff.info" "ediff"))
+         (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-msg-buffer
+              (princ ediff-BAD-INFO))
+            (if (window-live-p ctl-window)
+                (progn
+                  (select-window ctl-window)
+                  (set-window-buffer ctl-window ctl-buf)))))))
+    
+
+
+
+;;; Local Variables:
+;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
+;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
+;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
+;;; End:
+
 (require 'ediff-util)
 
+(run-hooks 'ediff-load-hook)
+
 ;;; ediff.el ends here