1 ;;; ediff.el --- a comprehensive visual interface to diff & patch
3 ;; Copyright (C) 1994, 1995 Free Software Foundation, Inc.
5 ;; Author: Michael Kifer <kifer@cs.sunysb.edu>
6 ;; Created: February 2, 1994
7 ;; Keywords: comparing, merging, patching, version control.
9 (defconst ediff-version
"2.47" "The current version of Ediff")
10 (defconst ediff-date
"October 11, 1995" "Date of last update")
12 ;; This file is part of GNU Emacs.
14 ;; GNU Emacs is free software; you can redistribute it and/or modify
15 ;; it under the terms of the GNU General Public License as published by
16 ;; the Free Software Foundation; either version 2, or (at your option)
19 ;; GNU Emacs is distributed in the hope that it will be useful,
20 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
21 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 ;; GNU General Public License for more details.
24 ;; You should have received a copy of the GNU General Public License
25 ;; along with GNU Emacs; see the file COPYING. If not, write to the
26 ;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
27 ;; Boston, MA 02111-1307, USA.
31 ;; Never read that diff output again!
32 ;; Apply patch selectively, like a pro!
35 ;; This package provides a convenient way of simultaneous browsing through
36 ;; the differences between a pair (or a triple) of files or buffers. The
37 ;; files being compared, file-A, file-B, and file-C (if applicable) are
38 ;; shown in separate windows (side by side, one above the another, or in
39 ;; separate frames), and the differences are highlighted as you step
40 ;; through them. You can also copy difference regions from one buffer to
41 ;; another (and recover old differences if you change your mind).
43 ;; Ediff also supports merging operations on files and buffers, including
44 ;; merging using ancestor versions. Both comparison and merging operations can
45 ;; be performed on directories, i.e., by pairwise comparison of files in those
48 ;; In addition, Ediff can apply a patch to a file and then let you step
49 ;; though both files, the patched and the original one, simultaneously,
50 ;; difference-by-difference. You can even apply a patch right out of a
51 ;; mail buffer, i.e., patches received by mail don't even have to be saved.
52 ;; Since Ediff lets you copy differences between buffers, you can, in
53 ;; effect, apply patches selectively (i.e., you can copy a difference
54 ;; region from file_orig to file, thereby undoing any particular patch that
57 ;; Ediff is aware of version control, which lets the user compare
58 ;; files with their older versions. Ediff can also work with remote and
59 ;; compressed files. Details are given below.
61 ;; Finally, Ediff supports directory-level comparison and merging operations.
62 ;; See the on-line manual for details.
64 ;; This package builds upon the ideas borrowed from emerge.el and several
65 ;; Ediff's functions are adaptations from emerge.el. Much of the functionality
66 ;; Ediff provides is also influenced by emerge.el.
68 ;; The present version of Ediff supersedes Emerge. It provides a superior user
69 ;; interface and has numerous major features not found in Emerge. In
70 ;; particular, it can do patching, and 2-way and 3-way file comparison,
71 ;; merging, and directory operations.
75 ;; 1. The undo command doesn't restore deleted regions well. That is, if
76 ;; you delete all characters in a difference region and then invoke
77 ;; `undo', the reinstated text will most likely be inserted outside of
78 ;; what Ediff thinks is the current difference region. (This problem
79 ;; doesn't seem to exist with XEmacs.)
81 ;; If at any point you feel that difference regions are no longer correct,
82 ;; you can hit '!' to recompute the differences.
84 ;; 2. On a monochrome display, the repertoire of faces with which to
85 ;; highlight fine differences is limited. By default, Ediff is using
86 ;; underlining. However, if the region is already underlined by some other
87 ;; overlays, there is no simple way to temporarily remove that residual
88 ;; underlining. This problem occurs when a buffer is highlighted with
89 ;; hilit19.el or font-lock.el packages. If this residual highlighting gets
90 ;; in the way, you can do the following. Both font-lock.el and hilit19.el
91 ;; provide commands for unhighlighting buffers. You can either place these
92 ;; commands in `ediff-prepare-buffer-hook' (which will unhighlight every
93 ;; buffer used by Ediff) or you can execute them interactively, at any time
98 ;; Ediff was inspired by Dale R. Worley's <drw@math.mit.edu> emerge.el.
99 ;; Ediff would not have been possible without the help and encouragement of
100 ;; its many users. See Ediff on-line Info for the full list of those who
101 ;; helped. Improved defaults in Ediff file-name reading commands.
105 (require 'ediff-init
)
106 (require 'ediff-mult
)
108 (defvar ediff-use-last-dir nil
109 "*If t, Ediff uses previous directory as default when reading file name.")
111 (defvar ediff-last-dir-A nil
112 "Last directory used by an Ediff command for file-A.")
113 (defvar ediff-last-dir-B nil
114 "Last directory used by an Ediff command for file-B.")
115 (defvar ediff-last-dir-C nil
116 "Last directory used by an Ediff command for file-C.")
117 (defvar ediff-last-dir-ancestor nil
118 "Last directory used by an Ediff command for the ancestor file.")
119 (defvar ediff-last-dir-patch nil
120 "Last directory used by an Ediff command for file to patch.")
124 (defvar ediff-backup-extension
125 (if (memq system-type
'(vax-vms axp-vms emx ms-dos windows-nt windows-95
))
127 "Default backup extension for the patch program.")
130 (defun ediff-patch-file (source-filename &optional startup-hooks job-name
)
131 "Run Ediff by patching FILE-TP-PATCH."
132 ;; This now returns the control buffer
134 (list (ediff-read-file-name
136 (if ediff-use-last-dir
139 (ediff-get-default-file-name))))
141 (setq source-filename
(expand-file-name source-filename
))
142 (ediff-get-patch-buffer
143 (if (eq job-name
'ediff-patch-buffer
)
144 (ediff-eval-in-buffer (get-file-buffer source-filename
)
146 (file-name-directory source-filename
)))
148 (let* ((backup-extension
149 ;; if the user specified a -b option, extract the backup
150 ;; extension from there; else use ediff-backup-extension
151 (substring ediff-patch-options
152 (if (string-match "-b[ \t]+" ediff-patch-options
)
154 (if (string-match "-b[ \t]+[^ \t]+" ediff-patch-options
)
156 (shell-file-name ediff-shell
)
157 ;; ediff-find-file may use a temp file to do the patch
158 ;; so, we save source-filename and true-source-filename as a var
159 ;; that initially is source-filename but may be changed to a temp
160 ;; file for the purpose of patching.
161 (true-source-filename source-filename
)
162 (target-filename source-filename
)
163 target-buf buf-to-patch file-name-magic-p ctl-buf
)
165 ;; if the user didn't specify a backup extension, use
166 ;; ediff-backup-extension
167 (if (string= backup-extension
"")
168 (setq backup-extension ediff-backup-extension
))
170 ;; Make a temp file, if source-filename has a magic file handler (or if
171 ;; it is handled via auto-mode-alist and similar magic).
172 ;; Check if there is a buffer visiting source-filename and if they are in
173 ;; sync; arrange for the deletion of temp file.
174 (ediff-find-file 'true-source-filename
'buf-to-patch
175 'ediff-last-dir-patch
'startup-hooks
)
177 ;; Check if source file name has triggered black magic, such as file name
178 ;; handlers or auto mode alist, and make a note of it.
179 ;; true-source-filename should be either the original name or a
180 ;; temporary file where we put the after-product of the file handler.
181 (setq file-name-magic-p
(not (equal (file-truename true-source-filename
)
182 (file-truename source-filename
))))
184 ;; Checkout orig file, if necessary, so that the patched file could be
186 (if (ediff-file-checked-in-p (buffer-file-name buf-to-patch
))
187 (ediff-toggle-read-only buf-to-patch
))
189 (ediff-eval-in-buffer ediff-patch-diagnostics
190 (message "Applying patch ... ")
192 ;; always pass patch the -f option, so it won't ask any questions
193 (shell-command-on-region
194 (point-min) (point-max)
195 (format "%s -f %s -b %s %s"
196 ediff-patch-program ediff-patch-options
198 (expand-file-name true-source-filename
))
200 ;;(message "Applying patch ... done")(sit-for 0)
201 (switch-to-buffer ediff-patch-diagnostics
)
202 (sit-for 0) ; synchronize - let the user see diagnostics
204 (or (file-exists-p (concat true-source-filename backup-extension
))
205 (error "Patch failed or didn't modify the original file"))
207 ;; If black magic is involved, apply patch to a temp copy of the
208 ;; file. Otherwise, apply patch to the orig copy. If patch is applied
209 ;; to temp copy, we name the result old-name_patched for local files
210 ;; and temp-copy_patched for remote files. The orig file name isn't
211 ;; changed, and the temp copy of the original is later deleted.
212 ;; Without magic, the original file is renamed (usually into
213 ;; old-name_orig) and the result of patching will have the same name as
215 (if (not file-name-magic-p
)
216 (ediff-eval-in-buffer buf-to-patch
217 (set-visited-file-name (concat source-filename backup-extension
))
218 (set-buffer-modified-p nil
))
220 ;; Black magic in effect.
221 ;; If orig file was remote, put the patched file in the temp directory.
222 ;; If orig file is local, put the patched file in the directory of
224 (setq target-filename
226 (if (ediff-file-remote-p (file-truename source-filename
))
231 (rename-file true-source-filename target-filename t
)
233 ;; arrange that the temp copy of orig will be deleted
234 (rename-file (concat true-source-filename backup-extension
)
235 true-source-filename t
))
237 ;; make orig buffer read-only
239 (cons 'ediff-set-read-only-in-buf-A startup-hooks
))
241 ;; set up a buf for the patched file
242 (setq target-buf
(find-file-noselect target-filename
))
245 (ediff-buffers-internal
246 buf-to-patch target-buf nil
247 startup-hooks
(or job-name
'ediff-patch-file
)))
249 (bury-buffer ediff-patch-diagnostics
)
250 (message "Patch diagnostics are available in buffer %s"
251 (buffer-name ediff-patch-diagnostics
))
254 (defun ediff-set-read-only-in-buf-A ()
255 "Used as a startup hook to set `_orig' patch file read-only."
256 (ediff-eval-in-buffer ediff-buffer-A
257 (toggle-read-only 1)))
259 ;; Return a plausible default for ediff's first file:
260 ;; In dired, return the file name under the point, unless it is a directory
261 ;; If the buffer has a file name, return that file name.
262 (defun ediff-get-default-file-name ()
263 (cond ((eq major-mode
'dired-mode
)
264 (let ((f (dired-get-filename nil
'no-error
)))
265 (if (and (stringp f
) (not (file-directory-p f
)))
267 ((buffer-file-name (current-buffer))
268 (file-name-nondirectory (buffer-file-name (current-buffer))))
272 (defalias 'epatch
'ediff-patch-file
)
274 (defalias 'epatch-buffer
'ediff-patch-buffer
)
276 ;;; Compare files/buffers
279 (defun ediff-files (file-A file-B
&optional startup-hooks
)
280 "Run Ediff on a pair of files, FILE-A and FILE-B."
282 (let ((dir-A (if ediff-use-last-dir
286 (list (setq f
(ediff-read-file-name
287 "File A to compare" dir-A
288 (ediff-get-default-file-name)))
289 (ediff-read-file-name "File B to compare"
291 (if ediff-use-last-dir
293 (file-name-directory f
)))
295 (setq file-name-history
296 (cons (ediff-abbreviate-file-name
298 (file-name-nondirectory f
)
303 (ediff-files-internal file-A
304 (if (file-directory-p file-B
)
306 (file-name-nondirectory file-A
) file-B
)
313 (defun ediff-files3 (file-A file-B file-C
&optional startup-hooks
)
314 "Run Ediff on three files, FILE-A, FILE-B, and FILE-C."
316 (let ((dir-A (if ediff-use-last-dir
320 (list (setq f
(ediff-read-file-name
321 "File A to compare" dir-A
322 (ediff-get-default-file-name)))
323 (setq ff
(ediff-read-file-name "File B to compare"
325 (if ediff-use-last-dir
327 (file-name-directory f
)))
329 (setq file-name-history
331 (ediff-abbreviate-file-name
333 (file-name-nondirectory f
)
337 (ediff-read-file-name "File C to compare"
338 (setq dir-C
(if ediff-use-last-dir
340 (file-name-directory ff
)))
342 (setq file-name-history
343 (cons (ediff-abbreviate-file-name
345 (file-name-nondirectory ff
)
350 (ediff-files-internal file-A
351 (if (file-directory-p file-B
)
353 (file-name-nondirectory file-A
) file-B
)
355 (if (file-directory-p file-C
)
357 (file-name-nondirectory file-A
) file-C
)
363 (defalias 'ediff3
'ediff-files3
)
366 (defun ediff-find-file (file-var buffer-name
&optional last-dir hooks-var
)
367 "Visit FILE and arrange its buffer to Ediff's liking.
368 FILE is actually a variable symbol that must contain a true file name.
369 BUFFER-NAME is a variable symbol, which will get the buffer object into which
370 FILE is read. LAST-DIR is the directory variable symbol where FILE's
371 directory name should be returned. HOOKS is a variable symbol that will be
372 assigned the hook to be executed after `ediff-startup' is finished.
373 `ediff-find-file' arranges that the temp files it might create will be
375 (let* ((file (symbol-value file-var
))
376 (file-magic (find-file-name-handler file
'find-file-noselect
))
377 (temp-file-name-prefix (file-name-nondirectory file
)))
378 (cond ((not (file-readable-p file
))
379 (error "File `%s' does not exist or is not readable" file
))
380 ((file-directory-p file
)
381 (error "File `%s' is a directory" file
)))
383 ;; some of the command, below, require full file name
384 (setq file
(expand-file-name file
))
386 ;; Record the directory of the file
388 (set last-dir
(expand-file-name (file-name-directory file
))))
391 (set buffer-name
(find-file-noselect file
))
393 (ediff-eval-in-buffer (symbol-value buffer-name
)
394 (widen) ; Make sure the entire file is seen
395 (cond (file-magic ;; file has handler, such as jka-compr-handler or
396 ;; ange-ftp-hook-function--arrange for temp file
397 (ediff-verify-file-buffer 'magic
)
399 (ediff-make-temp-file
400 (current-buffer) temp-file-name-prefix
))
401 (set hooks-var
(cons (` (lambda () (delete-file (, file
))))
402 (symbol-value hooks-var
))))
403 ;; file processed via auto-mode-alist, a la uncompress.el
404 ((not (equal (file-truename file
)
405 (file-truename (buffer-file-name))))
407 (ediff-make-temp-file
408 (current-buffer) temp-file-name-prefix
))
409 (set hooks-var
(cons (` (lambda () (delete-file (, file
))))
410 (symbol-value hooks-var
))))
411 (t ;; plain file---just check that the file matches the buffer
412 (ediff-verify-file-buffer))))
413 (set file-var file
)))
415 (defun ediff-files-internal (file-A file-B file-C startup-hooks job-name
)
416 (let (buf-A buf-B buf-C
)
417 (message "Reading file %s ... " file-A
)
419 (ediff-find-file 'file-A
'buf-A
'ediff-last-dir-A
'startup-hooks
)
420 (message "Reading file %s ... " file-B
)
422 (ediff-find-file 'file-B
'buf-B
'ediff-last-dir-B
'startup-hooks
)
425 (message "Reading file %s ... " file-C
)
429 (if (eq job-name
'ediff-merge-files-with-ancestor
)
430 'ediff-last-dir-ancestor
'ediff-last-dir-C
)
432 (ediff-setup buf-A file-A
436 (list (cons 'ediff-job-name job-name
)))))
440 (defalias 'ediff
'ediff-files
)
444 (defun ediff-buffers (buffer-A buffer-B
&optional startup-hooks job-name
)
445 "Run Ediff on a pair of buffers, BUFFER-A and BUFFER-B."
448 (list (setq bf
(read-buffer "Buffer A to compare: "
449 (ediff-other-buffer "") t
))
450 (read-buffer "Buffer B to compare: "
452 ;; realign buffers so that two visible bufs will be
454 (save-window-excursion (other-window 1))
455 (ediff-other-buffer bf
))
458 (or job-name
(setq job-name
'ediff-buffers
))
459 (ediff-buffers-internal buffer-A buffer-B nil startup-hooks job-name
))
462 (defun ediff-buffers3 (buffer-A buffer-B buffer-C
463 &optional startup-hooks job-name
)
464 "Run Ediff on three buffers, BUFFER-A, BUFFER-B, and BUFFER-C."
467 (list (setq bf
(read-buffer "Buffer A to compare: "
468 (ediff-other-buffer "") t
))
469 (setq bff
(read-buffer "Buffer B to compare: "
471 ;; realign buffers so that two visible
472 ;; bufs will be at the top
473 (save-window-excursion (other-window 1))
474 (ediff-other-buffer bf
))
476 (read-buffer "Buffer C to compare: "
478 ;; realign buffers so that three visible
479 ;; bufs will be at the top
480 (save-window-excursion (other-window 1))
481 (ediff-other-buffer (list bf bff
)))
485 (or job-name
(setq job-name
'ediff-buffers3
))
486 (ediff-buffers-internal buffer-A buffer-B buffer-C startup-hooks job-name
))
490 (defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name
)
491 (let* ((buf-A-file-name (buffer-file-name (get-buffer buf-A
)))
492 (buf-B-file-name (buffer-file-name (get-buffer buf-B
)))
493 (buf-C-is-alive (ediff-buffer-live-p buf-C
))
494 (buf-C-file-name (if buf-C-is-alive
495 (buffer-file-name (get-buffer buf-B
))))
496 file-A file-B file-C
)
497 (if (not (ediff-buffer-live-p buf-A
))
498 (error "Buffer %S doesn't exist" buf-A
))
499 (if (not (ediff-buffer-live-p buf-B
))
500 (error "Buffer %S doesn't exist" buf-B
))
501 (let ((ediff-job-name job-name
))
502 (if (and ediff-3way-comparison-job
503 (not buf-C-is-alive
))
504 (error "Buffer %S doesn't exist" buf-C
)))
505 (if (stringp buf-A-file-name
)
506 (setq buf-A-file-name
(file-name-nondirectory buf-A-file-name
)))
507 (if (stringp buf-B-file-name
)
508 (setq buf-B-file-name
(file-name-nondirectory buf-B-file-name
)))
509 (if (stringp buf-C-file-name
)
510 (setq buf-C-file-name
(file-name-nondirectory buf-C-file-name
)))
512 (setq file-A
(ediff-make-temp-file buf-A buf-A-file-name
))
513 (setq file-B
(ediff-make-temp-file buf-B buf-B-file-name
))
515 (setq file-C
(ediff-make-temp-file buf-C buf-C-file-name
)))
517 (ediff-setup (get-buffer buf-A
) file-A
518 (get-buffer buf-B
) file-B
519 (if buf-C-is-alive
(get-buffer buf-C
))
522 (delete-file (, file-A
))
523 (delete-file (, file-B
))
524 (if (stringp (, file-C
)) (delete-file (, file-C
)))
527 (list (cons 'ediff-job-name job-name
))
531 ;;; Directory and file group operations
533 ;; Get appropriate default name for directory:
534 ;; If ediff-use-last-dir, use ediff-last-dir-A.
535 ;; In dired mode, use the directory that is under the point (if any);
536 ;; otherwise, use default-directory
537 (defun ediff-get-default-directory-name ()
538 (cond (ediff-use-last-dir ediff-last-dir-A
)
539 ((eq major-mode
'dired-mode
)
540 (let ((f (dired-get-filename nil
'noerror
)))
541 (if (and (stringp f
) (file-directory-p f
))
544 (t default-directory
)))
548 (defun ediff-directories (dir1 dir2 regexp
)
549 "Run Ediff on a pair of directories, DIR1 and DIR2, comparing files that have
550 the same name in both. The third argument, REGEXP, is a regular expression that
551 further filters the file names."
553 (let ((dir-A (ediff-get-default-directory-name))
555 (list (setq f
(ediff-read-file-name "Directory A to compare" dir-A nil
))
556 (ediff-read-file-name "Directory B to compare"
557 (if ediff-use-last-dir
559 (ediff-strip-last-dir f
))
561 (read-string "Filter through regular expression: "
562 nil ediff-filtering-regexp-history
)
564 (ediff-directories-internal
565 dir1 dir2 nil regexp
'ediff-files
'ediff-directories
569 (defalias 'edirs
'ediff-directories
)
573 (defun ediff-directory-revisions (dir1 regexp
)
574 "Run Ediff on a directory, DIR1, comparing its files with their revisions.
575 The second argument, REGEXP, is a regular expression that filters the file
576 names. Only the files that are under revision control are taken into account."
578 (let ((dir-A (ediff-get-default-directory-name)))
579 (list (ediff-read-file-name
580 "Directory to compare with revision" dir-A nil
)
581 (read-string "Filter through regular expression: "
582 nil ediff-filtering-regexp-history
)
584 (ediff-directory-revisions-internal
585 dir1 regexp
'ediff-revision
'ediff-directory-revisions
589 (defalias 'edir-revisions
'ediff-directory-revisions
)
593 (defun ediff-directories3 (dir1 dir2 dir3 regexp
)
594 "Run Ediff on three directories, DIR1, DIR2, and DIR3, comparing files that
595 have the same name in all three. The last argument, REGEXP, is a regular
596 expression that further filters the file names."
598 (let ((dir-A (ediff-get-default-directory-name))
600 (list (setq f
(ediff-read-file-name "Directory A to compare" dir-A nil
))
601 (setq f
(ediff-read-file-name "Directory B to compare"
602 (if ediff-use-last-dir
604 (ediff-strip-last-dir f
))
606 (ediff-read-file-name "Directory C to compare"
607 (if ediff-use-last-dir
609 (ediff-strip-last-dir f
))
611 (read-string "Filter through regular expression: "
612 nil ediff-filtering-regexp-history
)
614 (ediff-directories-internal
615 dir1 dir2 dir3 regexp
'ediff-files3
'ediff-directories3
619 (defalias 'edirs3
'ediff-directories3
)
622 (defun ediff-merge-directories (dir1 dir2 regexp
)
623 "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have
624 the same name in both. The third argument, REGEXP, is a regular expression that
625 further filters the file names."
627 (let ((dir-A (ediff-get-default-directory-name))
629 (list (setq f
(ediff-read-file-name "Directory A to merge" dir-A nil
))
630 (ediff-read-file-name "Directory B to merge"
631 (if ediff-use-last-dir
633 (ediff-strip-last-dir f
))
635 (read-string "Filter through regular expression: "
636 nil ediff-filtering-regexp-history
)
638 (ediff-directories-internal
639 dir1 dir2 nil regexp
'ediff-merge-files
'ediff-merge-directories
643 (defalias 'edirs-merge
'ediff-merge-directories
)
646 (defun ediff-merge-directories-with-ancestor (dir1 dir2 dir3 regexp
)
647 "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have
648 the same name in both. The third argument, REGEXP, is a regular expression that
649 further filters the file names."
651 (let ((dir-A (ediff-get-default-directory-name))
653 (list (setq f
(ediff-read-file-name "Directory A to merge" dir-A nil
))
654 (setq f
(ediff-read-file-name "Directory B to merge"
655 (if ediff-use-last-dir
657 (ediff-strip-last-dir f
))
659 (ediff-read-file-name "Ancestor directory: "
660 (if ediff-use-last-dir
662 (ediff-strip-last-dir f
))
664 (read-string "Filter through regular expression: "
665 nil ediff-filtering-regexp-history
)
667 (ediff-directories-internal
668 dir1 dir2 dir3 regexp
669 'ediff-merge-files-with-ancestor
'ediff-merge-directories-with-ancestor
673 (defun ediff-merge-directory-revisions (dir1 regexp
)
674 "Run Ediff on a directory, DIR1, merging its files with their revisions.
675 The second argument, REGEXP, is a regular expression that filters the file
676 names. Only the files that are under revision control are taken into account."
678 (let ((dir-A (ediff-get-default-directory-name)))
679 (list (ediff-read-file-name
680 "Directory to merge with revisions" dir-A nil
)
681 (read-string "Filter through regular expression: "
682 nil ediff-filtering-regexp-history
)
684 (ediff-directory-revisions-internal
685 dir1 regexp
'ediff-merge-revisions
'ediff-merge-directory-revisions
689 (defalias 'edir-merge-revisions
'ediff-merge-directory-revisions
)
692 (defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp
)
693 "Run Ediff on a directory, DIR1, merging its files with their revisions and ancestors.
694 The second argument, REGEXP, is a regular expression that filters the file
695 names. Only the files that are under revision control are taken into account."
697 (let ((dir-A (ediff-get-default-directory-name)))
698 (list (ediff-read-file-name
699 "Directory to merge with revisions and ancestors" dir-A nil
)
700 (read-string "Filter through regular expression: "
701 nil ediff-filtering-regexp-history
)
703 (ediff-directory-revisions-internal
704 dir1 regexp
'ediff-merge-revisions-with-ancestor
705 'ediff-merge-directory-revisions-with-ancestor
710 'edir-merge-revisions-with-ancestor
711 'ediff-merge-directory-revisions-with-ancestor
)
714 (defalias 'edirs-merge-with-ancestor
'ediff-merge-directories-with-ancestor
)
716 ;; Run ediff-action (ediff-files, ediff-merge, ediff-merge-with-ancestors)
717 ;; on a pair of directories (three directories, in case of ancestor).
718 ;; The third argument, REGEXP, is a regular expression that further filters the
720 ;; JOBNAME is the symbol indicating the meta-job to be performed.
721 (defun ediff-directories-internal (dir1 dir2 dir3 regexp
723 &optional startup-hooks
)
724 ;; ediff-read-file-name is set to attach a previously entered file name if
725 ;; the currently entered file is a directory. This code takes care of that.
726 (setq dir1
(if (file-directory-p dir1
) dir1
(file-name-directory dir1
))
727 dir2
(if (file-directory-p dir2
) dir2
(file-name-directory dir2
)))
730 (setq dir3
(if (file-directory-p dir3
) dir3
(file-name-directory dir3
))))
732 (cond ((string= dir1 dir2
)
733 (error "Directories A and B are the same: %s" dir1
))
734 ((and (eq jobname
'ediff-directories3
)
736 (error "Directories A and C are the same: %s" dir1
))
737 ((and (eq jobname
'ediff-directories3
)
739 (error "Directories B and C are the same: %s" dir1
)))
741 (let (diffs ; var where ediff-intersect-directories returns the diff list
743 (setq file-list
(ediff-intersect-directories
744 jobname
'diffs regexp dir1 dir2 dir3
))
746 ;; this sets various vars in the meta buffer inside
747 ;; ediff-prepare-meta-buffer
749 ;; tell what to do if the user clicks on a session record
750 (setq ediff-session-action-function
(quote (, action
)))
751 ;; set ediff-dir-difference-list
752 (setq ediff-dir-difference-list
(quote (, diffs
)))))
754 (setq meta-buf
(ediff-prepare-meta-buffer
757 "*Ediff Session Group Panel"
758 'ediff-redraw-directory-group-buffer
761 (ediff-show-meta-buffer meta-buf
)
764 (defun ediff-directory-revisions-internal (dir1 regexp action jobname
765 &optional startup-hooks
)
766 (setq dir1
(if (file-directory-p dir1
) dir1
(file-name-directory dir1
)))
767 (let (file-list meta-buf
)
769 (ediff-get-directory-files-under-revision jobname regexp dir1
))
771 ;; this sets various vars in the meta buffer inside
772 ;; ediff-prepare-meta-buffer
774 ;; tell what to do if the user clicks on a session record
775 (setq ediff-session-action-function
(quote (, action
)))
778 (setq meta-buf
(ediff-prepare-meta-buffer
781 "*Ediff Session Group Panel"
782 'ediff-redraw-directory-group-buffer
785 (ediff-show-meta-buffer meta-buf
)
789 ;;; Compare regions and windows
792 (defun ediff-windows-wordwise (dumb-mode &optional wind-A wind-B startup-hooks
)
793 "Compare WIND-A and WIND-B, which are selected by clicking, wordwise.
794 With prefix argument, DUMB-MODE, or on a non-windowing display, works as
796 If WIND-A is nil, use selected window.
797 If WIND-B is nil, use window next to WIND-A."
799 (ediff-windows dumb-mode wind-A wind-B
800 startup-hooks
'ediff-windows-wordwise
'word-mode
))
803 (defun ediff-windows-linewise (dumb-mode &optional wind-A wind-B startup-hooks
)
804 "Compare WIND-A and WIND-B, which are selected by clicking, linewise.
805 With prefix argument, DUMB-MODE, or on a non-windowing display, works as
807 If WIND-A is nil, use selected window.
808 If WIND-B is nil, use window next to WIND-A."
810 (ediff-windows dumb-mode wind-A wind-B
811 startup-hooks
'ediff-windows-linewise nil
))
813 ;; Compare WIND-A and WIND-B, which are selected by clicking.
814 ;; With prefix argument, DUMB-MODE, or on a non-windowing display,
816 ;; If WIND-A is nil, use selected window.
817 ;; If WIND-B is nil, use window next to WIND-A.
818 (defun ediff-windows (dumb-mode wind-A wind-B startup-hooks job-name word-mode
)
819 (if (or dumb-mode
(not (ediff-window-display-p)))
820 (setq wind-A
(ediff-get-next-window wind-A nil
)
821 wind-B
(ediff-get-next-window wind-B wind-A
))
822 (setq wind-A
(ediff-get-window-by-clicking wind-A nil
1)
823 wind-B
(ediff-get-window-by-clicking wind-B wind-A
2)))
825 (let ((buffer-A (window-buffer wind-A
))
826 (buffer-B (window-buffer wind-B
))
827 beg-A end-A beg-B end-B
)
830 (save-window-excursion
831 (sit-for 0) ; sync before using window-start/end -- a precaution
832 (select-window wind-A
)
833 (setq beg-A
(window-start)
835 (select-window wind-B
)
836 (setq beg-B
(window-start)
837 end-B
(window-end))))
838 (ediff-regions-internal
839 buffer-A beg-A end-A buffer-B beg-B end-B
840 startup-hooks job-name word-mode
)))
843 (defun ediff-regions-wordwise (buffer-A buffer-B
&optional startup-hooks
)
844 "Run Ediff on a pair of regions in two different buffers.
845 Regions \(i.e., point and mark\) are assumed to be set in advance.
846 This function is effective only for relatively small regions, up to 200
847 lines. For large regions, use `ediff-regions-linewise'."
850 (list (setq bf
(read-buffer "Region's A buffer: "
851 (ediff-other-buffer "") t
))
852 (read-buffer "Region's B buffer: "
854 ;; realign buffers so that two visible bufs will be
856 (save-window-excursion (other-window 1))
857 (ediff-other-buffer bf
))
859 (if (not (ediff-buffer-live-p buffer-A
))
860 (error "Buffer %S doesn't exist" buffer-A
))
861 (if (not (ediff-buffer-live-p buffer-B
))
862 (error "Buffer %S doesn't exist" buffer-B
))
865 (let (reg-A-beg reg-A-end reg-B-beg reg-B-end
)
867 (set-buffer buffer-A
)
868 (setq reg-A-beg
(region-beginning)
869 reg-A-end
(region-end))
870 (set-buffer buffer-B
)
871 (setq reg-B-beg
(region-beginning)
872 reg-B-end
(region-end)))
874 (ediff-regions-internal
875 (get-buffer buffer-A
) reg-A-beg reg-A-end
876 (get-buffer buffer-B
) reg-B-beg reg-B-end
877 startup-hooks
'ediff-regions-wordwise
'word-mode
)))
880 (defun ediff-regions-linewise (buffer-A buffer-B
&optional startup-hooks
)
881 "Run Ediff on a pair of regions in two different buffers.
882 Regions \(i.e., point and mark\) are assumed to be set in advance.
883 Each region is enlarged to contain full lines.
884 This function is effective for large regions, over 100-200
885 lines. For small regions, use `ediff-regions-wordwise'."
888 (list (setq bf
(read-buffer "Region A's buffer: "
889 (ediff-other-buffer "") t
))
890 (read-buffer "Region B's buffer: "
892 ;; realign buffers so that two visible bufs will be
894 (save-window-excursion (other-window 1))
895 (ediff-other-buffer bf
))
897 (if (not (ediff-buffer-live-p buffer-A
))
898 (error "Buffer %S doesn't exist" buffer-A
))
899 (if (not (ediff-buffer-live-p buffer-B
))
900 (error "Buffer %S doesn't exist" buffer-B
))
902 (let (reg-A-beg reg-A-end reg-B-beg reg-B-end
)
904 (set-buffer buffer-A
)
905 (setq reg-A-beg
(region-beginning)
906 reg-A-end
(region-end))
907 ;; enlarge the region to hold full lines
908 (goto-char reg-A-beg
)
910 (setq reg-A-beg
(point))
911 (goto-char reg-A-end
)
913 (or (eobp) (forward-char)) ; include the newline char
914 (setq reg-A-end
(point))
916 (set-buffer buffer-B
)
917 (setq reg-B-beg
(region-beginning)
918 reg-B-end
(region-end))
919 ;; enlarge the region to hold full lines
920 (goto-char reg-A-beg
)
921 (goto-char reg-B-beg
)
923 (setq reg-B-beg
(point))
924 (goto-char reg-B-end
)
926 (or (eobp) (forward-char)) ; include the newline char
927 (setq reg-B-end
(point))
930 (ediff-regions-internal
931 (get-buffer buffer-A
) reg-A-beg reg-A-end
932 (get-buffer buffer-B
) reg-B-beg reg-B-end
933 startup-hooks
'ediff-regions-linewise nil
))) ; no word mode
935 ;; compare region beg-A to end-A of buffer-A
936 ;; to regions beg-B -- end-B in buffer-B.
937 (defun ediff-regions-internal (buffer-A beg-A end-A buffer-B beg-B end-B
938 startup-hooks job-name word-mode
)
939 (let ((tmp-buffer (get-buffer-create ediff-tmp-buffer
))
943 ;; in case beg/end-A/B aren't markers--make them into markers
944 (ediff-eval-in-buffer buffer-A
945 (setq beg-A
(move-marker (make-marker) beg-A
)
946 end-A
(move-marker (make-marker) end-A
)))
947 (ediff-eval-in-buffer buffer-B
948 (setq beg-B
(move-marker (make-marker) beg-B
)
949 end-B
(move-marker (make-marker) end-B
)))
951 (if (and (eq buffer-A buffer-B
)
952 (or (and (< beg-A end-B
) (<= beg-B beg-A
)) ; b-B b-A e-B
953 (and (< beg-B end-A
) (<= end-A end-B
)))) ; b-B e-A e-B
955 (with-output-to-temp-buffer ediff-msg-buffer
957 You have requested to compare overlapping regions of the same buffer.
959 In this case, Ediff's highlighting may be confusing---in the same window,
960 you may see highlighted regions that belong to different regions.
962 Continue anyway? (y/n) "))
964 (if (y-or-n-p "Continue anyway? ")
966 (error "%S aborted" job-name
))))
970 (ediff-wordify beg-A end-A buffer-A tmp-buffer
)
971 (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer
))
972 (setq file-A
(ediff-make-temp-file tmp-buffer
"regA"))
976 (ediff-wordify beg-B end-B buffer-B tmp-buffer
)
977 (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer
))
978 (setq file-B
(ediff-make-temp-file tmp-buffer
"regB"))
980 (setq overl-A
(ediff-make-bullet-proof-overlay beg-A end-A buffer-A
))
981 (setq overl-B
(ediff-make-bullet-proof-overlay beg-B end-B buffer-B
))
982 (ediff-setup buffer-A file-A
984 nil nil
; buffer & file C
986 (delete-file (, file-A
))
987 (delete-file (, file-B
))))
989 (list (cons 'ediff-word-mode word-mode
)
990 (cons 'ediff-narrow-bounds
(list overl-A overl-B
))
991 (cons 'ediff-job-name job-name
))
996 ;;; Merge files and buffers
999 (defalias 'ediff-merge
'ediff-merge-files
)
1001 (defsubst ediff-merge-on-startup
()
1003 (ediff-eval-in-buffer ediff-buffer-C
1004 (set-buffer-modified-p nil
)))
1007 (defun ediff-merge-files (file-A file-B
&optional startup-hooks
)
1008 "Merge two files without ancestor."
1010 (let ((dir-A (if ediff-use-last-dir
1014 (list (setq f
(ediff-read-file-name
1015 "File A to merge" dir-A
1016 (ediff-get-default-file-name)))
1017 (ediff-read-file-name "File B to merge"
1019 (if ediff-use-last-dir
1021 (file-name-directory f
)))
1023 (setq file-name-history
1024 (cons (ediff-abbreviate-file-name
1026 (file-name-nondirectory f
)
1031 (setq startup-hooks
(cons 'ediff-merge-on-startup startup-hooks
))
1032 (ediff-files-internal file-A
1033 (if (file-directory-p file-B
)
1035 (file-name-nondirectory file-A
) file-B
)
1039 'ediff-merge-files
))
1042 (defun ediff-merge-files-with-ancestor (file-A file-B file-ancestor
1043 &optional startup-hooks
)
1044 "Merge two files with ancestor."
1046 (let ((dir-A (if ediff-use-last-dir
1049 dir-B dir-ancestor f ff
)
1050 (list (setq f
(ediff-read-file-name
1051 "File A to merge" dir-A
1052 (ediff-get-default-file-name)))
1053 (setq ff
(ediff-read-file-name "File B to merge"
1055 (if ediff-use-last-dir
1057 (file-name-directory f
)))
1059 (setq file-name-history
1061 (ediff-abbreviate-file-name
1063 (file-name-nondirectory f
)
1067 (ediff-read-file-name "Ancestor file"
1069 (if ediff-use-last-dir
1070 ediff-last-dir-ancestor
1071 (file-name-directory ff
)))
1073 (setq file-name-history
1074 (cons (ediff-abbreviate-file-name
1076 (file-name-nondirectory ff
)
1081 (setq startup-hooks
(cons 'ediff-merge-on-startup startup-hooks
))
1082 (ediff-files-internal file-A
1083 (if (file-directory-p file-B
)
1085 (file-name-nondirectory file-A
) file-B
)
1089 'ediff-merge-files-with-ancestor
))
1092 (defalias 'ediff-merge-with-ancestor
'ediff-merge-files-with-ancestor
)
1095 (defun ediff-merge-buffers (buffer-A buffer-B
&optional startup-hooks job-name
)
1096 "Merge buffers without ancestor."
1099 (list (setq bf
(read-buffer "Buffer A to merge: "
1100 (ediff-other-buffer "") t
))
1101 (read-buffer "Buffer B to merge: "
1103 ;; realign buffers so that two visible bufs will be
1105 (save-window-excursion (other-window 1))
1106 (ediff-other-buffer bf
))
1109 (setq startup-hooks
(cons 'ediff-merge-on-startup startup-hooks
))
1110 (or job-name
(setq job-name
'ediff-merge-buffers
))
1111 (ediff-buffers-internal
1112 buffer-A buffer-B nil startup-hooks job-name
))
1115 (defun ediff-merge-buffers-with-ancestor (buffer-A
1116 buffer-B buffer-ancestor
1117 &optional startup-hooks job-name
)
1118 "Merge buffers with ancestor."
1121 (list (setq bf
(read-buffer "Buffer A to merge: "
1122 (ediff-other-buffer "") t
))
1123 (setq bff
(read-buffer "Buffer B to merge: "
1125 ;; realign buffers so that two visible
1126 ;; bufs will be at the top
1127 (save-window-excursion (other-window 1))
1128 (ediff-other-buffer bf
))
1130 (read-buffer "Ancestor buffer: "
1132 ;; realign buffers so that three visible
1133 ;; bufs will be at the top
1134 (save-window-excursion (other-window 1))
1135 (ediff-other-buffer (list bf bff
)))
1139 (setq startup-hooks
(cons 'ediff-merge-on-startup startup-hooks
))
1140 (or job-name
(setq job-name
'ediff-merge-buffers-with-ancestor
))
1141 (ediff-buffers-internal
1142 buffer-A buffer-B buffer-ancestor startup-hooks job-name
))
1146 (defun ediff-merge-revisions (&optional file startup-hooks
)
1147 "Run Ediff by merging two revisions of a file.
1148 The file is the optional FILE argument or the file visited by the current
1151 (ediff-load-version-control)
1152 (if (stringp file
) (find-file file
))
1153 (let (rev1 rev2 buf1 buf2
)
1157 "Version 1 to merge (default: %s's latest version): "
1159 (file-name-nondirectory file
) "current buffer")))
1163 "Version 2 to merge (default: %s): "
1165 (file-name-nondirectory file
) "current buffer"))))
1166 (cond ((eq ediff-version-control-package
'vc
)
1168 (vc-version-other-window rev1
)
1169 (setq buf1
(current-buffer)))
1171 (or (string= rev2
"")
1172 (vc-version-other-window rev2
))
1173 (setq buf2
(current-buffer)))
1177 (delete-file (, (buffer-file-name buf1
)))
1178 (or (, (string= rev2
""))
1179 (delete-file (, (buffer-file-name buf2
))))))
1181 ((eq ediff-version-control-package
'rcs
)
1182 (setq buf1
(rcs-ediff-view-revision rev1
)
1183 buf2
(if (string= rev2
"")
1185 (rcs-ediff-view-revision rev2
))))
1186 ((eq ediff-version-control-package
'generic-sc
)
1188 (if (string= rev1
"")
1189 (setq rev1
(generic-sc-get-latest-rev)))
1190 (sc-visit-previous-revision rev1
)
1191 (setq buf1
(current-buffer)))
1193 (or (string= rev2
"")
1194 (sc-visit-previous-revision rev2
))
1195 (setq buf2
(current-buffer))))
1197 (ediff-merge-buffers buf1 buf2 startup-hooks
'ediff-merge-revisions
)))
1201 (defun ediff-merge-revisions-with-ancestor (&optional file startup-hooks
)
1202 "Run Ediff by merging two revisions of a file with a common ancestor.
1203 The file is the the optional FILE argument or the file visited by the current
1206 (ediff-load-version-control)
1207 (if (stringp file
) (find-file file
))
1208 (let (rev1 rev2 ancestor-rev buf1 buf2 ancestor-buf
)
1212 "Version 1 to merge (default: %s's latest version): "
1214 (file-name-nondirectory file
) "current buffer")))
1218 "Version 2 to merge (default: %s): "
1220 (file-name-nondirectory file
) "current buffer")))
1224 "Ancestor version (default: %s): "
1226 (file-name-nondirectory file
) "current buffer"))))
1227 (cond ((eq ediff-version-control-package
'vc
)
1229 (vc-version-other-window rev1
)
1230 (setq buf1
(current-buffer)))
1232 (or (string= rev2
"")
1233 (vc-version-other-window rev2
))
1234 (setq buf2
(current-buffer)))
1236 (or (string= ancestor-rev
"")
1237 (vc-version-other-window ancestor-rev
))
1238 (setq ancestor-buf
(current-buffer)))
1242 (delete-file (, (buffer-file-name buf1
)))
1243 (or (, (string= rev2
""))
1244 (delete-file (, (buffer-file-name buf2
))))
1245 (or (, (string= ancestor-rev
""))
1246 (delete-file (, (buffer-file-name ancestor-buf
))))))
1248 ((eq ediff-version-control-package
'rcs
)
1249 (setq buf1
(rcs-ediff-view-revision rev1
)
1250 buf2
(if (string= rev2
"")
1252 (rcs-ediff-view-revision rev2
))
1253 ancestor-buf
(if (string= ancestor-rev
"")
1255 (rcs-ediff-view-revision ancestor-rev
))))
1256 ((eq ediff-version-control-package
'generic-sc
)
1258 (if (string= rev1
"")
1259 (setq rev1
(generic-sc-get-latest-rev)))
1260 (sc-visit-previous-revision rev1
)
1261 (setq buf1
(current-buffer)))
1263 (or (string= rev2
"")
1264 (sc-visit-previous-revision rev2
))
1265 (setq buf2
(current-buffer)))
1267 (or (string= ancestor-rev
"")
1268 (sc-visit-previous-revision ancestor-rev
))
1269 (setq ancestor-buf
(current-buffer))))
1271 (ediff-merge-buffers-with-ancestor
1272 buf1 buf2 ancestor-buf
1273 startup-hooks
'ediff-merge-revisions-with-ancestor
)))
1279 (defun ediff-patch-buffer (buffer-name &optional startup-hooks
)
1280 "Run Ediff by patching BUFFER-NAME."
1281 (interactive "bBuffer to patch: ")
1283 (let* ((buf-to-patch (get-buffer buffer-name
))
1284 (file-name-ok (if buf-to-patch
(buffer-file-name buf-to-patch
)))
1285 (buf-mod-status (buffer-modified-p buf-to-patch
))
1286 default-dir file-name ctl-buf
)
1288 (setq file-name file-name-ok
)
1289 (ediff-eval-in-buffer buffer-name
1290 (setq default-dir default-directory
)
1291 (setq file-name
(ediff-make-temp-file buffer-name
))
1292 (set-visited-file-name file-name
)
1293 (setq buffer-auto-save-file-name nil
) ; don't create auto-save file
1294 (rename-buffer buffer-name
) ; don't confuse the user with new buf name
1295 (set-buffer-modified-p nil
)
1296 (set-visited-file-modtime) ; sync buffer and temp file
1297 (setq default-directory default-dir
)
1301 (ediff-patch-file file-name startup-hooks
'ediff-patch-buffer
))
1305 (ediff-eval-in-buffer ctl-buf
1306 (delete-file (buffer-file-name ediff-buffer-A
))
1307 (delete-file (buffer-file-name ediff-buffer-B
))
1308 (ediff-eval-in-buffer ediff-buffer-A
1309 (if default-dir
(setq default-directory default-dir
))
1310 (set-visited-file-name nil
)
1311 (rename-buffer buffer-name
)
1312 (set-buffer-modified-p buf-mod-status
))
1313 (ediff-eval-in-buffer ediff-buffer-B
1314 (setq buffer-auto-save-file-name nil
) ; don't create auto-save file
1315 (if default-dir
(setq default-directory default-dir
))
1316 (set-visited-file-name nil
)
1317 (rename-buffer (ediff-unique-buffer-name
1318 (concat buffer-name
"_patched") ""))
1319 (set-buffer-modified-p t
))))
1323 (defun ediff-get-patch-buffer (dir)
1324 "Obtain patch buffer. If patch is already in a buffer---use it.
1325 Else, read patch file into a new buffer."
1326 (if (y-or-n-p "Is the patch file already in a buffer? ")
1327 (setq ediff-patch-buf
1328 (get-buffer (read-buffer "Patch buffer name: " nil t
))) ;must match
1329 (setq ediff-patch-buf
1330 (find-file-noselect (read-file-name "Patch file name: " dir
))))
1332 (setq ediff-patch-diagnostics
1333 (get-buffer-create "*ediff patch diagnostics*"))
1334 (ediff-eval-in-buffer ediff-patch-diagnostics
1335 (insert-buffer ediff-patch-buf
)))
1341 ;;; Versions Control functions
1344 (defun ediff-revision (&optional file startup-hooks
)
1345 "Run Ediff by comparing versions of a file.
1346 The file is an optional FILE argument or the file visited by the current
1347 buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'."
1348 ;; if buffer is non-nil, use that buffer instead of the current buffer
1350 (if (stringp file
) (find-file file
))
1354 (format "Version 1 to compare (default: %s's latest version): "
1356 (file-name-nondirectory file
) "current buffer")))
1359 (format "Version 2 to compare (default: %s): "
1361 (file-name-nondirectory file
) "current buffer"))))
1362 (ediff-load-version-control)
1364 (intern (format "%S-ediff-internal" ediff-version-control-package
))
1365 rev1 rev2 startup-hooks
)
1369 ;; Test if version control package is loaded and load if not
1370 ;; Is SILENT is non-nil, don't report error if package is not found.
1371 (defun ediff-load-version-control (&optional silent
)
1372 (or (featurep ediff-version-control-package
)
1373 (if (locate-library (symbol-name ediff-version-control-package
))
1375 (message "") ; kill the message from `locate-library'
1376 (require ediff-version-control-package
))
1378 (error "Version control package %S.el not found. Use vc.el instead"
1379 ediff-version-control-package
)))))
1382 (defun vc-ediff-internal (rev1 rev2
&optional startup-hooks
)
1383 "Run Ediff on versions of the current buffer.
1384 If REV2 is \"\" then compare current buffer with REV1.
1385 If the current buffer is named `F', the version is named `F.~REV~'.
1386 If `F.~REV~' already exists, it is used instead of being re-created."
1387 (let (file1 file2 rev1buf rev2buf
)
1389 (vc-version-other-window rev1
)
1390 (setq rev1buf
(current-buffer)
1391 file1
(buffer-file-name)))
1393 (or (string= rev2
"") ; use current buffer
1394 (vc-version-other-window rev2
))
1395 (setq rev2buf
(current-buffer)
1396 file2
(buffer-file-name)))
1399 (delete-file (, file1
))
1400 (or (, (string= rev2
"")) (delete-file (, file2
)))
1408 (defun rcs-ediff-view-revision (&optional rev
)
1409 "View previous RCS revision of current file.
1410 With prefix argument, prompts for a revision name."
1411 (interactive (list (if current-prefix-arg
1412 (read-string "Revision: "))))
1413 (let* ((filename (buffer-file-name (current-buffer)))
1414 (switches (append '("-p")
1415 (if rev
(list (concat "-r" rev
)) nil
)))
1416 (buff (concat (file-name-nondirectory filename
) ".~" rev
"~")))
1417 (message "Working ...")
1418 (setq filename
(expand-file-name filename
))
1419 (with-output-to-temp-buffer buff
1420 (let ((output-buffer (ediff-rcs-get-output-buffer filename buff
)))
1421 (delete-windows-on output-buffer
)
1423 (set-buffer output-buffer
)
1424 (apply 'call-process
"co" nil t nil
1425 ;; -q: quiet (no diagnostics)
1426 (append switches rcs-default-co-switches
1427 (list "-q" filename
)))))
1431 (defun ediff-rcs-get-output-buffer (file name
)
1432 ;; Get a buffer for RCS output for FILE, make it writable and clean it up.
1433 ;; Optional NAME is name to use instead of `*RCS-output*'.
1434 ;; This is a modified version from rcs.el v1.1. I use it here to make
1435 ;; Ediff immune to changes in rcs.el
1436 (let* ((default-major-mode 'fundamental-mode
) ; no frills!
1437 (buf (get-buffer-create name
)))
1440 (setq buffer-read-only nil
1441 default-directory
(file-name-directory (expand-file-name file
)))
1445 (defun rcs-ediff-internal (rev1 rev2
&optional startup-hooks
)
1446 "Run Ediff on versions of the current buffer.
1447 If REV2 is \"\" then use current buffer."
1448 (let ((rev2buf (if (string= rev2
"")
1450 (rcs-ediff-view-revision rev2
)))
1451 (rev1buf (rcs-ediff-view-revision rev1
)))
1453 ;; rcs.el doesn't create temp version files, so we don't have to delete
1454 ;; anything in startup hooks to ediff-buffers
1455 (ediff-buffers rev1buf rev2buf startup-hooks
'ediff-revision
)
1458 (defun generic-sc-get-latest-rev ()
1459 (cond ((eq sc-mode
'CCASE
)
1460 (eval "main/LATEST"))
1464 (defun generic-sc-ediff-internal (rev1 rev2
&optional startup-hooks
)
1465 "Run Ediff on versions of the current buffer.
1466 If REV2 is \"\" then compare current buffer with REV1.
1467 If the current buffer is named `F', the version is named `F.~REV~'.
1468 If `F.~REV~' already exists, it is used instead of being re-created."
1469 (let (rev1buf rev2buf
)
1471 (if (or (not rev1
) (string= rev1
""))
1472 (setq rev1
(generic-sc-get-latest-rev)))
1473 (sc-visit-previous-revision rev1
)
1474 (setq rev1buf
(current-buffer)))
1476 (or (string= rev2
"") ; use current buffer
1477 (sc-visit-previous-revision rev2
))
1478 (setq rev2buf
(current-buffer)))
1479 (ediff-buffers rev1buf rev2buf startup-hooks
'ediff-revision
)))
1482 (defun ediff-version ()
1483 "Return string describing the version of Ediff.
1484 When called interactively, displays the version."
1487 (message (ediff-version))
1488 (format "Ediff %s of %s" ediff-version ediff-date
)))
1492 (require 'ediff-util
)
1494 ;;; ediff.el ends here