home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 2: Applications / Linux Cubed Series 2 - Applications.iso / editors / emacs / xemacs / xemacs-1.006 / xemacs-1 / lib / xemacs-19.13 / lisp / ediff / ediff.el < prev    next >
Encoding:
Text File  |  1995-08-30  |  50.3 KB  |  1,446 lines

  1. ;;; ediff.el --- a comprehensive visual interface to diff & patch
  2. ;;; Copyright (C) 1994, 1995 Free Software Foundation, Inc.
  3.  
  4. ;; Author: Michael Kifer <kifer@cs.sunysb.edu>
  5. ;; Created: February 2, 1994
  6. ;; Keywords: comparing, merging, patching, version control.
  7.  
  8. (defconst ediff-version "2.41" "The current version of Ediff")
  9. (defconst ediff-date "August 6, 1995" "Date of last update")  
  10.  
  11. ;; This file is part of GNU Emacs.
  12.  
  13. ;; GNU Emacs is free software; you can redistribute it and/or modify
  14. ;; it under the terms of the GNU General Public License as published by
  15. ;; the Free Software Foundation; either version 2, or (at your option)
  16. ;; any later version.
  17.  
  18. ;; GNU Emacs is distributed in the hope that it will be useful,
  19. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  20. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  21. ;; GNU General Public License for more details.
  22.  
  23. ;; You should have received a copy of the GNU General Public License
  24. ;; along with GNU Emacs; see the file COPYING.  If not, write to
  25. ;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
  26.  
  27. ;;; Commentary:
  28. ;;  ----------
  29.  
  30. ;; Never read that diff output again!
  31. ;; Apply patch selectively, like a pro!
  32. ;; Merge with ease!
  33.  
  34. ;; This package provides a convenient way of simultaneous browsing through
  35. ;; the differences between a pair (or a triple) of files or buffers.  The
  36. ;; files being compared, file-A, file-B, and file-C (if applicable) are
  37. ;; shown in separate windows (side by side, one above the another, or in
  38. ;; separate frames), and the differences are highlighted as you step
  39. ;; through them.  You can also copy difference regions from one buffer to
  40. ;; another (and recover old differences if you change your mind).
  41.  
  42. ;; Ediff also supports merging operations on files and buffers, including
  43. ;; merging using ancestor versions. Both comparison and merging operations can
  44. ;; be performed on directories, i.e., by pairwise comparison of files in those
  45. ;; directories.
  46.  
  47. ;; In addition, Ediff can apply a patch to a file and then let you step
  48. ;; though both files, the patched and the original one, simultaneously,
  49. ;; difference-by-difference.  You can even apply a patch right out of a
  50. ;; mail buffer, i.e., patches received by mail don't even have to be saved.
  51. ;; Since Ediff lets you copy differences between buffers, you can, in
  52. ;; effect, apply patches selectively (i.e., you can copy a difference
  53. ;; region from file_orig to file, thereby undoing any particular patch that
  54. ;; you don't like).
  55.  
  56. ;; Ediff is aware of version control, which lets the user compare
  57. ;; files with their older versions. Ediff can also work with remote and
  58. ;; compressed files. Details are given below.
  59.  
  60. ;; Finally, Ediff supports directory-level comparison and merging operations.
  61. ;; See the on-line manual for details.
  62.  
  63. ;; This package builds upon the ideas borrowed from emerge.el and several
  64. ;; Ediff's functions are adaptations from emerge.el. Much of the functionality
  65. ;; Ediff provides is also influenced by emerge.el.
  66.  
  67. ;; The present version of Ediff supersedes Emerge. It provides a superior user
  68. ;; interface and has numerous major features not found in Emerge. In
  69. ;; particular, it can do patching, and 2-way and 3-way file comparison,
  70. ;; merging, and directory operations.
  71.  
  72.  
  73.  
  74. ;;; Bugs:
  75. ;;  -----
  76.  
  77. ;;  1. The undo command doesn't restore deleted regions well. That is, if
  78. ;;  you delete all characters in a difference region and then invoke
  79. ;;  `undo', the reinstated text will most likely be inserted outside of
  80. ;;  what Ediff thinks is the current difference region. (This problem
  81. ;;  doesn't seem to exist with XEmacs.)
  82. ;;
  83. ;;  If at any point you feel that difference regions are no longer correct,
  84. ;;  you can hit '!' to recompute the differences.
  85.  
  86. ;;  2. On a monochrome display, the repertoire of faces with which to
  87. ;;  highlight fine differences is limited. By default, Ediff is using
  88. ;;  underlining. However, if the region is already underlied by some other
  89. ;;  overlays, there is no simple way to temporarily remove that residual
  90. ;;  underlining. This problem occurs when a buffer is highlighted with
  91. ;;  hilit19.el or font-lock.el packages. If this residual highlighting gets
  92. ;;  in the way, you can do the following. Both font-lock.el and hilit19.el
  93. ;;  provide commands for unhighlighting buffers. You can either place these
  94. ;;  commands in `ediff-prepare-buffer-hooks' (which will unhighlight every
  95. ;;  buffer used by Ediff) or you can execute them interactively, at any time
  96. ;;  and on any buffer.
  97. ;;
  98.  
  99.  
  100. ;;; Acknowledgements:
  101.  
  102. ;; Ediff was inspired by Dale R. Worley's <drw@math.mit.edu> emerge.el.
  103. ;; Ediff would not have been possible without the help and encouragement of
  104. ;; its many users. See Ediff on-line Info for the full list of those who
  105. ;; helped. Improved defaults in Ediff file-name reading commands.
  106.  
  107.  
  108. ;;; Code:
  109.  
  110. (require 'ediff-init)
  111. (require 'ediff-meta)
  112.  
  113. (defvar ediff-version-control-package 'vc
  114.   "Version control package used.
  115. Currently, Ediff supports vc.el and rcs.el. Set this to `rcs' if you have
  116. rcs.el and want to use it instead of the standard vc.el.
  117.  
  118. Note: both packages provide access to RCS, but only vc.el comes with Emacs
  119. distribution.")
  120.  
  121. (defvar ediff-revision-key nil
  122.   "Key to which `ediff-revision' is to be bound.")
  123.   
  124. (defvar ediff-use-last-dir nil
  125.   "*If t, Ediff uses previous directory as default when reading file name.")
  126.   
  127. (defvar ediff-last-dir-A nil
  128.   "Last directory used by an Ediff command for file-A.")
  129. (defvar ediff-last-dir-B nil
  130.   "Last directory used by an Ediff command for file-B.")
  131. (defvar ediff-last-dir-C nil
  132.   "Last directory used by an Ediff command for file-C.")
  133. (defvar ediff-last-dir-ancestor nil
  134.   "Last directory used by an Ediff command for the ancestor file.")
  135. (defvar ediff-last-dir-patch nil
  136.   "Last directory used by an Ediff command for file to patch.")
  137.  
  138. (defvar ediff-synchronize-minibuffers nil
  139.   "If non-nil, Ediff sets `synchronize-minibuffers' under XEmacs.
  140. This is a temporary hack until the global minibuffer is implemented.")
  141.  
  142. ;;; Patching
  143.  
  144. ;;;###autoload
  145. (defun ediff-patch-file (source-filename &optional startup-hooks job-name)
  146.   "Run Ediff by patching FILE-TP-PATCH."
  147.   ;; This now returns the control buffer
  148.   (interactive 
  149.    (list (ediff-read-file-name
  150.       "File to patch"
  151.       (if ediff-use-last-dir
  152.           ediff-last-dir-patch
  153.         default-directory)
  154.       ;; default is the current buf file name
  155.       (if (buffer-file-name (current-buffer))
  156.           (file-name-nondirectory
  157.            (buffer-file-name (current-buffer)))))))
  158.   
  159.   (setq source-filename (expand-file-name source-filename))
  160.   (ediff-get-patch-buffer
  161.    (if (eq job-name 'ediff-patch-buffer)
  162.        (ediff-eval-in-buffer (get-file-buffer source-filename)
  163.      default-directory)
  164.      (file-name-directory source-filename)))
  165.   
  166.   (let* ((backup-extension 
  167.       ;; if the user specified a -b option, extract the backup
  168.       ;; extension from there; else use `_orig'
  169.       (substring ediff-patch-options
  170.              (if (string-match "-b[ \t]+" ediff-patch-options)
  171.              (match-end 0) 0)
  172.              (if (string-match "-b[ \t]+[^ \t]+" ediff-patch-options)
  173.              (match-end 0) 0)))
  174.      (shell-file-name ediff-shell)
  175.      ;; ediff-find-file may use a temp file to do the patch
  176.      ;; so, we save source-filename and true-source-filename as a var
  177.      ;; that initially is source-filename but may be changed to a temp
  178.      ;; file for the purpose of patching.
  179.      (true-source-filename source-filename)
  180.      (target-filename source-filename)
  181.      target-buf buf-to-patch file-name-magic-p ctl-buf)
  182.       
  183.     ;; if the user didn't specify a backup extension, use _orig
  184.     (if (string= backup-extension "")
  185.     (setq backup-extension "_orig"))
  186.                     
  187.     ;; Make a temp file, if source-filename has a magic file handler (or if
  188.     ;; it is handled via auto-mode-alist and similar magic).
  189.     ;; Check if there is a buffer visiting source-filename and if they are in
  190.     ;; synch; arrange for the deletion of temp file.
  191.     (ediff-find-file 'true-source-filename 'buf-to-patch
  192.              'ediff-last-dir-patch 'startup-hooks)
  193.  
  194.     ;; Check if source file name has triggered black magic, such as file name
  195.     ;; handlers or auto mode alist, and make a note of it.
  196.     ;; true-source-filename should be either the original name or a
  197.     ;; temporary file where we put the after-product of the file handler.
  198.     (setq file-name-magic-p (not (equal (file-truename true-source-filename)
  199.                     (file-truename source-filename))))
  200.     
  201.     ;; Checkout orig file, if necessary, so that the patched file could be
  202.     ;; checked back in.
  203.     (if (ediff-file-checked-in-p (buffer-file-name buf-to-patch))
  204.     (ediff-toggle-read-only buf-to-patch))
  205.     
  206.     (ediff-eval-in-buffer ediff-patch-diagnostics
  207.       (message "Applying patch ... ")
  208.       ;;(sit-for 0)
  209.       ;; always pass patch the -f option, so it won't ask any questions
  210.       (shell-command-on-region 
  211.        (point-min) (point-max)
  212.        (format "%s -f %s -b %s %s"
  213.            ediff-patch-program ediff-patch-options
  214.            backup-extension
  215.            (expand-file-name true-source-filename))
  216.        t))
  217.     ;;(message "Applying patch ... done")(sit-for 0)
  218.     (switch-to-buffer ediff-patch-diagnostics)
  219.     (sit-for 0) ; synchronize - let the user see diagnostics
  220.     
  221.     (or (file-exists-p (concat true-source-filename backup-extension))
  222.     (error "Patch failed or didn't modify the original file"))
  223.   
  224.     ;; If black magic is involved, apply patch to a temp copy of the
  225.     ;; file. Otherwise, apply patch to the orig copy.  If patch is applied
  226.     ;; to temp copy, we name the result old-name_patched for local files
  227.     ;; and temp-copy_patched for remote files. The orig file name isn't
  228.     ;; changed, and the temp copy of the original is later deleted.
  229.     ;; Without magic, the original file is renamed (usually into
  230.     ;; old-name_orig) and the result of patching will have the same name as
  231.     ;; the original.
  232.     (if (not file-name-magic-p)
  233.     (ediff-eval-in-buffer buf-to-patch
  234.       (set-visited-file-name (concat source-filename backup-extension))
  235.       (set-buffer-modified-p nil))
  236.       
  237.       ;; Black magic in effect.
  238.       ;; If orig file was remote, put the patched file in the temp directory.
  239.       ;; If orig file is local, put the patched file in the directory of
  240.       ;; the orig file.
  241.       (setq target-filename
  242.         (concat
  243.          (if (ediff-file-remote-p (file-truename source-filename))
  244.          true-source-filename
  245.            source-filename)
  246.          "_patched"))
  247.       
  248.       (rename-file true-source-filename target-filename t)
  249.       
  250.       ;; arrange that the temp copy of orig will be deleted
  251.       (rename-file (concat true-source-filename backup-extension)
  252.            true-source-filename t))
  253.     
  254.     ;; make orig buffer read-only
  255.     (setq startup-hooks
  256.       (cons 'ediff-set-read-only-in-buf-A startup-hooks))
  257.     
  258.     ;; set up a buf for the patched file
  259.     (setq target-buf (find-file-noselect target-filename))
  260.     
  261.     (setq ctl-buf
  262.       (ediff-buffers-internal
  263.        buf-to-patch target-buf nil
  264.        startup-hooks (or job-name 'ediff-patch-file)))
  265.   
  266.     (bury-buffer ediff-patch-diagnostics)
  267.     (message "Patch diagnostics are available in buffer %s"
  268.          (buffer-name ediff-patch-diagnostics))
  269.     ctl-buf))
  270.   
  271. (defun ediff-set-read-only-in-buf-A ()
  272.   "Used as a startup hook to set `_orig' patch file read-only."
  273.   (ediff-eval-in-buffer ediff-buffer-A
  274.     (toggle-read-only 1)))
  275.  
  276. ;;;###autoload
  277. (defalias 'epatch 'ediff-patch-file)
  278. ;;;###autoload
  279. (defalias 'epatch-buffer 'ediff-patch-buffer)
  280.  
  281. ;;; Compare files/buffers
  282.  
  283. ;;;###autoload
  284. (defun ediff-files (file-A file-B &optional startup-hooks)
  285.   "Run Ediff on a pair of files, FILE-A and FILE-B."
  286.   (interactive
  287.    (let ((dir-A (if ediff-use-last-dir
  288.             ediff-last-dir-A
  289.           default-directory))
  290.      dir-B f)
  291.      (list (setq f (ediff-read-file-name
  292.             "File A to compare" dir-A 
  293.             ;; default is the current buf file name
  294.             (if (buffer-file-name (current-buffer))
  295.             (file-name-nondirectory
  296.              (buffer-file-name (current-buffer))))))
  297.        (ediff-read-file-name "File B to compare" 
  298.                  (setq dir-B
  299.                        (if ediff-use-last-dir
  300.                        ediff-last-dir-B 
  301.                      (file-name-directory f)))
  302.                  (progn
  303.                    (setq file-name-history
  304.                      (cons (ediff-abbreviate-file-name
  305.                         (expand-file-name
  306.                          (file-name-nondirectory f)
  307.                          dir-B))
  308.                            file-name-history))
  309.                    f))
  310.        )))
  311.   (ediff-files-internal file-A 
  312.             (if (file-directory-p file-B)
  313.                 (expand-file-name
  314.                  (file-name-nondirectory file-A) file-B)
  315.               file-B)
  316.             nil ; file-C
  317.             startup-hooks
  318.             'ediff-files))
  319.   
  320. ;;;###autoload
  321. (defun ediff-files3 (file-A file-B file-C &optional startup-hooks)
  322.   "Run Ediff on three files, FILE-A, FILE-B, and FILE-C."
  323.   (interactive
  324.    (let ((dir-A (if ediff-use-last-dir
  325.             ediff-last-dir-A
  326.           default-directory))
  327.      dir-B dir-C f ff)
  328.      (list (setq f (ediff-read-file-name
  329.             "File A to compare" dir-A
  330.             ;; default is the current buf file name
  331.             (if (buffer-file-name (current-buffer))
  332.             (file-name-nondirectory
  333.              (buffer-file-name (current-buffer))))))
  334.        (setq ff (ediff-read-file-name "File B to compare" 
  335.                       (setq dir-B
  336.                         (if ediff-use-last-dir
  337.                             ediff-last-dir-B
  338.                           (file-name-directory f)))
  339.                       (progn
  340.                         (setq file-name-history
  341.                           (cons
  342.                            (ediff-abbreviate-file-name
  343.                             (expand-file-name
  344.                              (file-name-nondirectory f)
  345.                              dir-B))
  346.                            file-name-history))
  347.                         f)))
  348.        (ediff-read-file-name "File C to compare" 
  349.                  (setq dir-C (if ediff-use-last-dir
  350.                          ediff-last-dir-C
  351.                            (file-name-directory ff)))
  352.                  (progn
  353.                    (setq file-name-history
  354.                      (cons (ediff-abbreviate-file-name
  355.                         (expand-file-name
  356.                          (file-name-nondirectory ff)
  357.                          dir-C))
  358.                            file-name-history))
  359.                    ff))
  360.        )))
  361.   (ediff-files-internal file-A 
  362.             (if (file-directory-p file-B)
  363.                 (expand-file-name
  364.                  (file-name-nondirectory file-A) file-B)
  365.               file-B)
  366.             (if (file-directory-p file-C)
  367.                 (expand-file-name
  368.                  (file-name-nondirectory file-A) file-C)
  369.               file-C)
  370.             startup-hooks
  371.             'ediff-files3))
  372.  
  373. ;;;###autoload
  374. (defalias 'ediff3 'ediff-files3)
  375.  
  376.  
  377. (defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var)
  378.   "Visit FILE and arrange its buffer to Ediff's liking. 
  379. FILE is actually a variable symbol that must contain a true file name.
  380. BUFFER-NAME is a variable symbol, which will get the buffer object into which
  381. FILE is read.  LAST-DIR is the directory variable symbol where FILE's
  382. directory name should be returned. HOOKS is a variable symbol that will be
  383. assigned the hook to be executed after `ediff-startup' is finished.
  384. `ediff-find-file' arranges that the temp files it might create will be
  385. deleted."
  386.   (let* ((file (symbol-value file-var))
  387.      (file-magic (find-file-name-handler file 'find-file-noselect))
  388.      (temp-file-name-prefix (file-name-nondirectory file)))
  389.     (cond ((not (file-readable-p file))
  390.        (error "File `%s' does not exist or is not readable" file))
  391.       ((file-directory-p file)
  392.        (error "File `%s' is a directory" file)))
  393.     
  394.     ;; some of the command, below, require full file name
  395.     (setq file (expand-file-name file))
  396.   
  397.     ;; Record the directory of the file
  398.     (if last-dir
  399.     (set last-dir (expand-file-name (file-name-directory file))))
  400.     
  401.     ;; Setup the buffer
  402.     (set buffer-name (find-file-noselect file))
  403.   
  404.     (ediff-eval-in-buffer (symbol-value buffer-name)
  405.       (widen) ; Make sure the entire file is seen
  406.       (cond (file-magic  ;; file has handler, such as jka-compr-handler or
  407.          ;; ange-ftp-hook-function--arrange for temp file
  408.          (ediff-verify-file-buffer 'magic)
  409.          (setq file
  410.            (ediff-make-temp-file
  411.             (current-buffer) temp-file-name-prefix))
  412.          (set hooks-var (cons (` (lambda () (delete-file (, file))))
  413.                   (symbol-value hooks-var))))
  414.         ;; file processed via auto-mode-alist, a la uncompress.el
  415.         ((not (equal (file-truename file)
  416.              (file-truename (buffer-file-name))))
  417.          (setq file
  418.            (ediff-make-temp-file
  419.             (current-buffer) temp-file-name-prefix))
  420.          (set hooks-var (cons (` (lambda () (delete-file (, file))))
  421.                   (symbol-value hooks-var))))
  422.         (t ;; plain file---just check that the file matches the buffer
  423.          (ediff-verify-file-buffer))))
  424.     (set file-var file)))
  425.  
  426. (defun ediff-files-internal (file-A file-B file-C startup-hooks job-name)
  427.   (let (buf-A buf-B buf-C)
  428.     (message "Reading file %s ... " file-A)
  429.     ;;(sit-for 0)
  430.     (ediff-find-file 'file-A 'buf-A 'ediff-last-dir-A 'startup-hooks)
  431.     (message "Reading file %s ... " file-B)
  432.     ;;(sit-for 0)
  433.     (ediff-find-file 'file-B 'buf-B 'ediff-last-dir-B 'startup-hooks)
  434.     (if (stringp file-C)
  435.     (progn
  436.       (message "Reading file %s ... " file-C)
  437.       ;;(sit-for 0)
  438.       (ediff-find-file
  439.        'file-C 'buf-C
  440.        (if (eq job-name 'ediff-merge-files-with-ancestor)
  441.            'ediff-last-dir-ancestor 'ediff-last-dir-C)
  442.        'startup-hooks)))
  443.     (ediff-setup buf-A file-A
  444.          buf-B file-B
  445.          buf-C file-C
  446.          startup-hooks
  447.          (list (cons 'ediff-job-name job-name)))))
  448.   
  449.  
  450. ;;;###autoload
  451. (defalias 'ediff 'ediff-files)
  452.  
  453.  
  454. ;;;###autoload
  455. (defun ediff-buffers (buffer-A buffer-B &optional startup-hooks job-name)
  456.   "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B."
  457.   (interactive 
  458.    (let (bf)
  459.      (list (setq bf (read-buffer "Buffer A to compare: "
  460.                  (ediff-other-buffer "") t))
  461.        (read-buffer "Buffer B to compare: "
  462.             (progn
  463.               ;; realign buffers so that two visible bufs will be
  464.               ;; at the top
  465.               (save-window-excursion (other-window 1))
  466.               (ediff-other-buffer bf))
  467.             t))))
  468.   
  469.   (or job-name (setq job-name 'ediff-buffers))
  470.   (ediff-buffers-internal buffer-A buffer-B nil startup-hooks job-name))
  471.       
  472. ;;;###autoload
  473. (defun ediff-buffers3 (buffer-A buffer-B buffer-C
  474.                  &optional startup-hooks job-name)
  475.   "Run Ediff on three buffers, BUFFER-A, BUFFER-B, and BUFFER-C."
  476.   (interactive 
  477.    (let (bf bff)
  478.      (list (setq bf (read-buffer "Buffer A to compare: "
  479.                  (ediff-other-buffer "") t))
  480.        (setq bff (read-buffer "Buffer B to compare: "
  481.                   (progn
  482.                     ;; realign buffers so that two visible
  483.                     ;; bufs will be at the top
  484.                     (save-window-excursion (other-window 1))
  485.                     (ediff-other-buffer bf))
  486.                   t))
  487.        (read-buffer "Buffer C to compare: "
  488.                   (progn
  489.                     ;; realign buffers so that three visible
  490.                     ;; bufs will be at the top
  491.                     (save-window-excursion (other-window 1))
  492.                     (ediff-other-buffer (list bf bff)))
  493.                   t)
  494.        )))
  495.   
  496.   (or job-name (setq job-name 'ediff-buffers3))
  497.   (ediff-buffers-internal buffer-A buffer-B buffer-C startup-hooks job-name))
  498.       
  499.  
  500.             
  501. (defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name)
  502.   (let* ((buf-A-file-name (buffer-file-name (get-buffer buf-A)))
  503.      (buf-B-file-name (buffer-file-name (get-buffer buf-B)))
  504.      (buf-C-is-alive (ediff-buffer-live-p buf-C))
  505.      (buf-C-file-name (if buf-C-is-alive
  506.                   (buffer-file-name (get-buffer buf-B))))
  507.      file-A file-B file-C)
  508.     (if (not (ediff-buffer-live-p buf-A))
  509.     (error "Buffer %S doesn't exist" buf-A))
  510.     (if (not (ediff-buffer-live-p buf-B))
  511.     (error "Buffer %S doesn't exist" buf-B))
  512.     (let ((ediff-job-name job-name))
  513.       (if (and ediff-3way-comparison-job
  514.            (not buf-C-is-alive))
  515.       (error "Buffer %S doesn't exist" buf-C)))
  516.     (if (stringp buf-A-file-name)
  517.     (setq buf-A-file-name (file-name-nondirectory buf-A-file-name)))
  518.     (if (stringp buf-B-file-name)
  519.     (setq buf-B-file-name (file-name-nondirectory buf-B-file-name)))
  520.     (if (stringp buf-C-file-name)
  521.     (setq buf-C-file-name (file-name-nondirectory buf-C-file-name)))
  522.     
  523.     (setq file-A (ediff-make-temp-file buf-A buf-A-file-name))
  524.     (setq file-B (ediff-make-temp-file buf-B buf-B-file-name))
  525.     (if buf-C-is-alive
  526.     (setq file-C (ediff-make-temp-file buf-C buf-C-file-name)))
  527.       
  528.     (ediff-setup (get-buffer buf-A) file-A
  529.          (get-buffer buf-B) file-B
  530.          (if buf-C-is-alive (get-buffer buf-C))
  531.          file-C
  532.          (cons (` (lambda ()
  533.                 (delete-file (, file-A))
  534.                 (delete-file (, file-B))
  535.                 (if (stringp (, file-C)) (delete-file (, file-C)))
  536.                 ))
  537.                startup-hooks)
  538.          (list (cons 'ediff-job-name job-name))
  539.          )))
  540.  
  541.  
  542. ;;; Directory and file group operations
  543.  
  544. ;;;###autoload
  545. (defun ediff-directories (dir1 dir2 regexp)
  546.   "Run Ediff on a pair of directories, DIR1 and DIR2, comparing files that have
  547. the same name in both. The third argument, REGEXP, is a regular expression that
  548. further filters the file names."
  549.   (interactive
  550.    (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory))
  551.      f)
  552.      (list (setq f (ediff-read-file-name "Directory A to compare" dir-A nil))
  553.        (ediff-read-file-name "Directory B to compare" 
  554.                  (if ediff-use-last-dir
  555.                      ediff-last-dir-B 
  556.                    (ediff-strip-last-dir f))
  557.                  nil)
  558.        (read-string "Filter through regular expression: "
  559.             nil ediff-filtering-regexp-history)
  560.        )))
  561.   (ediff-directories-internal
  562.    dir1 dir2 nil regexp 'ediff-files 'ediff-directories
  563.    ))
  564.  
  565. ;;;###autoload
  566. (defalias 'edirs 'ediff-directories)
  567.  
  568.  
  569. ;;;###autoload
  570. (defun ediff-directory-revisions (dir1 regexp)
  571.   "Run Ediff on a directory, DIR1, comparing its files with their revisions.
  572. The second argument, REGEXP, is a regular expression that filters the file
  573. names. Only the files that are under revision control are taken into account."
  574.   (interactive
  575.    (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory)))
  576.      (list (ediff-read-file-name
  577.         "Directory to compare with revision" dir-A nil)
  578.        (read-string "Filter through regular expression: "
  579.             nil ediff-filtering-regexp-history)
  580.        )))
  581.   (ediff-directory-revisions-internal
  582.    dir1 regexp 'ediff-revision 'ediff-directory-revisions
  583.    ))
  584.  
  585. ;;;###autoload
  586. (defalias 'edir-revisions 'ediff-directory-revisions)
  587.  
  588.  
  589. ;;;###autoload
  590. (defun ediff-directories3 (dir1 dir2 dir3 regexp)
  591.   "Run Ediff on three directories, DIR1, DIR2, and DIR3, comparing files that
  592. have the same name in all three. The last argument, REGEXP, is a regular
  593. expression that further filters the file names."
  594.   (interactive
  595.    (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory))
  596.      f)
  597.      (list (setq f (ediff-read-file-name "Directory A to compare" dir-A nil))
  598.        (setq f (ediff-read-file-name "Directory B to compare" 
  599.                      (if ediff-use-last-dir
  600.                          ediff-last-dir-B 
  601.                        (ediff-strip-last-dir f))
  602.                      nil))
  603.        (ediff-read-file-name "Directory C to compare" 
  604.                  (if ediff-use-last-dir
  605.                      ediff-last-dir-C 
  606.                    (ediff-strip-last-dir f))
  607.                  nil)
  608.        (read-string "Filter through regular expression: "
  609.             nil ediff-filtering-regexp-history)
  610.        )))
  611.   (ediff-directories-internal
  612.    dir1 dir2 dir3 regexp 'ediff-files3 'ediff-directories3
  613.    ))
  614.  
  615. ;;;###autoload
  616. (defalias 'edirs3 'ediff-directories3)
  617.  
  618. ;;;###autoload
  619. (defun ediff-merge-directories (dir1 dir2 regexp)
  620.   "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have
  621. the same name in both. The third argument, REGEXP, is a regular expression that
  622. further filters the file names."
  623.   (interactive
  624.    (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory))
  625.      f)
  626.      (list (setq f (ediff-read-file-name "Directory A to merge" dir-A nil))
  627.        (ediff-read-file-name "Directory B to merge" 
  628.                  (if ediff-use-last-dir
  629.                      ediff-last-dir-B 
  630.                    (ediff-strip-last-dir f))
  631.                  nil)
  632.        (read-string "Filter through regular expression: "
  633.             nil ediff-filtering-regexp-history)
  634.        )))
  635.   (ediff-directories-internal
  636.    dir1 dir2 nil regexp 'ediff-merge-files 'ediff-merge-directories
  637.    ))
  638.  
  639. ;;;###autoload
  640. (defalias 'edirs-merge 'ediff-merge-directories)
  641.  
  642. ;;;###autoload
  643. (defun ediff-merge-directories-with-ancestor (dir1 dir2 dir3 regexp)
  644.   "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have
  645. the same name in both. The third argument, REGEXP, is a regular expression that
  646. further filters the file names."
  647.   (interactive
  648.    (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory))
  649.      f)
  650.      (list (setq f (ediff-read-file-name "Directory A to merge" dir-A nil))
  651.        (setq f (ediff-read-file-name "Directory B to merge" 
  652.                  (if ediff-use-last-dir
  653.                      ediff-last-dir-B 
  654.                    (ediff-strip-last-dir f))
  655.                  nil))
  656.        (ediff-read-file-name "Ancestor directory: "
  657.                  (if ediff-use-last-dir
  658.                      ediff-last-dir-C 
  659.                    (ediff-strip-last-dir f))
  660.                  nil)
  661.        (read-string "Filter through regular expression: "
  662.             nil ediff-filtering-regexp-history)
  663.        )))
  664.   (ediff-directories-internal
  665.    dir1 dir2 dir3 regexp
  666.    'ediff-merge-files-with-ancestor 'ediff-merge-directories-with-ancestor
  667.    ))
  668.  
  669. ;;;###autoload
  670. (defun ediff-merge-directory-revisions (dir1 regexp)
  671.   "Run Ediff on a directory, DIR1, merging its files with their revisions.
  672. The second argument, REGEXP, is a regular expression that filters the file
  673. names. Only the files that are under revision control are taken into account."
  674.   (interactive
  675.    (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory)))
  676.      (list (ediff-read-file-name
  677.         "Directory to merge with revisions" dir-A nil)
  678.        (read-string "Filter through regular expression: "
  679.             nil ediff-filtering-regexp-history)
  680.        )))
  681.   (ediff-directory-revisions-internal
  682.    dir1 regexp 'ediff-merge-revisions 'ediff-merge-directory-revisions
  683.    ))
  684.  
  685. ;;;###autoload
  686. (defalias 'edir-merge-revisions 'ediff-merge-directory-revisions)
  687.  
  688. ;;;###autoload
  689. (defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp)
  690.   "Run Ediff on a directory, DIR1, merging its files with their revisions and ancestors.
  691. The second argument, REGEXP, is a regular expression that filters the file
  692. names. Only the files that are under revision control are taken into account."
  693.   (interactive
  694.    (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory)))
  695.      (list (ediff-read-file-name
  696.         "Directory to merge with revisions and ancestors" dir-A nil)
  697.        (read-string "Filter through regular expression: "
  698.             nil ediff-filtering-regexp-history)
  699.        )))
  700.   (ediff-directory-revisions-internal
  701.    dir1 regexp 'ediff-merge-revisions-with-ancestor
  702.    'ediff-merge-directory-revisions-with-ancestor
  703.    ))
  704.  
  705. ;;;###autoload
  706. (defalias
  707.   'edir-merge-revisions-with-ancestor
  708.   'ediff-merge-directory-revisions-with-ancestor) 
  709.  
  710. ;;;###autoload
  711. (defalias 'edirs-merge-with-ancestor 'ediff-merge-directories-with-ancestor)
  712.  
  713. ;; Run ediff-action (ediff-files, ediff-merge, ediff-merge-with-ancestors)
  714. ;; on a pair of directories (three directories, in case of ancestor).
  715. ;; The third argument, REGEXP, is a regular expression that further filters the
  716. ;; file names.
  717. ;; JOBNAME is the symbol indicating the meta-job to be performed.
  718. (defun ediff-directories-internal (dir1 dir2 dir3 regexp 
  719.                     action jobname 
  720.                     &optional startup-hooks)
  721.   ;; ediff-read-file-name is set to attach a previously entered file name if
  722.   ;; the currently entered file is a directory. This code takes care of that.
  723.   (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1))
  724.     dir2 (if (file-directory-p dir2) dir2 (file-name-directory dir2)))
  725.  
  726.   (if (stringp dir3)
  727.       (setq dir3 (if (file-directory-p dir3) dir3 (file-name-directory dir3))))
  728.  
  729.   (cond ((string= dir1 dir2)
  730.      (error "Directories A and B are the same: %s" dir1))
  731.     ((and (eq jobname 'ediff-directories3)
  732.           (string= dir1 dir3))
  733.      (error "Directories A and C are the same: %s" dir1))
  734.     ((and (eq jobname 'ediff-directories3)
  735.           (string= dir2 dir3))
  736.      (error "Directories B and C are the same: %s" dir1)))
  737.  
  738.   (let (diffs ; var where ediff-intersect-directories returns the diff list
  739.     file-list meta-buf)
  740.     (setq file-list (ediff-intersect-directories 
  741.              jobname 'diffs regexp dir1 dir2 dir3))
  742.     (setq startup-hooks
  743.       ;; this sets various vars in the meta buffer inside
  744.       ;; ediff-prepare-meta-buffer
  745.       (cons (` (lambda ()
  746.              ;; tell what to do if the user clicks on a session record
  747.              (setq ediff-session-action-function (quote (, action)))
  748.              ;; set ediff-dir-difference-list 
  749.              (setq ediff-dir-difference-list (quote (, diffs)))))
  750.         startup-hooks))
  751.     (setq meta-buf (ediff-prepare-meta-buffer 
  752.             'ediff-dir-action
  753.             file-list
  754.             "*Ediff Session Group Panel"
  755.             'ediff-redraw-directory-group-buffer
  756.             jobname
  757.             startup-hooks))
  758.     (ediff-show-meta-buffer meta-buf)
  759.     ))
  760.  
  761. (defun ediff-directory-revisions-internal (dir1 regexp action jobname 
  762.                         &optional startup-hooks)
  763.   (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1)))
  764.   (let (file-list meta-buf)
  765.     (setq file-list
  766.       (ediff-get-directory-files-under-revision jobname regexp dir1))
  767.     (setq startup-hooks
  768.       ;; this sets various vars in the meta buffer inside
  769.       ;; ediff-prepare-meta-buffer
  770.       (cons (` (lambda ()
  771.              ;; tell what to do if the user clicks on a session record
  772.              (setq ediff-session-action-function (quote (, action)))
  773.              ))
  774.         startup-hooks))
  775.     (setq meta-buf (ediff-prepare-meta-buffer 
  776.             'ediff-dir-action
  777.             file-list
  778.             "*Ediff Session Group Panel"
  779.             'ediff-redraw-directory-group-buffer
  780.             jobname
  781.             startup-hooks))
  782.     (ediff-show-meta-buffer meta-buf)
  783.     ))
  784.  
  785.  
  786. ;;; Compare regions and windows
  787.  
  788. ;;;###autoload
  789. (defun ediff-windows-wordwise (dumb-mode &optional wind-A wind-B startup-hooks)
  790.   "Compare WIND-A and WIND-B, which are selected by clicking, wordwise.
  791. With prefix argument, DUMB-MODE, or on a non-windowing display, works as
  792. follows:
  793. If WIND-A is nil, use selected window.
  794. If WIND-B is nil, use window next to WIND-A."
  795.   (interactive "P")
  796.   (ediff-windows dumb-mode wind-A wind-B
  797.          startup-hooks 'ediff-windows-wordwise 'word-mode))
  798.          
  799. ;;;###autoload
  800. (defun ediff-windows-linewise (dumb-mode &optional wind-A wind-B startup-hooks)
  801.   "Compare WIND-A and WIND-B, which are selected by clicking, linewise.
  802. With prefix argument, DUMB-MODE, or on a non-windowing display, works as
  803. follows:
  804. If WIND-A is nil, use selected window.
  805. If WIND-B is nil, use window next to WIND-A."
  806.   (interactive "P")
  807.   (ediff-windows dumb-mode wind-A wind-B
  808.          startup-hooks 'ediff-windows-linewise nil))
  809.       
  810. ;; Compare WIND-A and WIND-B, which are selected by clicking.
  811. ;; With prefix argument, DUMB-MODE, or on a non-windowing display,
  812. ;; works as follows:
  813. ;; If WIND-A is nil, use selected window.
  814. ;; If WIND-B is nil, use window next to WIND-A.
  815. (defun ediff-windows (dumb-mode wind-A wind-B startup-hooks job-name word-mode)
  816.   (if (or dumb-mode (not (ediff-window-display-p)))
  817.       (setq wind-A (ediff-get-next-window wind-A nil)
  818.         wind-B (ediff-get-next-window wind-B wind-A))
  819.     (setq wind-A (ediff-get-window-by-clicking wind-A nil 1)
  820.       wind-B (ediff-get-window-by-clicking wind-B wind-A 2)))
  821.       
  822.   (let ((buffer-A (window-buffer wind-A))
  823.     (buffer-B (window-buffer wind-B))
  824.     beg-A end-A beg-B end-B)
  825.     
  826.     (save-excursion
  827.       (save-window-excursion
  828.     (sit-for 0) ; synch before using window-start/end -- a precaution
  829.     (select-window wind-A)
  830.     (setq beg-A (window-start)
  831.           end-A (window-end))
  832.     (select-window wind-B)
  833.     (setq beg-B (window-start)
  834.           end-B (window-end))))
  835.     (ediff-regions-internal
  836.      buffer-A beg-A end-A buffer-B beg-B end-B
  837.      startup-hooks job-name word-mode)))
  838.      
  839. ;;;###autoload
  840. (defun ediff-regions-wordwise (buffer-A buffer-B &optional startup-hooks)
  841.   "Run Ediff on a pair of regions in two different buffers.
  842. Regions \(i.e., point and mark\) are assumed to be set in advance.
  843. This function is effective only for relatively small regions, up to 200
  844. lines. For large regions, use `ediff-regions-linewise'."
  845.   (interactive 
  846.    (let (bf)
  847.      (list (setq bf (read-buffer "Region's A buffer: "
  848.                  (ediff-other-buffer "") t))
  849.        (read-buffer "Region's B buffer: "
  850.             (progn
  851.               ;; realign buffers so that two visible bufs will be
  852.               ;; at the top
  853.               (save-window-excursion (other-window 1))
  854.               (ediff-other-buffer bf))
  855.             t))))
  856.   (if (not (ediff-buffer-live-p buffer-A))
  857.       (error "Buffer %S doesn't exist" buffer-A))
  858.   (if (not (ediff-buffer-live-p buffer-B))
  859.       (error "Buffer %S doesn't exist" buffer-B))
  860.   
  861.   
  862.   (let (reg-A-beg reg-A-end reg-B-beg reg-B-end)
  863.     (save-excursion
  864.       (set-buffer buffer-A)
  865.       (setq reg-A-beg (region-beginning)
  866.         reg-A-end (region-end))
  867.       (set-buffer buffer-B)
  868.       (setq reg-B-beg (region-beginning)
  869.         reg-B-end (region-end)))
  870.         
  871.     (ediff-regions-internal
  872.      (get-buffer buffer-A) reg-A-beg reg-A-end
  873.      (get-buffer buffer-B) reg-B-beg reg-B-end
  874.      startup-hooks 'ediff-regions-wordwise 'word-mode)))
  875.      
  876. ;;;###autoload
  877. (defun ediff-regions-linewise (buffer-A buffer-B &optional startup-hooks)
  878.   "Run Ediff on a pair of regions in two different buffers.
  879. Regions \(i.e., point and mark\) are assumed to be set in advance.
  880. Each region is enlarged to contain full lines.
  881. This function is effective for large regions, over 100-200
  882. lines. For small regions, use `ediff-regions-wordwise'."
  883.   (interactive 
  884.    (let (bf)
  885.      (list (setq bf (read-buffer "Region A's buffer: "
  886.                  (ediff-other-buffer "") t))
  887.        (read-buffer "Region B's buffer: "
  888.             (progn
  889.               ;; realign buffers so that two visible bufs will be
  890.               ;; at the top
  891.               (save-window-excursion (other-window 1))
  892.               (ediff-other-buffer bf))
  893.             t))))
  894.   (if (not (ediff-buffer-live-p buffer-A))
  895.       (error "Buffer %S doesn't exist" buffer-A))
  896.   (if (not (ediff-buffer-live-p buffer-B))
  897.       (error "Buffer %S doesn't exist" buffer-B))
  898.   
  899.   (let (reg-A-beg reg-A-end reg-B-beg reg-B-end)
  900.     (save-excursion
  901.       (set-buffer buffer-A)
  902.       (setq reg-A-beg (region-beginning)
  903.         reg-A-end (region-end))
  904.       ;; enlarge the region to hold full lines
  905.       (goto-char reg-A-beg) 
  906.       (beginning-of-line)
  907.       (setq reg-A-beg (point))
  908.       (goto-char reg-A-end) 
  909.       (end-of-line)
  910.       (or (eobp) (forward-char)) ; include the newline char
  911.       (setq reg-A-end (point))
  912.       
  913.       (set-buffer buffer-B)
  914.       (setq reg-B-beg (region-beginning)
  915.         reg-B-end (region-end))
  916.       ;; enlarge the region to hold full lines
  917.       (goto-char reg-A-beg) 
  918.       (goto-char reg-B-beg) 
  919.       (beginning-of-line)
  920.       (setq reg-B-beg (point))
  921.       (goto-char reg-B-end) 
  922.       (end-of-line)
  923.       (or (eobp) (forward-char)) ; include the newline char
  924.       (setq reg-B-end (point))
  925.       ) ; save excursion
  926.         
  927.     (ediff-regions-internal
  928.      (get-buffer buffer-A) reg-A-beg reg-A-end
  929.      (get-buffer buffer-B) reg-B-beg reg-B-end
  930.      startup-hooks 'ediff-regions-linewise nil))) ; no word mode
  931.     
  932. ;; compare region beg-A to end-A of buffer-A
  933. ;; to regions beg-B -- end-B in buffer-B. 
  934. (defun ediff-regions-internal (buffer-A beg-A end-A buffer-B beg-B end-B
  935.                     startup-hooks job-name word-mode)
  936.   (let ((tmp-buffer (get-buffer-create ediff-tmp-buffer))
  937.     overl-A overl-B
  938.     file-A file-B)
  939.     
  940.     ;; in case beg/end-A/B aren't markers--make them into markers
  941.     (ediff-eval-in-buffer buffer-A
  942.       (setq beg-A (move-marker (make-marker) beg-A)
  943.         end-A (move-marker (make-marker) end-A)))
  944.     (ediff-eval-in-buffer buffer-B
  945.       (setq beg-B (move-marker (make-marker) beg-B)
  946.         end-B (move-marker (make-marker) end-B)))
  947.     
  948.     (if (and (eq buffer-A buffer-B)
  949.          (or (and (< beg-A end-B) (<= beg-B beg-A))   ; b-B b-A e-B
  950.          (and (< beg-B end-A) (<= end-A end-B)))) ; b-B e-A e-B
  951.     (progn
  952.       (with-output-to-temp-buffer ediff-msg-buffer
  953.         (princ "
  954. You have requested to compare overlapping regions of the same buffer.
  955.  
  956. In this case, Ediff's highlighting may be confusing---in the same window,
  957. you may see highlighted regions that belong to different regions.
  958.  
  959. Continue anyway? (y/n) "))
  960.  
  961.       (if (y-or-n-p "Continue anyway? ")
  962.           ()
  963.         (error "%S aborted" job-name))))
  964.         
  965.     ;; make file-A
  966.     (if word-mode
  967.     (ediff-wordify beg-A end-A buffer-A tmp-buffer)
  968.       (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer))
  969.     (setq file-A (ediff-make-temp-file tmp-buffer "regA"))
  970.     
  971.     ;; make file-B
  972.     (if word-mode
  973.     (ediff-wordify beg-B end-B buffer-B tmp-buffer)
  974.       (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer))
  975.     (setq file-B (ediff-make-temp-file tmp-buffer "regB"))
  976.      
  977.     (setq overl-A (ediff-make-bullet-proof-overlay beg-A end-A buffer-A))
  978.     (setq overl-B (ediff-make-bullet-proof-overlay beg-B end-B buffer-B))
  979.     (ediff-setup buffer-A file-A
  980.          buffer-B file-B
  981.          nil nil        ; buffer & file C
  982.          (cons (` (lambda ()
  983.                 (delete-file (, file-A))
  984.                 (delete-file (, file-B))))
  985.                startup-hooks)
  986.          (list (cons 'ediff-word-mode  word-mode)
  987.                (cons 'ediff-narrow-bounds (list overl-A overl-B))
  988.                (cons 'ediff-job-name job-name))
  989.          )
  990.     ))
  991.     
  992.  
  993. ;;; Merge files and buffers
  994.   
  995. ;;;###autoload
  996. (defalias 'ediff-merge 'ediff-merge-files)
  997.   
  998. (defsubst ediff-merge-on-startup ()
  999.   (ediff-do-merge 0)
  1000.   (ediff-eval-in-buffer ediff-buffer-C
  1001.     (set-buffer-modified-p nil)))
  1002.  
  1003. ;;;###autoload
  1004. (defun ediff-merge-files (file-A file-B &optional startup-hooks)
  1005.   "Merge two files without ancestor."
  1006.   (interactive
  1007.    (let ((dir-A (if ediff-use-last-dir
  1008.             ediff-last-dir-A
  1009.           default-directory))
  1010.      dir-B f)
  1011.      (list (setq f (ediff-read-file-name
  1012.             "File A to merge" dir-A
  1013.             ;; default is the current buf file name
  1014.             (if (buffer-file-name (current-buffer))
  1015.             (file-name-nondirectory
  1016.              (buffer-file-name (current-buffer))))))
  1017.        (ediff-read-file-name "File B to merge" 
  1018.                  (setq dir-B
  1019.                        (if ediff-use-last-dir
  1020.                        ediff-last-dir-B 
  1021.                      (file-name-directory f)))
  1022.                  (progn
  1023.                    (setq file-name-history
  1024.                      (cons (ediff-abbreviate-file-name
  1025.                         (expand-file-name
  1026.                          (file-name-nondirectory f)
  1027.                          dir-B))
  1028.                            file-name-history))
  1029.                    f))
  1030.        )))
  1031.   (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
  1032.   (ediff-files-internal file-A 
  1033.             (if (file-directory-p file-B)
  1034.                 (expand-file-name
  1035.                  (file-name-nondirectory file-A) file-B)
  1036.               file-B)
  1037.               nil ; file-C
  1038.               startup-hooks
  1039.               'ediff-merge-files))
  1040.               
  1041. ;;;###autoload
  1042. (defun ediff-merge-files-with-ancestor (file-A file-B file-ancestor
  1043.                            &optional startup-hooks)
  1044.   "Merge two files with ancestor."
  1045.   (interactive
  1046.    (let ((dir-A (if ediff-use-last-dir
  1047.             ediff-last-dir-A
  1048.           default-directory))
  1049.      dir-B dir-ancestor f ff)
  1050.      (list (setq f (ediff-read-file-name
  1051.             "File A to merge" dir-A
  1052.             ;; default is the current buf file name
  1053.             (if (buffer-file-name (current-buffer))
  1054.             (file-name-nondirectory
  1055.              (buffer-file-name (current-buffer))))))
  1056.        (setq ff (ediff-read-file-name "File B to merge" 
  1057.                       (setq dir-B
  1058.                         (if ediff-use-last-dir
  1059.                             ediff-last-dir-B 
  1060.                           (file-name-directory f)))
  1061.                       (progn
  1062.                         (setq file-name-history
  1063.                           (cons
  1064.                            (ediff-abbreviate-file-name
  1065.                             (expand-file-name
  1066.                              (file-name-nondirectory f)
  1067.                              dir-B))
  1068.                            file-name-history))
  1069.                         f)))
  1070.        (ediff-read-file-name "Ancestor file" 
  1071.                  (setq dir-ancestor
  1072.                        (if ediff-use-last-dir
  1073.                        ediff-last-dir-ancestor
  1074.                      (file-name-directory ff)))
  1075.                  (progn
  1076.                    (setq file-name-history
  1077.                      (cons (ediff-abbreviate-file-name
  1078.                         (expand-file-name
  1079.                          (file-name-nondirectory ff)
  1080.                          dir-ancestor))
  1081.                            file-name-history))
  1082.                    ff))
  1083.        )))
  1084.   (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
  1085.   (ediff-files-internal file-A 
  1086.             (if (file-directory-p file-B)
  1087.                 (expand-file-name
  1088.                  (file-name-nondirectory file-A) file-B)
  1089.               file-B)
  1090.               file-ancestor
  1091.               startup-hooks
  1092.               'ediff-merge-files-with-ancestor))
  1093.               
  1094. ;;;###autoload
  1095. (defalias 'ediff-merge-with-ancestor 'ediff-merge-files-with-ancestor)
  1096.               
  1097. ;;;###autoload
  1098. (defun ediff-merge-buffers (buffer-A buffer-B &optional startup-hooks job-name)
  1099.   "Merge buffers without ancestor."
  1100.   (interactive 
  1101.    (let (bf)
  1102.      (list (setq bf (read-buffer "Buffer A to merge: "
  1103.                  (ediff-other-buffer "") t))
  1104.        (read-buffer "Buffer B to merge: "
  1105.             (progn
  1106.               ;; realign buffers so that two visible bufs will be
  1107.               ;; at the top
  1108.               (save-window-excursion (other-window 1))
  1109.               (ediff-other-buffer bf))
  1110.             t))))
  1111.   
  1112.   (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
  1113.   (or job-name (setq job-name 'ediff-merge-buffers))
  1114.   (ediff-buffers-internal
  1115.    buffer-A buffer-B nil startup-hooks job-name))
  1116.    
  1117. ;;;###autoload
  1118. (defun ediff-merge-buffers-with-ancestor (buffer-A 
  1119.                       buffer-B buffer-ancestor
  1120.                       &optional startup-hooks job-name)
  1121.   "Merge buffers with ancestor."
  1122.   (interactive 
  1123.    (let (bf bff)
  1124.      (list (setq bf (read-buffer "Buffer A to merge: "
  1125.                  (ediff-other-buffer "") t))
  1126.        (setq bff (read-buffer "Buffer B to merge: "
  1127.                   (progn
  1128.                     ;; realign buffers so that two visible
  1129.                     ;; bufs will be at the top
  1130.                     (save-window-excursion (other-window 1))
  1131.                     (ediff-other-buffer bf))
  1132.                   t))
  1133.        (read-buffer "Ancestor buffer: "
  1134.                   (progn
  1135.                     ;; realign buffers so that three visible
  1136.                     ;; bufs will be at the top
  1137.                     (save-window-excursion (other-window 1))
  1138.                     (ediff-other-buffer (list bf bff)))
  1139.                   t)
  1140.        )))
  1141.   
  1142.   (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
  1143.   (or job-name (setq job-name 'ediff-merge-buffers-with-ancestor))
  1144.   (ediff-buffers-internal
  1145.    buffer-A buffer-B buffer-ancestor startup-hooks job-name))
  1146.       
  1147.  
  1148. ;;;###autoload
  1149. (defun ediff-merge-revisions (&optional file startup-hooks)
  1150.   "Run Ediff by merging two revisions of a file.
  1151. The file is the optional FILE argument or the file visited by the current
  1152. buffer."
  1153.   (interactive)
  1154.   (ediff-load-version-control)
  1155.   (if (stringp file) (find-file file))
  1156.   (let (rev1 rev2 buf1 buf2)
  1157.     (setq rev1
  1158.       (read-string
  1159.        (format
  1160.         "Version 1 to merge (default: %s's latest version): "
  1161.         (if (stringp file)
  1162.         (file-name-nondirectory file) "current buffer")))
  1163.       rev2
  1164.       (read-string
  1165.        (format
  1166.         "Version 2 to merge (default: %s): "
  1167.         (if (stringp file)
  1168.         (file-name-nondirectory file) "current buffer"))))
  1169.     (if (eq ediff-version-control-package 'vc)
  1170.      (progn
  1171.        (save-excursion
  1172.          (vc-version-other-window rev1)
  1173.          (setq buf1 (current-buffer)))
  1174.        (save-excursion
  1175.          (or (string= rev2 "")
  1176.          (vc-version-other-window rev2))
  1177.          (setq buf2 (current-buffer)))
  1178.        (setq startup-hooks 
  1179.          (cons 
  1180.           (` (lambda () 
  1181.                (delete-file (, (buffer-file-name buf1)))
  1182.                (or (, (string= rev2 ""))
  1183.                (delete-file (, (buffer-file-name buf2))))))
  1184.           startup-hooks)))
  1185.       (setq buf1 (rcs-ediff-view-revision rev1)
  1186.         buf2 (if (string= rev2 "")
  1187.              (current-buffer)
  1188.            (rcs-ediff-view-revision rev2))))
  1189.     (ediff-merge-buffers buf1 buf2 startup-hooks 'ediff-merge-revisions)))
  1190.     
  1191.  
  1192. ;;;###autoload
  1193. (defun ediff-merge-revisions-with-ancestor (&optional file startup-hooks)
  1194.   "Run Ediff by merging two revisions of a file with a common ancestor.
  1195. The file is the the optional FILE argument or the file visited by the current
  1196. buffer."
  1197.   (interactive)
  1198.   (ediff-load-version-control)
  1199.   (if (stringp file) (find-file file))
  1200.   (let (rev1 rev2 ancestor-rev buf1 buf2 ancestor-buf)
  1201.     (setq rev1
  1202.       (read-string
  1203.        (format
  1204.         "Version 1 to merge (default: %s's latest version): "
  1205.         (if (stringp file)
  1206.         (file-name-nondirectory file) "current buffer")))
  1207.       rev2
  1208.       (read-string
  1209.        (format
  1210.         "Version 2 to merge (default: %s): "
  1211.         (if (stringp file)
  1212.         (file-name-nondirectory file) "current buffer")))
  1213.       ancestor-rev
  1214.       (read-string
  1215.        (format
  1216.         "Ancestor version (default: %s): "
  1217.         (if (stringp file)
  1218.         (file-name-nondirectory file) "current buffer"))))
  1219.     (if (eq ediff-version-control-package 'vc)
  1220.      (progn
  1221.        (save-excursion
  1222.          (vc-version-other-window rev1)
  1223.          (setq buf1 (current-buffer)))
  1224.        (save-excursion
  1225.          (or (string= rev2 "")
  1226.          (vc-version-other-window rev2))
  1227.          (setq buf2 (current-buffer)))
  1228.        (save-excursion
  1229.          (or (string= ancestor-rev "")
  1230.          (vc-version-other-window ancestor-rev))
  1231.          (setq ancestor-buf (current-buffer)))
  1232.        (setq startup-hooks 
  1233.          (cons
  1234.           (` (lambda () 
  1235.                (delete-file (, (buffer-file-name buf1)))
  1236.                (or (, (string= rev2 ""))
  1237.                (delete-file (, (buffer-file-name buf2))))
  1238.                (or (, (string= ancestor-rev ""))
  1239.                (delete-file (, (buffer-file-name ancestor-buf))))))
  1240.           startup-hooks)))
  1241.       (setq buf1 (rcs-ediff-view-revision rev1)
  1242.         buf2 (if (string= rev2 "")
  1243.              (current-buffer)
  1244.            (rcs-ediff-view-revision rev2))
  1245.         ancestor-buf (if (string= ancestor-rev "")
  1246.                  (current-buffer)
  1247.                (rcs-ediff-view-revision ancestor-rev))))
  1248.     (ediff-merge-buffers-with-ancestor
  1249.      buf1 buf2 ancestor-buf
  1250.      startup-hooks 'ediff-merge-revisions-with-ancestor)))
  1251.      
  1252.      
  1253. ;;; Apply patch
  1254.     
  1255. ;;;###autoload
  1256. (defun ediff-patch-buffer (buffer-name &optional startup-hooks)          
  1257.   "Run Ediff by patching BUFFER-NAME."
  1258.   (interactive "bBuffer to patch: ")
  1259.   
  1260.   (let* ((buf-to-patch (get-buffer buffer-name))
  1261.      (file-name-ok (if buf-to-patch (buffer-file-name  buf-to-patch)))
  1262.      (buf-mod-status (buffer-modified-p buf-to-patch))
  1263.      default-dir file-name ctl-buf)
  1264.     (if file-name-ok
  1265.     (setq file-name file-name-ok)
  1266.       (ediff-eval-in-buffer buffer-name
  1267.     (setq default-dir default-directory)
  1268.     (setq file-name (ediff-make-temp-file buffer-name))
  1269.     (set-visited-file-name file-name)
  1270.     (setq buffer-auto-save-file-name nil) ; don't create auto-save file
  1271.     (rename-buffer buffer-name) ; don't confuse the user with new buf name
  1272.     (set-buffer-modified-p nil)
  1273.     (set-visited-file-modtime) ; sync buffer and temp file
  1274.     (setq default-directory default-dir)
  1275.     ))
  1276.     
  1277.     (setq ctl-buf
  1278.       (ediff-patch-file file-name startup-hooks 'ediff-patch-buffer))
  1279.     
  1280.     (if file-name-ok
  1281.     ()
  1282.       (ediff-eval-in-buffer ctl-buf
  1283.     (delete-file (buffer-file-name ediff-buffer-A))
  1284.     (delete-file (buffer-file-name ediff-buffer-B))
  1285.     (ediff-eval-in-buffer ediff-buffer-A
  1286.       (if default-dir (setq default-directory default-dir))
  1287.       (set-visited-file-name nil)
  1288.       (rename-buffer buffer-name)
  1289.       (set-buffer-modified-p buf-mod-status))
  1290.     (ediff-eval-in-buffer ediff-buffer-B
  1291.       (setq buffer-auto-save-file-name nil) ; don't create auto-save file
  1292.       (if default-dir (setq default-directory default-dir))
  1293.       (set-visited-file-name nil)
  1294.       (rename-buffer (ediff-unique-buffer-name 
  1295.               (concat buffer-name "_patched") ""))
  1296.       (set-buffer-modified-p t))))
  1297.     ))
  1298.  
  1299.  
  1300. (defun ediff-get-patch-buffer (dir)
  1301.   "Obtain patch buffer.  If patch is already in a buffer---use it.
  1302. Else, read patch file into a new buffer."
  1303.   (if (y-or-n-p "Is the patch file already in a buffer? ")
  1304.       (setq ediff-patch-buf
  1305.         (get-buffer (read-buffer "Patch buffer name: " nil t))) ;must match
  1306.     (setq ediff-patch-buf
  1307.       (find-file-noselect (read-file-name "Patch file name: " dir))))
  1308.   
  1309.   (setq ediff-patch-diagnostics
  1310.     (get-buffer-create "*ediff patch diagnostics*"))
  1311.   (ediff-eval-in-buffer ediff-patch-diagnostics
  1312.     (insert-buffer ediff-patch-buf)))
  1313.  
  1314.  
  1315.       
  1316.  
  1317.  
  1318. ;;; Versions Control functions      
  1319.       
  1320. ;;;###autoload
  1321. (defun ediff-revision (&optional file startup-hooks)
  1322.   "Run Ediff by comparing versions of a file.
  1323. The file is an optional FILE argument or the file visited by the current
  1324. buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'."
  1325.   ;; if buffer is non-nil, use that buffer instead of the current buffer
  1326.   (interactive "P")
  1327.   (if (stringp file) (find-file file))
  1328.   (let (rev1 rev2)
  1329.     (setq rev1
  1330.       (read-string
  1331.        (format "Version 1 to compare (default: %s's latest version): "
  1332.            (if (stringp file)
  1333.                (file-name-nondirectory file) "current buffer")))
  1334.       rev2
  1335.       (read-string 
  1336.        (format "Version 2 to compare (default: %s): "
  1337.            (if (stringp file)
  1338.                (file-name-nondirectory file) "current buffer"))))
  1339.     (ediff-load-version-control)
  1340.     (funcall
  1341.      (intern (format "%S-ediff-internal" ediff-version-control-package))
  1342.      rev1 rev2 startup-hooks)
  1343.     ))
  1344.    
  1345.    
  1346. ;; Test if version control package is loaded and load if not
  1347. ;; Is SILENT is non-nil, don't report error if package is not found.
  1348. (defun ediff-load-version-control (&optional silent)
  1349.   (or (featurep ediff-version-control-package)
  1350.       (if (locate-library (symbol-name ediff-version-control-package))
  1351.       (progn
  1352.         (message "") ; kill the message from `locate-library'
  1353.         (require ediff-version-control-package)
  1354.         (if ediff-revision-key
  1355.         (define-key
  1356.           (cond ((eq ediff-version-control-package 'vc) vc-prefix-map)
  1357.             ((eq ediff-version-control-package 'rcs) global-map)
  1358.             (t  global-map))
  1359.           ediff-revision-key 'ediff-revision)))
  1360.     (or silent
  1361.         (error "Version control package %S.el not found. Use vc.el instead"
  1362.            ediff-version-control-package)))))
  1363.   
  1364.       
  1365. (defun vc-ediff-internal (rev1 rev2 &optional startup-hooks)
  1366.   "Run Ediff on versions of the current buffer.
  1367. If REV2 is \"\" then compare current buffer with REV1.
  1368. If the current buffer is named `F', the version is named `F.~REV~'.
  1369. If `F.~REV~' already exists, it is used instead of being re-created."
  1370.   (let (file1 file2 rev1buf rev2buf)
  1371.     (save-excursion
  1372.       (vc-version-other-window rev1)
  1373.       (setq rev1buf (current-buffer)
  1374.         file1 (buffer-file-name)))
  1375.     (save-excursion
  1376.       (or (string= rev2 "")         ; use current buffer
  1377.       (vc-version-other-window rev2))
  1378.       (setq rev2buf (current-buffer)
  1379.         file2 (buffer-file-name)))
  1380.     (setq startup-hooks
  1381.       (cons (` (lambda ()
  1382.              (delete-file (, file1))
  1383.              (or (, (string= rev2 "")) (delete-file (, file2)))
  1384.              ))
  1385.         startup-hooks))
  1386.     (ediff-buffers
  1387.      rev1buf rev2buf
  1388.      startup-hooks
  1389.      'ediff-revision)))
  1390.     
  1391. (defun rcs-ediff-view-revision (&optional rev)
  1392.   "View previous RCS revision of current file.
  1393. With prefix argument, prompts for a revision name." 
  1394.   (interactive (list (if current-prefix-arg 
  1395.              (read-string "Revision: "))))
  1396.   (let* ((filename (buffer-file-name (current-buffer)))
  1397.      (switches (append '("-p")
  1398.                (if rev (list (concat "-r" rev)) nil)))
  1399.      (buff (concat (file-name-nondirectory filename) ".~" rev "~")))
  1400.     (message "Working ...")
  1401.     (setq filename (expand-file-name filename))
  1402.     (with-output-to-temp-buffer buff
  1403.       (let ((output-buffer (ediff-rcs-get-output-buffer filename buff)))
  1404.     (delete-windows-on output-buffer)
  1405.     (save-excursion
  1406.       (set-buffer output-buffer)
  1407.       (apply 'call-process "co" nil t nil
  1408.          ;; -q: quiet (no diagnostics)
  1409.          (append switches rcs-default-co-switches
  1410.              (list "-q" filename))))) 
  1411.       (message "")
  1412.       buff)))    
  1413.       
  1414. (defun ediff-rcs-get-output-buffer (file name)
  1415.   ;; Get a buffer for RCS output for FILE, make it writable and clean it up.
  1416.   ;; Optional NAME is name to use instead of `*RCS-output*'.
  1417.   ;; This is a modified version from rcs.el v1.1. I use it here to make
  1418.   ;; Ediff immune to changes in rcs.el
  1419.   (let* ((default-major-mode 'fundamental-mode) ; no frills!
  1420.      (buf (get-buffer-create name)))
  1421.     (save-excursion
  1422.       (set-buffer buf)
  1423.       (setq buffer-read-only nil
  1424.         default-directory (file-name-directory (expand-file-name file)))
  1425.       (erase-buffer))
  1426.     buf))
  1427.  
  1428. (defun rcs-ediff-internal (rev1 rev2 &optional startup-hooks)
  1429.   "Run Ediff on versions of the current buffer.
  1430. If REV2 is \"\" then use current buffer."
  1431.   (let ((rev2buf (if (string= rev2 "")
  1432.              (current-buffer)
  1433.            (rcs-ediff-view-revision rev2)))
  1434.     (rev1buf (rcs-ediff-view-revision rev1)))
  1435.     
  1436.     ;; rcs.el doesn't create temp version files, so we don't have to delete
  1437.     ;; anything in startup hooks to ediff-buffers
  1438.     (ediff-buffers rev1buf rev2buf startup-hooks 'ediff-revision)
  1439.     ))
  1440.  
  1441.  
  1442. (provide 'ediff)
  1443. (require 'ediff-util)
  1444.  
  1445. ;;; ediff.el ends here
  1446.