ediff.el 52.7 KB
Newer Older
1
;;; ediff.el --- a comprehensive visual interface to diff & patch
Erik Naggum's avatar
Erik Naggum committed
2

3
;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
Glenn Morris's avatar
Glenn Morris committed
4
;;   2003, 2004, 2005, 2006, 2007 Free Software Foundation, Inc.
Richard M. Stallman's avatar
Richard M. Stallman committed
5

6
;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
Richard M. Stallman's avatar
Richard M. Stallman committed
7
;; Created: February 2, 1994
8
;; Keywords: comparing, merging, patching, tools, unix
Richard M. Stallman's avatar
Richard M. Stallman committed
9

10
(defconst ediff-version "2.81.2" "The current version of Ediff")
11
(defconst ediff-date "August 18, 2007" "Date of last update")
Michael Kifer's avatar
Michael Kifer committed
12

13

Richard M. Stallman's avatar
Richard M. Stallman committed
14 15 16 17
;; This file is part of GNU Emacs.

;; GNU Emacs is free software; you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
18
;; the Free Software Foundation; either version 3, or (at your option)
Richard M. Stallman's avatar
Richard M. Stallman committed
19 20 21 22 23 24 25 26
;; any later version.

;; GNU Emacs is distributed in the hope that it will be useful,
;; but WITHOUT ANY WARRANTY; without even the implied warranty of
;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
;; GNU General Public License for more details.

;; You should have received a copy of the GNU General Public License
Erik Naggum's avatar
Erik Naggum committed
27
;; along with GNU Emacs; see the file COPYING.  If not, write to the
Lute Kamstra's avatar
Lute Kamstra committed
28 29
;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
;; Boston, MA 02110-1301, USA.
Richard M. Stallman's avatar
Richard M. Stallman committed
30 31 32

;;; Commentary:

33
;; Never read that diff output again!
Michael Kifer's avatar
Michael Kifer committed
34
;; Apply patch interactively!
35
;; Merge with ease!
Richard M. Stallman's avatar
Richard M. Stallman committed
36

37
;; This package provides a convenient way of simultaneous browsing through
Karl Heuer's avatar
Karl Heuer committed
38
;; the differences between a pair (or a triple) of files or buffers.  The
39 40 41 42 43
;; files being compared, file-A, file-B, and file-C (if applicable) are
;; shown in separate windows (side by side, one above the another, or in
;; separate frames), and the differences are highlighted as you step
;; through them.  You can also copy difference regions from one buffer to
;; another (and recover old differences if you change your mind).
Richard M. Stallman's avatar
Richard M. Stallman committed
44

45
;; Ediff also supports merging operations on files and buffers, including
Michael Kifer's avatar
Michael Kifer committed
46
;; merging using ancestor versions.  Both comparison and merging operations can
47 48 49
;; be performed on directories, i.e., by pairwise comparison of files in those
;; directories.

Richard M. Stallman's avatar
Richard M. Stallman committed
50
;; In addition, Ediff can apply a patch to a file and then let you step
51
;; though both files, the patched and the original one, simultaneously,
Richard M. Stallman's avatar
Richard M. Stallman committed
52 53 54 55
;; difference-by-difference.  You can even apply a patch right out of a
;; mail buffer, i.e., patches received by mail don't even have to be saved.
;; Since Ediff lets you copy differences between buffers, you can, in
;; effect, apply patches selectively (i.e., you can copy a difference
56
;; region from file_orig to file, thereby undoing any particular patch that
Richard M. Stallman's avatar
Richard M. Stallman committed
57 58
;; you don't like).

Karl Heuer's avatar
Karl Heuer committed
59
;; Ediff is aware of version control, which lets the user compare
Michael Kifer's avatar
Michael Kifer committed
60 61
;; files with their older versions.  Ediff can also work with remote and
;; compressed files.  Details are given below.
Karl Heuer's avatar
Karl Heuer committed
62

Michael Kifer's avatar
Michael Kifer committed
63
;; Finally, Ediff supports directory-level comparison, merging and patching.
64
;; See the on-line manual for details.
Richard M. Stallman's avatar
Richard M. Stallman committed
65

66
;; This package builds upon the ideas borrowed from emerge.el and several
Michael Kifer's avatar
Michael Kifer committed
67
;; Ediff's functions are adaptations from emerge.el.  Much of the functionality
68 69
;; Ediff provides is also influenced by emerge.el.

Michael Kifer's avatar
Michael Kifer committed
70 71
;; The present version of Ediff supersedes Emerge.  It provides a superior user
;; interface and has numerous major features not found in Emerge.  In
72 73
;; particular, it can do patching, and 2-way and 3-way file comparison,
;; merging, and directory operations.
74

Michael Kifer's avatar
Michael Kifer committed
75 76


Richard M. Stallman's avatar
Richard M. Stallman committed
77 78
;;; Bugs:

Michael Kifer's avatar
Michael Kifer committed
79
;;  1. The undo command doesn't restore deleted regions well.  That is, if
Richard M. Stallman's avatar
Richard M. Stallman committed
80
;;  you delete all characters in a difference region and then invoke
81
;;  `undo', the reinstated text will most likely be inserted outside of
Richard M. Stallman's avatar
Richard M. Stallman committed
82
;;  what Ediff thinks is the current difference region. (This problem
83
;;  doesn't seem to exist with XEmacs.)
Richard M. Stallman's avatar
Richard M. Stallman committed
84 85 86 87
;;
;;  If at any point you feel that difference regions are no longer correct,
;;  you can hit '!' to recompute the differences.

Karl Heuer's avatar
Karl Heuer committed
88
;;  2. On a monochrome display, the repertoire of faces with which to
Michael Kifer's avatar
Michael Kifer committed
89 90
;;  highlight fine differences is limited.  By default, Ediff is using
;;  underlining.  However, if the region is already underlined by some other
Richard M. Stallman's avatar
Richard M. Stallman committed
91
;;  overlays, there is no simple way to temporarily remove that residual
Michael Kifer's avatar
Michael Kifer committed
92 93 94 95
;;  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
96
;;  commands in `ediff-prepare-buffer-hook' (which will unhighlight every
Richard M. Stallman's avatar
Richard M. Stallman committed
97 98
;;  buffer used by Ediff) or you can execute them interactively, at any time
;;  and on any buffer.
99

