Merge from emacs--devo--0
[bpt/emacs.git] / lisp / ediff.el
CommitLineData
0f0b0a86 1;;; ediff.el --- a comprehensive visual interface to diff & patch
b578f267 2
0d30b337 3;; Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
aaef169d 4;; 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
813f532d 5
50a07e18 6;; Author: Michael Kifer <kifer@cs.stonybrook.edu>
813f532d 7;; Created: February 2, 1994
e4d2c130 8;; Keywords: comparing, merging, patching, tools, unix
813f532d 9
b6178721
MK
10(defconst ediff-version "2.81" "The current version of Ediff")
11(defconst ediff-date "February 18, 2006" "Date of last update")
bbe6126c 12
eaccd4d8 13
813f532d
RS
14;; This file is part of GNU Emacs.
15
16;; GNU Emacs is free software; you can redistribute it and/or modify
17;; it under the terms of the GNU General Public License as published by
18;; the Free Software Foundation; either version 2, or (at your option)
19;; any later version.
20
21;; GNU Emacs is distributed in the hope that it will be useful,
22;; but WITHOUT ANY WARRANTY; without even the implied warranty of
23;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
24;; GNU General Public License for more details.
25
26;; You should have received a copy of the GNU General Public License
b578f267 27;; along with GNU Emacs; see the file COPYING. If not, write to the
086add15
LK
28;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
29;; Boston, MA 02110-1301, USA.
813f532d 30
813f532d 31;;; Commentary:
813f532d 32
0f0b0a86 33;; Never read that diff output again!
bbe6126c 34;; Apply patch interactively!
0f0b0a86 35;; Merge with ease!
813f532d 36
fcbadd58 37;; This package provides a convenient way of simultaneous browsing through
f1a5512a 38;; the differences between a pair (or a triple) of files or buffers. The
0f0b0a86
KH
39;; files being compared, file-A, file-B, and file-C (if applicable) are
40;; shown in separate windows (side by side, one above the another, or in
41;; separate frames), and the differences are highlighted as you step
42;; through them. You can also copy difference regions from one buffer to
43;; another (and recover old differences if you change your mind).
813f532d 44
87c668b4 45;; Ediff also supports merging operations on files and buffers, including
3af0304a 46;; merging using ancestor versions. Both comparison and merging operations can
87c668b4
MK
47;; be performed on directories, i.e., by pairwise comparison of files in those
48;; directories.
49
813f532d 50;; In addition, Ediff can apply a patch to a file and then let you step
fcbadd58 51;; though both files, the patched and the original one, simultaneously,
813f532d
RS
52;; difference-by-difference. You can even apply a patch right out of a
53;; mail buffer, i.e., patches received by mail don't even have to be saved.
54;; Since Ediff lets you copy differences between buffers, you can, in
55;; effect, apply patches selectively (i.e., you can copy a difference
0f0b0a86 56;; region from file_orig to file, thereby undoing any particular patch that
813f532d
RS
57;; you don't like).
58
f1a5512a 59;; Ediff is aware of version control, which lets the user compare
3af0304a
MK
60;; files with their older versions. Ediff can also work with remote and
61;; compressed files. Details are given below.
f1a5512a 62
bbe6126c 63;; Finally, Ediff supports directory-level comparison, merging and patching.
87c668b4 64;; See the on-line manual for details.
813f532d 65
87c668b4 66;; This package builds upon the ideas borrowed from emerge.el and several
3af0304a 67;; Ediff's functions are adaptations from emerge.el. Much of the functionality
87c668b4
MK
68;; Ediff provides is also influenced by emerge.el.
69
3af0304a
MK
70;; The present version of Ediff supersedes Emerge. It provides a superior user
71;; interface and has numerous major features not found in Emerge. In
87c668b4
MK
72;; particular, it can do patching, and 2-way and 3-way file comparison,
73;; merging, and directory operations.
fcbadd58 74
4ae69eac
MK
75
76
813f532d 77;;; Bugs:
813f532d 78
3af0304a 79;; 1. The undo command doesn't restore deleted regions well. That is, if
813f532d 80;; you delete all characters in a difference region and then invoke
0f0b0a86 81;; `undo', the reinstated text will most likely be inserted outside of
b3a26225 82;; what Ediff thinks is the current difference region. (This problem
0f0b0a86 83;; doesn't seem to exist with XEmacs.)
b3a26225
RS
84;;
85;; If at any point you feel that difference regions are no longer correct,
86;; you can hit '!' to recompute the differences.
87
f1a5512a 88;; 2. On a monochrome display, the repertoire of faces with which to
3af0304a
MK
89;; highlight fine differences is limited. By default, Ediff is using
90;; underlining. However, if the region is already underlined by some other
b3a26225 91;; overlays, there is no simple way to temporarily remove that residual
3af0304a
MK
92;; underlining. This problem occurs when a buffer is highlighted with
93;; hilit19.el or font-lock.el packages. If this residual highlighting gets
94;; in the way, you can do the following. Both font-lock.el and hilit19.el
95;; provide commands for unhighlighting buffers. You can either place these
87c668b4 96;; commands in `ediff-prepare-buffer-hook' (which will unhighlight every
b3a26225
RS
97;; buffer used by Ediff) or you can execute them interactively, at any time
98;; and on any buffer.
0f0b0a86 99
4ae69eac 100
0f0b0a86
KH
101;;; Acknowledgements:
102
87c668b4
MK
103;; Ediff was inspired by Dale R. Worley's <drw@math.mit.edu> emerge.el.
104;; Ediff would not have been possible without the help and encouragement of
3af0304a
MK
105;; its many users. See Ediff on-line Info for the full list of those who
106;; helped. Improved defaults in Ediff file-name reading commands.
0f0b0a86 107
0f0b0a86
KH
108;;; Code:
109
eaccd4d8 110
ddc90f39 111;; Compiler pacifier
1e70790f
MK
112(defvar cvs-cookie-handle)
113(defvar ediff-last-dir-patch)
114(defvar ediff-patch-default-directory)
115
92c51e07
MK
116(and noninteractive
117 (eval-when-compile
92c51e07 118 (load-library "dired")
ddc90f39
MK
119 (load-library "info")
120 (load "pcl-cvs" 'noerror)))
121(eval-when-compile
122 (let ((load-path (cons (expand-file-name ".") load-path)))
b6178721 123 (provide 'ediff) ; to break recursive load cycle
ddc90f39
MK
124 (or (featurep 'ediff-init)
125 (load "ediff-init.el" nil nil 'nosuffix))
126 (or (featurep 'ediff-mult)
127 (load "ediff-mult.el" nil nil 'nosuffix))
128 (or (featurep 'ediff-ptch)
129 (load "ediff-ptch.el" nil nil 'nosuffix))
130 (or (featurep 'ediff-vers)
131 (load "ediff-vers.el" nil nil 'nosuffix))
132 ))
133;; end pacifier
134
135(require 'ediff-init)
136(require 'ediff-mult) ; required because of the registry stuff
137
138(defgroup ediff nil
371d768e 139 "A comprehensive visual interface to diff & patch."
34317da2 140 :tag "Ediff"
ddc90f39
MK
141 :group 'tools)
142
143
144(defcustom ediff-use-last-dir nil
1e70790f 145 "*If t, Ediff will use previous directory as default when reading file name."
ddc90f39
MK
146 :type 'boolean
147 :group 'ediff)
bbe6126c 148
1e70790f
MK
149;; Last directory used by an Ediff command for file-A.
150(defvar ediff-last-dir-A nil)
151;; Last directory used by an Ediff command for file-B.
152(defvar ediff-last-dir-B nil)
153;; Last directory used by an Ediff command for file-C.
154(defvar ediff-last-dir-C nil)
155;; Last directory used by an Ediff command for the ancestor file.
156(defvar ediff-last-dir-ancestor nil)
157;; Last directory used by an Ediff command as the output directory for merge.
c3912d54 158(defvar ediff-last-merge-autostore-dir nil)
813f532d 159
87c668b4 160
4ae69eac 161;; Used as a startup hook to set `_orig' patch file read-only.
f1a5512a 162(defun ediff-set-read-only-in-buf-A ()
e756eb9f 163 (ediff-with-current-buffer ediff-buffer-A
f1a5512a 164 (toggle-read-only 1)))
813f532d 165
87c668b4 166;; Return a plausible default for ediff's first file:
657f9cb8
MK
167;; In dired, return the file number FILENO (or 0) in the list
168;; (all-selected-files, filename under the cursor), where directories are
169;; ignored. Otherwise, return DEFAULT file name, if non-nil. Else,
170;; if the buffer is visiting a file, return that file name.
171(defun ediff-get-default-file-name (&optional default fileno)
87c668b4 172 (cond ((eq major-mode 'dired-mode)
657f9cb8
MK
173 (let ((current (dired-get-filename nil 'no-error))
174 (marked (condition-case nil
175 (dired-get-marked-files 'no-dir)
50a07e18 176 (error nil)))
657f9cb8
MK
177 aux-list choices result)
178 (or (integerp fileno) (setq fileno 0))
179 (if (stringp default)
180 (setq aux-list (cons default aux-list)))
181 (if (and (stringp current) (not (file-directory-p current)))
182 (setq aux-list (cons current aux-list)))
183 (setq choices (nconc marked aux-list))
184 (setq result (elt choices fileno))
185 (or result
186 default)))
187 ((stringp default) default)
87c668b4
MK
188 ((buffer-file-name (current-buffer))
189 (file-name-nondirectory (buffer-file-name (current-buffer))))
190 ))
191
0f0b0a86 192;;; Compare files/buffers
813f532d
RS
193
194;;;###autoload
195(defun ediff-files (file-A file-B &optional startup-hooks)
0f0b0a86 196 "Run Ediff on a pair of files, FILE-A and FILE-B."
813f532d 197 (interactive
0f0b0a86
KH
198 (let ((dir-A (if ediff-use-last-dir
199 ediff-last-dir-A
200 default-directory))
201 dir-B f)
87c668b4 202 (list (setq f (ediff-read-file-name
50a07e18 203 "File A to compare"
71296446 204 dir-A
50a07e18
MK
205 (ediff-get-default-file-name)
206 'no-dirs))
71296446 207 (ediff-read-file-name "File B to compare"
0f0b0a86
KH
208 (setq dir-B
209 (if ediff-use-last-dir
71296446 210 ediff-last-dir-B
0f0b0a86
KH
211 (file-name-directory f)))
212 (progn
640ad75b
KS
213 (add-to-history 'file-name-history
214 (ediff-abbreviate-file-name
215 (expand-file-name
216 (file-name-nondirectory f)
217 dir-B)))
657f9cb8 218 (ediff-get-default-file-name f 1)))
0f0b0a86 219 )))
71296446 220 (ediff-files-internal file-A
0f0b0a86
KH
221 (if (file-directory-p file-B)
222 (expand-file-name
223 (file-name-nondirectory file-A) file-B)
224 file-B)
225 nil ; file-C
226 startup-hooks
227 'ediff-files))
71296446 228
0f0b0a86
KH
229;;;###autoload
230(defun ediff-files3 (file-A file-B file-C &optional startup-hooks)
231 "Run Ediff on three files, FILE-A, FILE-B, and FILE-C."
232 (interactive
233 (let ((dir-A (if ediff-use-last-dir
234 ediff-last-dir-A
235 default-directory))
236 dir-B dir-C f ff)
87c668b4 237 (list (setq f (ediff-read-file-name
50a07e18
MK
238 "File A to compare"
239 dir-A
240 (ediff-get-default-file-name)
241 'no-dirs))
71296446 242 (setq ff (ediff-read-file-name "File B to compare"
0f0b0a86
KH
243 (setq dir-B
244 (if ediff-use-last-dir
245 ediff-last-dir-B
246 (file-name-directory f)))
247 (progn
640ad75b
KS
248 (add-to-history 'file-name-history
249 (ediff-abbreviate-file-name
250 (expand-file-name
251 (file-name-nondirectory f)
252 dir-B)))
657f9cb8 253 (ediff-get-default-file-name f 1))))
71296446 254 (ediff-read-file-name "File C to compare"
0f0b0a86
KH
255 (setq dir-C (if ediff-use-last-dir
256 ediff-last-dir-C
257 (file-name-directory ff)))
258 (progn
640ad75b
KS
259 (add-to-history 'file-name-history
260 (ediff-abbreviate-file-name
261 (expand-file-name
262 (file-name-nondirectory ff)
263 dir-C)))
657f9cb8 264 (ediff-get-default-file-name ff 2)))
813f532d 265 )))
71296446 266 (ediff-files-internal file-A
b3a26225
RS
267 (if (file-directory-p file-B)
268 (expand-file-name
269 (file-name-nondirectory file-A) file-B)
270 file-B)
0f0b0a86
KH
271 (if (file-directory-p file-C)
272 (expand-file-name
273 (file-name-nondirectory file-A) file-C)
274 file-C)
275 startup-hooks
276 'ediff-files3))
813f532d 277
0f0b0a86
KH
278;;;###autoload
279(defalias 'ediff3 'ediff-files3)
813f532d 280
813f532d 281
71296446 282;; Visit FILE and arrange its buffer to Ediff's liking.
4ae69eac
MK
283;; FILE is actually a variable symbol that must contain a true file name.
284;; BUFFER-NAME is a variable symbol, which will get the buffer object into
285;; which FILE is read.
286;; LAST-DIR is the directory variable symbol where FILE's
3af0304a 287;; directory name should be returned. HOOKS-VAR is a variable symbol that will
4ae69eac
MK
288;; be assigned the hook to be executed after `ediff-startup' is finished.
289;; `ediff-find-file' arranges that the temp files it might create will be
290;; deleted.
0f0b0a86 291(defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var)
0f0b0a86 292 (let* ((file (symbol-value file-var))
bd698e98 293 (file-magic (ediff-filename-magic-p file))
0f0b0a86 294 (temp-file-name-prefix (file-name-nondirectory file)))
87c668b4
MK
295 (cond ((not (file-readable-p file))
296 (error "File `%s' does not exist or is not readable" file))
297 ((file-directory-p file)
298 (error "File `%s' is a directory" file)))
71296446 299
4ae69eac 300 ;; some of the commands, below, require full file name
0f0b0a86 301 (setq file (expand-file-name file))
71296446 302
0f0b0a86
KH
303 ;; Record the directory of the file
304 (if last-dir
305 (set last-dir (expand-file-name (file-name-directory file))))
71296446 306
0f0b0a86
KH
307 ;; Setup the buffer
308 (set buffer-name (find-file-noselect file))
71296446 309
e756eb9f 310 (ediff-with-current-buffer (symbol-value buffer-name)
0f0b0a86 311 (widen) ; Make sure the entire file is seen
ddc90f39
MK
312 (cond (file-magic ; file has a handler, such as jka-compr-handler or
313 ;;; ange-ftp-hook-function--arrange for temp file
0f0b0a86 314 (ediff-verify-file-buffer 'magic)
87c668b4
MK
315 (setq file
316 (ediff-make-temp-file
317 (current-buffer) temp-file-name-prefix))
086171bf 318 (set hooks-var (cons `(lambda () (delete-file ,file))
0f0b0a86
KH
319 (symbol-value hooks-var))))
320 ;; file processed via auto-mode-alist, a la uncompress.el
321 ((not (equal (file-truename file)
322 (file-truename (buffer-file-name))))
87c668b4
MK
323 (setq file
324 (ediff-make-temp-file
325 (current-buffer) temp-file-name-prefix))
086171bf 326 (set hooks-var (cons `(lambda () (delete-file ,file))
0f0b0a86
KH
327 (symbol-value hooks-var))))
328 (t ;; plain file---just check that the file matches the buffer
329 (ediff-verify-file-buffer))))
330 (set file-var file)))
331
328b4b70
MK
332;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer
333(defun ediff-files-internal (file-A file-B file-C startup-hooks job-name
334 &optional merge-buffer-file)
0f0b0a86 335 (let (buf-A buf-B buf-C)
50a07e18
MK
336 (if (string= file-A file-B)
337 (error "Files A and B are the same"))
338 (if (stringp file-C)
339 (or (and (string= file-A file-C) (error "Files A and C are the same"))
340 (and (string= file-B file-C) (error "Files B and C are the same"))))
87c668b4
MK
341 (message "Reading file %s ... " file-A)
342 ;;(sit-for 0)
0f0b0a86 343 (ediff-find-file 'file-A 'buf-A 'ediff-last-dir-A 'startup-hooks)
87c668b4
MK
344 (message "Reading file %s ... " file-B)
345 ;;(sit-for 0)
0f0b0a86 346 (ediff-find-file 'file-B 'buf-B 'ediff-last-dir-B 'startup-hooks)
87c668b4 347 (if (stringp file-C)
0f0b0a86 348 (progn
87c668b4
MK
349 (message "Reading file %s ... " file-C)
350 ;;(sit-for 0)
0f0b0a86
KH
351 (ediff-find-file
352 'file-C 'buf-C
353 (if (eq job-name 'ediff-merge-files-with-ancestor)
354 'ediff-last-dir-ancestor 'ediff-last-dir-C)
355 'startup-hooks)))
356 (ediff-setup buf-A file-A
357 buf-B file-B
358 buf-C file-C
359 startup-hooks
328b4b70
MK
360 (list (cons 'ediff-job-name job-name))
361 merge-buffer-file)))
71296446 362
0f0b0a86
KH
363
364;;;###autoload
365(defalias 'ediff 'ediff-files)
813f532d 366
3755bd32
MR
367;;;###autoload
368(defun ediff-backup (file)
369 "Run Ediff on FILE and its backup file.
370Uses the latest backup, if there are several numerical backups.
371If this file is a backup, `ediff' it with its original."
372 (interactive (list (read-file-name "Ediff (file with backup): ")))
373 ;; The code is taken from `diff-backup'.
374 (require 'diff)
375 (let (bak ori)
376 (if (backup-file-name-p file)
377 (setq bak file
378 ori (file-name-sans-versions file))
379 (setq bak (or (diff-latest-backup-file file)
380 (error "No backup found for %s" file))
381 ori file))
382 (ediff-files bak ori)))
813f532d
RS
383
384;;;###autoload
0f0b0a86 385(defun ediff-buffers (buffer-A buffer-B &optional startup-hooks job-name)
813f532d 386 "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B."
71296446 387 (interactive
0f0b0a86
KH
388 (let (bf)
389 (list (setq bf (read-buffer "Buffer A to compare: "
390 (ediff-other-buffer "") t))
391 (read-buffer "Buffer B to compare: "
392 (progn
393 ;; realign buffers so that two visible bufs will be
394 ;; at the top
395 (save-window-excursion (other-window 1))
396 (ediff-other-buffer bf))
397 t))))
0f0b0a86
KH
398 (or job-name (setq job-name 'ediff-buffers))
399 (ediff-buffers-internal buffer-A buffer-B nil startup-hooks job-name))
bbe6126c
MK
400
401;;;###autoload
402(defalias 'ebuffers 'ediff-buffers)
403
71296446 404
0f0b0a86
KH
405;;;###autoload
406(defun ediff-buffers3 (buffer-A buffer-B buffer-C
407 &optional startup-hooks job-name)
408 "Run Ediff on three buffers, BUFFER-A, BUFFER-B, and BUFFER-C."
71296446 409 (interactive
0f0b0a86
KH
410 (let (bf bff)
411 (list (setq bf (read-buffer "Buffer A to compare: "
412 (ediff-other-buffer "") t))
413 (setq bff (read-buffer "Buffer B to compare: "
414 (progn
415 ;; realign buffers so that two visible
416 ;; bufs will be at the top
417 (save-window-excursion (other-window 1))
418 (ediff-other-buffer bf))
419 t))
420 (read-buffer "Buffer C to compare: "
421 (progn
422 ;; realign buffers so that three visible
423 ;; bufs will be at the top
424 (save-window-excursion (other-window 1))
425 (ediff-other-buffer (list bf bff)))
426 t)
427 )))
0f0b0a86
KH
428 (or job-name (setq job-name 'ediff-buffers3))
429 (ediff-buffers-internal buffer-A buffer-B buffer-C startup-hooks job-name))
bbe6126c
MK
430
431;;;###autoload
432(defalias 'ebuffers3 'ediff-buffers3)
0f0b0a86 433
71296446
JB
434
435
328b4b70
MK
436;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer
437(defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name
438 &optional merge-buffer-file)
0f0b0a86
KH
439 (let* ((buf-A-file-name (buffer-file-name (get-buffer buf-A)))
440 (buf-B-file-name (buffer-file-name (get-buffer buf-B)))
441 (buf-C-is-alive (ediff-buffer-live-p buf-C))
442 (buf-C-file-name (if buf-C-is-alive
443 (buffer-file-name (get-buffer buf-B))))
444 file-A file-B file-C)
445 (if (not (ediff-buffer-live-p buf-A))
446 (error "Buffer %S doesn't exist" buf-A))
447 (if (not (ediff-buffer-live-p buf-B))
448 (error "Buffer %S doesn't exist" buf-B))
449 (let ((ediff-job-name job-name))
450 (if (and ediff-3way-comparison-job
451 (not buf-C-is-alive))
452 (error "Buffer %S doesn't exist" buf-C)))
453 (if (stringp buf-A-file-name)
454 (setq buf-A-file-name (file-name-nondirectory buf-A-file-name)))
455 (if (stringp buf-B-file-name)
456 (setq buf-B-file-name (file-name-nondirectory buf-B-file-name)))
457 (if (stringp buf-C-file-name)
458 (setq buf-C-file-name (file-name-nondirectory buf-C-file-name)))
71296446 459
4ae69eac
MK
460 (setq file-A (ediff-make-temp-file buf-A buf-A-file-name)
461 file-B (ediff-make-temp-file buf-B buf-B-file-name))
0f0b0a86 462 (if buf-C-is-alive
87c668b4 463 (setq file-C (ediff-make-temp-file buf-C buf-C-file-name)))
71296446 464
0f0b0a86
KH
465 (ediff-setup (get-buffer buf-A) file-A
466 (get-buffer buf-B) file-B
467 (if buf-C-is-alive (get-buffer buf-C))
468 file-C
086171bf
MK
469 (cons `(lambda ()
470 (delete-file ,file-A)
471 (delete-file ,file-B)
472 (if (stringp ,file-C) (delete-file ,file-C)))
0f0b0a86
KH
473 startup-hooks)
474 (list (cons 'ediff-job-name job-name))
328b4b70 475 merge-buffer-file)))
87c668b4
MK
476
477
478;;; Directory and file group operations
479
480;; Get appropriate default name for directory:
481;; If ediff-use-last-dir, use ediff-last-dir-A.
482;; In dired mode, use the directory that is under the point (if any);
483;; otherwise, use default-directory
484(defun ediff-get-default-directory-name ()
485 (cond (ediff-use-last-dir ediff-last-dir-A)
486 ((eq major-mode 'dired-mode)
487 (let ((f (dired-get-filename nil 'noerror)))
488 (if (and (stringp f) (file-directory-p f))
489 f
490 default-directory)))
491 (t default-directory)))
492
493
494;;;###autoload
495(defun ediff-directories (dir1 dir2 regexp)
496 "Run Ediff on a pair of directories, DIR1 and DIR2, comparing files that have
6dfd1bcc
MK
497the same name in both. The third argument, REGEXP, is nil or a regular
498expression; only file names that match the regexp are considered."
87c668b4
MK
499 (interactive
500 (let ((dir-A (ediff-get-default-directory-name))
81ef8960 501 (default-regexp (eval ediff-default-filtering-regexp))
87c668b4 502 f)
ec6aebe8
MK
503 (list (setq f (read-directory-name
504 "Directory A to compare:" dir-A nil 'must-match))
505 (read-directory-name "Directory B to compare:"
506 (if ediff-use-last-dir
507 ediff-last-dir-B
508 (ediff-strip-last-dir f))
509 nil 'must-match)
81ef8960
MK
510 (read-string
511 (if (stringp default-regexp)
512 (format "Filter through regular expression (default %s): "
513 default-regexp)
514 "Filter through regular expression: ")
515 nil
516 'ediff-filtering-regexp-history
517 (eval ediff-default-filtering-regexp))
87c668b4
MK
518 )))
519 (ediff-directories-internal
520 dir1 dir2 nil regexp 'ediff-files 'ediff-directories
521 ))
522
523;;;###autoload
524(defalias 'edirs 'ediff-directories)
525
526
527;;;###autoload
528(defun ediff-directory-revisions (dir1 regexp)
529 "Run Ediff on a directory, DIR1, comparing its files with their revisions.
530The second argument, REGEXP, is a regular expression that filters the file
3af0304a 531names. Only the files that are under revision control are taken into account."
87c668b4 532 (interactive
81ef8960
MK
533 (let ((dir-A (ediff-get-default-directory-name))
534 (default-regexp (eval ediff-default-filtering-regexp))
535 )
ec6aebe8
MK
536 (list (read-directory-name
537 "Directory to compare with revision:" dir-A nil 'must-match)
81ef8960
MK
538 (read-string
539 (if (stringp default-regexp)
540 (format "Filter through regular expression (default %s): "
541 default-regexp)
542 "Filter through regular expression: ")
543 nil
544 'ediff-filtering-regexp-history
545 (eval ediff-default-filtering-regexp))
87c668b4
MK
546 )))
547 (ediff-directory-revisions-internal
548 dir1 regexp 'ediff-revision 'ediff-directory-revisions
549 ))
550
551;;;###autoload
552(defalias 'edir-revisions 'ediff-directory-revisions)
553
554
555;;;###autoload
556(defun ediff-directories3 (dir1 dir2 dir3 regexp)
557 "Run Ediff on three directories, DIR1, DIR2, and DIR3, comparing files that
6dfd1bcc
MK
558have the same name in all three. The last argument, REGEXP, is nil or a
559regular expression; only file names that match the regexp are considered."
560
87c668b4
MK
561 (interactive
562 (let ((dir-A (ediff-get-default-directory-name))
81ef8960 563 (default-regexp (eval ediff-default-filtering-regexp))
87c668b4 564 f)
ec6aebe8
MK
565 (list (setq f (read-directory-name "Directory A to compare:" dir-A nil))
566 (setq f (read-directory-name "Directory B to compare:"
567 (if ediff-use-last-dir
568 ediff-last-dir-B
569 (ediff-strip-last-dir f))
570 nil 'must-match))
571 (read-directory-name "Directory C to compare:"
572 (if ediff-use-last-dir
573 ediff-last-dir-C
574 (ediff-strip-last-dir f))
575 nil 'must-match)
81ef8960
MK
576 (read-string
577 (if (stringp default-regexp)
578 (format "Filter through regular expression (default %s): "
579 default-regexp)
580 "Filter through regular expression: ")
581 nil
582 'ediff-filtering-regexp-history
583 (eval ediff-default-filtering-regexp))
87c668b4
MK
584 )))
585 (ediff-directories-internal
586 dir1 dir2 dir3 regexp 'ediff-files3 'ediff-directories3
587 ))
588
589;;;###autoload
590(defalias 'edirs3 'ediff-directories3)
591
592;;;###autoload
3af0304a 593(defun ediff-merge-directories (dir1 dir2 regexp &optional merge-autostore-dir)
87c668b4 594 "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have
6dfd1bcc
MK
595the same name in both. The third argument, REGEXP, is nil or a regular
596expression; only file names that match the regexp are considered."
87c668b4
MK
597 (interactive
598 (let ((dir-A (ediff-get-default-directory-name))
81ef8960 599 (default-regexp (eval ediff-default-filtering-regexp))
87c668b4 600 f)
ec6aebe8
MK
601 (list (setq f (read-directory-name "Directory A to merge:"
602 dir-A nil 'must-match))
603 (read-directory-name "Directory B to merge:"
604 (if ediff-use-last-dir
605 ediff-last-dir-B
606 (ediff-strip-last-dir f))
607 nil 'must-match)
81ef8960
MK
608 (read-string
609 (if (stringp default-regexp)
610 (format "Filter through regular expression (default %s): "
611 default-regexp)
612 "Filter through regular expression: ")
613 nil
614 'ediff-filtering-regexp-history
615 (eval ediff-default-filtering-regexp))
87c668b4
MK
616 )))
617 (ediff-directories-internal
618 dir1 dir2 nil regexp 'ediff-merge-files 'ediff-merge-directories
3af0304a 619 nil merge-autostore-dir
87c668b4
MK
620 ))
621
622;;;###autoload
623(defalias 'edirs-merge 'ediff-merge-directories)
624
625;;;###autoload
3af0304a
MK
626(defun ediff-merge-directories-with-ancestor (dir1 dir2 ancestor-dir regexp
627 &optional
628 merge-autostore-dir)
bbe6126c 629 "Merge files in directories DIR1 and DIR2 using files in ANCESTOR-DIR as ancestors.
3af0304a 630Ediff merges files that have identical names in DIR1, DIR2. If a pair of files
bbe6126c 631in DIR1 and DIR2 doesn't have an ancestor in ANCESTOR-DIR, Ediff will merge
6dfd1bcc
MK
632without ancestor. The fourth argument, REGEXP, is nil or a regular expression;
633only file names that match the regexp are considered."
87c668b4
MK
634 (interactive
635 (let ((dir-A (ediff-get-default-directory-name))
81ef8960 636 (default-regexp (eval ediff-default-filtering-regexp))
87c668b4 637 f)
ec6aebe8
MK
638 (list (setq f (read-directory-name "Directory A to merge:" dir-A nil))
639 (setq f (read-directory-name "Directory B to merge:"
87c668b4 640 (if ediff-use-last-dir
71296446 641 ediff-last-dir-B
87c668b4 642 (ediff-strip-last-dir f))
ec6aebe8
MK
643 nil 'must-match))
644 (read-directory-name "Ancestor directory:"
87c668b4 645 (if ediff-use-last-dir
71296446 646 ediff-last-dir-C
87c668b4 647 (ediff-strip-last-dir f))
ec6aebe8 648 nil 'must-match)
81ef8960
MK
649 (read-string
650 (if (stringp default-regexp)
651 (format "Filter through regular expression (default %s): "
652 default-regexp)
653 "Filter through regular expression: ")
654 nil
655 'ediff-filtering-regexp-history
656 (eval ediff-default-filtering-regexp))
87c668b4
MK
657 )))
658 (ediff-directories-internal
bbe6126c 659 dir1 dir2 ancestor-dir regexp
87c668b4 660 'ediff-merge-files-with-ancestor 'ediff-merge-directories-with-ancestor
3af0304a 661 nil merge-autostore-dir
87c668b4
MK
662 ))
663
664;;;###autoload
3af0304a
MK
665(defun ediff-merge-directory-revisions (dir1 regexp
666 &optional merge-autostore-dir)
87c668b4
MK
667 "Run Ediff on a directory, DIR1, merging its files with their revisions.
668The second argument, REGEXP, is a regular expression that filters the file
3af0304a 669names. Only the files that are under revision control are taken into account."
87c668b4 670 (interactive
81ef8960
MK
671 (let ((dir-A (ediff-get-default-directory-name))
672 (default-regexp (eval ediff-default-filtering-regexp))
673 )
ec6aebe8
MK
674 (list (read-directory-name
675 "Directory to merge with revisions:" dir-A nil 'must-match)
81ef8960
MK
676 (read-string
677 (if (stringp default-regexp)
678 (format "Filter through regular expression (default %s): "
679 default-regexp)
680 "Filter through regular expression: ")
681 nil
682 'ediff-filtering-regexp-history
683 (eval ediff-default-filtering-regexp))
87c668b4
MK
684 )))
685 (ediff-directory-revisions-internal
686 dir1 regexp 'ediff-merge-revisions 'ediff-merge-directory-revisions
3af0304a 687 nil merge-autostore-dir
87c668b4
MK
688 ))
689
690;;;###autoload
691(defalias 'edir-merge-revisions 'ediff-merge-directory-revisions)
692
693;;;###autoload
3af0304a
MK
694(defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp
695 &optional
696 merge-autostore-dir)
87c668b4
MK
697 "Run Ediff on a directory, DIR1, merging its files with their revisions and ancestors.
698The second argument, REGEXP, is a regular expression that filters the file
3af0304a 699names. Only the files that are under revision control are taken into account."
87c668b4 700 (interactive
81ef8960
MK
701 (let ((dir-A (ediff-get-default-directory-name))
702 (default-regexp (eval ediff-default-filtering-regexp))
703 )
ec6aebe8
MK
704 (list (read-directory-name
705 "Directory to merge with revisions and ancestors:"
706 dir-A nil 'must-match)
81ef8960
MK
707 (read-string
708 (if (stringp default-regexp)
709 (format "Filter through regular expression (default %s): "
710 default-regexp)
711 "Filter through regular expression: ")
712 nil
713 'ediff-filtering-regexp-history
714 (eval ediff-default-filtering-regexp))
87c668b4
MK
715 )))
716 (ediff-directory-revisions-internal
717 dir1 regexp 'ediff-merge-revisions-with-ancestor
718 'ediff-merge-directory-revisions-with-ancestor
3af0304a 719 nil merge-autostore-dir
87c668b4
MK
720 ))
721
722;;;###autoload
723(defalias
724 'edir-merge-revisions-with-ancestor
71296446 725 'ediff-merge-directory-revisions-with-ancestor)
87c668b4
MK
726
727;;;###autoload
728(defalias 'edirs-merge-with-ancestor 'ediff-merge-directories-with-ancestor)
729
730;; Run ediff-action (ediff-files, ediff-merge, ediff-merge-with-ancestors)
731;; on a pair of directories (three directories, in case of ancestor).
6dfd1bcc
MK
732;; The third argument, REGEXP, is nil or a regular expression;
733;; only file names that match the regexp are considered.
87c668b4 734;; JOBNAME is the symbol indicating the meta-job to be performed.
3af0304a 735;; MERGE-AUTOSTORE-DIR is the directory in which to store merged files.
71296446 736(defun ediff-directories-internal (dir1 dir2 dir3 regexp action jobname
3af0304a
MK
737 &optional startup-hooks
738 merge-autostore-dir)
87c668b4
MK
739 (if (stringp dir3)
740 (setq dir3 (if (file-directory-p dir3) dir3 (file-name-directory dir3))))
741
742 (cond ((string= dir1 dir2)
743 (error "Directories A and B are the same: %s" dir1))
744 ((and (eq jobname 'ediff-directories3)
745 (string= dir1 dir3))
746 (error "Directories A and C are the same: %s" dir1))
747 ((and (eq jobname 'ediff-directories3)
748 (string= dir2 dir3))
749 (error "Directories B and C are the same: %s" dir1)))
750
3af0304a
MK
751 (if merge-autostore-dir
752 (or (stringp merge-autostore-dir)
753 (error "%s: Directory for storing merged files must be a string"
754 jobname)))
71296446 755 (let (;; dir-diff-struct is of the form (common-list diff-list)
c3912d54
MK
756 ;; It is a structure where ediff-intersect-directories returns
757 ;; commonalities and differences among directories
758 dir-diff-struct
759 meta-buf)
3af0304a
MK
760 (if (and ediff-autostore-merges
761 (ediff-merge-metajob jobname)
762 (not merge-autostore-dir))
71296446 763 (setq merge-autostore-dir
ec6aebe8 764 (read-directory-name "Save merged files in directory: "
3af0304a 765 (if ediff-use-last-dir
92c51e07
MK
766 ediff-last-merge-autostore-dir
767 (ediff-strip-last-dir dir1))
3af0304a
MK
768 nil
769 'must-match)))
92c51e07 770 ;; verify we are not merging into an orig directory
3af0304a 771 (if merge-autostore-dir
92c51e07 772 (cond ((and (stringp dir1) (string= merge-autostore-dir dir1))
3af0304a
MK
773 (or (y-or-n-p
774 "Directory for saving merged files = Directory A. Sure? ")
92c51e07
MK
775 (error "Directory merge aborted")))
776 ((and (stringp dir2) (string= merge-autostore-dir dir2))
3af0304a
MK
777 (or (y-or-n-p
778 "Directory for saving merged files = Directory B. Sure? ")
92c51e07
MK
779 (error "Directory merge aborted")))
780 ((and (stringp dir3) (string= merge-autostore-dir dir3))
781 (or (y-or-n-p
3af0304a 782 "Directory for saving merged files = Ancestor Directory. Sure? ")
92c51e07 783 (error "Directory merge aborted")))))
71296446
JB
784
785 (setq dir-diff-struct (ediff-intersect-directories
c3912d54
MK
786 jobname
787 regexp dir1 dir2 dir3 merge-autostore-dir))
87c668b4
MK
788 (setq startup-hooks
789 ;; this sets various vars in the meta buffer inside
790 ;; ediff-prepare-meta-buffer
086171bf
MK
791 (cons `(lambda ()
792 ;; tell what to do if the user clicks on a session record
793 (setq ediff-session-action-function (quote ,action))
71296446 794 ;; set ediff-dir-difference-list
c3912d54
MK
795 (setq ediff-dir-difference-list
796 (cdr (quote ,dir-diff-struct))))
87c668b4 797 startup-hooks))
71296446 798 (setq meta-buf (ediff-prepare-meta-buffer
bbe6126c 799 'ediff-filegroup-action
c3912d54 800 (car dir-diff-struct)
87c668b4
MK
801 "*Ediff Session Group Panel"
802 'ediff-redraw-directory-group-buffer
803 jobname
804 startup-hooks))
805 (ediff-show-meta-buffer meta-buf)
806 ))
807
3af0304a
MK
808;; MERGE-AUTOSTORE-DIR can be given to tell ediff where to store the merged
809;; files
71296446 810(defun ediff-directory-revisions-internal (dir1 regexp action jobname
3af0304a
MK
811 &optional startup-hooks
812 merge-autostore-dir)
87c668b4 813 (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1)))
92c51e07 814
3af0304a
MK
815 (if merge-autostore-dir
816 (or (stringp merge-autostore-dir)
817 (error "%S: Directory for storing merged files must be a string"
818 jobname)))
819 (let (file-list meta-buf)
71296446 820 (if (and ediff-autostore-merges
3af0304a
MK
821 (ediff-merge-metajob jobname)
822 (not merge-autostore-dir))
71296446 823 (setq merge-autostore-dir
ec6aebe8 824 (read-directory-name "Save merged files in directory: "
3af0304a
MK
825 (if ediff-use-last-dir
826 ediff-last-merge-autostore-dir
827 (ediff-strip-last-dir dir1))
828 nil
829 'must-match)))
92c51e07 830 ;; verify merge-autostore-dir != dir1
3af0304a 831 (if (and merge-autostore-dir
92c51e07
MK
832 (stringp dir1)
833 (string= merge-autostore-dir dir1))
834 (or (y-or-n-p
3af0304a 835 "Directory for saving merged file = directory A. Sure? ")
92c51e07 836 (error "Merge of directory revisions aborted")))
71296446 837
87c668b4 838 (setq file-list
92c51e07
MK
839 (ediff-get-directory-files-under-revision
840 jobname regexp dir1 merge-autostore-dir))
87c668b4
MK
841 (setq startup-hooks
842 ;; this sets various vars in the meta buffer inside
843 ;; ediff-prepare-meta-buffer
086171bf
MK
844 (cons `(lambda ()
845 ;; tell what to do if the user clicks on a session record
846 (setq ediff-session-action-function (quote ,action)))
87c668b4 847 startup-hooks))
71296446 848 (setq meta-buf (ediff-prepare-meta-buffer
bbe6126c 849 'ediff-filegroup-action
87c668b4
MK
850 file-list
851 "*Ediff Session Group Panel"
852 'ediff-redraw-directory-group-buffer
853 jobname
854 startup-hooks))
855 (ediff-show-meta-buffer meta-buf)
856 ))
0f0b0a86
KH
857
858
859;;; Compare regions and windows
860
861;;;###autoload
f1a5512a
KH
862(defun ediff-windows-wordwise (dumb-mode &optional wind-A wind-B startup-hooks)
863 "Compare WIND-A and WIND-B, which are selected by clicking, wordwise.
0f0b0a86
KH
864With prefix argument, DUMB-MODE, or on a non-windowing display, works as
865follows:
866If WIND-A is nil, use selected window.
867If WIND-B is nil, use window next to WIND-A."
0f0b0a86 868 (interactive "P")
f1a5512a
KH
869 (ediff-windows dumb-mode wind-A wind-B
870 startup-hooks 'ediff-windows-wordwise 'word-mode))
71296446 871
f1a5512a
KH
872;;;###autoload
873(defun ediff-windows-linewise (dumb-mode &optional wind-A wind-B startup-hooks)
874 "Compare WIND-A and WIND-B, which are selected by clicking, linewise.
875With prefix argument, DUMB-MODE, or on a non-windowing display, works as
876follows:
877If WIND-A is nil, use selected window.
878If WIND-B is nil, use window next to WIND-A."
879 (interactive "P")
880 (ediff-windows dumb-mode wind-A wind-B
881 startup-hooks 'ediff-windows-linewise nil))
71296446 882
f1a5512a
KH
883;; Compare WIND-A and WIND-B, which are selected by clicking.
884;; With prefix argument, DUMB-MODE, or on a non-windowing display,
885;; works as follows:
886;; If WIND-A is nil, use selected window.
887;; If WIND-B is nil, use window next to WIND-A.
888(defun ediff-windows (dumb-mode wind-A wind-B startup-hooks job-name word-mode)
889 (if (or dumb-mode (not (ediff-window-display-p)))
0f0b0a86
KH
890 (setq wind-A (ediff-get-next-window wind-A nil)
891 wind-B (ediff-get-next-window wind-B wind-A))
892 (setq wind-A (ediff-get-window-by-clicking wind-A nil 1)
893 wind-B (ediff-get-window-by-clicking wind-B wind-A 2)))
71296446 894
0f0b0a86
KH
895 (let ((buffer-A (window-buffer wind-A))
896 (buffer-B (window-buffer wind-B))
897 beg-A end-A beg-B end-B)
71296446 898
0f0b0a86
KH
899 (save-excursion
900 (save-window-excursion
a7acbbe4 901 (sit-for 0) ; sync before using window-start/end -- a precaution
0f0b0a86
KH
902 (select-window wind-A)
903 (setq beg-A (window-start)
904 end-A (window-end))
905 (select-window wind-B)
906 (setq beg-B (window-start)
907 end-B (window-end))))
50a07e18
MK
908 (setq buffer-A
909 (ediff-clone-buffer-for-window-comparison
c3912d54 910 buffer-A wind-A "-Window.A-")
50a07e18
MK
911 buffer-B
912 (ediff-clone-buffer-for-window-comparison
c3912d54 913 buffer-B wind-B "-Window.B-"))
0f0b0a86
KH
914 (ediff-regions-internal
915 buffer-A beg-A end-A buffer-B beg-B end-B
ddc90f39 916 startup-hooks job-name word-mode nil)))
71296446 917
50a07e18 918
0f0b0a86 919;;;###autoload
f1a5512a 920(defun ediff-regions-wordwise (buffer-A buffer-B &optional startup-hooks)
50a07e18
MK
921 "Run Ediff on a pair of regions in specified buffers.
922Regions \(i.e., point and mark\) are assumed to be set in advance except
923for the second region in the case both regions are from the same buffer.
924In such a case the user is asked to interactively establish the second
925region.
0f0b0a86 926This function is effective only for relatively small regions, up to 200
3af0304a 927lines. For large regions, use `ediff-regions-linewise'."
71296446 928 (interactive
0f0b0a86
KH
929 (let (bf)
930 (list (setq bf (read-buffer "Region's A buffer: "
931 (ediff-other-buffer "") t))
932 (read-buffer "Region's B buffer: "
933 (progn
934 ;; realign buffers so that two visible bufs will be
935 ;; at the top
936 (save-window-excursion (other-window 1))
937 (ediff-other-buffer bf))
938 t))))
939 (if (not (ediff-buffer-live-p buffer-A))
940 (error "Buffer %S doesn't exist" buffer-A))
941 (if (not (ediff-buffer-live-p buffer-B))
942 (error "Buffer %S doesn't exist" buffer-B))
71296446
JB
943
944
50a07e18 945 (let ((buffer-A
c3912d54 946 (ediff-clone-buffer-for-region-comparison buffer-A "-Region.A-"))
50a07e18 947 (buffer-B
c3912d54 948 (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-"))
50a07e18 949 reg-A-beg reg-A-end reg-B-beg reg-B-end)
0f0b0a86
KH
950 (save-excursion
951 (set-buffer buffer-A)
952 (setq reg-A-beg (region-beginning)
953 reg-A-end (region-end))
954 (set-buffer buffer-B)
955 (setq reg-B-beg (region-beginning)
956 reg-B-end (region-end)))
71296446 957
0f0b0a86
KH
958 (ediff-regions-internal
959 (get-buffer buffer-A) reg-A-beg reg-A-end
960 (get-buffer buffer-B) reg-B-beg reg-B-end
ddc90f39 961 startup-hooks 'ediff-regions-wordwise 'word-mode nil)))
71296446 962
0f0b0a86 963;;;###autoload
f1a5512a 964(defun ediff-regions-linewise (buffer-A buffer-B &optional startup-hooks)
50a07e18
MK
965 "Run Ediff on a pair of regions in specified buffers.
966Regions \(i.e., point and mark\) are assumed to be set in advance except
967for the second region in the case both regions are from the same buffer.
968In such a case the user is asked to interactively establish the second
969region.
0f0b0a86
KH
970Each region is enlarged to contain full lines.
971This function is effective for large regions, over 100-200
3af0304a 972lines. For small regions, use `ediff-regions-wordwise'."
71296446 973 (interactive
0f0b0a86
KH
974 (let (bf)
975 (list (setq bf (read-buffer "Region A's buffer: "
976 (ediff-other-buffer "") t))
977 (read-buffer "Region B's buffer: "
978 (progn
979 ;; realign buffers so that two visible bufs will be
980 ;; at the top
981 (save-window-excursion (other-window 1))
982 (ediff-other-buffer bf))
983 t))))
fcbadd58 984 (if (not (ediff-buffer-live-p buffer-A))
0f0b0a86 985 (error "Buffer %S doesn't exist" buffer-A))
fcbadd58 986 (if (not (ediff-buffer-live-p buffer-B))
0f0b0a86 987 (error "Buffer %S doesn't exist" buffer-B))
71296446 988
50a07e18 989 (let ((buffer-A
c3912d54 990 (ediff-clone-buffer-for-region-comparison buffer-A "-Region.A-"))
50a07e18 991 (buffer-B
c3912d54 992 (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-"))
50a07e18 993 reg-A-beg reg-A-end reg-B-beg reg-B-end)
0f0b0a86
KH
994 (save-excursion
995 (set-buffer buffer-A)
996 (setq reg-A-beg (region-beginning)
997 reg-A-end (region-end))
998 ;; enlarge the region to hold full lines
71296446 999 (goto-char reg-A-beg)
0f0b0a86
KH
1000 (beginning-of-line)
1001 (setq reg-A-beg (point))
71296446 1002 (goto-char reg-A-end)
0f0b0a86
KH
1003 (end-of-line)
1004 (or (eobp) (forward-char)) ; include the newline char
1005 (setq reg-A-end (point))
71296446 1006
0f0b0a86
KH
1007 (set-buffer buffer-B)
1008 (setq reg-B-beg (region-beginning)
1009 reg-B-end (region-end))
1010 ;; enlarge the region to hold full lines
71296446 1011 (goto-char reg-B-beg)
0f0b0a86
KH
1012 (beginning-of-line)
1013 (setq reg-B-beg (point))
71296446 1014 (goto-char reg-B-end)
0f0b0a86
KH
1015 (end-of-line)
1016 (or (eobp) (forward-char)) ; include the newline char
1017 (setq reg-B-end (point))
1018 ) ; save excursion
71296446 1019
0f0b0a86
KH
1020 (ediff-regions-internal
1021 (get-buffer buffer-A) reg-A-beg reg-A-end
1022 (get-buffer buffer-B) reg-B-beg reg-B-end
1e70790f 1023 startup-hooks 'ediff-regions-linewise nil nil))) ; no word mode
71296446 1024
0f0b0a86 1025;; compare region beg-A to end-A of buffer-A
71296446 1026;; to regions beg-B -- end-B in buffer-B.
0f0b0a86 1027(defun ediff-regions-internal (buffer-A beg-A end-A buffer-B beg-B end-B
ddc90f39
MK
1028 startup-hooks job-name word-mode
1029 setup-parameters)
0f0b0a86
KH
1030 (let ((tmp-buffer (get-buffer-create ediff-tmp-buffer))
1031 overl-A overl-B
1032 file-A file-B)
71296446 1033
0f0b0a86 1034 ;; in case beg/end-A/B aren't markers--make them into markers
e756eb9f 1035 (ediff-with-current-buffer buffer-A
0f0b0a86
KH
1036 (setq beg-A (move-marker (make-marker) beg-A)
1037 end-A (move-marker (make-marker) end-A)))
e756eb9f 1038 (ediff-with-current-buffer buffer-B
0f0b0a86
KH
1039 (setq beg-B (move-marker (make-marker) beg-B)
1040 end-B (move-marker (make-marker) end-B)))
71296446 1041
0f0b0a86
KH
1042 ;; make file-A
1043 (if word-mode
1044 (ediff-wordify beg-A end-A buffer-A tmp-buffer)
1045 (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer))
87c668b4 1046 (setq file-A (ediff-make-temp-file tmp-buffer "regA"))
71296446 1047
0f0b0a86
KH
1048 ;; make file-B
1049 (if word-mode
1050 (ediff-wordify beg-B end-B buffer-B tmp-buffer)
1051 (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer))
87c668b4 1052 (setq file-B (ediff-make-temp-file tmp-buffer "regB"))
71296446 1053
0f0b0a86
KH
1054 (setq overl-A (ediff-make-bullet-proof-overlay beg-A end-A buffer-A))
1055 (setq overl-B (ediff-make-bullet-proof-overlay beg-B end-B buffer-B))
1056 (ediff-setup buffer-A file-A
1057 buffer-B file-B
1058 nil nil ; buffer & file C
086171bf
MK
1059 (cons `(lambda ()
1060 (delete-file ,file-A)
1061 (delete-file ,file-B))
813f532d 1062 startup-hooks)
ddc90f39
MK
1063 (append
1064 (list (cons 'ediff-word-mode word-mode)
1065 (cons 'ediff-narrow-bounds (list overl-A overl-B))
1066 (cons 'ediff-job-name job-name))
328b4b70 1067 setup-parameters))
0f0b0a86 1068 ))
71296446
JB
1069
1070
0f0b0a86 1071;;; Merge files and buffers
71296446 1072
0f0b0a86
KH
1073;;;###autoload
1074(defalias 'ediff-merge 'ediff-merge-files)
71296446 1075
0f0b0a86
KH
1076(defsubst ediff-merge-on-startup ()
1077 (ediff-do-merge 0)
17561e4f
MK
1078 ;; Can't remember why this is here, but it may cause the automatically merged
1079 ;; buffer to be lost. So, keep the buffer modified.
1080 ;;(ediff-with-current-buffer ediff-buffer-C
1081 ;; (set-buffer-modified-p nil))
1082 )
0f0b0a86
KH
1083
1084;;;###autoload
328b4b70
MK
1085(defun ediff-merge-files (file-A file-B
1086 ;; MERGE-BUFFER-FILE is the file to be
71296446 1087 ;; associated with the merge buffer
328b4b70 1088 &optional startup-hooks merge-buffer-file)
0f0b0a86
KH
1089 "Merge two files without ancestor."
1090 (interactive
1091 (let ((dir-A (if ediff-use-last-dir
1092 ediff-last-dir-A
1093 default-directory))
1094 dir-B f)
87c668b4 1095 (list (setq f (ediff-read-file-name
50a07e18
MK
1096 "File A to merge"
1097 dir-A
1098 (ediff-get-default-file-name)
1099 'no-dirs))
71296446 1100 (ediff-read-file-name "File B to merge"
0f0b0a86
KH
1101 (setq dir-B
1102 (if ediff-use-last-dir
71296446 1103 ediff-last-dir-B
0f0b0a86
KH
1104 (file-name-directory f)))
1105 (progn
640ad75b
KS
1106 (add-to-history 'file-name-history
1107 (ediff-abbreviate-file-name
1108 (expand-file-name
1109 (file-name-nondirectory f)
1110 dir-B)))
657f9cb8 1111 (ediff-get-default-file-name f 1)))
0f0b0a86
KH
1112 )))
1113 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
71296446 1114 (ediff-files-internal file-A
0f0b0a86
KH
1115 (if (file-directory-p file-B)
1116 (expand-file-name
1117 (file-name-nondirectory file-A) file-B)
1118 file-B)
1119 nil ; file-C
1120 startup-hooks
328b4b70
MK
1121 'ediff-merge-files
1122 merge-buffer-file))
71296446 1123
0f0b0a86
KH
1124;;;###autoload
1125(defun ediff-merge-files-with-ancestor (file-A file-B file-ancestor
328b4b70
MK
1126 &optional
1127 startup-hooks
1128 ;; MERGE-BUFFER-FILE is the file
1129 ;; to be associated with the
1130 ;; merge buffer
1131 merge-buffer-file)
0f0b0a86
KH
1132 "Merge two files with ancestor."
1133 (interactive
1134 (let ((dir-A (if ediff-use-last-dir
1135 ediff-last-dir-A
1136 default-directory))
1137 dir-B dir-ancestor f ff)
87c668b4 1138 (list (setq f (ediff-read-file-name
50a07e18
MK
1139 "File A to merge"
1140 dir-A
1141 (ediff-get-default-file-name)
1142 'no-dirs))
71296446 1143 (setq ff (ediff-read-file-name "File B to merge"
0f0b0a86
KH
1144 (setq dir-B
1145 (if ediff-use-last-dir
71296446 1146 ediff-last-dir-B
0f0b0a86
KH
1147 (file-name-directory f)))
1148 (progn
640ad75b
KS
1149 (add-to-history 'file-name-history
1150 (ediff-abbreviate-file-name
1151 (expand-file-name
1152 (file-name-nondirectory f)
1153 dir-B)))
657f9cb8 1154 (ediff-get-default-file-name f 1))))
71296446 1155 (ediff-read-file-name "Ancestor file"
0f0b0a86
KH
1156 (setq dir-ancestor
1157 (if ediff-use-last-dir
1158 ediff-last-dir-ancestor
1159 (file-name-directory ff)))
1160 (progn
640ad75b
KS
1161 (add-to-history 'file-name-history
1162 (ediff-abbreviate-file-name
1163 (expand-file-name
1164 (file-name-nondirectory ff)
1165 dir-ancestor)))
657f9cb8 1166 (ediff-get-default-file-name ff 2)))
0f0b0a86
KH
1167 )))
1168 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
71296446 1169 (ediff-files-internal file-A
0f0b0a86
KH
1170 (if (file-directory-p file-B)
1171 (expand-file-name
1172 (file-name-nondirectory file-A) file-B)
1173 file-B)
1174 file-ancestor
1175 startup-hooks
328b4b70
MK
1176 'ediff-merge-files-with-ancestor
1177 merge-buffer-file))
71296446 1178
0f0b0a86
KH
1179;;;###autoload
1180(defalias 'ediff-merge-with-ancestor 'ediff-merge-files-with-ancestor)
71296446 1181
0f0b0a86 1182;;;###autoload
328b4b70
MK
1183(defun ediff-merge-buffers (buffer-A buffer-B
1184 &optional
1185 ;; MERGE-BUFFER-FILE is the file to be
1186 ;; associated with the merge buffer
1187 startup-hooks job-name merge-buffer-file)
0f0b0a86 1188 "Merge buffers without ancestor."
71296446 1189 (interactive
0f0b0a86
KH
1190 (let (bf)
1191 (list (setq bf (read-buffer "Buffer A to merge: "
1192 (ediff-other-buffer "") t))
1193 (read-buffer "Buffer B to merge: "
1194 (progn
1195 ;; realign buffers so that two visible bufs will be
1196 ;; at the top
1197 (save-window-excursion (other-window 1))
1198 (ediff-other-buffer bf))
1199 t))))
71296446 1200
0f0b0a86
KH
1201 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
1202 (or job-name (setq job-name 'ediff-merge-buffers))
1203 (ediff-buffers-internal
328b4b70 1204 buffer-A buffer-B nil startup-hooks job-name merge-buffer-file))
71296446 1205
0f0b0a86 1206;;;###autoload
328b4b70
MK
1207(defun ediff-merge-buffers-with-ancestor (buffer-A buffer-B buffer-ancestor
1208 &optional
1209 startup-hooks
1210 job-name
1211 ;; MERGE-BUFFER-FILE is the
1212 ;; file to be associated
1213 ;; with the merge buffer
1214 merge-buffer-file)
0f0b0a86 1215 "Merge buffers with ancestor."
71296446 1216 (interactive
0f0b0a86
KH
1217 (let (bf bff)
1218 (list (setq bf (read-buffer "Buffer A to merge: "
1219 (ediff-other-buffer "") t))
1220 (setq bff (read-buffer "Buffer B to merge: "
1221 (progn
1222 ;; realign buffers so that two visible
1223 ;; bufs will be at the top
1224 (save-window-excursion (other-window 1))
1225 (ediff-other-buffer bf))
1226 t))
1227 (read-buffer "Ancestor buffer: "
1228 (progn
1229 ;; realign buffers so that three visible
1230 ;; bufs will be at the top
1231 (save-window-excursion (other-window 1))
1232 (ediff-other-buffer (list bf bff)))
1233 t)
1234 )))
71296446 1235
0f0b0a86
KH
1236 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
1237 (or job-name (setq job-name 'ediff-merge-buffers-with-ancestor))
1238 (ediff-buffers-internal
328b4b70 1239 buffer-A buffer-B buffer-ancestor startup-hooks job-name merge-buffer-file))
71296446 1240
0f0b0a86
KH
1241
1242;;;###autoload
328b4b70
MK
1243(defun ediff-merge-revisions (&optional file startup-hooks merge-buffer-file)
1244 ;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer
0f0b0a86 1245 "Run Ediff by merging two revisions of a file.
87c668b4
MK
1246The file is the optional FILE argument or the file visited by the current
1247buffer."
1248 (interactive)
87c668b4 1249 (if (stringp file) (find-file file))
4ae69eac 1250 (let (rev1 rev2)
87c668b4
MK
1251 (setq rev1
1252 (read-string
1253 (format
5b76833f 1254 "Version 1 to merge (default %s's working version): "
87c668b4
MK
1255 (if (stringp file)
1256 (file-name-nondirectory file) "current buffer")))
1257 rev2
1258 (read-string
1259 (format
5b76833f 1260 "Version 2 to merge (default %s): "
87c668b4
MK
1261 (if (stringp file)
1262 (file-name-nondirectory file) "current buffer"))))
4ae69eac
MK
1263 (ediff-load-version-control)
1264 ;; ancestor-revision=nil
1265 (funcall
92c51e07 1266 (intern (format "ediff-%S-merge-internal" ediff-version-control-package))
328b4b70 1267 rev1 rev2 nil startup-hooks merge-buffer-file)))
71296446 1268
0f0b0a86
KH
1269
1270;;;###autoload
328b4b70
MK
1271(defun ediff-merge-revisions-with-ancestor (&optional
1272 file startup-hooks
1273 ;; MERGE-BUFFER-FILE is the file to
1274 ;; be associated with the merge
1275 ;; buffer
1276 merge-buffer-file)
87c668b4 1277 "Run Ediff by merging two revisions of a file with a common ancestor.
110c171f 1278The file is the optional FILE argument or the file visited by the current
87c668b4
MK
1279buffer."
1280 (interactive)
87c668b4 1281 (if (stringp file) (find-file file))
4ae69eac 1282 (let (rev1 rev2 ancestor-rev)
87c668b4
MK
1283 (setq rev1
1284 (read-string
1285 (format
5b76833f 1286 "Version 1 to merge (default %s's working version): "
87c668b4
MK
1287 (if (stringp file)
1288 (file-name-nondirectory file) "current buffer")))
1289 rev2
1290 (read-string
1291 (format
5b76833f 1292 "Version 2 to merge (default %s): "
87c668b4
MK
1293 (if (stringp file)
1294 (file-name-nondirectory file) "current buffer")))
1295 ancestor-rev
1296 (read-string
1297 (format
5b76833f 1298 "Ancestor version (default %s's base revision): "
87c668b4
MK
1299 (if (stringp file)
1300 (file-name-nondirectory file) "current buffer"))))
4ae69eac
MK
1301 (ediff-load-version-control)
1302 (funcall
92c51e07 1303 (intern (format "ediff-%S-merge-internal" ediff-version-control-package))
328b4b70 1304 rev1 rev2 ancestor-rev startup-hooks merge-buffer-file)))
4ae69eac 1305
50a07e18 1306;; MK: Check. This function doesn't seem to be used any more by pcvs or pcl-cvs
4ae69eac
MK
1307;;;###autoload
1308(defun run-ediff-from-cvs-buffer (pos)
1309 "Run Ediff-merge on appropriate revisions of the selected file.
3af0304a 1310First run after `M-x cvs-update'. Then place the cursor on a line describing a
4ae69eac
MK
1311file and then run `run-ediff-from-cvs-buffer'."
1312 (interactive "d")
1313 (ediff-load-version-control)
1314 (let ((tin (tin-locate cvs-cookie-handle pos)))
1315 (if tin
1316 (cvs-run-ediff-on-file-descriptor tin)
1317 (error "There is no file to merge"))))
71296446
JB
1318
1319
0f0b0a86 1320;;; Apply patch
0f0b0a86 1321
bbe6126c 1322;;;###autoload
3af0304a
MK
1323(defun ediff-patch-file (&optional arg patch-buf)
1324 "Run Ediff by patching SOURCE-FILENAME.
1325If optional PATCH-BUF is given, use the patch in that buffer
1326and don't ask the user.
1327If prefix argument, then: if even argument, assume that the patch is in a
1328buffer. If odd -- assume it is in a file."
1329 (interactive "P")
1330 (let (source-dir source-file)
bbe6126c 1331 (require 'ediff-ptch)
3af0304a
MK
1332 (setq patch-buf
1333 (ediff-get-patch-buffer
1334 (if arg (prefix-numeric-value arg)) patch-buf))
bbe6126c
MK
1335 (setq source-dir (cond (ediff-use-last-dir ediff-last-dir-patch)
1336 ((and (not ediff-patch-default-directory)
1337 (buffer-file-name patch-buf))
1338 (file-name-directory
1339 (expand-file-name
1340 (buffer-file-name patch-buf))))
1341 (t default-directory)))
1342 (setq source-file
71296446 1343 (read-file-name
3af0304a 1344 "File to patch (directory, if multifile patch): "
2550055a
MK
1345 ;; use an explicit initial file
1346 source-dir nil nil (ediff-get-default-file-name)))
bbe6126c 1347 (ediff-dispatch-file-patching-job patch-buf source-file)))
0f0b0a86 1348
bbe6126c 1349;;;###autoload
3af0304a 1350(defun ediff-patch-buffer (&optional arg patch-buf)
acfb6f24
MK
1351 "Run Ediff by patching the buffer specified at prompt.
1352Without the optional prefix ARG, asks if the patch is in some buffer and
1353prompts for the buffer or a file, depending on the answer.
1354With ARG=1, assumes the patch is in a file and prompts for the file.
1355With ARG=2, assumes the patch is in a buffer and prompts for the buffer.
1356PATCH-BUF is an optional argument, which specifies the buffer that contains the
1357patch. If not given, the user is prompted according to the prefix argument."
3af0304a
MK
1358 (interactive "P")
1359 (require 'ediff-ptch)
1360 (setq patch-buf
1361 (ediff-get-patch-buffer
1362 (if arg (prefix-numeric-value arg)) patch-buf))
1363 (ediff-patch-buffer-internal
1364 patch-buf
1365 (read-buffer
1366 "Which buffer to patch? "
b6178721 1367 (ediff-other-buffer patch-buf))))
71296446 1368
3af0304a 1369
bbe6126c
MK
1370;;;###autoload
1371(defalias 'epatch 'ediff-patch-file)
1372;;;###autoload
1373(defalias 'epatch-buffer 'ediff-patch-buffer)
0f0b0a86
KH
1374
1375
813f532d 1376
0f0b0a86 1377\f
71296446
JB
1378;;; Versions Control functions
1379
813f532d 1380;;;###autoload
87c668b4
MK
1381(defun ediff-revision (&optional file startup-hooks)
1382 "Run Ediff by comparing versions of a file.
7261ece3
MK
1383The file is an optional FILE argument or the file entered at the prompt.
1384Default: the file visited by the current buffer.
1385Uses `vc.el' or `rcs.el' depending on `ediff-version-control-package'."
87c668b4 1386 ;; if buffer is non-nil, use that buffer instead of the current buffer
f1a5512a 1387 (interactive "P")
7261ece3
MK
1388 (if (not (stringp file))
1389 (setq file
1390 (ediff-read-file-name "Compare revisions for file"
1391 (if ediff-use-last-dir
1392 ediff-last-dir-A
1393 default-directory)
50a07e18 1394 (ediff-get-default-file-name)
71296446 1395 'no-dirs)))
7261ece3
MK
1396 (find-file file)
1397 (if (and (buffer-modified-p)
1398 (y-or-n-p (message "Buffer %s is modified. Save buffer? "
1399 (buffer-name))))
1400 (save-buffer (current-buffer)))
f1a5512a 1401 (let (rev1 rev2)
87c668b4
MK
1402 (setq rev1
1403 (read-string
5b76833f 1404 (format "Revision 1 to compare (default %s's latest revision): "
7261ece3 1405 (file-name-nondirectory file)))
87c668b4 1406 rev2
71296446 1407 (read-string
5b76833f 1408 (format "Revision 2 to compare (default %s's current state): "
7261ece3 1409 (file-name-nondirectory file))))
f1a5512a
KH
1410 (ediff-load-version-control)
1411 (funcall
92c51e07 1412 (intern (format "ediff-%S-internal" ediff-version-control-package))
87c668b4 1413 rev1 rev2 startup-hooks)
f1a5512a 1414 ))
1e70790f
MK
1415
1416
1417;;;###autoload
1418(defalias 'erevision 'ediff-revision)
71296446
JB
1419
1420
0f0b0a86
KH
1421;; Test if version control package is loaded and load if not
1422;; Is SILENT is non-nil, don't report error if package is not found.
1423(defun ediff-load-version-control (&optional silent)
4ae69eac 1424 (require 'ediff-vers)
0f0b0a86
KH
1425 (or (featurep ediff-version-control-package)
1426 (if (locate-library (symbol-name ediff-version-control-package))
1427 (progn
1428 (message "") ; kill the message from `locate-library'
87c668b4 1429 (require ediff-version-control-package))
0f0b0a86 1430 (or silent
3af0304a 1431 (error "Version control package %S.el not found. Use vc.el instead"
0f0b0a86 1432 ediff-version-control-package)))))
87c668b4 1433
b3a26225 1434
138df2ce
MK
1435;;;###autoload
1436(defun ediff-version ()
1437 "Return string describing the version of Ediff.
1438When called interactively, displays the version."
1439 (interactive)
1440 (if (interactive-p)
1441 (message (ediff-version))
1442 (format "Ediff %s of %s" ediff-version ediff-date)))
1443
813f532d 1444
4ae69eac 1445;;;###autoload
92c51e07
MK
1446(defun ediff-documentation (&optional node)
1447 "Display Ediff's manual.
1448With optional NODE, goes to that node."
4ae69eac
MK
1449 (interactive)
1450 (let ((ctl-window ediff-control-window)
1451 (ctl-buf ediff-control-buffer))
1452
1453 (ediff-skip-unsuitable-frames)
1454 (condition-case nil
1455 (progn
1456 (pop-to-buffer (get-buffer-create "*info*"))
1457 (info (if ediff-xemacs-p "ediff.info" "ediff"))
92c51e07
MK
1458 (if node
1459 (Info-goto-node node)
1460 (message "Type `i' to search for a specific topic"))
1461 (raise-frame (selected-frame)))
4ae69eac 1462 (error (beep 1)
bbe6126c 1463 (with-output-to-temp-buffer ediff-msg-buffer
e4d2c130
DL
1464 (ediff-with-current-buffer standard-output
1465 (fundamental-mode))
92c51e07 1466 (princ ediff-BAD-INFO))
4ae69eac
MK
1467 (if (window-live-p ctl-window)
1468 (progn
1469 (select-window ctl-window)
1470 (set-window-buffer ctl-window ctl-buf)))))))
71296446 1471
4ae69eac 1472
fb0fb4fa
JL
1473(dolist (mess '("^Errors in diff output. Diff output is in "
1474 "^Hmm... I don't see an Ediff command around here...$"
1475 "^Undocumented command! Type `G' in Ediff Control Panel to drop a note to the Ediff maintainer$"
1476 ": This command runs in Ediff Control Buffer only!$"
1477 ": Invalid op in ediff-check-version$"
1478 "^ediff-shrink-window-C can be used only for merging jobs$"
1479 "^Lost difference info on these directories$"
1480 "^This command is inapplicable in the present context$"
1481 "^This session group has no parent$"
1482 "^Can't hide active session, $"
1483 "^Ediff: something wrong--no multiple diffs buffer$"
1484 "^Can't make context diff for Session $"
1485 "^The patch buffer wasn't found$"
1486 "^Aborted$"
1487 "^This Ediff session is not part of a session group$"
1488 "^No active Ediff sessions or corrupted session registry$"
1489 "^No session info in this line$"
1490 "^`.*' is not an ordinary file$"
1491 "^Patch appears to have failed$"
1492 "^Recomputation of differences cancelled$"
1493 "^No fine differences in this mode$"
1494 "^Lost connection to ancestor buffer...sorry$"
1495 "^Not merging with ancestor$"
1496 "^Don't know how to toggle read-only in buffer "
1497 "Emacs is not running as a window application$"
1498 "^This command makes sense only when merging with an ancestor$"
1499 "^At end of the difference list$"
1500 "^At beginning of the difference list$"
1501 "^Nothing saved for diff .* in buffer "
1502 "^Buffer is out of sync for file "
1503 "^Buffer out of sync for file "
1504 "^Output from `diff' not found$"
1505 "^You forgot to specify a region in buffer "
1506 "^All right. Make up your mind and come back...$"
1507 "^Current buffer is not visiting any file$"
1508 "^Failed to retrieve revision: $"
1509 "^Can't determine display width.$"
1510 "^File `.*' does not exist or is not readable$"
1511 "^File `.*' is a directory$"
1512 "^Buffer .* doesn't exist$"
1513 "^Directories . and . are the same: "
1514 "^Directory merge aborted$"
1515 "^Merge of directory revisions aborted$"
1516 "^Buffer .* doesn't exist$"
1517 "^There is no file to merge$"
1518 "^Version control package .*.el not found. Use vc.el instead$"))
1519 (add-to-list 'debug-ignored-errors mess))
4ae69eac
MK
1520
1521
8ea74b0e
MK
1522(require 'ediff-util)
1523
1524(run-hooks 'ediff-load-hook)
1525
b6178721
MK
1526(provide 'ediff)
1527
1528
4ae69eac
MK
1529;;; Local Variables:
1530;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
e756eb9f
MK
1531;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1)
1532;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body))
4ae69eac
MK
1533;;; End:
1534
ab5796a9 1535;;; arch-tag: 97c71396-db02-4f41-8b48-6a51c3348fcc
813f532d 1536;;; ediff.el ends here