(vc-diff): Make NOT-URGENT default to t.
[emacs.git] / lisp / ediff.el
blobeac0157d7924e5f4809be78d9b9f7fef5dcadbd2
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)
17 ;; any later version.
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.
29 ;;; Commentary:
31 ;; Never read that diff output again!
32 ;; Apply patch selectively, like a pro!
33 ;; Merge with ease!
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
46 ;; directories.
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
55 ;; you don't like).
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.
73 ;;; Bugs:
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
94 ;; and on any buffer.
96 ;;; Acknowledgements:
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.
103 ;;; Code:
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.")
122 ;;; Patching
124 (defvar ediff-backup-extension
125 (if (memq system-type '(vax-vms axp-vms emx ms-dos windows-nt windows-95))
126 "_orig" ".orig")
127 "Default backup extension for the patch program.")
129 ;;;###autoload
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
133 (interactive
134 (list (ediff-read-file-name
135 "File to patch"
136 (if ediff-use-last-dir
137 ediff-last-dir-patch
138 default-directory)
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)
145 default-directory)
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)
153 (match-end 0) 0)
154 (if (string-match "-b[ \t]+[^ \t]+" ediff-patch-options)
155 (match-end 0) 0)))
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
185 ;; checked back in.
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 ... ")
191 ;;(sit-for 0)
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
197 backup-extension
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
214 ;; the original.
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
223 ;; the orig file.
224 (setq target-filename
225 (concat
226 (if (ediff-file-remote-p (file-truename source-filename))
227 true-source-filename
228 source-filename)
229 "_patched"))
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
238 (setq startup-hooks
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))
244 (setq ctl-buf
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))
252 ctl-buf))
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)))
266 f)))
267 ((buffer-file-name (current-buffer))
268 (file-name-nondirectory (buffer-file-name (current-buffer))))
271 ;;;###autoload
272 (defalias 'epatch 'ediff-patch-file)
273 ;;;###autoload
274 (defalias 'epatch-buffer 'ediff-patch-buffer)
276 ;;; Compare files/buffers
278 ;;;###autoload
279 (defun ediff-files (file-A file-B &optional startup-hooks)
280 "Run Ediff on a pair of files, FILE-A and FILE-B."
281 (interactive
282 (let ((dir-A (if ediff-use-last-dir
283 ediff-last-dir-A
284 default-directory))
285 dir-B f)
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"
290 (setq dir-B
291 (if ediff-use-last-dir
292 ediff-last-dir-B
293 (file-name-directory f)))
294 (progn
295 (setq file-name-history
296 (cons (ediff-abbreviate-file-name
297 (expand-file-name
298 (file-name-nondirectory f)
299 dir-B))
300 file-name-history))
303 (ediff-files-internal file-A
304 (if (file-directory-p file-B)
305 (expand-file-name
306 (file-name-nondirectory file-A) file-B)
307 file-B)
308 nil ; file-C
309 startup-hooks
310 'ediff-files))
312 ;;;###autoload
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."
315 (interactive
316 (let ((dir-A (if ediff-use-last-dir
317 ediff-last-dir-A
318 default-directory))
319 dir-B dir-C f ff)
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"
324 (setq dir-B
325 (if ediff-use-last-dir
326 ediff-last-dir-B
327 (file-name-directory f)))
328 (progn
329 (setq file-name-history
330 (cons
331 (ediff-abbreviate-file-name
332 (expand-file-name
333 (file-name-nondirectory f)
334 dir-B))
335 file-name-history))
336 f)))
337 (ediff-read-file-name "File C to compare"
338 (setq dir-C (if ediff-use-last-dir
339 ediff-last-dir-C
340 (file-name-directory ff)))
341 (progn
342 (setq file-name-history
343 (cons (ediff-abbreviate-file-name
344 (expand-file-name
345 (file-name-nondirectory ff)
346 dir-C))
347 file-name-history))
348 ff))
350 (ediff-files-internal file-A
351 (if (file-directory-p file-B)
352 (expand-file-name
353 (file-name-nondirectory file-A) file-B)
354 file-B)
355 (if (file-directory-p file-C)
356 (expand-file-name
357 (file-name-nondirectory file-A) file-C)
358 file-C)
359 startup-hooks
360 'ediff-files3))
362 ;;;###autoload
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
374 deleted."
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
387 (if last-dir
388 (set last-dir (expand-file-name (file-name-directory file))))
390 ;; Setup the buffer
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)
398 (setq file
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))))
406 (setq file
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)
418 ;;(sit-for 0)
419 (ediff-find-file 'file-A 'buf-A 'ediff-last-dir-A 'startup-hooks)
420 (message "Reading file %s ... " file-B)
421 ;;(sit-for 0)
422 (ediff-find-file 'file-B 'buf-B 'ediff-last-dir-B 'startup-hooks)
423 (if (stringp file-C)
424 (progn
425 (message "Reading file %s ... " file-C)
426 ;;(sit-for 0)
427 (ediff-find-file
428 'file-C 'buf-C
429 (if (eq job-name 'ediff-merge-files-with-ancestor)
430 'ediff-last-dir-ancestor 'ediff-last-dir-C)
431 'startup-hooks)))
432 (ediff-setup buf-A file-A
433 buf-B file-B
434 buf-C file-C
435 startup-hooks
436 (list (cons 'ediff-job-name job-name)))))
439 ;;;###autoload
440 (defalias 'ediff 'ediff-files)
443 ;;;###autoload
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."
446 (interactive
447 (let (bf)
448 (list (setq bf (read-buffer "Buffer A to compare: "
449 (ediff-other-buffer "") t))
450 (read-buffer "Buffer B to compare: "
451 (progn
452 ;; realign buffers so that two visible bufs will be
453 ;; at the top
454 (save-window-excursion (other-window 1))
455 (ediff-other-buffer bf))
456 t))))
458 (or job-name (setq job-name 'ediff-buffers))
459 (ediff-buffers-internal buffer-A buffer-B nil startup-hooks job-name))
461 ;;;###autoload
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."
465 (interactive
466 (let (bf bff)
467 (list (setq bf (read-buffer "Buffer A to compare: "
468 (ediff-other-buffer "") t))
469 (setq bff (read-buffer "Buffer B to compare: "
470 (progn
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: "
477 (progn
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))
514 (if buf-C-is-alive
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))
520 file-C
521 (cons (` (lambda ()
522 (delete-file (, file-A))
523 (delete-file (, file-B))
524 (if (stringp (, file-C)) (delete-file (, file-C)))
526 startup-hooks)
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))
543 default-directory)))
544 (t default-directory)))
547 ;;;###autoload
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."
552 (interactive
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
558 ediff-last-dir-B
559 (ediff-strip-last-dir f))
560 nil)
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
568 ;;;###autoload
569 (defalias 'edirs 'ediff-directories)
572 ;;;###autoload
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."
577 (interactive
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
588 ;;;###autoload
589 (defalias 'edir-revisions 'ediff-directory-revisions)
592 ;;;###autoload
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."
597 (interactive
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
603 ediff-last-dir-B
604 (ediff-strip-last-dir f))
605 nil))
606 (ediff-read-file-name "Directory C to compare"
607 (if ediff-use-last-dir
608 ediff-last-dir-C
609 (ediff-strip-last-dir f))
610 nil)
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
618 ;;;###autoload
619 (defalias 'edirs3 'ediff-directories3)
621 ;;;###autoload
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."
626 (interactive
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
632 ediff-last-dir-B
633 (ediff-strip-last-dir f))
634 nil)
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
642 ;;;###autoload
643 (defalias 'edirs-merge 'ediff-merge-directories)
645 ;;;###autoload
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."
650 (interactive
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
656 ediff-last-dir-B
657 (ediff-strip-last-dir f))
658 nil))
659 (ediff-read-file-name "Ancestor directory: "
660 (if ediff-use-last-dir
661 ediff-last-dir-C
662 (ediff-strip-last-dir f))
663 nil)
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
672 ;;;###autoload
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."
677 (interactive
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
688 ;;;###autoload
689 (defalias 'edir-merge-revisions 'ediff-merge-directory-revisions)
691 ;;;###autoload
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."
696 (interactive
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
708 ;;;###autoload
709 (defalias
710 'edir-merge-revisions-with-ancestor
711 'ediff-merge-directory-revisions-with-ancestor)
713 ;;;###autoload
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
719 ;; file names.
720 ;; JOBNAME is the symbol indicating the meta-job to be performed.
721 (defun ediff-directories-internal (dir1 dir2 dir3 regexp
722 action jobname
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)))
729 (if (stringp dir3)
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)
735 (string= dir1 dir3))
736 (error "Directories A and C are the same: %s" dir1))
737 ((and (eq jobname 'ediff-directories3)
738 (string= dir2 dir3))
739 (error "Directories B and C are the same: %s" dir1)))
741 (let (diffs ; var where ediff-intersect-directories returns the diff list
742 file-list meta-buf)
743 (setq file-list (ediff-intersect-directories
744 jobname 'diffs regexp dir1 dir2 dir3))
745 (setq startup-hooks
746 ;; this sets various vars in the meta buffer inside
747 ;; ediff-prepare-meta-buffer
748 (cons (` (lambda ()
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)))))
753 startup-hooks))
754 (setq meta-buf (ediff-prepare-meta-buffer
755 'ediff-dir-action
756 file-list
757 "*Ediff Session Group Panel"
758 'ediff-redraw-directory-group-buffer
759 jobname
760 startup-hooks))
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)
768 (setq file-list
769 (ediff-get-directory-files-under-revision jobname regexp dir1))
770 (setq startup-hooks
771 ;; this sets various vars in the meta buffer inside
772 ;; ediff-prepare-meta-buffer
773 (cons (` (lambda ()
774 ;; tell what to do if the user clicks on a session record
775 (setq ediff-session-action-function (quote (, action)))
777 startup-hooks))
778 (setq meta-buf (ediff-prepare-meta-buffer
779 'ediff-dir-action
780 file-list
781 "*Ediff Session Group Panel"
782 'ediff-redraw-directory-group-buffer
783 jobname
784 startup-hooks))
785 (ediff-show-meta-buffer meta-buf)
789 ;;; Compare regions and windows
791 ;;;###autoload
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
795 follows:
796 If WIND-A is nil, use selected window.
797 If WIND-B is nil, use window next to WIND-A."
798 (interactive "P")
799 (ediff-windows dumb-mode wind-A wind-B
800 startup-hooks 'ediff-windows-wordwise 'word-mode))
802 ;;;###autoload
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
806 follows:
807 If WIND-A is nil, use selected window.
808 If WIND-B is nil, use window next to WIND-A."
809 (interactive "P")
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,
815 ;; works as follows:
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)
829 (save-excursion
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)
834 end-A (window-end))
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)))
842 ;;;###autoload
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'."
848 (interactive
849 (let (bf)
850 (list (setq bf (read-buffer "Region's A buffer: "
851 (ediff-other-buffer "") t))
852 (read-buffer "Region's B buffer: "
853 (progn
854 ;; realign buffers so that two visible bufs will be
855 ;; at the top
856 (save-window-excursion (other-window 1))
857 (ediff-other-buffer bf))
858 t))))
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)
866 (save-excursion
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)))
879 ;;;###autoload
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'."
886 (interactive
887 (let (bf)
888 (list (setq bf (read-buffer "Region A's buffer: "
889 (ediff-other-buffer "") t))
890 (read-buffer "Region B's buffer: "
891 (progn
892 ;; realign buffers so that two visible bufs will be
893 ;; at the top
894 (save-window-excursion (other-window 1))
895 (ediff-other-buffer bf))
896 t))))
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)
903 (save-excursion
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)
909 (beginning-of-line)
910 (setq reg-A-beg (point))
911 (goto-char reg-A-end)
912 (end-of-line)
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)
922 (beginning-of-line)
923 (setq reg-B-beg (point))
924 (goto-char reg-B-end)
925 (end-of-line)
926 (or (eobp) (forward-char)) ; include the newline char
927 (setq reg-B-end (point))
928 ) ; save excursion
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))
940 overl-A overl-B
941 file-A file-B)
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
954 (progn
955 (with-output-to-temp-buffer ediff-msg-buffer
956 (princ "
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))))
968 ;; make file-A
969 (if word-mode
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"))
974 ;; make file-B
975 (if word-mode
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
983 buffer-B file-B
984 nil nil ; buffer & file C
985 (cons (` (lambda ()
986 (delete-file (, file-A))
987 (delete-file (, file-B))))
988 startup-hooks)
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
998 ;;;###autoload
999 (defalias 'ediff-merge 'ediff-merge-files)
1001 (defsubst ediff-merge-on-startup ()
1002 (ediff-do-merge 0)
1003 (ediff-eval-in-buffer ediff-buffer-C
1004 (set-buffer-modified-p nil)))
1006 ;;;###autoload
1007 (defun ediff-merge-files (file-A file-B &optional startup-hooks)
1008 "Merge two files without ancestor."
1009 (interactive
1010 (let ((dir-A (if ediff-use-last-dir
1011 ediff-last-dir-A
1012 default-directory))
1013 dir-B f)
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"
1018 (setq dir-B
1019 (if ediff-use-last-dir
1020 ediff-last-dir-B
1021 (file-name-directory f)))
1022 (progn
1023 (setq file-name-history
1024 (cons (ediff-abbreviate-file-name
1025 (expand-file-name
1026 (file-name-nondirectory f)
1027 dir-B))
1028 file-name-history))
1031 (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks))
1032 (ediff-files-internal file-A
1033 (if (file-directory-p file-B)
1034 (expand-file-name
1035 (file-name-nondirectory file-A) file-B)
1036 file-B)
1037 nil ; file-C
1038 startup-hooks
1039 'ediff-merge-files))
1041 ;;;###autoload
1042 (defun ediff-merge-files-with-ancestor (file-A file-B file-ancestor
1043 &optional startup-hooks)
1044 "Merge two files with ancestor."
1045 (interactive
1046 (let ((dir-A (if ediff-use-last-dir
1047 ediff-last-dir-A
1048 default-directory))
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"
1054 (setq dir-B
1055 (if ediff-use-last-dir
1056 ediff-last-dir-B
1057 (file-name-directory f)))
1058 (progn
1059 (setq file-name-history
1060 (cons
1061 (ediff-abbreviate-file-name
1062 (expand-file-name
1063 (file-name-nondirectory f)
1064 dir-B))
1065 file-name-history))
1066 f)))
1067 (ediff-read-file-name "Ancestor file"
1068 (setq dir-ancestor
1069 (if ediff-use-last-dir
1070 ediff-last-dir-ancestor
1071 (file-name-directory ff)))
1072 (progn
1073 (setq file-name-history
1074 (cons (ediff-abbreviate-file-name
1075 (expand-file-name
1076 (file-name-nondirectory ff)
1077 dir-ancestor))
1078 file-name-history))
1079 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)
1084 (expand-file-name
1085 (file-name-nondirectory file-A) file-B)
1086 file-B)
1087 file-ancestor
1088 startup-hooks
1089 'ediff-merge-files-with-ancestor))
1091 ;;;###autoload
1092 (defalias 'ediff-merge-with-ancestor 'ediff-merge-files-with-ancestor)
1094 ;;;###autoload
1095 (defun ediff-merge-buffers (buffer-A buffer-B &optional startup-hooks job-name)
1096 "Merge buffers without ancestor."
1097 (interactive
1098 (let (bf)
1099 (list (setq bf (read-buffer "Buffer A to merge: "
1100 (ediff-other-buffer "") t))
1101 (read-buffer "Buffer B to merge: "
1102 (progn
1103 ;; realign buffers so that two visible bufs will be
1104 ;; at the top
1105 (save-window-excursion (other-window 1))
1106 (ediff-other-buffer bf))
1107 t))))
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))
1114 ;;;###autoload
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."
1119 (interactive
1120 (let (bf bff)
1121 (list (setq bf (read-buffer "Buffer A to merge: "
1122 (ediff-other-buffer "") t))
1123 (setq bff (read-buffer "Buffer B to merge: "
1124 (progn
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: "
1131 (progn
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))
1145 ;;;###autoload
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
1149 buffer."
1150 (interactive)
1151 (ediff-load-version-control)
1152 (if (stringp file) (find-file file))
1153 (let (rev1 rev2 buf1 buf2)
1154 (setq rev1
1155 (read-string
1156 (format
1157 "Version 1 to merge (default: %s's latest version): "
1158 (if (stringp file)
1159 (file-name-nondirectory file) "current buffer")))
1160 rev2
1161 (read-string
1162 (format
1163 "Version 2 to merge (default: %s): "
1164 (if (stringp file)
1165 (file-name-nondirectory file) "current buffer"))))
1166 (cond ((eq ediff-version-control-package 'vc)
1167 (save-excursion
1168 (vc-version-other-window rev1)
1169 (setq buf1 (current-buffer)))
1170 (save-excursion
1171 (or (string= rev2 "")
1172 (vc-version-other-window rev2))
1173 (setq buf2 (current-buffer)))
1174 (setq startup-hooks
1175 (cons
1176 (` (lambda ()
1177 (delete-file (, (buffer-file-name buf1)))
1178 (or (, (string= rev2 ""))
1179 (delete-file (, (buffer-file-name buf2))))))
1180 startup-hooks)))
1181 ((eq ediff-version-control-package 'rcs)
1182 (setq buf1 (rcs-ediff-view-revision rev1)
1183 buf2 (if (string= rev2 "")
1184 (current-buffer)
1185 (rcs-ediff-view-revision rev2))))
1186 ((eq ediff-version-control-package 'generic-sc)
1187 (save-excursion
1188 (if (string= rev1 "")
1189 (setq rev1 (generic-sc-get-latest-rev)))
1190 (sc-visit-previous-revision rev1)
1191 (setq buf1 (current-buffer)))
1192 (save-excursion
1193 (or (string= rev2 "")
1194 (sc-visit-previous-revision rev2))
1195 (setq buf2 (current-buffer))))
1196 ) ; cond
1197 (ediff-merge-buffers buf1 buf2 startup-hooks 'ediff-merge-revisions)))
1200 ;;;###autoload
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
1204 buffer."
1205 (interactive)
1206 (ediff-load-version-control)
1207 (if (stringp file) (find-file file))
1208 (let (rev1 rev2 ancestor-rev buf1 buf2 ancestor-buf)
1209 (setq rev1
1210 (read-string
1211 (format
1212 "Version 1 to merge (default: %s's latest version): "
1213 (if (stringp file)
1214 (file-name-nondirectory file) "current buffer")))
1215 rev2
1216 (read-string
1217 (format
1218 "Version 2 to merge (default: %s): "
1219 (if (stringp file)
1220 (file-name-nondirectory file) "current buffer")))
1221 ancestor-rev
1222 (read-string
1223 (format
1224 "Ancestor version (default: %s): "
1225 (if (stringp file)
1226 (file-name-nondirectory file) "current buffer"))))
1227 (cond ((eq ediff-version-control-package 'vc)
1228 (save-excursion
1229 (vc-version-other-window rev1)
1230 (setq buf1 (current-buffer)))
1231 (save-excursion
1232 (or (string= rev2 "")
1233 (vc-version-other-window rev2))
1234 (setq buf2 (current-buffer)))
1235 (save-excursion
1236 (or (string= ancestor-rev "")
1237 (vc-version-other-window ancestor-rev))
1238 (setq ancestor-buf (current-buffer)))
1239 (setq startup-hooks
1240 (cons
1241 (` (lambda ()
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))))))
1247 startup-hooks)))
1248 ((eq ediff-version-control-package 'rcs)
1249 (setq buf1 (rcs-ediff-view-revision rev1)
1250 buf2 (if (string= rev2 "")
1251 (current-buffer)
1252 (rcs-ediff-view-revision rev2))
1253 ancestor-buf (if (string= ancestor-rev "")
1254 (current-buffer)
1255 (rcs-ediff-view-revision ancestor-rev))))
1256 ((eq ediff-version-control-package 'generic-sc)
1257 (save-excursion
1258 (if (string= rev1 "")
1259 (setq rev1 (generic-sc-get-latest-rev)))
1260 (sc-visit-previous-revision rev1)
1261 (setq buf1 (current-buffer)))
1262 (save-excursion
1263 (or (string= rev2 "")
1264 (sc-visit-previous-revision rev2))
1265 (setq buf2 (current-buffer)))
1266 (save-excursion
1267 (or (string= ancestor-rev "")
1268 (sc-visit-previous-revision ancestor-rev))
1269 (setq ancestor-buf (current-buffer))))
1270 ) ; cond
1271 (ediff-merge-buffers-with-ancestor
1272 buf1 buf2 ancestor-buf
1273 startup-hooks 'ediff-merge-revisions-with-ancestor)))
1276 ;;; Apply patch
1278 ;;;###autoload
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)
1287 (if file-name-ok
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)
1300 (setq ctl-buf
1301 (ediff-patch-file file-name startup-hooks 'ediff-patch-buffer))
1303 (if file-name-ok
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
1343 ;;;###autoload
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
1349 (interactive "P")
1350 (if (stringp file) (find-file file))
1351 (let (rev1 rev2)
1352 (setq rev1
1353 (read-string
1354 (format "Version 1 to compare (default: %s's latest version): "
1355 (if (stringp file)
1356 (file-name-nondirectory file) "current buffer")))
1357 rev2
1358 (read-string
1359 (format "Version 2 to compare (default: %s): "
1360 (if (stringp file)
1361 (file-name-nondirectory file) "current buffer"))))
1362 (ediff-load-version-control)
1363 (funcall
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))
1374 (progn
1375 (message "") ; kill the message from `locate-library'
1376 (require ediff-version-control-package))
1377 (or silent
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)
1388 (save-excursion
1389 (vc-version-other-window rev1)
1390 (setq rev1buf (current-buffer)
1391 file1 (buffer-file-name)))
1392 (save-excursion
1393 (or (string= rev2 "") ; use current buffer
1394 (vc-version-other-window rev2))
1395 (setq rev2buf (current-buffer)
1396 file2 (buffer-file-name)))
1397 (setq startup-hooks
1398 (cons (` (lambda ()
1399 (delete-file (, file1))
1400 (or (, (string= rev2 "")) (delete-file (, file2)))
1402 startup-hooks))
1403 (ediff-buffers
1404 rev1buf rev2buf
1405 startup-hooks
1406 'ediff-revision)))
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)
1422 (save-excursion
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)))))
1428 (message "")
1429 buff)))
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)))
1438 (save-excursion
1439 (set-buffer buf)
1440 (setq buffer-read-only nil
1441 default-directory (file-name-directory (expand-file-name file)))
1442 (erase-buffer))
1443 buf))
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 "")
1449 (current-buffer)
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"))
1461 (t (eval "")))
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)
1470 (save-excursion
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)))
1475 (save-excursion
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)))
1481 ;;;###autoload
1482 (defun ediff-version ()
1483 "Return string describing the version of Ediff.
1484 When called interactively, displays the version."
1485 (interactive)
1486 (if (interactive-p)
1487 (message (ediff-version))
1488 (format "Ediff %s of %s" ediff-version ediff-date)))
1491 (provide 'ediff)
1492 (require 'ediff-util)
1494 ;;; ediff.el ends here