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