]> code.delx.au - gnu-emacs/blob - lisp/ediff.el
Use cl only at compile time.
[gnu-emacs] / lisp / ediff.el
1 ;;; ediff.el --- a comprehensive visual interface to diff & patch
2
3 ;; Copyright (C) 1994, 1995, 1996 Free Software Foundation, Inc.
4
5 ;; Author: Michael Kifer <kifer@cs.sunysb.edu>
6 ;; Created: February 2, 1994
7 ;; Keywords: comparing, merging, patching, version control.
8
9 (defconst ediff-version "2.63" "The current version of Ediff")
10 (defconst ediff-date "September 12, 1996" "Date of last update")
11
12
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
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.
29
30 ;;; Commentary:
31
32 ;; Never read that diff output again!
33 ;; Apply patch interactively!
34 ;; Merge with ease!
35
36 ;; This package provides a convenient way of simultaneous browsing through
37 ;; the differences between a pair (or a triple) of files or buffers. The
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).
43
44 ;; Ediff also supports merging operations on files and buffers, including
45 ;; merging using ancestor versions. Both comparison and merging operations can
46 ;; be performed on directories, i.e., by pairwise comparison of files in those
47 ;; directories.
48
49 ;; In addition, Ediff can apply a patch to a file and then let you step
50 ;; though both files, the patched and the original one, simultaneously,
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
55 ;; region from file_orig to file, thereby undoing any particular patch that
56 ;; you don't like).
57
58 ;; Ediff is aware of version control, which lets the user compare
59 ;; files with their older versions. Ediff can also work with remote and
60 ;; compressed files. Details are given below.
61
62 ;; Finally, Ediff supports directory-level comparison, merging and patching.
63 ;; See the on-line manual for details.
64
65 ;; This package builds upon the ideas borrowed from emerge.el and several
66 ;; Ediff's functions are adaptations from emerge.el. Much of the functionality
67 ;; Ediff provides is also influenced by emerge.el.
68
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
71 ;; particular, it can do patching, and 2-way and 3-way file comparison,
72 ;; merging, and directory operations.
73
74
75
76 ;;; Bugs:
77
78 ;; 1. The undo command doesn't restore deleted regions well. That is, if
79 ;; you delete all characters in a difference region and then invoke
80 ;; `undo', the reinstated text will most likely be inserted outside of
81 ;; what Ediff thinks is the current difference region. (This problem
82 ;; doesn't seem to exist with XEmacs.)
83 ;;
84 ;; If at any point you feel that difference regions are no longer correct,
85 ;; you can hit '!' to recompute the differences.
86
87 ;; 2. On a monochrome display, the repertoire of faces with which to
88 ;; highlight fine differences is limited. By default, Ediff is using
89 ;; underlining. However, if the region is already underlined by some other
90 ;; overlays, there is no simple way to temporarily remove that residual
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
95 ;; commands in `ediff-prepare-buffer-hook' (which will unhighlight every
96 ;; buffer used by Ediff) or you can execute them interactively, at any time
97 ;; and on any buffer.
98
99
100 ;;; Acknowledgements:
101
102 ;; Ediff was inspired by Dale R. Worley's <drw@math.mit.edu> emerge.el.
103 ;; Ediff would not have been possible without the help and encouragement of
104 ;; its many users. See Ediff on-line Info for the full list of those who
105 ;; helped. Improved defaults in Ediff file-name reading commands.
106
107 ;;; Code:
108
109 (require 'ediff-init)
110 ;; ediff-mult is always required, because of the registry stuff
111 (require 'ediff-mult)
112
113 (and noninteractive
114 (eval-when-compile
115 (let ((load-path (cons (expand-file-name ".") load-path)))
116 (load-library "dired")
117 (load-file "ediff-ptch.el")
118 (load-file "ediff-vers.el")
119 (load "pcl-cvs" 'noerror))))
120
121 (defvar ediff-use-last-dir nil
122 "*If t, Ediff uses previous directory as default when reading file name.")
123
124 (defvar ediff-last-dir-A nil
125 "Last directory used by an Ediff command for file-A.")
126 (defvar ediff-last-dir-B nil
127 "Last directory used by an Ediff command for file-B.")
128 (defvar ediff-last-dir-C nil
129 "Last directory used by an Ediff command for file-C.")
130 (defvar ediff-last-dir-ancestor nil
131 "Last directory used by an Ediff command for the ancestor file.")
132 (defvar ediff-last-merge-autostore-dir
133 "Last directory used by an Ediff command as the output directory for merge.")
134
135 ;; Some defvars to reduce the number of compiler warnings
136 (defvar cvs-cookie-handle)
137 (defvar ediff-last-dir-patch)
138 (defvar ediff-patch-default-directory)
139 ;; end of compiler pacifier
140
141
142 ;; Used as a startup hook to set `_orig' patch file read-only.
143 (defun ediff-set-read-only-in-buf-A ()
144 (ediff-eval-in-buffer ediff-buffer-A
145 (toggle-read-only 1)))
146
147 ;; Return a plausible default for ediff's first file:
148 ;; In dired, return the file name under the point, unless it is a directory
149 ;; If the buffer has a file name, return that file name.
150 (defun ediff-get-default-file-name ()
151 (cond ((eq major-mode 'dired-mode)
152 (let ((f (dired-get-filename nil 'no-error)))
153 (if (and (stringp f) (not (file-directory-p f)))
154 f)))
155 ((buffer-file-name (current-buffer))
156 (file-name-nondirectory (buffer-file-name (current-buffer))))
157 ))
158
159 ;;; Compare files/buffers
160
161 ;;;###autoload
162 (defun ediff-files (file-A file-B &optional startup-hooks)
163 "Run Ediff on a pair of files, FILE-A and FILE-B."
164 (interactive
165 (let ((dir-A (if ediff-use-last-dir
166 ediff-last-dir-A
167 default-directory))
168 dir-B f)
169 (list (setq f (ediff-read-file-name
170 "File A to compare" dir-A
171 (ediff-get-default-file-name)))
172 (ediff-read-file-name "File B to compare"
173 (setq dir-B
174 (if ediff-use-last-dir
175 ediff-last-dir-B
176 (file-name-directory f)))
177 (progn
178 (setq file-name-history
179 (cons (ediff-abbreviate-file-name
180 (expand-file-name
181 (file-name-nondirectory f)
182 dir-B))
183 file-name-history))
184 f))
185 )))
186 (ediff-files-internal file-A
187 (if (file-directory-p file-B)
188 (expand-file-name
189 (file-name-nondirectory file-A) file-B)
190 file-B)
191 nil ; file-C
192 startup-hooks
193 'ediff-files))
194
195 ;;;###autoload
196 (defun ediff-files3 (file-A file-B file-C &optional startup-hooks)
197 "Run Ediff on three files, FILE-A, FILE-B, and FILE-C."
198 (interactive
199 (let ((dir-A (if ediff-use-last-dir
200 ediff-last-dir-A
201 default-directory))
202 dir-B dir-C f ff)
203 (list (setq f (ediff-read-file-name
204 "File A to compare" dir-A
205 (ediff-get-default-file-name)))
206 (setq ff (ediff-read-file-name "File B to compare"
207 (setq dir-B
208 (if ediff-use-last-dir
209 ediff-last-dir-B
210 (file-name-directory f)))
211 (progn
212 (setq file-name-history
213 (cons
214 (ediff-abbreviate-file-name
215 (expand-file-name
216 (file-name-nondirectory f)
217 dir-B))
218 file-name-history))
219 f)))
220 (ediff-read-file-name "File C to compare"
221 (setq dir-C (if ediff-use-last-dir
222 ediff-last-dir-C
223 (file-name-directory ff)))
224 (progn
225 (setq file-name-history
226 (cons (ediff-abbreviate-file-name
227 (expand-file-name
228 (file-name-nondirectory ff)
229 dir-C))
230 file-name-history))
231 ff))
232 )))
233 (ediff-files-internal file-A
234 (if (file-directory-p file-B)
235 (expand-file-name
236 (file-name-nondirectory file-A) file-B)
237 file-B)
238 (if (file-directory-p file-C)
239 (expand-file-name
240 (file-name-nondirectory file-A) file-C)
241 file-C)
242 startup-hooks
243 'ediff-files3))
244
245 ;;;###autoload
246 (defalias 'ediff3 'ediff-files3)
247
248
249 ;; Visit FILE and arrange its buffer to Ediff's liking.
250 ;; FILE is actually a variable symbol that must contain a true file name.
251 ;; BUFFER-NAME is a variable symbol, which will get the buffer object into
252 ;; which FILE is read.
253 ;; LAST-DIR is the directory variable symbol where FILE's
254 ;; directory name should be returned. HOOKS-VAR is a variable symbol that will
255 ;; be assigned the hook to be executed after `ediff-startup' is finished.
256 ;; `ediff-find-file' arranges that the temp files it might create will be
257 ;; deleted.
258 (defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var)
259 (let* ((file (symbol-value file-var))
260 (file-magic (find-file-name-handler file 'find-file-noselect))
261 (temp-file-name-prefix (file-name-nondirectory file)))
262 (cond ((not (file-readable-p file))
263 (error "File `%s' does not exist or is not readable" file))
264 ((file-directory-p file)
265 (error "File `%s' is a directory" file)))
266
267 ;; some of the commands, below, require full file name
268 (setq file (expand-file-name file))
269
270 ;; Record the directory of the file
271 (if last-dir
272 (set last-dir (expand-file-name (file-name-directory file))))
273
274 ;; Setup the buffer
275 (set buffer-name (find-file-noselect file))
276
277 (ediff-eval-in-buffer (symbol-value buffer-name)
278 (widen) ; Make sure the entire file is seen
279 (cond (file-magic ;; file has handler, such as jka-compr-handler or
280 ;; ange-ftp-hook-function--arrange for temp file
281 (ediff-verify-file-buffer 'magic)
282 (setq file
283 (ediff-make-temp-file
284 (current-buffer) temp-file-name-prefix))
285 (set hooks-var (cons (` (lambda () (delete-file (, file))))
286 (symbol-value hooks-var))))
287 ;; file processed via auto-mode-alist, a la uncompress.el
288 ((not (equal (file-truename file)
289 (file-truename (buffer-file-name))))
290 (setq file
291 (ediff-make-temp-file
292 (current-buffer) temp-file-name-prefix))
293 (set hooks-var (cons (` (lambda () (delete-file (, file))))
294 (symbol-value hooks-var))))
295 (t ;; plain file---just check that the file matches the buffer
296 (ediff-verify-file-buffer))))
297 (set file-var file)))
298
299 (defun ediff-files-internal (file-A file-B file-C startup-hooks job-name)
300 (let (buf-A buf-B buf-C)
301 (message "Reading file %s ... " file-A)
302 ;;(sit-for 0)
303 (ediff-find-file 'file-A 'buf-A 'ediff-last-dir-A 'startup-hooks)
304 (message "Reading file %s ... " file-B)
305 ;;(sit-for 0)
306 (ediff-find-file 'file-B 'buf-B 'ediff-last-dir-B 'startup-hooks)
307 (if (stringp file-C)
308 (progn
309 (message "Reading file %s ... " file-C)
310 ;;(sit-for 0)
311 (ediff-find-file
312 'file-C 'buf-C
313 (if (eq job-name 'ediff-merge-files-with-ancestor)
314 'ediff-last-dir-ancestor 'ediff-last-dir-C)
315 'startup-hooks)))
316 (ediff-setup buf-A file-A
317 buf-B file-B
318 buf-C file-C
319 startup-hooks
320 (list (cons 'ediff-job-name job-name)))))
321
322
323 ;;;###autoload
324 (defalias 'ediff 'ediff-files)
325
326
327 ;;;###autoload
328 (defun ediff-buffers (buffer-A buffer-B &optional startup-hooks job-name)
329 "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B."
330 (interactive
331 (let (bf)
332 (list (setq bf (read-buffer "Buffer A to compare: "
333 (ediff-other-buffer "") t))
334 (read-buffer "Buffer B to compare: "
335 (progn
336 ;; realign buffers so that two visible bufs will be
337 ;; at the top
338 (save-window-excursion (other-window 1))
339 (ediff-other-buffer bf))
340 t))))
341 (or job-name (setq job-name 'ediff-buffers))
342 (ediff-buffers-internal buffer-A buffer-B nil startup-hooks job-name))
343
344 ;;;###autoload
345 (defalias 'ebuffers 'ediff-buffers)
346
347
348 ;;;###autoload
349 (defun ediff-buffers3 (buffer-A buffer-B buffer-C
350 &optional startup-hooks job-name)
351 "Run Ediff on three buffers, BUFFER-A, BUFFER-B, and BUFFER-C."
352 (interactive
353 (let (bf bff)
354 (list (setq bf (read-buffer "Buffer A to compare: "
355 (ediff-other-buffer "") t))
356 (setq bff (read-buffer "Buffer B to compare: "
357 (progn
358 ;; realign buffers so that two visible
359 ;; bufs will be at the top
360 (save-window-excursion (other-window 1))
361 (ediff-other-buffer bf))
362 t))
363 (read-buffer "Buffer C to compare: "
364 (progn
365 ;; realign buffers so that three visible
366 ;; bufs will be at the top
367 (save-window-excursion (other-window 1))
368 (ediff-other-buffer (list bf bff)))
369 t)
370 )))
371 (or job-name (setq job-name 'ediff-buffers3))
372 (ediff-buffers-internal buffer-A buffer-B buffer-C startup-hooks job-name))
373
374 ;;;###autoload
375 (defalias 'ebuffers3 'ediff-buffers3)
376
377
378
379 (defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name)
380 (let* ((buf-A-file-name (buffer-file-name (get-buffer buf-A)))
381 (buf-B-file-name (buffer-file-name (get-buffer buf-B)))
382 (buf-C-is-alive (ediff-buffer-live-p buf-C))
383 (buf-C-file-name (if buf-C-is-alive
384 (buffer-file-name (get-buffer buf-B))))
385 file-A file-B file-C)
386 (if (not (ediff-buffer-live-p buf-A))
387 (error "Buffer %S doesn't exist" buf-A))
388 (if (not (ediff-buffer-live-p buf-B))
389 (error "Buffer %S doesn't exist" buf-B))
390 (let ((ediff-job-name job-name))
391 (if (and ediff-3way-comparison-job
392 (not buf-C-is-alive))
393 (error "Buffer %S doesn't exist" buf-C)))
394 (if (stringp buf-A-file-name)
395 (setq buf-A-file-name (file-name-nondirectory buf-A-file-name)))
396 (if (stringp buf-B-file-name)
397 (setq buf-B-file-name (file-name-nondirectory buf-B-file-name)))
398 (if (stringp buf-C-file-name)
399 (setq buf-C-file-name (file-name-nondirectory buf-C-file-name)))
400
401 (setq file-A (ediff-make-temp-file buf-A buf-A-file-name)
402 file-B (ediff-make-temp-file buf-B buf-B-file-name))
403 (if buf-C-is-alive
404 (setq file-C (ediff-make-temp-file buf-C buf-C-file-name)))
405
406 (ediff-setup (get-buffer buf-A) file-A
407 (get-buffer buf-B) file-B
408 (if buf-C-is-alive (get-buffer buf-C))
409 file-C
410 (cons (` (lambda ()
411 (delete-file (, file-A))
412 (delete-file (, file-B))
413 (if (stringp (, file-C)) (delete-file (, file-C)))
414 ))
415 startup-hooks)
416 (list (cons 'ediff-job-name job-name))
417 )))
418
419
420 ;;; Directory and file group operations
421
422 ;; Get appropriate default name for directory:
423 ;; If ediff-use-last-dir, use ediff-last-dir-A.
424 ;; In dired mode, use the directory that is under the point (if any);
425 ;; otherwise, use default-directory
426 (defun ediff-get-default-directory-name ()
427 (cond (ediff-use-last-dir ediff-last-dir-A)
428 ((eq major-mode 'dired-mode)
429 (let ((f (dired-get-filename nil 'noerror)))
430 (if (and (stringp f) (file-directory-p f))
431 f
432 default-directory)))
433 (t default-directory)))
434
435
436 ;;;###autoload
437 (defun ediff-directories (dir1 dir2 regexp)
438 "Run Ediff on a pair of directories, DIR1 and DIR2, comparing files that have
439 the same name in both. The third argument, REGEXP, is a regular expression that
440 can be used to filter out certain file names."
441 (interactive
442 (let ((dir-A (ediff-get-default-directory-name))
443 f)
444 (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil))
445 (ediff-read-file-name "Directory B to compare:"
446 (if ediff-use-last-dir
447 ediff-last-dir-B
448 (ediff-strip-last-dir f))
449 nil)
450 (read-string "Filter through regular expression: "
451 nil 'ediff-filtering-regexp-history)
452 )))
453 (ediff-directories-internal
454 dir1 dir2 nil regexp 'ediff-files 'ediff-directories
455 ))
456
457 ;;;###autoload
458 (defalias 'edirs 'ediff-directories)
459
460
461 ;;;###autoload
462 (defun ediff-directory-revisions (dir1 regexp)
463 "Run Ediff on a directory, DIR1, comparing its files with their revisions.
464 The second argument, REGEXP, is a regular expression that filters the file
465 names. Only the files that are under revision control are taken into account."
466 (interactive
467 (let ((dir-A (ediff-get-default-directory-name)))
468 (list (ediff-read-file-name
469 "Directory to compare with revision:" dir-A nil)
470 (read-string "Filter through regular expression: "
471 nil 'ediff-filtering-regexp-history)
472 )))
473 (ediff-directory-revisions-internal
474 dir1 regexp 'ediff-revision 'ediff-directory-revisions
475 ))
476
477 ;;;###autoload
478 (defalias 'edir-revisions 'ediff-directory-revisions)
479
480
481 ;;;###autoload
482 (defun ediff-directories3 (dir1 dir2 dir3 regexp)
483 "Run Ediff on three directories, DIR1, DIR2, and DIR3, comparing files that
484 have the same name in all three. The last argument, REGEXP, is a regular
485 expression that can be used to filter out certain file names."
486 (interactive
487 (let ((dir-A (ediff-get-default-directory-name))
488 f)
489 (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil))
490 (setq f (ediff-read-file-name "Directory B to compare:"
491 (if ediff-use-last-dir
492 ediff-last-dir-B
493 (ediff-strip-last-dir f))
494 nil))
495 (ediff-read-file-name "Directory C to compare:"
496 (if ediff-use-last-dir
497 ediff-last-dir-C
498 (ediff-strip-last-dir f))
499 nil)
500 (read-string "Filter through regular expression: "
501 nil 'ediff-filtering-regexp-history)
502 )))
503 (ediff-directories-internal
504 dir1 dir2 dir3 regexp 'ediff-files3 'ediff-directories3
505 ))
506
507 ;;;###autoload
508 (defalias 'edirs3 'ediff-directories3)
509
510 ;;;###autoload
511 (defun ediff-merge-directories (dir1 dir2 regexp)
512 "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have
513 the same name in both. The third argument, REGEXP, is a regular expression that
514 can be used to filter out certain file names."
515 (interactive
516 (let ((dir-A (ediff-get-default-directory-name))
517 f)
518 (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil))
519 (ediff-read-file-name "Directory B to merge:"
520 (if ediff-use-last-dir
521 ediff-last-dir-B
522 (ediff-strip-last-dir f))
523 nil)
524 (read-string "Filter through regular expression: "
525 nil 'ediff-filtering-regexp-history)
526 )))
527 (ediff-directories-internal
528 dir1 dir2 nil regexp 'ediff-merge-files 'ediff-merge-directories
529 ))
530
531 ;;;###autoload
532 (defalias 'edirs-merge 'ediff-merge-directories)
533
534 ;;;###autoload
535 (defun ediff-merge-directories-with-ancestor (dir1 dir2 ancestor-dir regexp)
536 "Merge files in directories DIR1 and DIR2 using files in ANCESTOR-DIR as ancestors.
537 Ediff merges files that have identical names in DIR1, DIR2. If a pair of files
538 in DIR1 and DIR2 doesn't have an ancestor in ANCESTOR-DIR, Ediff will merge
539 without ancestor. The fourth argument, REGEXP, is a regular expression that
540 can be used to filter out certain file names."
541 (interactive
542 (let ((dir-A (ediff-get-default-directory-name))
543 f)
544 (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil))
545 (setq f (ediff-read-file-name "Directory B to merge:"
546 (if ediff-use-last-dir
547 ediff-last-dir-B
548 (ediff-strip-last-dir f))
549 nil))
550 (ediff-read-file-name "Ancestor directory:"
551 (if ediff-use-last-dir
552 ediff-last-dir-C
553 (ediff-strip-last-dir f))
554 nil)
555 (read-string "Filter through regular expression: "
556 nil 'ediff-filtering-regexp-history)
557 )))
558 (ediff-directories-internal
559 dir1 dir2 ancestor-dir regexp
560 'ediff-merge-files-with-ancestor 'ediff-merge-directories-with-ancestor
561 ))
562
563 ;;;###autoload
564 (defun ediff-merge-directory-revisions (dir1 regexp)
565 "Run Ediff on a directory, DIR1, merging its files with their revisions.
566 The second argument, REGEXP, is a regular expression that filters the file
567 names. Only the files that are under revision control are taken into account."
568 (interactive
569 (let ((dir-A (ediff-get-default-directory-name)))
570 (list (ediff-read-file-name
571 "Directory to merge with revisions:" dir-A nil)
572 (read-string "Filter through regular expression: "
573 nil 'ediff-filtering-regexp-history)
574 )))
575 (ediff-directory-revisions-internal
576 dir1 regexp 'ediff-merge-revisions 'ediff-merge-directory-revisions
577 ))
578
579 ;;;###autoload
580 (defalias 'edir-merge-revisions 'ediff-merge-directory-revisions)
581
582 ;;;###autoload
583 (defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp)
584 "Run Ediff on a directory, DIR1, merging its files with their revisions and ancestors.
585 The second argument, REGEXP, is a regular expression that filters the file
586 names. Only the files that are under revision control are taken into account."
587 (interactive
588 (let ((dir-A (ediff-get-default-directory-name)))
589 (list (ediff-read-file-name
590 "Directory to merge with revisions and ancestors:" dir-A nil)
591 (read-string "Filter through regular expression: "
592 nil 'ediff-filtering-regexp-history)
593 )))
594 (ediff-directory-revisions-internal
595 dir1 regexp 'ediff-merge-revisions-with-ancestor
596 'ediff-merge-directory-revisions-with-ancestor
597 ))
598
599 ;;;###autoload
600 (defalias
601 'edir-merge-revisions-with-ancestor
602 'ediff-merge-directory-revisions-with-ancestor)
603
604 ;;;###autoload
605 (defalias 'edirs-merge-with-ancestor 'ediff-merge-directories-with-ancestor)
606
607 ;; Run ediff-action (ediff-files, ediff-merge, ediff-merge-with-ancestors)
608 ;; on a pair of directories (three directories, in case of ancestor).
609 ;; The third argument, REGEXP, is a regular expression that can be used to
610 ;; filter out certain file names.
611 ;; JOBNAME is the symbol indicating the meta-job to be performed.
612 ;; MERGE-DIR is the directory in which to store merged files.
613 (defun ediff-directories-internal (dir1 dir2 dir3 regexp action jobname
614 &optional startup-hooks)
615 ;; ediff-read-file-name is set to attach a previously entered file name if
616 ;; the currently entered file is a directory. This code takes care of that.
617 (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1))
618 dir2 (if (file-directory-p dir2) dir2 (file-name-directory dir2)))
619
620 (if (stringp dir3)
621 (setq dir3 (if (file-directory-p dir3) dir3 (file-name-directory dir3))))
622
623 (cond ((string= dir1 dir2)
624 (error "Directories A and B are the same: %s" dir1))
625 ((and (eq jobname 'ediff-directories3)
626 (string= dir1 dir3))
627 (error "Directories A and C are the same: %s" dir1))
628 ((and (eq jobname 'ediff-directories3)
629 (string= dir2 dir3))
630 (error "Directories B and C are the same: %s" dir1)))
631
632 (let (diffs ; var where ediff-intersect-directories returns the diff list
633 merge-autostore-dir
634 file-list meta-buf)
635 (if (and ediff-autostore-merges (ediff-merge-metajob jobname))
636 (setq merge-autostore-dir
637 (ediff-read-file-name "Directory to save merged files:"
638 (if ediff-use-last-dir
639 ediff-last-merge-autostore-dir
640 (ediff-strip-last-dir dir1))
641 nil)))
642 ;; verify we are not merging into an orig directory
643 (if (stringp merge-autostore-dir)
644 (cond ((and (stringp dir1) (string= merge-autostore-dir dir1))
645 (or (y-or-n-p "Merge directory same as directory A, sure? ")
646 (error "Directory merge aborted")))
647 ((and (stringp dir2) (string= merge-autostore-dir dir2))
648 (or (y-or-n-p "Merge directory same as directory B, sure? ")
649 (error "Directory merge aborted")))
650 ((and (stringp dir3) (string= merge-autostore-dir dir3))
651 (or (y-or-n-p
652 "Merge directory same as ancestor directory, sure? ")
653 (error "Directory merge aborted")))))
654
655 (setq file-list (ediff-intersect-directories
656 jobname 'diffs
657 regexp dir1 dir2 dir3 merge-autostore-dir))
658 (setq startup-hooks
659 ;; this sets various vars in the meta buffer inside
660 ;; ediff-prepare-meta-buffer
661 (cons (` (lambda ()
662 ;; tell what to do if the user clicks on a session record
663 (setq ediff-session-action-function (quote (, action)))
664 ;; set ediff-dir-difference-list
665 (setq ediff-dir-difference-list (quote (, diffs)))))
666 startup-hooks))
667 (setq meta-buf (ediff-prepare-meta-buffer
668 'ediff-filegroup-action
669 file-list
670 "*Ediff Session Group Panel"
671 'ediff-redraw-directory-group-buffer
672 jobname
673 startup-hooks))
674 (ediff-show-meta-buffer meta-buf)
675 ))
676
677 (defun ediff-directory-revisions-internal (dir1 regexp action jobname
678 &optional startup-hooks)
679 (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1)))
680
681 (let (file-list meta-buf merge-autostore-dir)
682 (if (and ediff-autostore-merges (ediff-merge-metajob jobname))
683 (setq merge-autostore-dir
684 (ediff-read-file-name "Directory to save merged files:"
685 (if ediff-use-last-dir
686 ediff-last-merge-autostore-dir
687 (ediff-strip-last-dir dir1))
688 nil)))
689 ;; verify merge-autostore-dir != dir1
690 (if (and (stringp merge-autostore-dir)
691 (stringp dir1)
692 (string= merge-autostore-dir dir1))
693 (or (y-or-n-p
694 "Directory for saving merges is the same as directory A. Sure? ")
695 (error "Merge of directory revisions aborted")))
696
697 (setq file-list
698 (ediff-get-directory-files-under-revision
699 jobname regexp dir1 merge-autostore-dir))
700 (setq startup-hooks
701 ;; this sets various vars in the meta buffer inside
702 ;; ediff-prepare-meta-buffer
703 (cons (` (lambda ()
704 ;; tell what to do if the user clicks on a session record
705 (setq ediff-session-action-function (quote (, action)))
706 ))
707 startup-hooks))
708 (setq meta-buf (ediff-prepare-meta-buffer
709 'ediff-filegroup-action
710 file-list
711 "*Ediff Session Group Panel"
712 'ediff-redraw-directory-group-buffer
713 jobname
714 startup-hooks))
715 (ediff-show-meta-buffer meta-buf)
716 ))
717
718
719 ;;; Compare regions and windows
720
721 ;;;###autoload
722 (defun ediff-windows-wordwise (dumb-mode &optional wind-A wind-B startup-hooks)
723 "Compare WIND-A and WIND-B, which are selected by clicking, wordwise.
724 With prefix argument, DUMB-MODE, or on a non-windowing display, works as
725 follows:
726 If WIND-A is nil, use selected window.
727 If WIND-B is nil, use window next to WIND-A."
728 (interactive "P")
729 (ediff-windows dumb-mode wind-A wind-B
730 startup-hooks 'ediff-windows-wordwise 'word-mode))
731
732 ;;;###autoload
733 (defun ediff-windows-linewise (dumb-mode &optional wind-A wind-B startup-hooks)
734 "Compare WIND-A and WIND-B, which are selected by clicking, linewise.
735 With prefix argument, DUMB-MODE, or on a non-windowing display, works as
736 follows:
737 If WIND-A is nil, use selected window.
738 If WIND-B is nil, use window next to WIND-A."
739 (interactive "P")
740 (ediff-windows dumb-mode wind-A wind-B
741 startup-hooks 'ediff-windows-linewise nil))
742
743 ;; Compare WIND-A and WIND-B, which are selected by clicking.
744 ;; With prefix argument, DUMB-MODE, or on a non-windowing display,
745 ;; works as follows:
746 ;; If WIND-A is nil, use selected window.
747 ;; If WIND-B is nil, use window next to WIND-A.
748 (defun ediff-windows (dumb-mode wind-A wind-B startup-hooks job-name word-mode)
749 (if (or dumb-mode (not (ediff-window-display-p)))
750 (setq wind-A (ediff-get-next-window wind-A nil)
751 wind-B (ediff-get-next-window wind-B wind-A))
752 (setq wind-A (ediff-get-window-by-clicking wind-A nil 1)
753 wind-B (ediff-get-window-by-clicking wind-B wind-A 2)))
754
755 (let ((buffer-A (window-buffer wind-A))
756 (buffer-B (window-buffer wind-B))
757 beg-A end-A beg-B end-B)
758
759 (save-excursion
760 (save-window-excursion
761 (sit-for 0) ; sync before using window-start/end -- a precaution
762 (select-window wind-A)
763 (setq beg-A (window-start)
764 end-A (window-end))
765 (select-window wind-B)
766 (setq beg-B (window-start)
767 end-B (window-end))))
768 (ediff-regions-internal
769 buffer-A beg-A end-A buffer-B beg-B end-B
770 startup-hooks job-name word-mode)))
771
772 ;;;###autoload
773 (defun ediff-regions-wordwise (buffer-A buffer-B &optional startup-hooks)
774 "Run Ediff on a pair of regions in two different buffers.
775 Regions \(i.e., point and mark\) are assumed to be set in advance.
776 This function is effective only for relatively small regions, up to 200
777 lines. For large regions, use `ediff-regions-linewise'."
778 (interactive
779 (let (bf)
780 (list (setq bf (read-buffer "Region's A buffer: "
781 (ediff-other-buffer "") t))
782 (read-buffer "Region's B buffer: "
783 (progn
784 ;; realign buffers so that two visible bufs will be
785 ;; at the top
786 (save-window-excursion (other-window 1))
787 (ediff-other-buffer bf))
788 t))))
789 (if (not (ediff-buffer-live-p buffer-A))
790 (error "Buffer %S doesn't exist" buffer-A))
791 (if (not (ediff-buffer-live-p buffer-B))
792 (error "Buffer %S doesn't exist" buffer-B))
793
794
795 (let (reg-A-beg reg-A-end reg-B-beg reg-B-end)
796 (save-excursion
797 (set-buffer buffer-A)
798 (setq reg-A-beg (region-beginning)
799 reg-A-end (region-end))
800 (set-buffer buffer-B)
801 (setq reg-B-beg (region-beginning)
802 reg-B-end (region-end)))
803
804 (ediff-regions-internal
805 (get-buffer buffer-A) reg-A-beg reg-A-end
806 (get-buffer buffer-B) reg-B-beg reg-B-end
807 startup-hooks 'ediff-regions-wordwise 'word-mode)))
808
809 ;;;###autoload
810 (defun ediff-regions-linewise (buffer-A buffer-B &optional startup-hooks)
811 "Run Ediff on a pair of regions in two different buffers.
812 Regions \(i.e., point and mark\) are assumed to be set in advance.
813 Each region is enlarged to contain full lines.
814 This function is effective for large regions, over 100-200
815 lines. For small regions, use `ediff-regions-wordwise'."
816 (interactive
817 (let (bf)
818 (list (setq bf (read-buffer "Region A's buffer: "
819 (ediff-other-buffer "") t))
820 (read-buffer "Region B's buffer: "
821 (progn
822 ;; realign buffers so that two visible bufs will be
823 ;; at the top
824 (save-window-excursion (other-window 1))
825 (ediff-other-buffer bf))
826 t))))
827 (if (not (ediff-buffer-live-p buffer-A))
828 (error "Buffer %S doesn't exist" buffer-A))
829 (if (not (ediff-buffer-live-p buffer-B))
830 (error "Buffer %S doesn't exist" buffer-B))
831
832 (let (reg-A-beg reg-A-end reg-B-beg reg-B-end)
833 (save-excursion
834 (set-buffer buffer-A)
835 (setq reg-A-beg (region-beginning)
836 reg-A-end (region-end))
837 ;; enlarge the region to hold full lines
838 (goto-char reg-A-beg)
839 (beginning-of-line)
840 (setq reg-A-beg (point))
841 (goto-char reg-A-end)
842 (end-of-line)
843 (or (eobp) (forward-char)) ; include the newline char
844 (setq reg-A-end (point))
845
846 (set-buffer buffer-B)
847 (setq reg-B-beg (region-beginning)
848 reg-B-end (region-end))
849 ;; enlarge the region to hold full lines
850 (goto-char reg-B-beg)
851 (beginning-of-line)
852 (setq reg-B-beg (point))
853 (goto-char reg-B-end)
854 (end-of-line)
855 (or (eobp) (forward-char)) ; include the newline char
856 (setq reg-B-end (point))
857 ) ; save excursion
858
859 (ediff-regions-internal
860 (get-buffer buffer-A) reg-A-beg reg-A-end
861 (get-buffer buffer-B) reg-B-beg reg-B-end
862 startup-hooks 'ediff-regions-linewise nil))) ; no word mode
863
864 ;; compare region beg-A to end-A of buffer-A
865 ;; to regions beg-B -- end-B in buffer-B.
866 (defun ediff-regions-internal (buffer-A beg-A end-A buffer-B beg-B end-B
867 startup-hooks job-name word-mode)
868 (let ((tmp-buffer (get-buffer-create ediff-tmp-buffer))
869 overl-A overl-B
870 file-A file-B)
871
872 ;; in case beg/end-A/B aren't markers--make them into markers
873 (ediff-eval-in-buffer buffer-A
874 (setq beg-A (move-marker (make-marker) beg-A)
875 end-A (move-marker (make-marker) end-A)))
876 (ediff-eval-in-buffer buffer-B
877 (setq beg-B (move-marker (make-marker) beg-B)
878 end-B (move-marker (make-marker) end-B)))
879
880 (if (and (eq buffer-A buffer-B)
881 (or (and (< beg-A end-B) (<= beg-B beg-A)) ; b-B b-A e-B
882 (and (< beg-B end-A) (<= end-A end-B)))) ; b-B e-A e-B
883 (progn
884 (with-output-to-temp-buffer ediff-msg-buffer
885 (princ "
886 You have requested to compare overlapping regions of the same buffer.
887
888 In this case, Ediff's highlighting may be confusing---in the same window,
889 you may see highlighted regions that belong to different regions.
890
891 Continue anyway? (y/n) "))
892
893 (if (y-or-n-p "Continue anyway? ")
894 ()
895 (error "%S aborted" job-name))))
896
897 ;; make file-A
898 (if word-mode
899 (ediff-wordify beg-A end-A buffer-A tmp-buffer)
900 (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer))
901 (setq file-A (ediff-make-temp-file tmp-buffer "regA"))
902
903 ;; make file-B
904 (if word-mode
905 (ediff-wordify beg-B end-B buffer-B tmp-buffer)
906 (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer))
907 (setq file-B (ediff-make-temp-file tmp-buffer "regB"))
908
909 (setq overl-A (ediff-make-bullet-proof-overlay beg-A end-A buffer-A))
910 (setq overl-B (ediff-make-bullet-proof-overlay beg-B end-B buffer-B))
911 (ediff-setup buffer-A file-A
912 buffer-B file-B
913 nil nil ; buffer & file C
914 (cons (` (lambda ()
915 (delete-file (, file-A))
916 (delete-file (, file-B))))
917 startup-hooks)
918 (list (cons 'ediff-word-mode word-mode)
919 (cons 'ediff-narrow-bounds (list overl-A overl-B))
920 (cons 'ediff-job-name job-name))
921 )
922 ))
923
924
925 ;;; Merge files and buffers
926
927 ;;;###autoload
928 (defalias 'ediff-merge 'ediff-merge-files)
929
930 (defsubst ediff-merge-on-startup ()
931 (ediff-do-merge 0)
932 (ediff-eval-in-buffer ediff-buffer-C
933 (set-buffer-modified-p nil)))
934
935 ;;;###autoload
936 (defun ediff-merge-files (file-A file-B &optional startup-hooks)
937 "Merge two files without ancestor."
938 (interactive
939 (let ((dir-A (if ediff-use-last-dir
940 ediff-last-dir-A
941 default-directory))
942 dir-B f)
943 (list (setq f (ediff-read-file-name
944 "File A to merge" dir-A
945 (ediff-get-default-file-name)))
946 (ediff-read-file-name "File B to merge"
947 (setq dir-B
948 (if ediff-use-last-dir
949 ediff-last-dir-B
950 (file-name-directory f)))
951 (progn
952 (setq file-name-history
953 (cons (ediff-abbreviate-file-name
954 (expand-file-name
955 (file-name-nondirectory f)
956 dir-B))
957 file-name-history))
958 f))
959 )))
960 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
961 (ediff-files-internal file-A
962 (if (file-directory-p file-B)
963 (expand-file-name
964 (file-name-nondirectory file-A) file-B)
965 file-B)
966 nil ; file-C
967 startup-hooks
968 'ediff-merge-files))
969
970 ;;;###autoload
971 (defun ediff-merge-files-with-ancestor (file-A file-B file-ancestor
972 &optional startup-hooks)
973 "Merge two files with ancestor."
974 (interactive
975 (let ((dir-A (if ediff-use-last-dir
976 ediff-last-dir-A
977 default-directory))
978 dir-B dir-ancestor f ff)
979 (list (setq f (ediff-read-file-name
980 "File A to merge" dir-A
981 (ediff-get-default-file-name)))
982 (setq ff (ediff-read-file-name "File B to merge"
983 (setq dir-B
984 (if ediff-use-last-dir
985 ediff-last-dir-B
986 (file-name-directory f)))
987 (progn
988 (setq file-name-history
989 (cons
990 (ediff-abbreviate-file-name
991 (expand-file-name
992 (file-name-nondirectory f)
993 dir-B))
994 file-name-history))
995 f)))
996 (ediff-read-file-name "Ancestor file"
997 (setq dir-ancestor
998 (if ediff-use-last-dir
999 ediff-last-dir-ancestor
1000 (file-name-directory ff)))
1001 (progn
1002 (setq file-name-history
1003 (cons (ediff-abbreviate-file-name
1004 (expand-file-name
1005 (file-name-nondirectory ff)
1006 dir-ancestor))
1007 file-name-history))
1008 ff))
1009 )))
1010 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
1011 (ediff-files-internal file-A
1012 (if (file-directory-p file-B)
1013 (expand-file-name
1014 (file-name-nondirectory file-A) file-B)
1015 file-B)
1016 file-ancestor
1017 startup-hooks
1018 'ediff-merge-files-with-ancestor))
1019
1020 ;;;###autoload
1021 (defalias 'ediff-merge-with-ancestor 'ediff-merge-files-with-ancestor)
1022
1023 ;;;###autoload
1024 (defun ediff-merge-buffers (buffer-A buffer-B &optional startup-hooks job-name)
1025 "Merge buffers without ancestor."
1026 (interactive
1027 (let (bf)
1028 (list (setq bf (read-buffer "Buffer A to merge: "
1029 (ediff-other-buffer "") t))
1030 (read-buffer "Buffer B to merge: "
1031 (progn
1032 ;; realign buffers so that two visible bufs will be
1033 ;; at the top
1034 (save-window-excursion (other-window 1))
1035 (ediff-other-buffer bf))
1036 t))))
1037
1038 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
1039 (or job-name (setq job-name 'ediff-merge-buffers))
1040 (ediff-buffers-internal
1041 buffer-A buffer-B nil startup-hooks job-name))
1042
1043 ;;;###autoload
1044 (defun ediff-merge-buffers-with-ancestor (buffer-A
1045 buffer-B buffer-ancestor
1046 &optional startup-hooks job-name)
1047 "Merge buffers with ancestor."
1048 (interactive
1049 (let (bf bff)
1050 (list (setq bf (read-buffer "Buffer A to merge: "
1051 (ediff-other-buffer "") t))
1052 (setq bff (read-buffer "Buffer B to merge: "
1053 (progn
1054 ;; realign buffers so that two visible
1055 ;; bufs will be at the top
1056 (save-window-excursion (other-window 1))
1057 (ediff-other-buffer bf))
1058 t))
1059 (read-buffer "Ancestor buffer: "
1060 (progn
1061 ;; realign buffers so that three visible
1062 ;; bufs will be at the top
1063 (save-window-excursion (other-window 1))
1064 (ediff-other-buffer (list bf bff)))
1065 t)
1066 )))
1067
1068 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
1069 (or job-name (setq job-name 'ediff-merge-buffers-with-ancestor))
1070 (ediff-buffers-internal
1071 buffer-A buffer-B buffer-ancestor startup-hooks job-name))
1072
1073
1074 ;;;###autoload
1075 (defun ediff-merge-revisions (&optional file startup-hooks)
1076 "Run Ediff by merging two revisions of a file.
1077 The file is the optional FILE argument or the file visited by the current
1078 buffer."
1079 (interactive)
1080 (if (stringp file) (find-file file))
1081 (let (rev1 rev2)
1082 (setq rev1
1083 (read-string
1084 (format
1085 "Version 1 to merge (default: %s's latest version): "
1086 (if (stringp file)
1087 (file-name-nondirectory file) "current buffer")))
1088 rev2
1089 (read-string
1090 (format
1091 "Version 2 to merge (default: %s): "
1092 (if (stringp file)
1093 (file-name-nondirectory file) "current buffer"))))
1094 (ediff-load-version-control)
1095 ;; ancestor-revision=nil
1096 (funcall
1097 (intern (format "ediff-%S-merge-internal" ediff-version-control-package))
1098 rev1 rev2 nil startup-hooks)))
1099
1100
1101 ;;;###autoload
1102 (defun ediff-merge-revisions-with-ancestor (&optional file startup-hooks)
1103 "Run Ediff by merging two revisions of a file with a common ancestor.
1104 The file is the the optional FILE argument or the file visited by the current
1105 buffer."
1106 (interactive)
1107 (if (stringp file) (find-file file))
1108 (let (rev1 rev2 ancestor-rev)
1109 (setq rev1
1110 (read-string
1111 (format
1112 "Version 1 to merge (default: %s's latest version): "
1113 (if (stringp file)
1114 (file-name-nondirectory file) "current buffer")))
1115 rev2
1116 (read-string
1117 (format
1118 "Version 2 to merge (default: %s): "
1119 (if (stringp file)
1120 (file-name-nondirectory file) "current buffer")))
1121 ancestor-rev
1122 (read-string
1123 (format
1124 "Ancestor version (default: %s): "
1125 (if (stringp file)
1126 (file-name-nondirectory file) "current buffer"))))
1127 (ediff-load-version-control)
1128 (funcall
1129 (intern (format "ediff-%S-merge-internal" ediff-version-control-package))
1130 rev1 rev2 ancestor-rev startup-hooks)))
1131
1132 ;;;###autoload
1133 (defun run-ediff-from-cvs-buffer (pos)
1134 "Run Ediff-merge on appropriate revisions of the selected file.
1135 First run after `M-x cvs-update'. Then place the cursor on a lide describing a
1136 file and then run `run-ediff-from-cvs-buffer'."
1137 (interactive "d")
1138 (ediff-load-version-control)
1139 (let ((tin (tin-locate cvs-cookie-handle pos)))
1140 (if tin
1141 (cvs-run-ediff-on-file-descriptor tin)
1142 (error "There is no file to merge"))))
1143
1144
1145 ;;; Apply patch
1146
1147 ;;;###autoload
1148 (defun ediff-patch-file ()
1149 "Run Ediff by patching SOURCE-FILENAME."
1150 ;; This now returns the control buffer
1151 (interactive)
1152 (let (source-dir source-file patch-buf)
1153 (require 'ediff-ptch)
1154 (setq patch-buf (ediff-get-patch-buffer))
1155 (setq source-dir (cond (ediff-use-last-dir ediff-last-dir-patch)
1156 ((and (not ediff-patch-default-directory)
1157 (buffer-file-name patch-buf))
1158 (file-name-directory
1159 (expand-file-name
1160 (buffer-file-name patch-buf))))
1161 (t default-directory)))
1162 (setq source-file
1163 ;; the default is the directory, not the visited file name
1164 (ediff-read-file-name "Which file to patch? " source-dir source-dir))
1165 (ediff-dispatch-file-patching-job patch-buf source-file)))
1166
1167 ;;;###autoload
1168 (defun ediff-patch-buffer ()
1169 "Run Ediff by patching BUFFER-NAME."
1170 (interactive)
1171 (let (patch-buf)
1172 (require 'ediff-ptch)
1173 (setq patch-buf (ediff-get-patch-buffer))
1174 (ediff-patch-buffer-internal
1175 patch-buf
1176 (read-buffer "Which buffer to patch? "
1177 (cond ((eq patch-buf (current-buffer))
1178 (window-buffer (other-window 1)))
1179 (t (current-buffer)))
1180 'must-match))))
1181
1182 ;;;###autoload
1183 (defalias 'epatch 'ediff-patch-file)
1184 ;;;###autoload
1185 (defalias 'epatch-buffer 'ediff-patch-buffer)
1186
1187
1188
1189 \f
1190 ;;; Versions Control functions
1191
1192 ;;;###autoload
1193 (defun ediff-revision (&optional file startup-hooks)
1194 "Run Ediff by comparing versions of a file.
1195 The file is an optional FILE argument or the file visited by the current
1196 buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'."
1197 ;; if buffer is non-nil, use that buffer instead of the current buffer
1198 (interactive "P")
1199 (if (stringp file) (find-file file))
1200 (let (rev1 rev2)
1201 (setq rev1
1202 (read-string
1203 (format "Version 1 to compare (default: %s's latest version): "
1204 (if (stringp file)
1205 (file-name-nondirectory file) "current buffer")))
1206 rev2
1207 (read-string
1208 (format "Version 2 to compare (default: %s): "
1209 (if (stringp file)
1210 (file-name-nondirectory file) "current buffer"))))
1211 (ediff-load-version-control)
1212 (funcall
1213 (intern (format "ediff-%S-internal" ediff-version-control-package))
1214 rev1 rev2 startup-hooks)
1215 ))
1216
1217
1218 ;; Test if version control package is loaded and load if not
1219 ;; Is SILENT is non-nil, don't report error if package is not found.
1220 (defun ediff-load-version-control (&optional silent)
1221 (require 'ediff-vers)
1222 (or (featurep ediff-version-control-package)
1223 (if (locate-library (symbol-name ediff-version-control-package))
1224 (progn
1225 (message "") ; kill the message from `locate-library'
1226 (require ediff-version-control-package))
1227 (or silent
1228 (error "Version control package %S.el not found. Use vc.el instead"
1229 ediff-version-control-package)))))
1230
1231
1232 ;;;###autoload
1233 (defun ediff-version ()
1234 "Return string describing the version of Ediff.
1235 When called interactively, displays the version."
1236 (interactive)
1237 (if (interactive-p)
1238 (message (ediff-version))
1239 (format "Ediff %s of %s" ediff-version ediff-date)))
1240
1241
1242 ;;;###autoload
1243 (defun ediff-documentation (&optional node)
1244 "Display Ediff's manual.
1245 With optional NODE, goes to that node."
1246 (interactive)
1247 (let ((ctl-window ediff-control-window)
1248 (ctl-buf ediff-control-buffer))
1249
1250 (ediff-skip-unsuitable-frames)
1251 (condition-case nil
1252 (progn
1253 (pop-to-buffer (get-buffer-create "*info*"))
1254 (info (if ediff-xemacs-p "ediff.info" "ediff"))
1255 (if node
1256 (Info-goto-node node)
1257 (message "Type `i' to search for a specific topic"))
1258 (raise-frame (selected-frame)))
1259 (error (beep 1)
1260 (with-output-to-temp-buffer ediff-msg-buffer
1261 (princ ediff-BAD-INFO))
1262 (if (window-live-p ctl-window)
1263 (progn
1264 (select-window ctl-window)
1265 (set-window-buffer ctl-window ctl-buf)))))))
1266
1267
1268
1269
1270 ;;; Local Variables:
1271 ;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun)
1272 ;;; eval: (put 'ediff-eval-in-buffer 'lisp-indent-hook 1)
1273 ;;; eval: (put 'ediff-eval-in-buffer 'edebug-form-spec '(form body))
1274 ;;; End:
1275
1276 (provide 'ediff)
1277 (require 'ediff-util)
1278
1279 ;;; ediff.el ends here