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