Michael Kifer's avatar
Michael Kifer committed
100

101 102
;;; Acknowledgements:

103 104
;; 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
Michael Kifer's avatar
Michael Kifer committed
105 106
;; its many users.  See Ediff on-line Info for the full list of those who
;; helped.  Improved defaults in Ediff file-name reading commands.
107 108 109

;;; Code:

110

Michael Kifer's avatar
Michael Kifer committed
111
;; Compiler pacifier
Michael Kifer's avatar
Michael Kifer committed
112 113 114 115
(defvar cvs-cookie-handle)
(defvar ediff-last-dir-patch)
(defvar ediff-patch-default-directory)

Michael Kifer's avatar
Michael Kifer committed
116 117 118
(and noninteractive
     (eval-when-compile
	 (load-library "dired")
Michael Kifer's avatar
Michael Kifer committed
119 120 121 122
	 (load-library "info")
	 (load "pcl-cvs" 'noerror)))
(eval-when-compile
  (let ((load-path (cons (expand-file-name ".") load-path)))
123
    (provide 'ediff) ; to break recursive load cycle
Michael Kifer's avatar
Michael Kifer committed
124 125 126 127 128 129 130 131 132 133 134 135 136 137 138
    (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)  ; required because of the registry stuff

(defgroup ediff nil
139
  "A comprehensive visual interface to diff & patch."
Michael Kifer's avatar
Michael Kifer committed
140
  :tag "Ediff"
Michael Kifer's avatar
Michael Kifer committed
141 142 143 144
  :group 'tools)


(defcustom ediff-use-last-dir nil
Michael Kifer's avatar
Michael Kifer committed
145
  "*If t, Ediff will use previous directory as default when reading file name."
Michael Kifer's avatar
Michael Kifer committed
146 147
  :type 'boolean
  :group 'ediff)
Michael Kifer's avatar
Michael Kifer committed
148

Michael Kifer's avatar
Michael Kifer committed
149 150 151 152 153 154 155 156 157
;; 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.
Michael Kifer's avatar
Michael Kifer committed
158
(defvar ediff-last-merge-autostore-dir nil)
Richard M. Stallman's avatar
Richard M. Stallman committed
159

160

Michael Kifer's avatar
Michael Kifer committed
161
;; Used as a startup hook to set `_orig' patch file read-only.
Karl Heuer's avatar
Karl Heuer committed
162
(defun ediff-set-read-only-in-buf-A ()
Michael Kifer's avatar
Michael Kifer committed
163
  (ediff-with-current-buffer ediff-buffer-A
Karl Heuer's avatar
Karl Heuer committed
164
    (toggle-read-only 1)))
Richard M. Stallman's avatar
Richard M. Stallman committed
165

166
;; Return a plausible default for ediff's first file:
167 168 169 170 171
;; In dired, return the file number FILENO (or 0) in the list
;; (all-selected-files, filename under the cursor), where directories are
;; ignored. Otherwise, return DEFAULT file name, if non-nil. Else,
;; if the buffer is visiting a file, return that file name.
(defun ediff-get-default-file-name (&optional default fileno)
172
  (cond ((eq major-mode 'dired-mode)
173 174 175
	 (let ((current (dired-get-filename nil 'no-error))
	       (marked (condition-case nil
			   (dired-get-marked-files 'no-dir)
176
			 (error nil)))
177 178 179 180 181 182 183 184 185 186 187
	       aux-list choices result)
	   (or (integerp fileno) (setq fileno 0))
	   (if (stringp default)
	       (setq aux-list (cons default aux-list)))
	   (if (and (stringp current) (not (file-directory-p current)))
	       (setq aux-list (cons current aux-list)))
	   (setq choices (nconc  marked aux-list))
	   (setq result (elt choices fileno))
	   (or result
	       default)))
	((stringp default) default)
188 189 190 191
	((buffer-file-name (current-buffer))
	 (file-name-nondirectory (buffer-file-name (current-buffer))))
	))

192
;;; Compare files/buffers
Richard M. Stallman's avatar
Richard M. Stallman committed
193 194 195

;;;###autoload
(defun ediff-files (file-A file-B &optional startup-hooks)
196
  "Run Ediff on a pair of files, FILE-A and FILE-B."
Richard M. Stallman's avatar
Richard M. Stallman committed
197
  (interactive
198 199 200 201
   (let ((dir-A (if ediff-use-last-dir
		    ediff-last-dir-A
		  default-directory))
	 dir-B f)
202
     (list (setq f (ediff-read-file-name
203
		    "File A to compare"
204
		    dir-A
205 206
		    (ediff-get-default-file-name)
		    'no-dirs))
207
	   (ediff-read-file-name "File B to compare"
208 209
				 (setq dir-B
				       (if ediff-use-last-dir
210
					   ediff-last-dir-B
211 212
					 (file-name-directory f)))
				 (progn
213 214 215 216 217 218
				   (ediff-add-to-history
				    'file-name-history
				    (ediff-abbreviate-file-name
				     (expand-file-name
				      (file-name-nondirectory f)
				      dir-B)))
219
				   (ediff-get-default-file-name f 1)))
220
	   )))
221
  (ediff-files-internal file-A
222 223 224 225 226 227 228
			(if (file-directory-p file-B)
			    (expand-file-name
			     (file-name-nondirectory file-A) file-B)
			  file-B)
			nil ; file-C
			startup-hooks
			'ediff-files))
229

230 231 232 233 234 235 236 237
;;;###autoload
(defun ediff-files3 (file-A file-B file-C &optional startup-hooks)
  "Run Ediff on three files, FILE-A, FILE-B, and FILE-C."
  (interactive
   (let ((dir-A (if ediff-use-last-dir
		    ediff-last-dir-A
		  default-directory))
	 dir-B dir-C f ff)
238
     (list (setq f (ediff-read-file-name
239 240 241 242
		    "File A to compare"
		    dir-A
		    (ediff-get-default-file-name)
		    'no-dirs))
243
	   (setq ff (ediff-read-file-name "File B to compare"
244 245 246 247 248
					  (setq dir-B
						(if ediff-use-last-dir
						    ediff-last-dir-B
						  (file-name-directory f)))
					  (progn
249 250 251 252 253 254
					    (ediff-add-to-history
					     'file-name-history
					     (ediff-abbreviate-file-name
					      (expand-file-name
					       (file-name-nondirectory f)
					       dir-B)))
255
					    (ediff-get-default-file-name f 1))))
256
	   (ediff-read-file-name "File C to compare"
257 258 259 260
				 (setq dir-C (if ediff-use-last-dir
						 ediff-last-dir-C
					       (file-name-directory ff)))
				 (progn
261 262 263 264 265 266
				   (ediff-add-to-history
				    'file-name-history
				    (ediff-abbreviate-file-name
				     (expand-file-name
				      (file-name-nondirectory ff)
				      dir-C)))
267
				   (ediff-get-default-file-name ff 2)))
Richard M. Stallman's avatar
Richard M. Stallman committed
268
	   )))
269
  (ediff-files-internal file-A
Richard M. Stallman's avatar
Richard M. Stallman committed
270 271 272 273
			(if (file-directory-p file-B)
			    (expand-file-name
			     (file-name-nondirectory file-A) file-B)
			  file-B)
274 275 276 277 278 279
			(if (file-directory-p file-C)
			    (expand-file-name
			     (file-name-nondirectory file-A) file-C)
			  file-C)
			startup-hooks
			'ediff-files3))
Richard M. Stallman's avatar
Richard M. Stallman committed
280

281 282
;;;###autoload
(defalias 'ediff3 'ediff-files3)
Richard M. Stallman's avatar
Richard M. Stallman committed
283 284


285
;; Visit FILE and arrange its buffer to Ediff's liking.
Michael Kifer's avatar
Michael Kifer committed
286 287 288 289
;; 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
Michael Kifer's avatar
Michael Kifer committed
290
;; directory name should be returned.  HOOKS-VAR is a variable symbol that will
Michael Kifer's avatar
Michael Kifer committed
291 292 293
;; 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.
294 295
(defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var)
  (let* ((file (symbol-value file-var))
Michael Kifer's avatar
Michael Kifer committed
296
	 (file-magic (ediff-filename-magic-p file))
297
	 (temp-file-name-prefix (file-name-nondirectory file)))
298 299 300 301
    (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)))
302

Michael Kifer's avatar
Michael Kifer committed
303
    ;; some of the commands, below, require full file name
304
    (setq file (expand-file-name file))
305

306 307 308
    ;; Record the directory of the file
    (if last-dir
	(set last-dir (expand-file-name (file-name-directory file))))
309

310 311
    ;; Setup the buffer
    (set buffer-name (find-file-noselect file))
312

Michael Kifer's avatar
Michael Kifer committed
313
    (ediff-with-current-buffer (symbol-value buffer-name)
314
      (widen) ; Make sure the entire file is seen
Michael Kifer's avatar
Michael Kifer committed
315 316
      (cond (file-magic  ;   file has a handler, such as jka-compr-handler or
	     		 ;;; ange-ftp-hook-function--arrange for temp file
317
	     (ediff-verify-file-buffer 'magic)
318 319 320
	     (setq file
		   (ediff-make-temp-file
		    (current-buffer) temp-file-name-prefix))
Michael Kifer's avatar
Michael Kifer committed
321
	     (set hooks-var (cons `(lambda () (delete-file ,file))
322 323 324 325
				  (symbol-value hooks-var))))
	    ;; file processed via auto-mode-alist, a la uncompress.el
	    ((not (equal (file-truename file)
			 (file-truename (buffer-file-name))))
326 327 328
	     (setq file
		   (ediff-make-temp-file
		    (current-buffer) temp-file-name-prefix))
Michael Kifer's avatar
Michael Kifer committed
329
	     (set hooks-var (cons `(lambda () (delete-file ,file))
330 331 332 333 334
				  (symbol-value hooks-var))))
	    (t ;; plain file---just check that the file matches the buffer
	     (ediff-verify-file-buffer))))
    (set file-var file)))

Michael Kifer's avatar
Michael Kifer committed
335 336 337
;; 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)
338
  (let (buf-A buf-B buf-C)
339 340 341 342 343
    (if (string= file-A file-B)
	(error "Files A and B are the same"))
    (if (stringp file-C)
	(or (and (string= file-A file-C) (error "Files A and C are the same"))
	    (and (string= file-B file-C) (error "Files B and C are the same"))))
344 345
    (message "Reading file %s ... " file-A)
    ;;(sit-for 0)
346
    (ediff-find-file 'file-A 'buf-A 'ediff-last-dir-A 'startup-hooks)
347 348
    (message "Reading file %s ... " file-B)
    ;;(sit-for 0)
349
    (ediff-find-file 'file-B 'buf-B 'ediff-last-dir-B 'startup-hooks)
350
    (if (stringp file-C)
351
	(progn
352 353
	  (message "Reading file %s ... " file-C)
	  ;;(sit-for 0)
354 355 356 357 358 359 360 361 362
	  (ediff-find-file
	   'file-C 'buf-C
	   (if (eq job-name 'ediff-merge-files-with-ancestor)
	       'ediff-last-dir-ancestor 'ediff-last-dir-C)
	   'startup-hooks)))
    (ediff-setup buf-A file-A
		 buf-B file-B
		 buf-C file-C
		 startup-hooks
Michael Kifer's avatar
Michael Kifer committed
363 364
		 (list (cons 'ediff-job-name job-name))
		 merge-buffer-file)))
365

366
(declare-function diff-latest-backup-file "diff" (fn))
367 368 369

;;;###autoload
(defalias 'ediff 'ediff-files)
Richard M. Stallman's avatar
Richard M. Stallman committed
370

371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
;;;###autoload
(defun ediff-backup (file)
  "Run Ediff on FILE and its backup file.
Uses the latest backup, if there are several numerical backups.
If this file is a backup, `ediff' it with its original."
  (interactive (list (read-file-name "Ediff (file with backup): ")))
  ;; The code is taken from `diff-backup'.
  (require 'diff)
  (let (bak ori)
    (if (backup-file-name-p file)
	(setq bak file
	      ori (file-name-sans-versions file))
      (setq bak (or (diff-latest-backup-file file)
		    (error "No backup found for %s" file))
	    ori file))
    (ediff-files bak ori)))
Richard M. Stallman's avatar
Richard M. Stallman committed
387 388

;;;###autoload
389
(defun ediff-buffers (buffer-A buffer-B &optional startup-hooks job-name)
Richard M. Stallman's avatar
Richard M. Stallman committed
390
  "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B."
391
  (interactive
392 393 394 395 396 397 398 399 400 401 402 403
   (let (bf)
     (list (setq bf (read-buffer "Buffer A to compare: "
				 (ediff-other-buffer "") t))
	   (read-buffer "Buffer B to compare: "
			(progn
			  ;; realign buffers so that two visible bufs will be
			  ;; at the top
			  (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))
Michael Kifer's avatar
Michael Kifer committed
404 405 406 407

;;;###autoload
(defalias 'ebuffers 'ediff-buffers)

408

409 410 411 412
;;;###autoload
(defun ediff-buffers3 (buffer-A buffer-B buffer-C
				 &optional startup-hooks job-name)
  "Run Ediff on three buffers, BUFFER-A, BUFFER-B, and BUFFER-C."
413
  (interactive
414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433
   (let (bf bff)
     (list (setq bf (read-buffer "Buffer A to compare: "
				 (ediff-other-buffer "") t))
	   (setq bff (read-buffer "Buffer B to compare: "
				  (progn
				    ;; realign buffers so that two visible
				    ;; bufs will be at the top
				    (save-window-excursion (other-window 1))
				    (ediff-other-buffer bf))
				  t))
	   (read-buffer "Buffer C to compare: "
				  (progn
				    ;; realign buffers so that three visible
				    ;; bufs will be at the top
				    (save-window-excursion (other-window 1))
				    (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))
Michael Kifer's avatar
Michael Kifer committed
434 435 436

;;;###autoload
(defalias 'ebuffers3 'ediff-buffers3)
437

438 439


Michael Kifer's avatar
Michael Kifer committed
440 441 442
;; 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)
443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462
  (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))
	 (buf-C-file-name (if buf-C-is-alive
			      (buffer-file-name (get-buffer buf-B))))
	 file-A file-B file-C)
    (if (not (ediff-buffer-live-p buf-A))
	(error "Buffer %S doesn't exist" buf-A))
    (if (not (ediff-buffer-live-p buf-B))
	(error "Buffer %S doesn't exist" buf-B))
    (let ((ediff-job-name job-name))
      (if (and ediff-3way-comparison-job
	       (not buf-C-is-alive))
	  (error "Buffer %S doesn't exist" buf-C)))
    (if (stringp buf-A-file-name)
	(setq buf-A-file-name (file-name-nondirectory buf-A-file-name)))
    (if (stringp buf-B-file-name)
	(setq buf-B-file-name (file-name-nondirectory buf-B-file-name)))
    (if (stringp buf-C-file-name)
	(setq buf-C-file-name (file-name-nondirectory buf-C-file-name)))
463

Michael Kifer's avatar
Michael Kifer committed
464 465
    (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))
466
    (if buf-C-is-alive
467
	(setq file-C (ediff-make-temp-file buf-C buf-C-file-name)))
468

469 470 471 472
    (ediff-setup (get-buffer buf-A) file-A
		 (get-buffer buf-B) file-B
		 (if buf-C-is-alive (get-buffer buf-C))
		 file-C
Michael Kifer's avatar
Michael Kifer committed
473 474 475 476
		 (cons `(lambda ()
			  (delete-file ,file-A)
			  (delete-file ,file-B)
			  (if (stringp ,file-C) (delete-file ,file-C)))
477 478
		       startup-hooks)
		 (list (cons 'ediff-job-name job-name))
Michael Kifer's avatar
Michael Kifer committed
479
		 merge-buffer-file)))
480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500


;;; Directory and file group operations

;; Get appropriate default name for directory:
;; If ediff-use-last-dir, use ediff-last-dir-A.
;; In dired mode, use the directory that is under the point (if any);
;; otherwise, use default-directory
(defun ediff-get-default-directory-name ()
  (cond (ediff-use-last-dir ediff-last-dir-A)
	((eq major-mode 'dired-mode)
	 (let ((f (dired-get-filename nil 'noerror)))
	   (if (and (stringp f) (file-directory-p f))
	       f
	     default-directory)))
	(t default-directory)))


;;;###autoload
(defun ediff-directories (dir1 dir2 regexp)
  "Run Ediff on a pair of directories, DIR1 and DIR2, comparing files that have
501 502
the same name in both.  The third argument, REGEXP, is nil or a regular
expression; only file names that match the regexp are considered."
503 504
  (interactive
   (let ((dir-A (ediff-get-default-directory-name))
505
	 (default-regexp (eval ediff-default-filtering-regexp))
506
	 f)
507 508 509 510 511 512 513
     (list (setq f (read-directory-name
		    "Directory A to compare:" dir-A nil 'must-match))
	   (read-directory-name "Directory B to compare:"
			   (if ediff-use-last-dir
			       ediff-last-dir-B
			     (ediff-strip-last-dir f))
			   nil 'must-match)
514 515 516 517 518 519 520 521
	   (read-string
	    (if (stringp default-regexp)
		(format "Filter through regular expression (default %s): "
			 default-regexp)
	      "Filter through regular expression: ")
	    nil
	    'ediff-filtering-regexp-history
	    (eval ediff-default-filtering-regexp))
522 523 524 525 526 527 528 529 530 531 532 533 534
	   )))
  (ediff-directories-internal
   dir1 dir2 nil regexp 'ediff-files 'ediff-directories
   ))

;;;###autoload
(defalias 'edirs 'ediff-directories)


;;;###autoload
(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
Michael Kifer's avatar
Michael Kifer committed
535
names.  Only the files that are under revision control are taken into account."
536
  (interactive
537 538 539
   (let ((dir-A (ediff-get-default-directory-name))
	 (default-regexp (eval ediff-default-filtering-regexp))
	 )
540 541
     (list (read-directory-name
	    "Directory to compare with revision:" dir-A nil 'must-match)
542 543 544 545 546 547 548 549
	   (read-string
	    (if (stringp default-regexp)
		(format "Filter through regular expression (default %s): "
			 default-regexp)
	      "Filter through regular expression: ")
	    nil
	    'ediff-filtering-regexp-history
	    (eval ediff-default-filtering-regexp))
550 551 552 553 554 555 556 557 558 559 560 561
	   )))
  (ediff-directory-revisions-internal
   dir1 regexp 'ediff-revision 'ediff-directory-revisions
   ))

;;;###autoload
(defalias 'edir-revisions 'ediff-directory-revisions)


;;;###autoload
(defun ediff-directories3 (dir1 dir2 dir3 regexp)
  "Run Ediff on three directories, DIR1, DIR2, and DIR3, comparing files that
562 563 564
have the same name in all three.  The last argument, REGEXP, is nil or a
regular expression; only file names that match the regexp are considered."

565 566
  (interactive
   (let ((dir-A (ediff-get-default-directory-name))
567
	 (default-regexp (eval ediff-default-filtering-regexp))
568
	 f)
569 570 571 572 573 574 575 576 577 578 579
     (list (setq f (read-directory-name "Directory A to compare:" dir-A nil))
	   (setq f (read-directory-name "Directory B to compare:"
				   (if ediff-use-last-dir
				       ediff-last-dir-B
				     (ediff-strip-last-dir f))
				   nil 'must-match))
	   (read-directory-name "Directory C to compare:"
			   (if ediff-use-last-dir
			       ediff-last-dir-C
			     (ediff-strip-last-dir f))
			   nil 'must-match)
580 581 582 583 584 585 586 587
	   (read-string
	    (if (stringp default-regexp)
		(format "Filter through regular expression (default %s): "
			 default-regexp)
	      "Filter through regular expression: ")
	    nil
	    'ediff-filtering-regexp-history
	    (eval ediff-default-filtering-regexp))
588 589 590 591 592 593 594 595 596
	   )))
  (ediff-directories-internal
   dir1 dir2 dir3 regexp 'ediff-files3 'ediff-directories3
   ))

;;;###autoload
(defalias 'edirs3 'ediff-directories3)

;;;###autoload
Michael Kifer's avatar
Michael Kifer committed
597
(defun ediff-merge-directories (dir1 dir2 regexp &optional merge-autostore-dir)
598
  "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have
599 600
the same name in both.  The third argument, REGEXP, is nil or a regular
expression; only file names that match the regexp are considered."
601 602
  (interactive
   (let ((dir-A (ediff-get-default-directory-name))
603
	 (default-regexp (eval ediff-default-filtering-regexp))
604
	 f)
605 606 607 608 609 610 611
     (list (setq f (read-directory-name "Directory A to merge:"
					dir-A nil 'must-match))
	   (read-directory-name "Directory B to merge:"
			   (if ediff-use-last-dir
			       ediff-last-dir-B
			     (ediff-strip-last-dir f))
			   nil 'must-match)
612 613 614 615 616 617 618 619
	   (read-string
	    (if (stringp default-regexp)
		(format "Filter through regular expression (default %s): "
			 default-regexp)
	      "Filter through regular expression: ")
	    nil
	    'ediff-filtering-regexp-history
	    (eval ediff-default-filtering-regexp))
620 621 622
	   )))
  (ediff-directories-internal
   dir1 dir2 nil regexp 'ediff-merge-files 'ediff-merge-directories
Michael Kifer's avatar
Michael Kifer committed
623
   nil merge-autostore-dir
624 625 626 627 628 629
   ))

;;;###autoload
(defalias 'edirs-merge 'ediff-merge-directories)

;;;###autoload
Michael Kifer's avatar
Michael Kifer committed
630 631 632
(defun ediff-merge-directories-with-ancestor (dir1 dir2 ancestor-dir regexp
						   &optional
						   merge-autostore-dir)
Michael Kifer's avatar
Michael Kifer committed
633
  "Merge files in directories DIR1 and DIR2 using files in ANCESTOR-DIR as ancestors.
Michael Kifer's avatar
Michael Kifer committed
634
Ediff merges files that have identical names in DIR1, DIR2.  If a pair of files
Michael Kifer's avatar
Michael Kifer committed
635
in DIR1 and DIR2 doesn't have an ancestor in ANCESTOR-DIR, Ediff will merge
636 637
without ancestor.  The fourth argument, REGEXP, is nil or a regular expression;
only file names that match the regexp are considered."
638 639
  (interactive
   (let ((dir-A (ediff-get-default-directory-name))
640
	 (default-regexp (eval ediff-default-filtering-regexp))
641
	 f)
642 643
     (list (setq f (read-directory-name "Directory A to merge:" dir-A nil))
	   (setq f (read-directory-name "Directory B to merge:"
644
				 (if ediff-use-last-dir
645
				     ediff-last-dir-B
646
				   (ediff-strip-last-dir f))
647 648
				 nil 'must-match))
	   (read-directory-name "Ancestor directory:"
649
				 (if ediff-use-last-dir
650
				     ediff-last-dir-C
651
				   (ediff-strip-last-dir f))
652
				 nil 'must-match)
653 654 655 656 657 658 659 660
	   (read-string
	    (if (stringp default-regexp)
		(format "Filter through regular expression (default %s): "
			 default-regexp)
	      "Filter through regular expression: ")
	    nil
	    'ediff-filtering-regexp-history
	    (eval ediff-default-filtering-regexp))
661 662
	   )))
  (ediff-directories-internal
Michael Kifer's avatar
Michael Kifer committed
663
   dir1 dir2 ancestor-dir regexp
664
   'ediff-merge-files-with-ancestor 'ediff-merge-directories-with-ancestor
Michael Kifer's avatar
Michael Kifer committed
665
   nil merge-autostore-dir
666 667 668
   ))

;;;###autoload
Michael Kifer's avatar
Michael Kifer committed
669 670
(defun ediff-merge-directory-revisions (dir1 regexp
					     &optional merge-autostore-dir)
671 672
  "Run Ediff on a directory, DIR1, merging its files with their revisions.
The second argument, REGEXP, is a regular expression that filters the file
Michael Kifer's avatar
Michael Kifer committed
673
names.  Only the files that are under revision control are taken into account."
674
  (interactive
675 676 677
   (let ((dir-A (ediff-get-default-directory-name))
	 (default-regexp (eval ediff-default-filtering-regexp))
	 )
678 679
     (list (read-directory-name
	    "Directory to merge with revisions:" dir-A nil 'must-match)
680 681 682 683 684 685 686 687
	   (read-string
	    (if (stringp default-regexp)
		(format "Filter through regular expression (default %s): "
			 default-regexp)
	      "Filter through regular expression: ")
	    nil
	    'ediff-filtering-regexp-history
	    (eval ediff-default-filtering-regexp))
688 689 690
	   )))
  (ediff-directory-revisions-internal
   dir1 regexp 'ediff-merge-revisions 'ediff-merge-directory-revisions
Michael Kifer's avatar
Michael Kifer committed
691
   nil merge-autostore-dir
692 693 694 695 696 697
   ))

;;;###autoload
(defalias 'edir-merge-revisions 'ediff-merge-directory-revisions)

;;;###autoload
Michael Kifer's avatar
Michael Kifer committed
698 699 700
(defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp
							   &optional
							   merge-autostore-dir)
701 702
  "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
Michael Kifer's avatar
Michael Kifer committed
703
names.  Only the files that are under revision control are taken into account."
704
  (interactive
705 706 707
   (let ((dir-A (ediff-get-default-directory-name))
	 (default-regexp (eval ediff-default-filtering-regexp))
	 )
708 709 710
     (list (read-directory-name
	    "Directory to merge with revisions and ancestors:"
	    dir-A nil 'must-match)
711 712 713 714 715 716 717 718
	   (read-string
	    (if (stringp default-regexp)
		(format "Filter through regular expression (default %s): "
			 default-regexp)
	      "Filter through regular expression: ")
	    nil
	    'ediff-filtering-regexp-history
	    (eval ediff-default-filtering-regexp))
719 720 721 722
	   )))
  (ediff-directory-revisions-internal
   dir1 regexp 'ediff-merge-revisions-with-ancestor
   'ediff-merge-directory-revisions-with-ancestor
Michael Kifer's avatar
Michael Kifer committed
723
   nil merge-autostore-dir
724 725 726 727 728
   ))

;;;###autoload
(defalias
  'edir-merge-revisions-with-ancestor
729
  'ediff-merge-directory-revisions-with-ancestor)
730 731 732 733 734 735

;;;###autoload
(defalias 'edirs-merge-with-ancestor 'ediff-merge-directories-with-ancestor)

;; Run ediff-action (ediff-files, ediff-merge, ediff-merge-with-ancestors)
;; on a pair of directories (three directories, in case of ancestor).
736 737
;; The third argument, REGEXP, is nil or a regular expression;
;; only file names that match the regexp are considered.
738
;; JOBNAME is the symbol indicating the meta-job to be performed.
Michael Kifer's avatar
Michael Kifer committed
739
;; MERGE-AUTOSTORE-DIR is the directory in which to store merged files.
740
(defun ediff-directories-internal (dir1 dir2 dir3 regexp action jobname
Michael Kifer's avatar
Michael Kifer committed
741 742
					&optional startup-hooks
					merge-autostore-dir)
743 744 745 746 747 748 749 750 751 752 753 754
  (if (stringp dir3)
      (setq dir3 (if (file-directory-p dir3) dir3 (file-name-directory dir3))))

  (cond ((string= dir1 dir2)
	 (error "Directories A and B are the same: %s" dir1))
	((and (eq jobname 'ediff-directories3)
	      (string= dir1 dir3))
	 (error "Directories A and C are the same: %s" dir1))
	((and (eq jobname 'ediff-directories3)
	      (string= dir2 dir3))
	 (error "Directories B and C are the same: %s" dir1)))

Michael Kifer's avatar
Michael Kifer committed
755 756 757 758
  (if merge-autostore-dir
      (or (stringp merge-autostore-dir)
	  (error "%s: Directory for storing merged files must be a string"
		 jobname)))
759
  (let (;; dir-diff-struct is of the form (common-list diff-list)
Michael Kifer's avatar
Michael Kifer committed
760 761 762 763
	;; It is a structure where ediff-intersect-directories returns
	;; commonalities and differences among directories
	dir-diff-struct
	meta-buf)
Michael Kifer's avatar
Michael Kifer committed
764 765 766
    (if (and ediff-autostore-merges
	     (ediff-merge-metajob jobname)
	     (not merge-autostore-dir))
767
	(setq merge-autostore-dir
768
	      (read-directory-name "Save merged files in directory: "
Michael Kifer's avatar
Michael Kifer committed
769
			      (if ediff-use-last-dir
Michael Kifer's avatar
Michael Kifer committed
770 771
					ediff-last-merge-autostore-dir
				      (ediff-strip-last-dir dir1))
Michael Kifer's avatar
Michael Kifer committed
772 773
			      nil
			      'must-match)))
Michael Kifer's avatar
Michael Kifer committed
774
    ;; verify we are not merging into an orig directory
Michael Kifer's avatar
Michael Kifer committed
775
    (if merge-autostore-dir
Michael Kifer's avatar
Michael Kifer committed
776
	(cond ((and (stringp dir1) (string= merge-autostore-dir dir1))
Michael Kifer's avatar
Michael Kifer committed
777 778
	       (or (y-or-n-p
		    "Directory for saving merged files = Directory A.  Sure? ")
Michael Kifer's avatar
Michael Kifer committed
779 780
		   (error "Directory merge aborted")))
	      ((and (stringp dir2) (string= merge-autostore-dir dir2))
Michael Kifer's avatar
Michael Kifer committed
781 782
	       (or (y-or-n-p
		    "Directory for saving merged files = Directory B.  Sure? ")
Michael Kifer's avatar
Michael Kifer committed
783 784 785
		   (error "Directory merge aborted")))
	      ((and (stringp dir3) (string= merge-autostore-dir dir3))
	       (or (y-or-n-p
Michael Kifer's avatar
Michael Kifer committed
786
		    "Directory for saving merged files = Ancestor Directory.  Sure? ")
Michael Kifer's avatar
Michael Kifer committed
787
		   (error "Directory merge aborted")))))
788 789

    (setq dir-diff-struct (ediff-intersect-directories
Michael Kifer's avatar
Michael Kifer committed
790 791
			   jobname
			   regexp dir1 dir2 dir3 merge-autostore-dir))
792 793 794
    (setq startup-hooks
	  ;; this sets various vars in the meta buffer inside
	  ;; ediff-prepare-meta-buffer
Michael Kifer's avatar
Michael Kifer committed
795 796 797
	  (cons `(lambda ()
		   ;; tell what to do if the user clicks on a session record
		   (setq ediff-session-action-function (quote ,action))
798
		   ;; set ediff-dir-difference-list
Michael Kifer's avatar
Michael Kifer committed
799 800
		   (setq ediff-dir-difference-list
			 (cdr (quote ,dir-diff-struct))))
801
		startup-hooks))
802
    (setq meta-buf (ediff-prepare-meta-buffer
Michael Kifer's avatar
Michael Kifer committed
803
		    'ediff-filegroup-action
Michael Kifer's avatar
Michael Kifer committed
804
		    (car dir-diff-struct)
805 806 807 808 809 810 811
		    "*Ediff Session Group Panel"
		    'ediff-redraw-directory-group-buffer
		    jobname
		    startup-hooks))
    (ediff-show-meta-buffer meta-buf)
    ))

Michael Kifer's avatar
Michael Kifer committed
812 813
;; MERGE-AUTOSTORE-DIR can be given to tell ediff where to store the merged
;; files
814
(defun ediff-directory-revisions-internal (dir1 regexp action jobname
Michael Kifer's avatar
Michael Kifer committed
815 816
						&optional startup-hooks
						merge-autostore-dir)
817
  (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1)))
Michael Kifer's avatar
Michael Kifer committed
818

Michael Kifer's avatar
Michael Kifer committed
819 820 821 822 823
  (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)
824
    (if (and ediff-autostore-merges
Michael Kifer's avatar
Michael Kifer committed
825 826
	     (ediff-merge-metajob jobname)
	     (not merge-autostore-dir))
827
	(setq merge-autostore-dir
828
	      (read-directory-name "Save merged files in directory: "
Michael Kifer's avatar
Michael Kifer committed
829 830 831 832 833
			      (if ediff-use-last-dir
				  ediff-last-merge-autostore-dir
				(ediff-strip-last-dir dir1))
			      nil
			      'must-match)))
Michael Kifer's avatar
Michael Kifer committed
834
    ;; verify merge-autostore-dir != dir1
Michael Kifer's avatar
Michael Kifer committed
835
    (if (and merge-autostore-dir
Michael Kifer's avatar
Michael Kifer committed
836 837 838
	     (stringp dir1)
	     (string= merge-autostore-dir dir1))
	(or (y-or-n-p
Michael Kifer's avatar
Michael Kifer committed
839
	     "Directory for saving merged file = directory A.  Sure? ")
Michael Kifer's avatar
Michael Kifer committed
840
	    (error "Merge of directory revisions aborted")))
841

842
    (setq file-list
Michael Kifer's avatar
Michael Kifer committed
843 844
	  (ediff-get-directory-files-under-revision
	   jobname regexp dir1 merge-autostore-dir))
845 846 847
    (setq startup-hooks
	  ;; this sets various vars in the meta buffer inside
	  ;; ediff-prepare-meta-buffer
Michael Kifer's avatar
Michael Kifer committed
848 849 850
	  (cons `(lambda ()
		   ;; tell what to do if the user clicks on a session record
		   (setq ediff-session-action-function (quote ,action)))
851
		startup-hooks))
852
    (setq meta-buf (ediff-prepare-meta-buffer
Michael Kifer's avatar
Michael Kifer committed
853
		    'ediff-filegroup-action
854 855 856 857 858 859 860
		    file-list
		    "*Ediff Session Group Panel"
		    'ediff-redraw-directory-group-buffer
		    jobname
		    startup-hooks))
    (ediff-show-meta-buffer meta-buf)
    ))
861 862 863 864 865


;;; Compare regions and windows

;;;###autoload
Karl Heuer's avatar
Karl Heuer committed
866 867
(defun ediff-windows-wordwise (dumb-mode &optional wind-A wind-B startup-hooks)
  "Compare WIND-A and WIND-B, which are selected by clicking, wordwise.
868 869 870 871 872
With prefix argument, DUMB-MODE, or on a non-windowing display, works as
follows:
If WIND-A is nil, use selected window.
If WIND-B is nil, use window next to WIND-A."
  (interactive "P")
Karl Heuer's avatar
Karl Heuer committed
873 874
  (ediff-windows dumb-mode wind-A wind-B
		 startup-hooks 'ediff-windows-wordwise 'word-mode))
875

Karl Heuer's avatar
Karl Heuer committed
876 877 878 879 880 881 882 883 884 885
;;;###autoload
(defun ediff-windows-linewise (dumb-mode &optional wind-A wind-B startup-hooks)
  "Compare WIND-A and WIND-B, which are selected by clicking, linewise.
With prefix argument, DUMB-MODE, or on a non-windowing display, works as
follows:
If WIND-A is nil, use selected window.
If WIND-B is nil, use window next to WIND-A."
  (interactive "P")
  (ediff-windows dumb-mode wind-A wind-B
		 startup-hooks 'ediff-windows-linewise nil))
886

Karl Heuer's avatar
Karl Heuer committed
887 888 889 890 891 892 893
;; Compare WIND-A and WIND-B, which are selected by clicking.
;; With prefix argument, DUMB-MODE, or on a non-windowing display,
;; works as follows:
;; If WIND-A is nil, use selected window.
;; If WIND-B is nil, use window next to WIND-A.
(defun ediff-windows (dumb-mode wind-A wind-B startup-hooks job-name word-mode)
  (if (or dumb-mode (not (ediff-window-display-p)))
894 895 896 897
      (setq wind-A (ediff-get-next-window wind-A nil)
	    wind-B (ediff-get-next-window wind-B wind-A))
    (setq wind-A (ediff-get-window-by-clicking wind-A nil 1)
	  wind-B (ediff-get-window-by-clicking wind-B wind-A 2)))
898

899 900 901
  (let ((buffer-A (window-buffer wind-A))
	(buffer-B (window-buffer wind-B))
	beg-A end-A beg-B end-B)
902

903 904
    (save-excursion
      (save-window-excursion
Karl Heuer's avatar
Karl Heuer committed
905
	(sit-for 0) ; sync before using window-start/end -- a precaution
906 907 908 909 910 911
	(select-window wind-A)
	(setq beg-A (window-start)
	      end-A (window-end))
	(select-window wind-B)
	(setq beg-B (window-start)
	      end-B (window-end))))
912 913
    (setq buffer-A
	  (ediff-clone-buffer-for-window-comparison
Michael Kifer's avatar
Michael Kifer committed
914
	   buffer-A wind-A "-Window.A-")
915 916
	  buffer-B
	  (ediff-clone-buffer-for-window-comparison
Michael Kifer's avatar
Michael Kifer committed
917
	   buffer-B wind-B "-Window.B-"))
918 919
    (ediff-regions-internal
     buffer-A beg-A end-A buffer-B beg-B end-B
Michael Kifer's avatar
Michael Kifer committed
920
     startup-hooks job-name word-mode nil)))
921

922

923
;;;###autoload
Karl Heuer's avatar
Karl Heuer committed
924
(defun ediff-regions-wordwise (buffer-A buffer-B &optional startup-hooks)
925
  "Run Ediff on a pair of regions in specified buffers.
926
Regions \(i.e., point and mark\) can be set in advance or marked interactively.
927
This function is effective only for relatively small regions, up to 200
Michael Kifer's avatar
Michael Kifer committed
928
lines.  For large regions, use `ediff-regions-linewise'."
929
  (interactive
930 931 932 933 934 935 936 937 938 939 940 941 942 943
   (let (bf)
     (list (setq bf (read-buffer "Region's A buffer: "
				 (ediff-other-buffer "") t))
	   (read-buffer "Region's B buffer: "
			(progn
			  ;; realign buffers so that two visible bufs will be
			  ;; at the top
			  (save-window-excursion (other-window 1))
			  (ediff-other-buffer bf))
			t))))
  (if (not (ediff-buffer-live-p buffer-A))
      (error "Buffer %S doesn't exist" buffer-A))
  (if (not (ediff-buffer-live-p buffer-B))
      (error "Buffer %S doesn't exist" buffer-B))
944 945


946
  (let ((buffer-A
Michael Kifer's avatar
Michael Kifer committed
947
         (ediff-clone-buffer-for-region-comparison buffer-A "-Region.A-"))
948
	(buffer-B
Michael Kifer's avatar
Michael Kifer committed
949
         (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-"))
950
        reg-A-beg reg-A-end reg-B-beg reg-B-end)
951 952 953 954 955 956 957
    (save-excursion
      (set-buffer buffer-A)
      (setq reg-A-beg (region-beginning)
	    reg-A-end (region-end))
      (set-buffer buffer-B)
      (setq reg-B-beg (region-beginning)
	    reg-B-end (region-end)))
958