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