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