X-Git-Url: https://code.delx.au/gnu-emacs/blobdiff_plain/2b9118484ef607d08a3cfb4a45614cf112436bcd..cd30a521aa838f6bcf08ee6ffb62986cd2bf8daa:/lisp/ediff.el diff --git a/lisp/ediff.el b/lisp/ediff.el index bee087450a..4e6cd8667f 100644 --- a/lisp/ediff.el +++ b/lisp/ediff.el @@ -1,12 +1,14 @@ ;;; ediff.el --- a comprehensive visual interface to diff & patch -;;; Copyright (C) 1994, 1995 Free Software Foundation, Inc. + +;; Copyright (C) 1994, 1995, 1996, 1997 Free Software Foundation, Inc. ;; Author: Michael Kifer ;; Created: February 2, 1994 ;; Keywords: comparing, merging, patching, version control. -(defconst ediff-version "2.26" "The current version of Ediff") -(defconst ediff-date "June 3, 1995" "Date of last update") +(defconst ediff-version "2.70.2" "The current version of Ediff") +(defconst ediff-date "May 21, 1998" "Date of last update") + ;; This file is part of GNU Emacs. @@ -21,14 +23,14 @@ ;; GNU General Public License for more details. ;; You should have received a copy of the GNU General Public License -;; along with GNU Emacs; see the file COPYING. If not, write to -;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. +;; along with GNU Emacs; see the file COPYING. If not, write to the +;; Free Software Foundation, Inc., 59 Temple Place - Suite 330, +;; Boston, MA 02111-1307, USA. ;;; Commentary: -;; ---------- ;; Never read that diff output again! -;; Apply patch selectively, like a pro! +;; Apply patch interactively! ;; Merge with ease! ;; This package provides a convenient way of simultaneous browsing through @@ -39,6 +41,11 @@ ;; through them. You can also copy difference regions from one buffer to ;; another (and recover old differences if you change your mind). +;; Ediff also supports merging operations on files and buffers, including +;; merging using ancestor versions. Both comparison and merging operations can +;; be performed on directories, i.e., by pairwise comparison of files in those +;; directories. + ;; In addition, Ediff can apply a patch to a file and then let you step ;; though both files, the patched and the original one, simultaneously, ;; difference-by-difference. You can even apply a patch right out of a @@ -52,25 +59,21 @@ ;; files with their older versions. Ediff can also work with remote and ;; compressed files. Details are given below. -;; This package builds upon the ideas borrowed from emerge.el and -;; several Ediff's functions are adaptations from emerge.el. -;; Much of the functionality of Ediff is also influenced by emerge.el. +;; Finally, Ediff supports directory-level comparison, merging and patching. +;; See the on-line manual for details. -;; The present version of Ediff supersedes Emerge. It provides a superior -;; user interface and has many features not found in Emerge. In particular, -;; it can do patching and 2-way and 3-way file comparison in addition to -;; merging. +;; This package builds upon the ideas borrowed from emerge.el and several +;; Ediff's functions are adaptations from emerge.el. Much of the functionality +;; Ediff provides is also influenced by emerge.el. + +;; The present version of Ediff supersedes Emerge. It provides a superior user +;; interface and has numerous major features not found in Emerge. In +;; particular, it can do patching, and 2-way and 3-way file comparison, +;; merging, and directory operations. -;;; Compilation -;; ----------- -;; -;; When you byte-compile Ediff, you will get some warnings about functions -;; being undefined. These can be safely ignored. -;; ;;; Bugs: -;; ----- ;; 1. The undo command doesn't restore deleted regions well. That is, if ;; you delete all characters in a difference region and then invoke @@ -83,787 +86,93 @@ ;; 2. On a monochrome display, the repertoire of faces with which to ;; highlight fine differences is limited. By default, Ediff is using -;; underlining. However, if the region is already underlied by some other +;; underlining. However, if the region is already underlined by some other ;; overlays, there is no simple way to temporarily remove that residual ;; underlining. This problem occurs when a buffer is highlighted with ;; hilit19.el or font-lock.el packages. If this residual highlighting gets ;; in the way, you can do the following. Both font-lock.el and hilit19.el ;; provide commands for unhighlighting buffers. You can either place these -;; commands in `ediff-prepare-buffer-hooks' (which will unhighlight every +;; commands in `ediff-prepare-buffer-hook' (which will unhighlight every ;; buffer used by Ediff) or you can execute them interactively, at any time ;; and on any buffer. -;; - -;;; Change Log: -;; ---------- - -;; Thu Feb 3, 1994 - -;; Fixed a bug in ediff-setup-windows that caused control window to -;; appear in a wrong place when split-window-keep-point is nil -;; (Thanks to Kevin Broadey .) -;; -;; Added mechanism for using faces instead of before/after flags. This -;; looks much better on an X display, especially on a color one. -;; (Thanks to Boris Goldowsky for the code -;; that led to ediff-highlight-diff. -;; Also, thanks to Kevin Esler for suggestions -;; regarding highlighting differences on X displays.) -;; -;; Added functions to apply patches. -;; (Thanks to Kevin Broadey for this -;; suggestion.) - -;; Fri Feb 4, 1994 - -;; Added mechanism for toggling vertical/horizontal window split. -;; (Inspired by a suggestion from Allan Gottlieb -;; -- thanks.) -;; -;; Added mechanism for toggling between highlighting using faces and -;; highlighting using ASCII flags. -;; -;; Fixed a problem with undo. Now, Ediff has smartened up and doesn't -;; keep undo info on ASCII flags inserted in buffer-A and buffer-B. -;; So, if you edit the files while browsing through them, undo behaves -;; as you would expect, i.e., faces/flags don't get in the way. - -;; Sun Feb 6, 1994 - -;; Added horizontal scrolling. Added ediff-position-region to ensure -;; that difference regions in buffer-A and buffer-B are aligned with -;; each other. Disabled ediff-toggle-split when buffers are displayed -;; in different frames. -;; -;; Added toggle-window help (Suggested by Boris Goldowsky -;; .) -;; Added functions to copy differences from one buffer to another and to -;; recover old differences. -;; Added prefix arguments to ediff-next-difference and -;; ediff-previous-difference. - -;; Tue Feb 8, 1994 - -;; Replaced text properties with overlays. Fixed ediff-setup-windows. -;; Added ediff-save-buffer to local-write-file-hooks to prevent user -;; from saving corrupted states. (Thanks to -;; for suggestion.) Instead, Ediff now has a pair of functions for -;; safe saving of buffers. -;; Changed ediff-read-file-name to be more intuitive on ediff-files. -;; Added ediff-prepare-buffer-hooks. (Thanks to Kevin Esler -;; for the idea.) -;; -;; Cleanups in ediff-patch-file. Protected ediff-copy-diff against -;; a bug that Emacs has in kill-region. -;; -;; Added support for Lemacs. (Thanks to Alastair Burt -;; for coercing Ediff into working under Lemacs.) -;; Added ediff-kill-buffer-carefully and other suggestions by Boris -;; Goldowsky . -;; Refined the protection against interference with highlighting caused -;; by Hilit19. Added the variable ediff-third-party-highlighting. -;; Added mechanisn for unhighlighting regions highlighted with Hilit19 -;; before hightlighting them with Ediff's overlays. (And for -;; rehighlighting them with Hilit19, when the current difference moves on.) - -;; Sun Feb 13, 1994 - -;; Added ediff-place-flags-in-buffer and ediff-remote-exit, which are -;; modifications of Emerge's similar functions. The difference is that -;; in Ediff they make ediff-before-flag and ediff-after-flag into -;; read-only regions, so the user can't change them by mistake. -;; -;; Adopted a suggestion by Boris Goldowsky -;; that led to a more elegant treatment of faces. -;; -;; Added protection against interference with Font-Lock highlighting -;; similar to that of Hilit19's protection. - -;; Tue Feb 15, 1994 - -;; Deleted spurious (auto-save-mode 1) in ediff-control-buffer, which -;; was causing this buffer to be auto-saved for no good reason. -;; Added read-only protection to ediff-before/after-flags in Lemacs. -;; (Thanks to Alastair Burt for help in testing.) -;; -;; Further fixes in the XEmacs part. Changed highlighted region in -;; ediff-highlight-diff so that an extra character will be highlighted -;; only if a difference is empty (thereby allowing the user to see where an -;; insertion or a deletion has taken place). -;; -;; Simplified interaction with other highlighting packages by giving -;; Ediff overlays the highest priority. (Taking a cue from -;; ediff-highlight-diff-lemacs written by Alastair Burt -;; .) Zapped ediff-third-party-highlighting -;; variable and hooks that were previously used to -;; unhighlight/rehighlight buffers when hilit19/font-lock are on. - -;; Fri Feb 18, 1994 - -;; Added a bit more sophistication to ediff-read-file-name. Now, -;; ediff-files remembers both, the file-A and the file-B directories. -;; They are offered as defaults when ediff-use-last-dir is set to t. - -;; Fri Feb 22, 1994 - -;; Added ediff-before-change-guard to remove ASCII highlighting when -;; the user attempts to change buffer-A/B. This is needed because -;; otherwise the undo info may become screwed up in those buffers. -;; Hitting `h' (ediff-toggle-hilit) on a dumb terminal will toggle -;; between ASCII highlighting and no highlighting. - -;; Fri Feb 24, 1994 - -;; Fixed problems with multiple Ediff sessions running simultaneously. - -;; Tue Mar 1, 1994 - -;; Added vc-ediff, the Ediff interface to vc.el. (Thanks to Eric -;; Freudenthal for contributing this -;; function.) - -;; Sun Mar 6, 1994 - -;; Added rcs-ediff, an Ediff interface to RCS via rcs.el. (Thanks to -;; Alastair Burt .) -;; Some minor improvements. - -;; Tue March 15, 1994 - -;; Fixed a buglet in defining ediff-current-diff-face-A/B. -;; (Thanks to Job Ganzevoort .) - -;; Tue March 22, 1994 - -;; Fixed a bug with ediffing narrowed buffers, reported by Kevin -;; Broadey . -;; Made Ediff to work with files that have incomplete last line. -;; Made Ediff execute diff and patch using Bourne Shell, which -;; should eliminate problems with $prompt that some people had. - -;; Thu March 24, 1994 - -;; Achieved quadratic speedup in the size of the file by replacing the -;; slow goto-line by forward-line. -;; Converted demarkation of difference regions -;; from markers to overlays. This will later allow us to highlight all -;; diffs, not just the current one. - -;; Wed March 30, 1994 - -;; Under X, Ediff now highlights all differences in dim colors and the -;; current difference in bright colors. Improved XEmacs support. -;; Changed toggle hilit to cycle through 3 states: highlighting all -;; diffs, highlighting only the current diff, and highlighting using -;; ASCII flags. -;; Added support for difference regions that are not full lines. - -;; Fri April 1, 1994 - -;; Fixed bugs related to writing buffers A and B. -;; Added commands `ga', `gb' to jump directly to the closest diff in -;; buffer A and B, respectively. - -;; Fri April 11, 1994 - -;; Added `ediff-update-diffs', a function that lets the user recompute -;; difference regions after extensive editing done to buffers A and B -;; (bound to `!'). - -;; Wed April 13, 1994 - -;; Added the new feature: refining the current difference region. -;; This would highlight the precise differences between the regions in -;; buffer A and B. (A way to implement this was suggested by Boris -;; Goldowsky .) -;; -;; Fixed Ediff to be immune to several different versions of rcs.el -;; that are currently in distribution. - -;; Thu April 14, 1994 - -;; Ediff now respects X resources for the faces it uses. It no longer -;; barks when the colormap has no colors it is using; or when face -;; fonts can't be italicized, etc. - -;; Fri April 15, 1994 - -;; Changed `ediff-setup-windows' to minimize the need to delete and -;; create windows. Now jumps faster from diff to diff. -;; Added Ediff to the File menu on the menu bar (FSF's version). - -;; Mon April 18, 1994 - -;; Fixed to work with OS/2's PM-Emacs. - -;; Thu April 21, 1994 - -;; Lemacs' menus added (thanks to Alastair Burt for the help). - -;; Wed April 28, 1994 - -;; Fixed ediff-keep-window-config (thanks to Norbert Kiesel -;; ), ediff-shell and -;; ediff-protect-metachars (thanks to Richard Stanton -;; ). Made access to difference -;; overlays structure-independent, making it less bug-prone. -;; Patched ediff-read-file-name to work more intuitively with directory -;; names (thanks to Kevin Broadey ). - -;; Mon May 2, 1994 - -;; Added `ediff-frame-has-menubar' to guard against the possibility that -;; the current frame has no menu bar. - -;; Fri May 6, 1994 - -;; Fixed buglet in vc-ediff (thanks to Ray Nickson ). - -;; Wed May 18, 1994 - -;; Modified ediff-read-file-name to not put long file names in the -;; default prompt area, as suggested by KevinB@bartley.demon.co.uk. -;; Applied patch supplied by burt@dfki.uni-kl.de, fixing a problem with -;; ediff-diff-to-diff in Lemacs. - -;; Tue May 31, 1994 - -;; Added ediff-forward-word-function (as suggested by Job Ganzevoort -;; ). - -;; Thu Jun 2, 1994 - -;; Added `ediff-toggle-regexp-match', which allows the user to step -;; through only those difference regions that match some regexp; or, -;; vice versa, to skip over regions that match a regexp. (This feature -;; was suggested by Andy Scott .) -;; Added ediff-eval-in-buffer, which is a modified emerge-eval-in-buffer. -;; The function ediff-status-info, bound to `i', now replaces and extends -;; ediff-file-names and ediff-line-numbers, which were bound to `f' -;; and `i', respectively. - - -;; Wed Jun 10, 1994 - -;; Improved `ediff-read-file-name' and `ediff-buffers' so they are now -;; providing more intuitive defaults. Modified `ediff-read-file-name' -;; so it won't cause problems under OS/2. - -;; Fri Jun 24, 1994 - -;; Modified ediff-find-file, ediff-files-internal, and made -;; emerge-verify-file-buffer into ediff-verify-file-buffer so that -;; Ediff will work correctly with remote and compressed -;; files. (Suggested by Sandy Rutherford .) - -;; Fri Jun 28, 1994 - -;; Fixed ediff-patch-files to work with remote and compressed files. -;; Wed July 20, 1994 -;; Changed menu bar items per RMS's suggestion. Changed odd/even faces -;; in Lemacs to italic. Changed ediff-*-face-* variables so that they -;; will contain names of faces instead of the face internal -;; representation. (Copy-face works better with face names than with -;; face internal representation. With face internal representation, if -;; a face vector mentions a font explicitly, copy-face may attempt to -;; copy this font, which would cause an error if the font has a wrong -;; size for one of the existing frames.) Improved the way -;; mode-line-buffer-identification is set in ediff-setup so that Ediff -;; will accommodate the way buffers are identified in mode-line.el and -;; uniquify.el. - -;; Fri August 5, 1994 - -;; Ediff can now automatically skip over regions that differ only in -;; the white space and line breaks. This is controled with the variable -;; `ediff-ignore-similar-regions' and can be toggled on/off by typing -;; `##'. - -;; Mon August 8, 1994 - -;; If ediff-save-buffer is invoked with `wd', it'll save the diff -;; output in a file. - -;; Wed August 24, 1994 - -;; Fixed ediff-toggle-read-only and ediff-patch-file so that they will -;; check out version-controled files before modifying them. This will -;; permit checking the modified versions back in. In earlier -;; versions, such modifications could be lost, unless the user takes -;; special care of preserving them. - -;; Tue August 30, 1994 - -;; Added ediff-submit-report. -;; Introduced ediff-revision as a uniform way of calling vc.el and -;; rcs.el. This is controled by ediff-version-control-package -;; variable. Functions vc-ediff, rcs-ediff are replaced by their -;; internal versions. - -;; Thu September 1, 1994 - -;; Made ediff-overlay-put and ediff-move-overlay into bona fide -;; functions (rather than fset symbols). These now check if overlay's -;; buffer is alive. If not, overlay is deleted. This overcomes some of -;; the problems with Lemacs. - -;; Thu September 8, 1994 - -;; Added ediff-revision-key, ediff-load-version-control and streamlined -;; vc/rcs-ediff[-internal]. Eliminated dependency on emerge.el. - -;; Fri September 23, 1994 - -;; Added ediff-windows and ediff-regions. -;; Changed ediff-setup-windows and related procedures to create -;; a separate dedicated control frame for each invocation of Ediff. - -;; Tue September 27, 1994 - -;; Added redraw-display everywhere before creating or deleting -;; frames. It appears that this cures Emacs' bug where it trashes some -;; fonts which leads to crashes. Also, some code cleanups and bug fixes. - -;; Fri September 30, 1994 - -;; Fixed ediff-update-diffs so it'll work correctly with -;; ediff-windows and ediff-regions. Added narrowing and widening to the -;; suite of commands available for ediff-windows and ediff-regions. - -;; Fri October 7, 1994 - -;; Changed ediff-setup-windows to funcall the actual window setting -;; function, which is either ediff-setup-windows-multiframe or -;; ediff-setup-windows-plain. Changed all temp file names to use `_' -;; instead of `.'. Presumably, this makes VMS happier. -;; Ported to VMS (thanks to Richard Levitte ). -;; Added ediff-prefer-long-help-message and changed defaults. -;; Fighting with XEmacs bugs. Made it possible to switch between plain -;; and multiframe display easier, but XEmacs is still getting confused -;; at times. Added ediff-check-version and ediff-set-help-message. -;; Made more sensible temp file names, which is important when -;; generating context diffs. Added ediff-make-frame-position and -;; ediff-control-frame-position-function. - -;; Wed October 12, 1994 - -;; ediff-window-visible-p now makes a call to frame-visible-p -;; only when window-system is non-nil. Rearranged the block of fset's -;; so that the wrong things won't be defined when window-system is nil. -;; Added ediff-revert-buffers-then-recompute-diffs function. -;; Removed flag-argument from a background call to shell-command. -;; Added ediff-shell-command to enable custom diff execute in the -;; background and insert output in ediff-custom-diff-buffer. -;; Added ediff-shell-command-filter, because XEmacs doesn't have it. -;; Made ediff-revision set up ediff-job-name to `ediff-revision'. -;; This enables users do ediff-revision-specific actions on exiting -;; Ediff using ediff-quit-hooks. -;; Reshaffled some fset's so that functions for checking color and -;; faces won't be touched on a non-windowing display. - -;; Thu October 20, 1994 - -;; Modified ediff-make-fine-diffs so that no fine diffs are computed if -;; one of the diff regions is empty. Saves time and also works around -;; the buggy diff program in AIX. -;; ediff no longer loads the version control package -;; automatically---only when ediff-revision is called. -;; Enabled focusing/hiding diff regions that match both or just one of the -;; regexps. -;; Changed ediff-regions to ediff-small-regions. Added ediff-large-regions. -;; Modified ediff-next/previous-difference to work right when both -;; skipping regexp matches and skipping similar regions is enabled. -;; Fixed bugs in positioning the control frame. - -;; Fri October 28, 1994 - -;; Fixed bugs in ediff-next/previous-difference, ediff-set-visible-region -;; Changed/added ediff-word-[1-4]. - -;; Tue November 1, 1994 - -;; Made ediff-revision delete the temporary version files it creates. - -;; Tue November 29, 1994 - -;; Added ediff-swap-buffers. Split ediff-difference-vector into -;; ediff-difference-vector-A and ediff-difference-vector-B, which -;; allowed to factor out a lot of stuff. -;; Made the code buffer-C ready. - -;; Thu December 1, 1994 - -;; Lotsa bug fixes. Further rationalized the code. -;; Added ediff-display-help-hooks, ediff-mode-hooks. -;; Replaced almost identical ediff-scroll-up/down with -;; ediff-scroll-vertically. -;; Replaced almost identical ediff-scroll-left/right with -;; ediff-scroll-horizontally. -;; Made the code buffer-C ready. - -;; Thu December 8, 1994 - -;; Added ediff-toggle-wide-display. In plain display, help message is -;; now centered correctly. - -;; Fri December 9, 1994 - -;; Added ediff-toggle-multiframe. -;; Fixed ediff-pop-diff and ediff-copy-diff, so that they will -;; invoke auto-refining, if necessary. - -;; Mon December 12, 1994 - -;; Modified ediff-toggle-wide-display so it would funcall -;; ediff-make-wide-display-function. - -;; Tue December 13, 1994 - -;; Ediff now chooses its surrogate minibuffer from frame A. - -;; Mon December 20, 1994 - -;; Added ediff-keymap-setup-hooks. - -;; Fri December 23, 1994 - -;; Changed the representation in ediff-killed-diffs-alist so that -;; ediff-save-diff-region and ediff-pop-diff won't be confused when the -;; user swaps buffers. - -;; Mon December 26, 1994 - -;; Placated OS/2 by making Ediff to synchronize the call to startup -;; hooks with the acynchronous process that computes custom diffs. -;; This has no effect on Unix installations (and VMS?), but -;; may increase Ediff's startup time under OS/2 by 1 or 2 seconds. - -;; Thu December 29, 1994 - -;; ediff-recenter now deactivates the mark, so that transient mark mode -;; highlighting won't interfere with Ediff's highlighting. -;; Also, ediff-recenter tries to not deiconify control frame, if it is -;; not needed. - -;; Fri December 30, 1994 - -;; Small bugs. Worked around the OS/2 bug where -;; modify-frame-parameters has no effect on iconified frames. Ediff -;; now remembers the iconification status of the control frame and uses -;; it as a preferred way of displaying it when help is toggled -;; off. (This can be observed only in Emacs (not XEmacs) and only if -;; the window manager lets icons accept keyboard input.) - -;; Tue January 3, 1995 - -;; Some futher work on incorporating buffer C in ediff-extract-diffs, -;; ediff-focus/hide-on-regexp-matches, and -;; ediff-setup-windows-plain/multiframe. The preceding two functions -;; now dispatch the appropriate setup function depending on whether the -;; current job is comparison or merge. - -;; Wed January 4, 1995 - -;; Made it work under Emacs built without the X support. - -;; Fri January 6, 1995 - -;; Slightly changed the prompting behavior of ediff-files. Bug fix in -;; mode-line-buffer-identification. - -;; Wed January 18, 1995 - -;; Added 3way comparison and support for diff3. Ported to NeXTStep -;; -;; Fri January 20, 1995 - -;; Added ediff-merge-files, ediff-merge-buffers, -;; ediff-merge-files-with-ancestor, ediff-merge-buffers-with-ancestor, -;; ediff-merge-revisions, ediff-merge-revisions-with-ancestor. - -;; Tue January 24, 1995 - -;; Bug fixes. Split into several files. - -;; Thu January 26, 1995 - -;; `*' is now bound to ediff-make-or-kill-fine-diffs. This lets the -;; user to kill fine diffs for the current region (by providing a -;; negative prefix arg), if there are so many of them as to hamper -;; the viewing. - -;; Mon January 30, 1995 - -;; Changed ediff-selective-display to ediff-set-visible-region, -;; ediff-toggle-selective-display to ediff-toggle-narrow-region. -;; Ediff now turns selective display off before starting. Restores -;; selective display on exit. -;; In 2-way comparison, `a' now copies to buf `b' and `b' to buf `a'. -;; Previously, the bindings were `ab' and `ba'. - -;; Wed February 1, 1995 - -;; Added ediff-undo-selective-display (thanks to Stig ). -;; Rearranged autoloads. Renamed ediff-entry.el into ediff.el and -;; ediff.el into ediff-util.el. - -;; Fri February 3 - -;; Added ediff-toggle-show-clashes-only, which is bound to `$'. - -;; Fri February 19 - -;; Some minor patches from Stig. Also, made ediff-xemacs-p and -;; ediff-*-job into variables for better performance. -;; In ediff-setup, diff regions are now computed after buffers A/B/C -;; are set up. Previously, it didn't work right with selective display. -;; Also, added ediff-profile to time Ediff commands and -;; ediff-debug-info for civilized display of the difference vectors -;; (and possibly more in the future). - -;; Tue March 18 - -;; Fixed ediff-diff-at-point and ediff-toggle-multiframe. -;; Added ediff-destroy-control-frame, ediff-window-display-p. The latter -;; replaces window-system in many cases. Needed because in XEmacs 19.12 -;; window-system returns 'tty on a tty display. -;; Converted xemacs *screen* nomenclature to *frame*. -;; Made ediff-patch-buffer cope with buffers that don't visit any file. -;; Fixed ediff-toggle-read-only so it knows the difference between -;; version-controlled files and others. It also knows whether we are using -;; patch or not. -;; Renamed ediff-windows to ediff-windows-wordwise, added -;; ediff-windows-linewise. Changed ediff-small/large-regions to -;; ediff-regions-wordwise/linewise - -;; Tue May 2 - -;; Added ediff-documentation. Fixes for XEmacs 19.12. -;; Merge buffer now assumes the major mode of ediff-default-variant. - -;; Mon May 31, 1995 - -;; Ediff-revision now takes a prefix argument. Can compare two versions of -;; the same file. Cleaned up ediff-make-control-frame. -;; Fixed a bug in ediff-get-visible-buffer-window. -;; Added ediff-cleanup-hooks, ediff-janitor. -;; ediff-cleanup-hooks is called before ediff-quit-hooks. +;;; Acknowledgements: +;; Ediff was inspired by Dale R. Worley's emerge.el. +;; Ediff would not have been possible without the help and encouragement of +;; its many users. See Ediff on-line Info for the full list of those who +;; helped. Improved defaults in Ediff file-name reading commands. -;;; TO DO: -;; ------ -;; -;; 1. Add support for multiple sessions. (At present, one can run -;; multiple Ediff sessions, but they won't be related.) The idea is to -;; have vars local to each control buffer, which will tell which buffer is -;; the next and which is the previous one. The user could then go forward -;; and backward by typing C-SPC and C-DEL (or C-n and C-p). -;; This will probably entail some minor modifications to ediff-setup. -;; The primary use of this feature would be comparing directories of -;; similarly named files and multi-file patch. For the latter, Ediff will -;; have to parse patches to extract the names of files. - +;;; Code: -;;; Acknowledgements: +(provide 'ediff) -;; Special thanks to Alastair Burt , Kevin Broadey -;; , Harald Boegeholz -;; , Jin S. Choi" , -;; Eric Eide , Kevin Esler , Robert -;; Estes , Eric Freudenthal -;; , -;; Job Ganzevoort , Boris Goldowsky -;; , Allan Gottlieb , -;; Xiaoli Huang , Larry Gouge , -;; Karl Heuer , , -;; , David Karr -;; , Norbert Kiesel -;; , Fritz Knabe , -;; Heinz Knutzen , Ken Laprade -;; , Richard Levitte -;; , Martin Maechler , -;; Richard Mlynarik , Chris Murphy -;; , Eyvind Ness , Ray Nickson -;; , Paul Raines , Tibor -;; Polgar , C.S. Roberson , -;; Kevin Rodgers , Sandy Rutherford -;; , Heribert Schuetz , Andy Scott -;; , Axel Seibert -;; , Richard Stallman -;; , Richard Stanton , -;; Ake Stenhoff , -;; Stig , Peter Stout , -;; Raymond Toy , -;; and Ilya Zakharevich -;; for contributing ideas, patches, and bug reports. -;; -;; Thanks also to many others who felt obliged to drop a thank you note. +;; Compiler pacifier +(defvar cvs-cookie-handle) +(defvar ediff-last-dir-patch) +(defvar ediff-patch-default-directory) + +(and noninteractive + (eval-when-compile + (load-library "dired") + (load-library "info") + (load "pcl-cvs" 'noerror))) +(eval-when-compile + (let ((load-path (cons (expand-file-name ".") load-path))) + (or (featurep 'ediff-init) + (load "ediff-init.el" nil nil 'nosuffix)) + (or (featurep 'ediff-mult) + (load "ediff-mult.el" nil nil 'nosuffix)) + (or (featurep 'ediff-ptch) + (load "ediff-ptch.el" nil nil 'nosuffix)) + (or (featurep 'ediff-vers) + (load "ediff-vers.el" nil nil 'nosuffix)) + )) +;; end pacifier +(require 'ediff-init) +(require 'ediff-mult) ; required because of the registry stuff -;;; Code: +(defgroup ediff nil + "A comprehensive visual interface to diff & patch" + :tag "Ediff" + :group 'tools) -(require 'ediff-init) -(defvar ediff-version-control-package 'vc - "Version control package used. -Currently, Ediff supports vc.el and rcs.el. Set this to `rcs' if you have -rcs.el and want to use it instead of the standard vc.el. +(defcustom ediff-use-last-dir nil + "*If t, Ediff will use previous directory as default when reading file name." + :type 'boolean + :group 'ediff) -Note: both packages provide access to RCS, but only vc.el comes with Emacs -distribution.") +;; Last directory used by an Ediff command for file-A. +(defvar ediff-last-dir-A nil) +;; Last directory used by an Ediff command for file-B. +(defvar ediff-last-dir-B nil) +;; Last directory used by an Ediff command for file-C. +(defvar ediff-last-dir-C nil) +;; Last directory used by an Ediff command for the ancestor file. +(defvar ediff-last-dir-ancestor nil) +;; Last directory used by an Ediff command as the output directory for merge. +(defvar ediff-last-merge-autostore-dir) -(defvar ediff-revision-key nil - "Key to which `ediff-revision' is to be bound.") - -(defvar ediff-use-last-dir nil - "*If t, Ediff uses previous directory as default when reading file name.") - -(defvar ediff-last-dir-A nil - "Last directory used by an Ediff command for file-A.") -(defvar ediff-last-dir-B nil - "Last directory used by an Ediff command for file-B.") -(defvar ediff-last-dir-C nil - "Last directory used by an Ediff command for file-C.") -(defvar ediff-last-dir-ancestor nil - "Last directory used by an Ediff command for the ancestor file.") -(defvar ediff-last-dir-patch nil - "Last directory used by an Ediff command for file to patch.") - -;;; Patching -;;;###autoload -(defun ediff-patch-file (source-filename &optional startup-hooks job-name) - "Run Ediff by patching FILE-TP-PATCH." - ;; This now returns the control buffer - (interactive - (list (ediff-read-file-name "File to patch" - (if ediff-use-last-dir - ediff-last-dir-patch - default-directory) - nil))) - - (setq source-filename (expand-file-name source-filename)) - (ediff-get-patch-buffer - (if (eq job-name 'ediff-patch-buffer) - (ediff-eval-in-buffer (get-file-buffer source-filename) - default-directory) - (file-name-directory source-filename))) - - (let* ((backup-extension - ;; if the user specified a -b option, extract the backup - ;; extension from there; else use `_orig' - (substring ediff-patch-options - (if (string-match "-b[ \t]+" ediff-patch-options) - (match-end 0) 0) - (if (string-match "-b[ \t]+[^ \t]+" ediff-patch-options) - (match-end 0) 0))) - (shell-file-name ediff-shell) - ;; ediff-find-file may use a temp file to do the patch - ;; so, we save source-filename and true-source-filename as a var - ;; that initially is source-filename but may be changed to a temp - ;; file for the purpose of patching. - (true-source-filename source-filename) - (target-filename source-filename) - target-buf buf-to-patch file-name-magic-p ctl-buf) - - ;; if the user didn't specify a backup extension, use _orig - (if (string= backup-extension "") - (setq backup-extension "_orig")) - - ;; Make a temp file, if source-filename has a magic file handler (or if - ;; it is handled via auto-mode-alist and similar magic). - ;; Check if there is a buffer visiting source-filename and if they are in - ;; synch; arrange for the deletion of temp file. - (ediff-find-file 'true-source-filename 'buf-to-patch - 'ediff-last-dir-patch 'startup-hooks) - - ;; Check if source file name has triggered black magic, such as file name - ;; handlers or auto mode alist, and make a note of it. - ;; true-source-filename should be either the original name or a - ;; temporary file where we put the after-product of the file handler. - (setq file-name-magic-p (not (equal (file-truename true-source-filename) - (file-truename source-filename)))) - - ;; Checkout orig file, if necessary, so that the patched file could be - ;; checked back in. - (if (ediff-file-checked-in-p (buffer-file-name buf-to-patch)) - (ediff-toggle-read-only buf-to-patch)) - - (ediff-eval-in-buffer ediff-patch-diagnostics - (message "Applying patch ... ")(sit-for 0) - ;; always pass patch the -f option, so it won't ask any questions - (shell-command-on-region - (point-min) (point-max) - (format "%s -f %s -b %s %s" - ediff-patch-program ediff-patch-options - backup-extension - (expand-file-name true-source-filename)) - t)) - (message "Applying patch ... done")(sit-for 0) - (switch-to-buffer ediff-patch-diagnostics) - (sit-for 0) ; synchronize - - (or (file-exists-p (concat true-source-filename backup-extension)) - (error "Patch failed or didn't modify the original file")) - - ;; If black magic is involved, apply patch to a temp copy of the - ;; file. Otherwise, apply patch to the orig copy. If patch is applied - ;; to temp copy, we name the result old-name_patched for local files - ;; and temp-copy_patched for remote files. The orig file name isn't - ;; changed, and the temp copy of the original is later deleted. - ;; Without magic, the original file is renamed (usually into - ;; old-name_orig) and the result of patching will have the same name as - ;; the original. - (if (not file-name-magic-p) - (ediff-eval-in-buffer buf-to-patch - (set-visited-file-name (concat source-filename backup-extension)) - (set-buffer-modified-p nil)) - - ;; Black magic in effect. - ;; If orig file was remote, put the patched file in the temp directory. - ;; If orig file is local, put the patched file in the directory of - ;; the orig file. - (setq target-filename - (concat - (if (ediff-file-remote-p (file-truename source-filename)) - true-source-filename - source-filename) - "_patched")) - - (rename-file true-source-filename target-filename t) - - ;; arrange that the temp copy of orig will be deleted - (rename-file (concat true-source-filename backup-extension) - true-source-filename t)) - - ;; make orig buffer read-only - (setq startup-hooks - (cons 'ediff-set-read-only-in-buf-A startup-hooks)) - - ;; set up a buf for the patched file - (setq target-buf (find-file-noselect target-filename)) - - (setq ctl-buf - (ediff-buffers-internal - buf-to-patch target-buf nil - startup-hooks '(or job-name ediff-patch-file))) - - (bury-buffer ediff-patch-diagnostics) - (message "Patch diagnostics are available in buffer %s" - (buffer-name ediff-patch-diagnostics)) - ctl-buf)) - +;; Used as a startup hook to set `_orig' patch file read-only. (defun ediff-set-read-only-in-buf-A () - "Used as a startup hook to set `_orig' patch file read-only." - (ediff-eval-in-buffer ediff-buffer-A + (ediff-with-current-buffer ediff-buffer-A (toggle-read-only 1))) -;;;###autoload -(defalias 'epatch 'ediff-patch-file) -;;;###autoload -(defalias 'epatch-buffer 'ediff-patch-buffer) +;; Return a plausible default for ediff's first file: +;; In dired, return the file name under the point, unless it is a directory +;; If the buffer has a file name, return that file name. +(defun ediff-get-default-file-name () + (cond ((eq major-mode 'dired-mode) + (let ((f (dired-get-filename nil 'no-error))) + (if (and (stringp f) (not (file-directory-p f))) + f))) + ((buffer-file-name (current-buffer)) + (file-name-nondirectory (buffer-file-name (current-buffer)))) + )) ;;; Compare files/buffers @@ -875,7 +184,9 @@ distribution.") ediff-last-dir-A default-directory)) dir-B f) - (list (setq f (ediff-read-file-name "File A to compare" dir-A nil)) + (list (setq f (ediff-read-file-name + "File A to compare" dir-A + (ediff-get-default-file-name))) (ediff-read-file-name "File B to compare" (setq dir-B (if ediff-use-last-dir @@ -883,7 +194,7 @@ distribution.") (file-name-directory f))) (progn (setq file-name-history - (cons (abbreviate-file-name + (cons (ediff-abbreviate-file-name (expand-file-name (file-name-nondirectory f) dir-B)) @@ -907,7 +218,9 @@ distribution.") ediff-last-dir-A default-directory)) dir-B dir-C f ff) - (list (setq f (ediff-read-file-name "File A to compare" dir-A nil)) + (list (setq f (ediff-read-file-name + "File A to compare" dir-A + (ediff-get-default-file-name))) (setq ff (ediff-read-file-name "File B to compare" (setq dir-B (if ediff-use-last-dir @@ -916,7 +229,7 @@ distribution.") (progn (setq file-name-history (cons - (abbreviate-file-name + (ediff-abbreviate-file-name (expand-file-name (file-name-nondirectory f) dir-B)) @@ -928,7 +241,7 @@ distribution.") (file-name-directory ff))) (progn (setq file-name-history - (cons (abbreviate-file-name + (cons (ediff-abbreviate-file-name (expand-file-name (file-name-nondirectory ff) dir-C)) @@ -951,22 +264,25 @@ distribution.") (defalias 'ediff3 'ediff-files3) +;; Visit FILE and arrange its buffer to Ediff's liking. +;; FILE is actually a variable symbol that must contain a true file name. +;; BUFFER-NAME is a variable symbol, which will get the buffer object into +;; which FILE is read. +;; LAST-DIR is the directory variable symbol where FILE's +;; directory name should be returned. HOOKS-VAR is a variable symbol that will +;; be assigned the hook to be executed after `ediff-startup' is finished. +;; `ediff-find-file' arranges that the temp files it might create will be +;; deleted. (defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var) - "Visit FILE and arrange its buffer to Ediff's liking. -FILE is actually a variable symbol that must contain a true file name. -BUFFER-NAME is a variable symbol, which will get the buffer object into which -FILE is read. LAST-DIR is the directory variable symbol where FILE's -directory name should be returned. HOOKS is a variable symbol that will be -assigned the hook to be executed after `ediff-startup' is finished. -`ediff-find-file' arranges that the temp files it might create will be -deleted." (let* ((file (symbol-value file-var)) - (file-magic (find-file-name-handler file 'find-file-noselect)) + (file-magic (ediff-filename-magic-p file)) (temp-file-name-prefix (file-name-nondirectory file))) - (if (not (file-readable-p file)) - (error "File `%s' does not exist or is not readable" file)) + (cond ((not (file-readable-p file)) + (error "File `%s' does not exist or is not readable" file)) + ((file-directory-p file) + (error "File `%s' is a directory" file))) - ;; some of the command, below, require full file name + ;; some of the commands, below, require full file name (setq file (expand-file-name file)) ;; Record the directory of the file @@ -976,33 +292,42 @@ deleted." ;; Setup the buffer (set buffer-name (find-file-noselect file)) - (ediff-eval-in-buffer (symbol-value buffer-name) + (ediff-with-current-buffer (symbol-value buffer-name) (widen) ; Make sure the entire file is seen - (cond (file-magic ;; file has handler, such as jka-compr-handler or - ;; ange-ftp-hook-function--arrange for temp file + (cond (file-magic ; file has a handler, such as jka-compr-handler or + ;;; ange-ftp-hook-function--arrange for temp file (ediff-verify-file-buffer 'magic) - (setq file (ediff-make-temp-file temp-file-name-prefix)) + (setq file + (ediff-make-temp-file + (current-buffer) temp-file-name-prefix)) (set hooks-var (cons (` (lambda () (delete-file (, file)))) (symbol-value hooks-var)))) ;; file processed via auto-mode-alist, a la uncompress.el ((not (equal (file-truename file) (file-truename (buffer-file-name)))) - (setq file (ediff-make-temp-file temp-file-name-prefix)) + (setq file + (ediff-make-temp-file + (current-buffer) temp-file-name-prefix)) (set hooks-var (cons (` (lambda () (delete-file (, file)))) (symbol-value hooks-var)))) (t ;; plain file---just check that the file matches the buffer (ediff-verify-file-buffer)))) (set file-var file))) -(defun ediff-files-internal (file-A file-B file-C startup-hooks job-name) +;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer +(defun ediff-files-internal (file-A file-B file-C startup-hooks job-name + &optional merge-buffer-file) (let (buf-A buf-B buf-C) - (message "Reading file %s ... " file-A)(sit-for 0) + (message "Reading file %s ... " file-A) + ;;(sit-for 0) (ediff-find-file 'file-A 'buf-A 'ediff-last-dir-A 'startup-hooks) - (message "Reading file %s ... " file-B)(sit-for 0) + (message "Reading file %s ... " file-B) + ;;(sit-for 0) (ediff-find-file 'file-B 'buf-B 'ediff-last-dir-B 'startup-hooks) - (if (and (stringp file-C) (not ediff-merge-job)) + (if (stringp file-C) (progn - (message "Reading file %s ... " file-C)(sit-for 0) + (message "Reading file %s ... " file-C) + ;;(sit-for 0) (ediff-find-file 'file-C 'buf-C (if (eq job-name 'ediff-merge-files-with-ancestor) @@ -1012,7 +337,8 @@ deleted." buf-B file-B buf-C file-C startup-hooks - (list (cons 'ediff-job-name job-name))))) + (list (cons 'ediff-job-name job-name)) + merge-buffer-file))) ;;;###autoload @@ -1033,9 +359,12 @@ deleted." (save-window-excursion (other-window 1)) (ediff-other-buffer bf)) t)))) - (or job-name (setq job-name 'ediff-buffers)) (ediff-buffers-internal buffer-A buffer-B nil startup-hooks job-name)) + +;;;###autoload +(defalias 'ebuffers 'ediff-buffers) + ;;;###autoload (defun ediff-buffers3 (buffer-A buffer-B buffer-C @@ -1060,13 +389,17 @@ deleted." (ediff-other-buffer (list bf bff))) t) ))) - (or job-name (setq job-name 'ediff-buffers3)) (ediff-buffers-internal buffer-A buffer-B buffer-C startup-hooks job-name)) + +;;;###autoload +(defalias 'ebuffers3 'ediff-buffers3) -(defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name) +;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer +(defun ediff-buffers-internal (buf-A buf-B buf-C startup-hooks job-name + &optional merge-buffer-file) (let* ((buf-A-file-name (buffer-file-name (get-buffer buf-A))) (buf-B-file-name (buffer-file-name (get-buffer buf-B))) (buf-C-is-alive (ediff-buffer-live-p buf-C)) @@ -1088,16 +421,10 @@ deleted." (if (stringp buf-C-file-name) (setq buf-C-file-name (file-name-nondirectory buf-C-file-name))) - ;; these three need to be evaluated in their buffers, since - ;; ediff-make-temp-file checks the current buffer when assigning file - ;; names - (ediff-eval-in-buffer buf-A - (setq file-A (ediff-make-temp-file buf-A-file-name))) - (ediff-eval-in-buffer buf-B - (setq file-B (ediff-make-temp-file buf-B-file-name))) + (setq file-A (ediff-make-temp-file buf-A buf-A-file-name) + file-B (ediff-make-temp-file buf-B buf-B-file-name)) (if buf-C-is-alive - (ediff-eval-in-buffer buf-C - (setq file-C (ediff-make-temp-file buf-C-file-name)))) + (setq file-C (ediff-make-temp-file buf-C buf-C-file-name))) (ediff-setup (get-buffer buf-A) file-A (get-buffer buf-B) file-B @@ -1110,8 +437,306 @@ deleted." )) startup-hooks) (list (cons 'ediff-job-name job-name)) - ))) - + merge-buffer-file))) + + +;;; Directory and file group operations + +;; Get appropriate default name for directory: +;; If ediff-use-last-dir, use ediff-last-dir-A. +;; In dired mode, use the directory that is under the point (if any); +;; otherwise, use default-directory +(defun ediff-get-default-directory-name () + (cond (ediff-use-last-dir ediff-last-dir-A) + ((eq major-mode 'dired-mode) + (let ((f (dired-get-filename nil 'noerror))) + (if (and (stringp f) (file-directory-p f)) + f + default-directory))) + (t default-directory))) + + +;;;###autoload +(defun ediff-directories (dir1 dir2 regexp) + "Run Ediff on a pair of directories, DIR1 and DIR2, comparing files that have +the same name in both. The third argument, REGEXP, is a regular expression that +can be used to filter out certain file names." + (interactive + (let ((dir-A (ediff-get-default-directory-name)) + f) + (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil)) + (ediff-read-file-name "Directory B to compare:" + (if ediff-use-last-dir + ediff-last-dir-B + (ediff-strip-last-dir f)) + nil) + (read-string "Filter through regular expression: " + nil 'ediff-filtering-regexp-history) + ))) + (ediff-directories-internal + dir1 dir2 nil regexp 'ediff-files 'ediff-directories + )) + +;;;###autoload +(defalias 'edirs 'ediff-directories) + + +;;;###autoload +(defun ediff-directory-revisions (dir1 regexp) + "Run Ediff on a directory, DIR1, comparing its files with their revisions. +The second argument, REGEXP, is a regular expression that filters the file +names. Only the files that are under revision control are taken into account." + (interactive + (let ((dir-A (ediff-get-default-directory-name))) + (list (ediff-read-file-name + "Directory to compare with revision:" dir-A nil) + (read-string "Filter through regular expression: " + nil 'ediff-filtering-regexp-history) + ))) + (ediff-directory-revisions-internal + dir1 regexp 'ediff-revision 'ediff-directory-revisions + )) + +;;;###autoload +(defalias 'edir-revisions 'ediff-directory-revisions) + + +;;;###autoload +(defun ediff-directories3 (dir1 dir2 dir3 regexp) + "Run Ediff on three directories, DIR1, DIR2, and DIR3, comparing files that +have the same name in all three. The last argument, REGEXP, is a regular +expression that can be used to filter out certain file names." + (interactive + (let ((dir-A (ediff-get-default-directory-name)) + f) + (list (setq f (ediff-read-file-name "Directory A to compare:" dir-A nil)) + (setq f (ediff-read-file-name "Directory B to compare:" + (if ediff-use-last-dir + ediff-last-dir-B + (ediff-strip-last-dir f)) + nil)) + (ediff-read-file-name "Directory C to compare:" + (if ediff-use-last-dir + ediff-last-dir-C + (ediff-strip-last-dir f)) + nil) + (read-string "Filter through regular expression: " + nil 'ediff-filtering-regexp-history) + ))) + (ediff-directories-internal + dir1 dir2 dir3 regexp 'ediff-files3 'ediff-directories3 + )) + +;;;###autoload +(defalias 'edirs3 'ediff-directories3) + +;;;###autoload +(defun ediff-merge-directories (dir1 dir2 regexp) + "Run Ediff on a pair of directories, DIR1 and DIR2, merging files that have +the same name in both. The third argument, REGEXP, is a regular expression that +can be used to filter out certain file names." + (interactive + (let ((dir-A (ediff-get-default-directory-name)) + f) + (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil)) + (ediff-read-file-name "Directory B to merge:" + (if ediff-use-last-dir + ediff-last-dir-B + (ediff-strip-last-dir f)) + nil) + (read-string "Filter through regular expression: " + nil 'ediff-filtering-regexp-history) + ))) + (ediff-directories-internal + dir1 dir2 nil regexp 'ediff-merge-files 'ediff-merge-directories + )) + +;;;###autoload +(defalias 'edirs-merge 'ediff-merge-directories) + +;;;###autoload +(defun ediff-merge-directories-with-ancestor (dir1 dir2 ancestor-dir regexp) + "Merge files in directories DIR1 and DIR2 using files in ANCESTOR-DIR as ancestors. +Ediff merges files that have identical names in DIR1, DIR2. If a pair of files +in DIR1 and DIR2 doesn't have an ancestor in ANCESTOR-DIR, Ediff will merge +without ancestor. The fourth argument, REGEXP, is a regular expression that +can be used to filter out certain file names." + (interactive + (let ((dir-A (ediff-get-default-directory-name)) + f) + (list (setq f (ediff-read-file-name "Directory A to merge:" dir-A nil)) + (setq f (ediff-read-file-name "Directory B to merge:" + (if ediff-use-last-dir + ediff-last-dir-B + (ediff-strip-last-dir f)) + nil)) + (ediff-read-file-name "Ancestor directory:" + (if ediff-use-last-dir + ediff-last-dir-C + (ediff-strip-last-dir f)) + nil) + (read-string "Filter through regular expression: " + nil 'ediff-filtering-regexp-history) + ))) + (ediff-directories-internal + dir1 dir2 ancestor-dir regexp + 'ediff-merge-files-with-ancestor 'ediff-merge-directories-with-ancestor + )) + +;;;###autoload +(defun ediff-merge-directory-revisions (dir1 regexp) + "Run Ediff on a directory, DIR1, merging its files with their revisions. +The second argument, REGEXP, is a regular expression that filters the file +names. Only the files that are under revision control are taken into account." + (interactive + (let ((dir-A (ediff-get-default-directory-name))) + (list (ediff-read-file-name + "Directory to merge with revisions:" dir-A nil) + (read-string "Filter through regular expression: " + nil 'ediff-filtering-regexp-history) + ))) + (ediff-directory-revisions-internal + dir1 regexp 'ediff-merge-revisions 'ediff-merge-directory-revisions + )) + +;;;###autoload +(defalias 'edir-merge-revisions 'ediff-merge-directory-revisions) + +;;;###autoload +(defun ediff-merge-directory-revisions-with-ancestor (dir1 regexp) + "Run Ediff on a directory, DIR1, merging its files with their revisions and ancestors. +The second argument, REGEXP, is a regular expression that filters the file +names. Only the files that are under revision control are taken into account." + (interactive + (let ((dir-A (ediff-get-default-directory-name))) + (list (ediff-read-file-name + "Directory to merge with revisions and ancestors:" dir-A nil) + (read-string "Filter through regular expression: " + nil 'ediff-filtering-regexp-history) + ))) + (ediff-directory-revisions-internal + dir1 regexp 'ediff-merge-revisions-with-ancestor + 'ediff-merge-directory-revisions-with-ancestor + )) + +;;;###autoload +(defalias + 'edir-merge-revisions-with-ancestor + 'ediff-merge-directory-revisions-with-ancestor) + +;;;###autoload +(defalias 'edirs-merge-with-ancestor 'ediff-merge-directories-with-ancestor) + +;; Run ediff-action (ediff-files, ediff-merge, ediff-merge-with-ancestors) +;; on a pair of directories (three directories, in case of ancestor). +;; The third argument, REGEXP, is a regular expression that can be used to +;; filter out certain file names. +;; JOBNAME is the symbol indicating the meta-job to be performed. +;; MERGE-DIR is the directory in which to store merged files. +(defun ediff-directories-internal (dir1 dir2 dir3 regexp action jobname + &optional startup-hooks) + ;; ediff-read-file-name is set to attach a previously entered file name if + ;; the currently entered file is a directory. This code takes care of that. + (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1)) + dir2 (if (file-directory-p dir2) dir2 (file-name-directory dir2))) + + (if (stringp dir3) + (setq dir3 (if (file-directory-p dir3) dir3 (file-name-directory dir3)))) + + (cond ((string= dir1 dir2) + (error "Directories A and B are the same: %s" dir1)) + ((and (eq jobname 'ediff-directories3) + (string= dir1 dir3)) + (error "Directories A and C are the same: %s" dir1)) + ((and (eq jobname 'ediff-directories3) + (string= dir2 dir3)) + (error "Directories B and C are the same: %s" dir1))) + + (let (diffs ; var where ediff-intersect-directories returns the diff list + merge-autostore-dir + file-list meta-buf) + (if (and ediff-autostore-merges (ediff-merge-metajob jobname)) + (setq merge-autostore-dir + (ediff-read-file-name "Directory to save merged files:" + (if ediff-use-last-dir + ediff-last-merge-autostore-dir + (ediff-strip-last-dir dir1)) + nil))) + ;; verify we are not merging into an orig directory + (if (stringp merge-autostore-dir) + (cond ((and (stringp dir1) (string= merge-autostore-dir dir1)) + (or (y-or-n-p "Merge directory same as directory A, sure? ") + (error "Directory merge aborted"))) + ((and (stringp dir2) (string= merge-autostore-dir dir2)) + (or (y-or-n-p "Merge directory same as directory B, sure? ") + (error "Directory merge aborted"))) + ((and (stringp dir3) (string= merge-autostore-dir dir3)) + (or (y-or-n-p + "Merge directory same as ancestor directory, sure? ") + (error "Directory merge aborted"))))) + + (setq file-list (ediff-intersect-directories + jobname 'diffs + regexp dir1 dir2 dir3 merge-autostore-dir)) + (setq startup-hooks + ;; this sets various vars in the meta buffer inside + ;; ediff-prepare-meta-buffer + (cons (` (lambda () + ;; tell what to do if the user clicks on a session record + (setq ediff-session-action-function (quote (, action))) + ;; set ediff-dir-difference-list + (setq ediff-dir-difference-list (quote (, diffs))))) + startup-hooks)) + (setq meta-buf (ediff-prepare-meta-buffer + 'ediff-filegroup-action + file-list + "*Ediff Session Group Panel" + 'ediff-redraw-directory-group-buffer + jobname + startup-hooks)) + (ediff-show-meta-buffer meta-buf) + )) + +(defun ediff-directory-revisions-internal (dir1 regexp action jobname + &optional startup-hooks) + (setq dir1 (if (file-directory-p dir1) dir1 (file-name-directory dir1))) + + (let (file-list meta-buf merge-autostore-dir) + (if (and ediff-autostore-merges (ediff-merge-metajob jobname)) + (setq merge-autostore-dir + (ediff-read-file-name "Directory to save merged files:" + (if ediff-use-last-dir + ediff-last-merge-autostore-dir + (ediff-strip-last-dir dir1)) + nil))) + ;; verify merge-autostore-dir != dir1 + (if (and (stringp merge-autostore-dir) + (stringp dir1) + (string= merge-autostore-dir dir1)) + (or (y-or-n-p + "Directory for saving merges is the same as directory A. Sure? ") + (error "Merge of directory revisions aborted"))) + + (setq file-list + (ediff-get-directory-files-under-revision + jobname regexp dir1 merge-autostore-dir)) + (setq startup-hooks + ;; this sets various vars in the meta buffer inside + ;; ediff-prepare-meta-buffer + (cons (` (lambda () + ;; tell what to do if the user clicks on a session record + (setq ediff-session-action-function (quote (, action))) + )) + startup-hooks)) + (setq meta-buf (ediff-prepare-meta-buffer + 'ediff-filegroup-action + file-list + "*Ediff Session Group Panel" + 'ediff-redraw-directory-group-buffer + jobname + startup-hooks)) + (ediff-show-meta-buffer meta-buf) + )) ;;; Compare regions and windows @@ -1156,7 +781,7 @@ If WIND-B is nil, use window next to WIND-A." (save-excursion (save-window-excursion - (sit-for 0) ; synch before using window-start/end -- a precaution + (sit-for 0) ; sync before using window-start/end -- a precaution (select-window wind-A) (setq beg-A (window-start) end-A (window-end)) @@ -1165,7 +790,7 @@ If WIND-B is nil, use window next to WIND-A." end-B (window-end)))) (ediff-regions-internal buffer-A beg-A end-A buffer-B beg-B end-B - startup-hooks job-name word-mode))) + startup-hooks job-name word-mode nil))) ;;;###autoload (defun ediff-regions-wordwise (buffer-A buffer-B &optional startup-hooks) @@ -1202,7 +827,7 @@ lines. For large regions, use `ediff-regions-linewise'." (ediff-regions-internal (get-buffer buffer-A) reg-A-beg reg-A-end (get-buffer buffer-B) reg-B-beg reg-B-end - startup-hooks 'ediff-regions-wordwise 'word-mode))) + startup-hooks 'ediff-regions-wordwise 'word-mode nil))) ;;;###autoload (defun ediff-regions-linewise (buffer-A buffer-B &optional startup-hooks) @@ -1245,7 +870,6 @@ lines. For small regions, use `ediff-regions-wordwise'." (setq reg-B-beg (region-beginning) reg-B-end (region-end)) ;; enlarge the region to hold full lines - (goto-char reg-A-beg) (goto-char reg-B-beg) (beginning-of-line) (setq reg-B-beg (point)) @@ -1258,21 +882,22 @@ lines. For small regions, use `ediff-regions-wordwise'." (ediff-regions-internal (get-buffer buffer-A) reg-A-beg reg-A-end (get-buffer buffer-B) reg-B-beg reg-B-end - startup-hooks 'ediff-regions-linewise nil))) ; no word mode + startup-hooks 'ediff-regions-linewise nil nil))) ; no word mode ;; compare region beg-A to end-A of buffer-A ;; to regions beg-B -- end-B in buffer-B. (defun ediff-regions-internal (buffer-A beg-A end-A buffer-B beg-B end-B - startup-hooks job-name word-mode) + startup-hooks job-name word-mode + setup-parameters) (let ((tmp-buffer (get-buffer-create ediff-tmp-buffer)) overl-A overl-B file-A file-B) ;; in case beg/end-A/B aren't markers--make them into markers - (ediff-eval-in-buffer buffer-A + (ediff-with-current-buffer buffer-A (setq beg-A (move-marker (make-marker) beg-A) end-A (move-marker (make-marker) end-A))) - (ediff-eval-in-buffer buffer-B + (ediff-with-current-buffer buffer-B (setq beg-B (move-marker (make-marker) beg-B) end-B (move-marker (make-marker) end-B))) @@ -1297,15 +922,13 @@ Continue anyway? (y/n) ")) (if word-mode (ediff-wordify beg-A end-A buffer-A tmp-buffer) (ediff-copy-to-buffer beg-A end-A buffer-A tmp-buffer)) - (ediff-eval-in-buffer tmp-buffer - (setq file-A (ediff-make-temp-file "regA"))) + (setq file-A (ediff-make-temp-file tmp-buffer "regA")) ;; make file-B (if word-mode (ediff-wordify beg-B end-B buffer-B tmp-buffer) (ediff-copy-to-buffer beg-B end-B buffer-B tmp-buffer)) - (ediff-eval-in-buffer tmp-buffer - (setq file-B (ediff-make-temp-file "regB"))) + (setq file-B (ediff-make-temp-file tmp-buffer "regB")) (setq overl-A (ediff-make-bullet-proof-overlay beg-A end-A buffer-A)) (setq overl-B (ediff-make-bullet-proof-overlay beg-B end-B buffer-B)) @@ -1316,10 +939,11 @@ Continue anyway? (y/n) ")) (delete-file (, file-A)) (delete-file (, file-B)))) startup-hooks) - (list (cons 'ediff-word-mode word-mode) - (cons 'ediff-narrow-bounds (list overl-A overl-B)) - (cons 'ediff-job-name job-name)) - ) + (append + (list (cons 'ediff-word-mode word-mode) + (cons 'ediff-narrow-bounds (list overl-A overl-B)) + (cons 'ediff-job-name job-name)) + setup-parameters)) )) @@ -1330,18 +954,23 @@ Continue anyway? (y/n) ")) (defsubst ediff-merge-on-startup () (ediff-do-merge 0) - (ediff-eval-in-buffer ediff-buffer-C + (ediff-with-current-buffer ediff-buffer-C (set-buffer-modified-p nil))) ;;;###autoload -(defun ediff-merge-files (file-A file-B &optional startup-hooks) +(defun ediff-merge-files (file-A file-B + ;; MERGE-BUFFER-FILE is the file to be + ;; associated with the merge buffer + &optional startup-hooks merge-buffer-file) "Merge two files without ancestor." (interactive (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory)) dir-B f) - (list (setq f (ediff-read-file-name "File A to merge" dir-A nil)) + (list (setq f (ediff-read-file-name + "File A to merge" dir-A + (ediff-get-default-file-name))) (ediff-read-file-name "File B to merge" (setq dir-B (if ediff-use-last-dir @@ -1349,7 +978,7 @@ Continue anyway? (y/n) ")) (file-name-directory f))) (progn (setq file-name-history - (cons (abbreviate-file-name + (cons (ediff-abbreviate-file-name (expand-file-name (file-name-nondirectory f) dir-B)) @@ -1364,18 +993,26 @@ Continue anyway? (y/n) ")) file-B) nil ; file-C startup-hooks - 'ediff-merge-files)) + 'ediff-merge-files + merge-buffer-file)) ;;;###autoload (defun ediff-merge-files-with-ancestor (file-A file-B file-ancestor - &optional startup-hooks) + &optional + startup-hooks + ;; MERGE-BUFFER-FILE is the file + ;; to be associated with the + ;; merge buffer + merge-buffer-file) "Merge two files with ancestor." (interactive (let ((dir-A (if ediff-use-last-dir ediff-last-dir-A default-directory)) dir-B dir-ancestor f ff) - (list (setq f (ediff-read-file-name "File A to merge" dir-A nil)) + (list (setq f (ediff-read-file-name + "File A to merge" dir-A + (ediff-get-default-file-name))) (setq ff (ediff-read-file-name "File B to merge" (setq dir-B (if ediff-use-last-dir @@ -1384,7 +1021,7 @@ Continue anyway? (y/n) ")) (progn (setq file-name-history (cons - (abbreviate-file-name + (ediff-abbreviate-file-name (expand-file-name (file-name-nondirectory f) dir-B)) @@ -1397,7 +1034,7 @@ Continue anyway? (y/n) ")) (file-name-directory ff))) (progn (setq file-name-history - (cons (abbreviate-file-name + (cons (ediff-abbreviate-file-name (expand-file-name (file-name-nondirectory ff) dir-ancestor)) @@ -1412,13 +1049,18 @@ Continue anyway? (y/n) ")) file-B) file-ancestor startup-hooks - 'ediff-merge-files-with-ancestor)) + 'ediff-merge-files-with-ancestor + merge-buffer-file)) ;;;###autoload (defalias 'ediff-merge-with-ancestor 'ediff-merge-files-with-ancestor) ;;;###autoload -(defun ediff-merge-buffers (buffer-A buffer-B &optional startup-hooks job-name) +(defun ediff-merge-buffers (buffer-A buffer-B + &optional + ;; MERGE-BUFFER-FILE is the file to be + ;; associated with the merge buffer + startup-hooks job-name merge-buffer-file) "Merge buffers without ancestor." (interactive (let (bf) @@ -1435,12 +1077,17 @@ Continue anyway? (y/n) ")) (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks)) (or job-name (setq job-name 'ediff-merge-buffers)) (ediff-buffers-internal - buffer-A buffer-B nil startup-hooks job-name)) + buffer-A buffer-B nil startup-hooks job-name merge-buffer-file)) ;;;###autoload -(defun ediff-merge-buffers-with-ancestor (buffer-A - buffer-B buffer-ancestor - &optional startup-hooks job-name) +(defun ediff-merge-buffers-with-ancestor (buffer-A buffer-B buffer-ancestor + &optional + startup-hooks + job-name + ;; MERGE-BUFFER-FILE is the + ;; file to be associated + ;; with the merge buffer + merge-buffer-file) "Merge buffers with ancestor." (interactive (let (bf bff) @@ -1465,413 +1112,222 @@ Continue anyway? (y/n) ")) (setq startup-hooks (cons 'ediff-merge-on-startup startup-hooks)) (or job-name (setq job-name 'ediff-merge-buffers-with-ancestor)) (ediff-buffers-internal - buffer-A buffer-B buffer-ancestor startup-hooks job-name)) + buffer-A buffer-B buffer-ancestor startup-hooks job-name merge-buffer-file)) ;;;###autoload -(defun ediff-merge-revisions (rev1 rev2 &optional startup-hooks) +(defun ediff-merge-revisions (&optional file startup-hooks merge-buffer-file) + ;; MERGE-BUFFER-FILE is the file to be associated with the merge buffer "Run Ediff by merging two revisions of a file. -The file is the one visited by the current buffer." - (interactive - "sVersion 1 to merge (default is the latest version): \nsVersion 2 to merge (default is the latest version): ") - (ediff-load-version-control) - (let (buf1 buf2) - (if (eq ediff-version-control-package 'vc) - (progn - (save-excursion - (vc-version-other-window rev1) - (setq buf1 (current-buffer))) - (save-excursion - (vc-version-other-window rev2) - (setq buf2 (current-buffer))) - (setq startup-hooks - (list (` (lambda () - (delete-file (, (buffer-file-name buf1))) - (delete-file (, (buffer-file-name buf2)))))))) - (setq buf1 (rcs-ediff-view-revision rev1) - buf2 (rcs-ediff-view-revision rev2))) - (ediff-merge-buffers buf1 buf2 startup-hooks 'ediff-merge-revisions))) +The file is the optional FILE argument or the file visited by the current +buffer." + (interactive) + (if (stringp file) (find-file file)) + (let (rev1 rev2) + (setq rev1 + (read-string + (format + "Version 1 to merge (default: %s's latest version): " + (if (stringp file) + (file-name-nondirectory file) "current buffer"))) + rev2 + (read-string + (format + "Version 2 to merge (default: %s): " + (if (stringp file) + (file-name-nondirectory file) "current buffer")))) + (ediff-load-version-control) + ;; ancestor-revision=nil + (funcall + (intern (format "ediff-%S-merge-internal" ediff-version-control-package)) + rev1 rev2 nil startup-hooks merge-buffer-file))) ;;;###autoload -(defun ediff-merge-revisions-with-ancestor (rev1 - rev2 ancestor-rev - &optional startup-hooks) - "Run Ediff by merging with ancestor of two revisions of a file. -The file is the one visited by the current buffer." - (interactive - "sVersion 1 to merge (default: the latest version): \nsVersion 2 to merge (default: the latest version): \nsAncestor version (default: the latest version): ") +(defun ediff-merge-revisions-with-ancestor (&optional + file startup-hooks + ;; MERGE-BUFFER-FILE is the file to + ;; be associated with the merge + ;; buffer + merge-buffer-file) + "Run Ediff by merging two revisions of a file with a common ancestor. +The file is the the optional FILE argument or the file visited by the current +buffer." + (interactive) + (if (stringp file) (find-file file)) + (let (rev1 rev2 ancestor-rev) + (setq rev1 + (read-string + (format + "Version 1 to merge (default: %s's latest version): " + (if (stringp file) + (file-name-nondirectory file) "current buffer"))) + rev2 + (read-string + (format + "Version 2 to merge (default: %s): " + (if (stringp file) + (file-name-nondirectory file) "current buffer"))) + ancestor-rev + (read-string + (format + "Ancestor version (default: %s): " + (if (stringp file) + (file-name-nondirectory file) "current buffer")))) + (ediff-load-version-control) + (funcall + (intern (format "ediff-%S-merge-internal" ediff-version-control-package)) + rev1 rev2 ancestor-rev startup-hooks merge-buffer-file))) + +;;;###autoload +(defun run-ediff-from-cvs-buffer (pos) + "Run Ediff-merge on appropriate revisions of the selected file. +First run after `M-x cvs-update'. Then place the cursor on a line describing a +file and then run `run-ediff-from-cvs-buffer'." + (interactive "d") (ediff-load-version-control) - (let (buf1 buf2 ancestor-buf) - (if (eq ediff-version-control-package 'vc) - (progn - (save-excursion - (vc-version-other-window rev1) - (setq buf1 (current-buffer))) - (save-excursion - (vc-version-other-window rev2) - (setq buf2 (current-buffer))) - (save-excursion - (vc-version-other-window ancestor-rev) - (setq ancestor-buf (current-buffer))) - (setq startup-hooks - (list (` (lambda () - (delete-file (, (buffer-file-name buf1))) - (delete-file (, (buffer-file-name buf2))) - (delete-file (, (buffer-file-name ancestor-buf))) - ))))) - (setq buf1 (rcs-ediff-view-revision rev1) - buf2 (rcs-ediff-view-revision rev2) - ancestor-buf (rcs-ediff-view-revision ancestor-rev))) - (ediff-merge-buffers-with-ancestor - buf1 buf2 ancestor-buf - startup-hooks 'ediff-merge-revisions-with-ancestor))) + (let ((tin (tin-locate cvs-cookie-handle pos))) + (if tin + (cvs-run-ediff-on-file-descriptor tin) + (error "There is no file to merge")))) ;;; Apply patch - - -;;;###autoload -(defun ediff-patch-buffer (buffer-name &optional startup-hooks) - "Run Ediff by patching BUFFER-NAME." - (interactive "bBuffer to patch: ") - - (let* ((buf-to-patch (get-buffer buffer-name)) - (file-name-ok (if buf-to-patch (buffer-file-name buf-to-patch))) - (buf-mod-status (buffer-modified-p buf-to-patch)) - default-dir file-name ctl-buf) - (if file-name-ok - (setq file-name file-name-ok) - (ediff-eval-in-buffer buffer-name - (setq default-dir default-directory) - (setq file-name (ediff-make-temp-file)) - (set-visited-file-name file-name) - (setq buffer-auto-save-file-name nil) ; don't create auto-save file - (rename-buffer buffer-name) ; don't confuse the user with new buf name - (set-buffer-modified-p nil) - (set-visited-file-modtime) ; sync buffer and temp file - (setq default-directory default-dir) - )) - - (setq ctl-buf - (ediff-patch-file file-name startup-hooks 'ediff-patch-buffer)) - - (if file-name-ok - () - (ediff-eval-in-buffer ctl-buf - (delete-file (buffer-file-name ediff-buffer-A)) - (delete-file (buffer-file-name ediff-buffer-B)) - (ediff-eval-in-buffer ediff-buffer-A - (if default-dir (setq default-directory default-dir)) - (set-visited-file-name nil) - (rename-buffer buffer-name) - (set-buffer-modified-p buf-mod-status)) - (ediff-eval-in-buffer ediff-buffer-B - (setq buffer-auto-save-file-name nil) ; don't create auto-save file - (if default-dir (setq default-directory default-dir)) - (set-visited-file-name nil) - (rename-buffer (ediff-unique-buffer-name - (concat buffer-name "_patched") "")) - (set-buffer-modified-p t)))) - )) +;;;###autoload +(defun ediff-patch-file () + "Run Ediff by patching SOURCE-FILENAME." + ;; This now returns the control buffer + (interactive) + (let (source-dir source-file patch-buf) + (require 'ediff-ptch) + (setq patch-buf (ediff-get-patch-buffer)) + (setq source-dir (cond (ediff-use-last-dir ediff-last-dir-patch) + ((and (not ediff-patch-default-directory) + (buffer-file-name patch-buf)) + (file-name-directory + (expand-file-name + (buffer-file-name patch-buf)))) + (t default-directory))) + (setq source-file + ;; the default is the directory, not the visited file name + (ediff-read-file-name + "Which file to patch? " source-dir (ediff-get-default-file-name))) + (ediff-dispatch-file-patching-job patch-buf source-file))) -(defun ediff-get-patch-buffer (dir) - "Obtain patch buffer. If patch is already in a buffer---use it. -Else, read patch file into a new buffer." - (if (y-or-n-p "Is the patch file already in a buffer? ") - (setq ediff-patch-buf - (get-buffer (read-buffer "Patch buffer name: " nil t))) ;must match - (setq ediff-patch-buf - (find-file-noselect (read-file-name "Patch file name: " dir)))) +;;;###autoload +(defun ediff-patch-buffer () + "Run Ediff by patching BUFFER-NAME." + (interactive) + (let (patch-buf) + (require 'ediff-ptch) + (setq patch-buf (ediff-get-patch-buffer)) + (ediff-patch-buffer-internal + patch-buf + (read-buffer "Which buffer to patch? " + (cond ((eq patch-buf (current-buffer)) + (ediff-other-buffer (current-buffer))) + (t (current-buffer))) + 'must-match)))) - (setq ediff-patch-diagnostics - (get-buffer-create "*ediff patch diagnostics*")) - (ediff-eval-in-buffer ediff-patch-diagnostics - (insert-buffer ediff-patch-buf))) +;;;###autoload +(defalias 'epatch 'ediff-patch-file) +;;;###autoload +(defalias 'epatch-buffer 'ediff-patch-buffer) - ;;; Versions Control functions ;;;###autoload -(defun ediff-revision (arg) - "Call `vc.el' or `rcs.el' depending on `ediff-version-control-package'. -Without prefix argument, compares the current buffer with an older version. -With prefix argument, compares two older versions of the current buffer." +(defun ediff-revision (&optional file startup-hooks) + "Run Ediff by comparing versions of a file. +The file is an optional FILE argument or the file visited by the current +buffer. Use `vc.el' or `rcs.el' depending on `ediff-version-control-package'." + ;; if buffer is non-nil, use that buffer instead of the current buffer (interactive "P") + (if (stringp file) (find-file file)) (let (rev1 rev2) - (if arg - (setq rev1 - (read-string - "This buffer's version-1 to compare (default: the latest version): ") - rev2 - (read-string "This buffer's version-2 to compare (default: the latest version): ")) - (setq rev1 - (read-string "Version to compare the current buffer with (default: the latest version): "))) + (setq rev1 + (read-string + (format "Version 1 to compare (default: %s's latest version): " + (if (stringp file) + (file-name-nondirectory file) "current buffer"))) + rev2 + (read-string + (format "Version 2 to compare (default: %s): " + (if (stringp file) + (file-name-nondirectory file) "current buffer")))) (ediff-load-version-control) (funcall - (intern (format "%S-ediff-internal" ediff-version-control-package)) - rev1 rev2) + (intern (format "ediff-%S-internal" ediff-version-control-package)) + rev1 rev2 startup-hooks) )) - -;; Backward compatibility -;;;###autoload -(defun vc-ediff () - (interactive) - (beep 1) - (with-output-to-temp-buffer ediff-msg-buffer - (princ " -You have invoked an obsolete function `vc-ediff' or `rcs-ediff'. -Please use `M-x ediff-revision' instead. -Also, please check the variables `ediff-version-control-package' -and `ediff-revision-key' for customization."))) -(defalias 'rcs-ediff 'vc-ediff) +;;;###autoload +(defalias 'erevision 'ediff-revision) + ;; Test if version control package is loaded and load if not ;; Is SILENT is non-nil, don't report error if package is not found. (defun ediff-load-version-control (&optional silent) + (require 'ediff-vers) (or (featurep ediff-version-control-package) (if (locate-library (symbol-name ediff-version-control-package)) (progn (message "") ; kill the message from `locate-library' - (require ediff-version-control-package) - (if ediff-revision-key - (define-key - (cond ((eq ediff-version-control-package 'vc) vc-prefix-map) - ((eq ediff-version-control-package 'rcs) global-map) - (t global-map)) - ediff-revision-key 'ediff-revision))) + (require ediff-version-control-package)) (or silent (error "Version control package %S.el not found. Use vc.el instead" ediff-version-control-package))))) - - -(defun vc-ediff-internal (rev1 &optional rev2) - "Run Ediff on versions of the current buffer. -If both REV1 and REV2 are given then these two versions are compared. -If only REV1 is given then the current buffer is compared against version REV1. -If the current buffer is named `F', the version is named `F.~REV~'. -If `F.~REV~' already exists, it is used instead of being re-created." - (let ((curbuf (current-buffer)) - (curwind (selected-window)) - file1 file2 - rev1buf rev2buf) - (vc-version-other-window rev1) - (setq rev1buf (current-buffer) - file1 (buffer-file-name)) - (select-window curwind) - (if (not (stringp rev2)) - (setq rev2buf curbuf) - (vc-version-other-window rev2) - (setq rev2buf (current-buffer) - file2 (buffer-file-name))) - (ediff-buffers - rev1buf rev2buf - (list (` (lambda () - (delete-file (, file1)) - (if (, file2) (delete-file (, file2))) - ))) - 'ediff-revision))) - -(defun rcs-ediff-view-revision (&optional rev) - "View previous RCS revision of current file. -With prefix argument, prompts for a revision name." - (interactive (list (if current-prefix-arg - (read-string "Revision: ")))) - (let* ((filename (buffer-file-name (current-buffer))) - (switches (append '("-p") - (if rev (list (concat "-r" rev)) nil))) - (buff (concat (file-name-nondirectory filename) ".~" rev "~"))) - (message "Working ...") - (setq filename (expand-file-name filename)) - (with-output-to-temp-buffer buff - (let ((output-buffer (ediff-rcs-get-output-buffer filename buff))) - (delete-windows-on output-buffer) - (save-excursion - (set-buffer output-buffer) - (apply 'call-process "co" nil t nil - ;; -q: quiet (no diagnostics) - (append switches rcs-default-co-switches - (list "-q" filename))))) - (message "") - buff))) - -(defun ediff-rcs-get-output-buffer (file name) - ;; Get a buffer for RCS output for FILE, make it writable and clean it up. - ;; Optional NAME is name to use instead of `*RCS-output*'. - ;; This is a modified version from rcs.el v1.1. I use it here to make - ;; Ediff immune to changes in rcs.el - (let* ((default-major-mode 'fundamental-mode) ; no frills! - (buf (get-buffer-create name))) - (save-excursion - (set-buffer buf) - (setq buffer-read-only nil - default-directory (file-name-directory (expand-file-name file))) - (erase-buffer)) - buf)) - -(defun rcs-ediff-internal (rev1 &optional rev2) - "Run Ediff on the current buffer, comparing it with previous RCS revision." - (let ((rev2buf (if (stringp rev2) - (rcs-ediff-view-revision rev2) - (current-buffer))) - (rev1buf (rcs-ediff-view-revision rev1))) - - ;; rcs.el doesn't create temp version files, so we don't have to delete - ;; anything in startup hooks to ediff-buffers - (ediff-buffers rev1buf rev2buf nil 'ediff-revision) - )) -;;; Menu bar - -;;; This is split in several parts to avoid -;;; making a line in loaddefs.el that is too long for patch. -;;; Note that autoload.el currently looks for cookies -;;; only at top level in the file. -;;; So I moved these to top level. But the conditionals on -;;; purify-flag make these no-ops when you load ediff. -;;; They only do something in loaddefs.el. ;;;###autoload -(if purify-flag - ;; explicit string-match, as ediff-xemacs-p is not defined at build time - (if (string-match "\\(Lucid\\|Xemacs\\)" emacs-version) - () - (defvar menu-bar-epatch-menu (make-sparse-keymap "Epatch")) - (fset 'menu-bar-epatch-menu (symbol-value 'menu-bar-epatch-menu)) - (defvar menu-bar-ediff-merge-menu (make-sparse-keymap "Ediff merge")) - (fset 'menu-bar-ediff-merge-menu - (symbol-value 'menu-bar-ediff-merge-menu)) - (defvar menu-bar-ediff-menu (make-sparse-keymap "Ediff")) - (fset 'menu-bar-ediff-menu (symbol-value 'menu-bar-ediff-menu)) - )) - -;;; These must be placed in menu-bar.el in Emacs -;; -;; (define-key menu-bar-tools-menu [epatch] -;; '("Apply Patch" . menu-bar-epatch-menu)) -;; (define-key menu-bar-tools-menu [ediff-merge] -;; '("Merge" . menu-bar-ediff-merge-menu)) -;; (define-key menu-bar-tools-menu [ediff] -;; '("Compare" . menu-bar-ediff-menu)) +(defun ediff-version () + "Return string describing the version of Ediff. +When called interactively, displays the version." + (interactive) + (if (interactive-p) + (message (ediff-version)) + (format "Ediff %s of %s" ediff-version ediff-date))) ;;;###autoload -(if purify-flag - ;; explicit string-match, as ediff-xemacs-p is not defined at build time - (if (string-match "\\(Lucid\\|Xemacs\\)" emacs-version) - () - (define-key menu-bar-ediff-menu [ediff-revision] - '("File with Revision ..." . ediff-revision)) - (define-key menu-bar-ediff-menu [separator-ediff-files] '("--")) - (define-key menu-bar-ediff-menu [ediff-buffers3] - '("Three Buffers ..." . ediff-buffers3)) - (define-key menu-bar-ediff-menu [ediff-files3] - '("Three Files ..." . ediff-files3)) - (define-key menu-bar-ediff-menu [ediff-buffers] - '("Two Buffers ..." . ediff-buffers)) - (define-key menu-bar-ediff-menu [ediff-files] - '("Two Files ..." . ediff-files)) - )) +(defun ediff-documentation (&optional node) + "Display Ediff's manual. +With optional NODE, goes to that node." + (interactive) + (let ((ctl-window ediff-control-window) + (ctl-buf ediff-control-buffer)) -;;;###autoload -(if purify-flag - ;; explicit string-match, as ediff-xemacs-p is not defined at build time - (if (string-match "\\(Lucid\\|Xemacs\\)" emacs-version) - () - (define-key menu-bar-ediff-menu [separator-ediff-regions] '("--")) - (define-key menu-bar-ediff-menu [ediff-regions-linewise] - '("Regions Line-by-line ..." . ediff-regions-linewise)) - (define-key menu-bar-ediff-menu [ediff-regions-wordwise] - '("Regions Word-by-word ..." . ediff-regions-wordwise)) - (define-key menu-bar-ediff-menu [separator-ediff-windows] '("--")) - (define-key menu-bar-ediff-menu [ediff-windows-linewise] - '("Windows Line-by-line ..." . ediff-windows-linewise)) - (define-key menu-bar-ediff-menu [ediff-windows-wordwise] - '("Windows Word-by-word ..." . ediff-windows-wordwise)) - )) + (ediff-skip-unsuitable-frames) + (condition-case nil + (progn + (pop-to-buffer (get-buffer-create "*info*")) + (info (if ediff-xemacs-p "ediff.info" "ediff")) + (if node + (Info-goto-node node) + (message "Type `i' to search for a specific topic")) + (raise-frame (selected-frame))) + (error (beep 1) + (with-output-to-temp-buffer ediff-msg-buffer + (princ ediff-BAD-INFO)) + (if (window-live-p ctl-window) + (progn + (select-window ctl-window) + (set-window-buffer ctl-window ctl-buf))))))) + -;;;###autoload -(if purify-flag - ;; explicit string-match, as ediff-xemacs-p is not defined at build time - (if (string-match "\\(Lucid\\|Xemacs\\)" emacs-version) - () - (define-key - menu-bar-ediff-merge-menu [ediff-merge-revisions-with-ancestor] - '("Revisions with Ancestor ..." . ediff-merge-revisions-with-ancestor)) - (define-key menu-bar-ediff-merge-menu [ediff-merge-revisions] - '("Revisions ..." . ediff-merge-revisions)) - (define-key menu-bar-ediff-merge-menu [separator-ediff-merge] '("--")) - (define-key menu-bar-ediff-merge-menu [ediff-merge-buffers-with-ancestor] - '("Buffers with Ancestor ..." . ediff-merge-buffers-with-ancestor)) - (define-key menu-bar-ediff-merge-menu [ediff-merge-buffers] - '("Buffers ..." . ediff-merge-buffers)) - (define-key menu-bar-ediff-merge-menu [ediff-merge-files-with-ancestor] - '("Files with Ancestor ..." . ediff-merge-files-with-ancestor)) - (define-key menu-bar-ediff-merge-menu [ediff-merge-files] - '("Files ..." . ediff-merge-files)) - )) - -;;;###autoload -(if purify-flag - ;; explicit string-match, as ediff-xemacs-p is not defined at build time - (if (string-match "\\(Lucid\\|Xemacs\\)" emacs-version) - () - (define-key menu-bar-epatch-menu [ediff-patch-buffer] - '("To a Buffer ..." . ediff-patch-buffer)) - (define-key menu-bar-epatch-menu [ediff-patch-file] - '("To a File ..." . ediff-patch-file)) - )) - -;;;###autoload -(if purify-flag - ;; explicit string-match, as ediff-xemacs-p is not defined at build time - (if (string-match "\\(Lucid\\|Xemacs\\)" emacs-version) - (progn - (defvar ediff-menu - '("Compare" - ["Two Files ..." ediff-files t] - ["Two Buffers ..." ediff-buffers t] - ["Three Files ..." ediff-files3 t] - ["Three Buffers ..." ediff-buffers3 t] - "---" - ["File with Revision ..." ediff-revision t] - "---" - ["Windows Word-by-word ..." ediff-windows-wordwise t] - ["Windows Line-by-line ..." ediff-windows-linewise t] - "---" - ["Regions Word-by-word ..." ediff-regions-wordwise t] - ["Regions Line-by-line ..." ediff-regions-linewise t])) - (defvar ediff-merge-menu - '("Merge" - ["Files ..." ediff-merge-files t] - ["Files with Ancestor ..." ediff-merge-files-with-ancestor t] - ["Buffers ..." ediff-merge-buffers t] - ["Buffers with Ancestor ..." - ediff-merge-buffers-with-ancestor t] - "---" - ["Revisions ..." ediff-merge-revisions t] - ["Revisions with Ancestor ..." - ediff-merge-revisions-with-ancestor t])) - (defvar epatch-menu - '("Apply Patch" - ["To a file ..." ediff-patch-file t] - ["To a buffer ..." ediff-patch-buffer t])) - (add-submenu '("Tools") ediff-menu "VC") - (add-submenu '("Tools") ediff-merge-menu "VC") - (add-submenu '("Tools") epatch-menu "VC") - ;; Display a solid horizontal line - (add-menu-button '("Tools") ["---" nil nil] "VC") - ))) +;;; Local Variables: +;;; eval: (put 'ediff-defvar-local 'lisp-indent-hook 'defun) +;;; eval: (put 'ediff-with-current-buffer 'lisp-indent-hook 1) +;;; eval: (put 'ediff-with-current-buffer 'edebug-form-spec '(form body)) +;;; End: -(provide 'ediff) (require 'ediff-util) ;;; ediff.el ends here