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