"*Options to pass to `ediff-diff-program'.
If diff\(1\) is used as `ediff-diff-program', then the most useful options are
`-w', to ignore space, and `-i', to ignore case of letters.
-At present, the option `-c' is ignored, since Ediff doesn't understand this
-type of output."
+At present, the option `-c' is not allowed."
:type 'string
:group 'ediff-diff)
;; ediff-setup-diff-regions-function, which can also have the value
;; ediff-setup-diff-regions3, which takes 4 arguments.
(defun ediff-setup-diff-regions (file-A file-B file-C)
-;;; ;; Force all minibuffers to display ediff's messages.
-;;; ;; When xemacs implements minibufferless frames, this won't be necessary
-;;; (if ediff-xemacs-p (setq synchronize-minibuffers t))
+ (if (string-match "c" ediff-diff-options)
+ (error "Option `-c' is not allowed in `ediff-diff-options'"))
;; create, if it doesn't exist
(or (ediff-buffer-live-p ediff-diff-buffer)
diff-buffer
'synchronize
ediff-diff-options file1 file2)
- ;;(message "Computing differences ... done")
(message "")
(ediff-with-current-buffer diff-buffer
(buffer-size))))))
;; File-C is either the third file to compare (in case of 3-way comparison)
;; or it is the ancestor file.
(defun ediff-setup-diff-regions3 (file-A file-B file-C)
-
-;;; ;; force all minibuffers to display ediff's messages.
-;;; ;; when xemacs implements minibufferless frames, this won't be necessary
-;;; (if ediff-xemacs-p (setq synchronize-minibuffers t))
-
(or (ediff-buffer-live-p ediff-diff-buffer)
(setq ediff-diff-buffer
(get-buffer-create (ediff-unique-buffer-name "*ediff-diff" "*"))))
;; history var to use for filtering groups
(defvar ediff-filtering-regexp-history nil "")
-;; This has the form ((ctl-buf file1 file2) (stl-buf file1 file2) ...)
-;; If ctl-buf is nil, the file-pair wasn't processed yet. If it is
+;; This has the form ((meta-buf regexp dir1 dir2 dir3 merge-auto-store-dir)
+;; (ctl-buf session-status (file1 . eq-status) (file2 . eq-status) (file3
+;; . eq-status)) (ctl-buf session-status (file1 . eq-status) (file2
+;; . eq-status)) ...)
+;; If ctl-buf is nil, the file-pair hasn't processed yet. If it is
;; killed-buffer object, the file pair has been processed. If it is a live
-;; buffer, this means ediff is still working on the pair
+;; buffer, this means ediff is still working on the pair.
+;; Eq-status of a file is t if the file equals some other file in the same
+;; group.
(ediff-defvar-local ediff-meta-list nil "")
;;; API for ediff-meta-list
;; group buffer/regexp
-(defun ediff-get-group-buffer (meta-list)
+(defsubst ediff-get-group-buffer (meta-list)
(nth 0 (car meta-list)))
-(defun ediff-get-group-regexp (meta-list)
+(defsubst ediff-get-group-regexp (meta-list)
(nth 1 (car meta-list)))
;; group objects
-(defun ediff-get-group-objA (meta-list)
+(defsubst ediff-get-group-objA (meta-list)
(nth 2 (car meta-list)))
-(defun ediff-get-group-objB (meta-list)
+(defsubst ediff-get-group-objB (meta-list)
(nth 3 (car meta-list)))
-(defun ediff-get-group-objC (meta-list)
+(defsubst ediff-get-group-objC (meta-list)
(nth 4 (car meta-list)))
-(defun ediff-get-group-merge-autostore-dir (meta-list)
+(defsubst ediff-get-group-merge-autostore-dir (meta-list)
(nth 5 (car meta-list)))
;; session buffer
-(defun ediff-get-session-buffer (elt)
+(defsubst ediff-get-session-buffer (elt)
(nth 0 elt))
-(defun ediff-get-session-status (elt)
+(defsubst ediff-get-session-status (elt)
(nth 1 elt))
-(defun ediff-set-session-status (session-info new-status)
+(defsubst ediff-set-session-status (session-info new-status)
(setcar (cdr session-info) new-status))
;; session objects
-(defun ediff-get-session-objA (elt)
+(defsubst ediff-get-session-objA (elt)
(nth 2 elt))
-(defun ediff-get-session-objB (elt)
+(defsubst ediff-get-session-objB (elt)
(nth 3 elt))
-(defun ediff-get-session-objC (elt)
+(defsubst ediff-get-session-objC (elt)
(nth 4 elt))
-(defun ediff-get-session-objA-name (elt)
+(defsubst ediff-get-session-objA-name (elt)
(car (nth 2 elt)))
-(defun ediff-get-session-objB-name (elt)
+(defsubst ediff-get-session-objB-name (elt)
(car (nth 3 elt)))
-(defun ediff-get-session-objC-name (elt)
+(defsubst ediff-get-session-objC-name (elt)
(car (nth 4 elt)))
;; equality indicators
(defsubst ediff-get-file-eqstatus (elt)
(defsubst ediff-set-file-eqstatus (elt value)
(setcar (cdr elt) value))
+;; checks if the session is a meta session
+(defun ediff-meta-session-p (session-info)
+ (and (stringp (ediff-get-session-objA-name session-info))
+ (file-directory-p (ediff-get-session-objA-name session-info))
+ (stringp (ediff-get-session-objB-name session-info))
+ (file-directory-p (ediff-get-session-objB-name session-info))
+ (if (stringp (ediff-get-session-objC-name session-info))
+ (file-directory-p (ediff-get-session-objC-name session-info)) t)))
+
;; set up the keymap in the meta buffer
(defun ediff-setup-meta-map()
(setq ediff-meta-buffer-map (make-sparse-keymap))
(marksym ?*)
(numMarked 0)
(sessionNum 0)
- elt)
+ (diff-buffer ediff-meta-diff-buffer)
+ session-buf elt)
(while meta-list
(setq elt (car meta-list)
meta-list (cdr meta-list)
sessionNum (1+ sessionNum))
- (if (eq (ediff-get-session-status elt) marksym)
- (save-excursion
- (setq numMarked (1+ numMarked))
- (funcall operation elt sessionNum))))
+ (cond ((eq (ediff-get-session-status elt) marksym)
+ (save-excursion
+ (setq numMarked (1+ numMarked))
+ (funcall operation elt sessionNum)))
+ ((and (ediff-meta-session-p elt)
+ (ediff-buffer-live-p
+ (setq session-buf (ediff-get-session-buffer elt))))
+ (setq numMarked
+ (+ numMarked
+ (ediff-with-current-buffer session-buf
+ ;; pass meta-diff along
+ (setq ediff-meta-diff-buffer diff-buffer)
+ ;; collect diffs in child group
+ (ediff-operate-on-marked-sessions operation)))))))
(ediff-update-meta-buffer grp-buf) ; just in case
numMarked
))
(defun ediff-append-custom-diff (session sessionNum)
(or (ediff-collect-diffs-metajob)
- (error "Sorry, I don't do this for everyone..."))
+ (error "Hmm, I'd hate to do it to you ..."))
(let ((session-buf (ediff-get-session-buffer session))
(meta-diff-buff ediff-meta-diff-buffer)
(metajob ediff-metajob-name)
;; First handle sessions involving directories (which are themselves
;; session groups)
;; After that handle individual sessions
- (cond ((and (file-directory-p file1)
- (stringp file2) (file-directory-p file2)
- (if (stringp file3) (file-directory-p file1) t))
+ (cond ((ediff-meta-session-p info)
;; do ediff/ediff-merge on subdirectories
(if (ediff-buffer-live-p session-buf)
(ediff-show-meta-buffer session-buf)
:type 'string
:group 'ediff-ptch)
+(defun ediff-test-patch-utility ()
+ (cond ((zerop (call-process ediff-patch-program nil nil nil "-z." "-b"))
+ ;; GNU `patch' v. >= 2.2
+ 'gnu)
+ ((zerop (call-process ediff-patch-program nil nil nil "-b"))
+ 'posix)
+ (t 'traditional)))
+
(defcustom ediff-backup-specs
- (cond
- ((zerop (call-process ediff-patch-program nil nil nil "-z." "-b"))
- ;; GNU `patch' v. >= 2.2
- (format "-z%s -b" ediff-backup-extension))
- ((zerop (call-process ediff-patch-program nil nil nil "-b"))
- ;; POSIX `patch' -- ediff-backup-extension must be ".orig"
- (setq ediff-backup-extension ediff-default-backup-extension)
- "-b")
- (t
- ;; traditional `patch'
- (format "-b %s" ediff-backup-extension)))
+ (let ((type (ediff-test-patch-utility)))
+ (cond ((eq type 'gnu)
+ ;; GNU `patch' v. >= 2.2
+ (format "-z%s -b" ediff-backup-extension))
+ ((eq type 'posix)
+ ;; POSIX `patch' -- ediff-backup-extension must be ".orig"
+ (setq ediff-backup-extension ediff-default-backup-extension)
+ "-b")
+ (t
+ ;; traditional `patch'
+ (format "-b %s" ediff-backup-extension))))
"*Backup directives to pass to the patch program.
Ediff requires that the old version of the file \(before applying the patch\)
be saved in a file named `the-patch-file.extension'. Usually `extension' is
Note that both `ediff-backup-extension' and `ediff-backup-specs'
must be set properly. If your patch program takes the option `-b',
but not `-b extension', the variable `ediff-backup-extension' must
-still be set so Ediff will know which extension to use."
+still be set so Ediff will know which extension to use.
+
+Ediff tries to guess the appropriate value for this variables. It is believed
+to be working for `traditional' patch, all versions of GNU patch, and for POSIX
+patch. So, don't change these variables, unless the default doesn't work."
:type 'string
:group 'ediff-ptch)
(princ
(format "
The patch file contains a context diff for
-
%s
%s
-
However, Ediff cannot infer the name of the actual file
to be patched on your system. If you know the correct file name,
please enter it now.
Ediff has inferred that
%s
%s
-are possible targets for applying the patch.
+are two possible targets for applying the patch.
Both files seem to be plausible alternatives.
Please advice:
(f1-exists (setcar triple file1))
(t
(with-output-to-temp-buffer ediff-msg-buffer
- (princ (format "
-Ediff inferred that
+ (princ "\nEdiff has inferred that")
+ (if (string= file1 file2)
+ (princ (format "
%s
+is the target for this patch. However, this file does not exist."
+ file1))
+ (princ (format "
%s
-are possible alternative targets for this patch.
-
-However, these files do not exist.
-
-Please enter an alternative patch target ...
-"
- file1 file2)))
+ %s
+are two possible targets for this patch. However, these files do not exist."
+ file1 file2)))
+ (princ "
+\nPlease enter an alternative patch target ...\n"))
(let ((directory t)
target)
(while directory
(ediff-use-last-dir ediff-last-dir-patch)
(t default-directory)))
patch-buf)
- (if (y-or-n-p "Is the patch already in a buffer? ")
+ (if (let ((last-nonmenu-event t) ; Emacs: don't use dialog box
+ last-command-event) ; XEmacs: don't use dialog box
+ (y-or-n-p "Is the patch already in a buffer? "))
(setq patch-buf
(get-buffer
(read-buffer
"Which buffer contains the patch? "
- (current-buffer) 'must-match)))
+ (ediff-other-buffer
+ (if (eq (next-window (selected-window)) (selected-window))
+ ;; only one window in frame --- don't skip current buff
+ ""
+ ;; >1 window --- skip current buff, assuming this is the one
+ ;; to patch, not the one that has the patch
+ (current-buffer)))
+ 'must-match)))
(setq patch-buf
(find-file-noselect
(read-file-name "Which file contains the patch? "
))
-(defun ediff-patch-buffer-internal (patch-buf buf-to-patch-name
- &optional startup-hooks)
+;; When patching a buffer, never change the orig file. Instead, create a new
+;; buffer, ***_patched, even if the buff visits a file.
+;; Users who want to actually patch the buffer should use
+;; ediff-patch-file, not ediff-patch-buffer.
+(defun ediff-patch-buffer-internal (patch-buf
+ buf-to-patch-name
+ &optional startup-hooks)
(let* ((buf-to-patch (get-buffer buf-to-patch-name))
- (file-name-ok (if buf-to-patch (buffer-file-name buf-to-patch)))
+ (visited-file (if buf-to-patch (buffer-file-name buf-to-patch)))
(buf-mod-status (buffer-modified-p buf-to-patch))
(multifile-patch-p (> (length (ediff-with-current-buffer patch-buf
ediff-patch-map)) 1))
default-dir file-name ctl-buf)
- (if file-name-ok
- (setq file-name file-name-ok)
- (if multifile-patch-p
- (error
- "Can't apply multi-file patches to buffers that visit no files"))
- (ediff-with-current-buffer buf-to-patch
- (setq default-dir default-directory)
- (setq file-name (ediff-make-temp-file buf-to-patch))
- (set-visited-file-name file-name)
- (setq buffer-auto-save-file-name nil) ; don't create auto-save file
- ;;don't confuse the user with a new bufname
- (rename-buffer buf-to-patch-name)
- (set-buffer-modified-p nil)
- (set-visited-file-modtime) ; sync buffer and temp file
- (setq default-directory default-dir)
- ))
-
+ (if multifile-patch-p
+ (error
+ "Can't apply multi-file patches to buffers that visit no files"))
+
+ ;; create a temp file to patch
+ (ediff-with-current-buffer buf-to-patch
+ (setq default-dir default-directory)
+ (setq file-name (ediff-make-temp-file buf-to-patch))
+ ;; temporarily switch visited file name, if any
+ (set-visited-file-name file-name)
+ ;; don't create auto-save file, if buff was visiting a file
+ (or visited-file
+ (setq buffer-auto-save-file-name nil))
+ ;; don't confuse the user with a new bufname
+ (rename-buffer buf-to-patch-name)
+ (set-buffer-modified-p nil)
+ (set-visited-file-modtime) ; sync buffer and temp file
+ (setq default-directory default-dir)
+ )
+
;; dispatch a patch function
(setq ctl-buf (ediff-dispatch-file-patching-job
patch-buf file-name startup-hooks))
- (if file-name-ok
- ()
- ;; buffer wasn't visiting any file,
- ;; so we will not run meta-level ediff here
- (ediff-with-current-buffer ctl-buf
- (delete-file (buffer-file-name ediff-buffer-A))
- (delete-file (buffer-file-name ediff-buffer-B))
- (ediff-with-current-buffer ediff-buffer-A
- (if default-dir (setq default-directory default-dir))
- (set-visited-file-name nil)
- (rename-buffer buf-to-patch-name)
- (set-buffer-modified-p buf-mod-status))
- (ediff-with-current-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 buf-to-patch-name "_patched") ""))
- (set-buffer-modified-p t))))
+ (ediff-with-current-buffer ctl-buf
+ (delete-file (buffer-file-name ediff-buffer-A))
+ (delete-file (buffer-file-name ediff-buffer-B))
+ (ediff-with-current-buffer ediff-buffer-A
+ (if default-dir (setq default-directory default-dir))
+ (set-visited-file-name visited-file) ; visited-file might be nil
+ (rename-buffer buf-to-patch-name)
+ (set-buffer-modified-p buf-mod-status))
+ (ediff-with-current-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 buf-to-patch-name "_patched") ""))
+ (set-buffer-modified-p t)))
))
+
+;; Traditional patch has weird return codes.
+;; GNU and Posix return 1 if some hanks failed and 2 in case of trouble.
+;; 0 is a good code in all cases.
+;; We'll do the concervative thing.
+(defun ediff-patch-return-code-ok (code)
+ (eq code 0))
+;;; (if (eq (ediff-test-patch-utility) 'traditional)
+;;; (eq code 0)
+;;; (not (eq code 2))))
+
(defun ediff-patch-file-internal (patch-buf source-filename
&optional startup-hooks)
(setq source-filename (expand-file-name source-filename))
target-buf buf-to-patch file-name-magic-p
patch-return-code ctl-buf backup-style aux-wind)
- (if (string-match "-V" ediff-patch-options)
+ (if (string-match "V" ediff-patch-options)
(error
"Ediff doesn't take the -V option in `ediff-patch-options'--sorry"))
(switch-to-buffer patch-diagnostics)
(sit-for 0) ; synchronize - let the user see diagnostics
- (or (and (eq patch-return-code 0) ; patch reported success
+ (or (and (ediff-patch-return-code-ok patch-return-code)
(file-exists-p
(concat true-source-filename ediff-backup-extension)))
(progn
(with-output-to-temp-buffer ediff-msg-buffer
(princ (format
- "Patch program has failed due to a bad patch file OR
-because it couldn't create the backup for the file to be patched.
+ "Patch program has failed due to a bad patch file,
+it couldn't apply all hunks, OR
+it couldn't create the backup for the file being patched.
The former could be caused by a corrupt patch file or because the %S
program doesn't understand the format of the patch file in use.
The second problem might be due to an incompatibility among these settings:
- ediff-patch-program = %S
- ediff-patch-options = %S
- ediff-backup-extension = %S
- ediff-backup-specs = %S
+ ediff-patch-program = %S ediff-patch-options = %S
+ ediff-backup-extension = %S ediff-backup-specs = %S
See Ediff on-line manual for more details on these variables.
-In particular, check the documentation for `ediff-backup-specs'. "
+In particular, check the documentation for `ediff-backup-specs'.
+
+In any of the above cases, Ediff doesn't compare files automatically.
+However, if the patch was applied partially and the backup file was created,
+you can still examine the changes via M-x ediff-files"
ediff-patch-program
ediff-patch-program
ediff-patch-options
(define-key ediff-mode-map "p" 'ediff-previous-difference)
(define-key ediff-mode-map "\C-?" 'ediff-previous-difference)
- (define-key ediff-mode-map [backspace] 'ediff-previous-difference)
(define-key ediff-mode-map [delete] 'ediff-previous-difference)
(define-key ediff-mode-map "\C-h" (if ediff-no-emacs-help-in-control-buffer
'ediff-previous-difference nil))
+ ;; must come after C-h, or else C-h wipes out backspace's binding in XEmacs
+ (define-key ediff-mode-map [backspace] 'ediff-previous-difference)
(define-key ediff-mode-map "n" 'ediff-next-difference)
(define-key ediff-mode-map " " 'ediff-next-difference)
(define-key ediff-mode-map "j" 'ediff-jump-to-difference)
Reestablish the default three-window display."
(interactive)
(ediff-barf-if-not-control-buffer)
-
-;; ;; No longer needed: XEmacs has surrogate minibuffers now.
-;; (if ediff-xemacs-p (setq synchronize-minibuffers t))
-
(let (buffer-read-only)
(if (and (ediff-buffer-live-p ediff-buffer-A)
(ediff-buffer-live-p ediff-buffer-B)
(error "Buffer out of sync for file %s" buffer-file-name))))
+(defun ediff-file-compressed-p (file)
+ (require 'jka-compr)
+ (string-match (jka-compr-build-file-regexp) file))
+
+(defun ediff-filename-magic-p (file)
+ (or (ediff-file-compressed-p file)
+ (ediff-file-remote-p file)))
+
+
(defun ediff-save-buffer (arg)
"Safe way of saving buffers A, B, C, and the diff output.
`wa' saves buffer A, `wb' saves buffer B, `wc' saves buffer C,
;; other insignificant buffers (those beginning with "^[ *]").
;; Gets one arg--buffer name or a list of buffer names (it won't return
;; these buffers).
-(defun ediff-other-buffer (buff)
- (if (not (listp buff)) (setq buff (list buff)))
+(defun ediff-other-buffer (buff-lst)
+ (or (listp buff-lst) (setq buff-lst (list buff-lst)))
(let* ((frame-buffers (buffer-list))
+ (buff-name-list
+ (mapcar
+ (function (lambda (b)
+ (cond ((stringp b) b)
+ ((bufferp b) (buffer-name b)))))
+ buff-lst))
(significant-buffers
(mapcar
(function (lambda (x)
- (cond ((member (buffer-name x) buff)
- nil)
- ((not (ediff-get-visible-buffer-window x))
- nil)
- ((string-match "^ " (buffer-name x))
+ (cond ((member (buffer-name x) buff-name-list) nil)
+ ((not (ediff-get-visible-buffer-window x)) nil)
+ ((string-match "^[ *]" (buffer-name x)) nil)
+ ((memq (ediff-with-current-buffer x major-mode)
+ '(dired-mode))
nil)
(t x))))
frame-buffers))
(mapcar
(function
(lambda (x)
- (cond ((member (buffer-name x) buff) nil)
+ (cond ((member (buffer-name x) buff-name-list) nil)
((string-match "^[ *]" (buffer-name x)) nil)
+ ((memq
+ (ediff-with-current-buffer x major-mode)
+ '(dired-mode))
+ nil)
(t x))))
frame-buffers)))
(car less-significant-buffers))
;; Keywords: comparing, merging, patching, version control.
(defconst ediff-version "2.67" "The current version of Ediff")
-(defconst ediff-date "August 7, 1997" "Date of last update")
+(defconst ediff-date "September 3, 1997" "Date of last update")
;; This file is part of GNU Emacs.
;; deleted.
(defun ediff-find-file (file-var buffer-name &optional last-dir hooks-var)
(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)))
(cond ((not (file-readable-p file))
(error "File `%s' does not exist or is not readable" file))
(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 source-dir))
+ (ediff-read-file-name
+ "Which file to patch? " source-dir (ediff-get-default-file-name)))
(ediff-dispatch-file-patching-job patch-buf source-file)))
;;;###autoload
patch-buf
(read-buffer "Which buffer to patch? "
(cond ((eq patch-buf (current-buffer))
- (window-buffer (other-window 1)))
+ (ediff-other-buffer (current-buffer)))
(t (current-buffer)))
'must-match))))