Commits

Anonymous committed b262126

* ediff-init.el: comments.

* ediff-hook.el: Got rid of autoloads. Not needed, since the hook
is loaded.

* ediff-mult.el (ediff-intersect-directories,
ediff-prepare-meta-buffer,ediff-get-directory-files-under-revision):
cleanup.
(ediff-draw-dir-diffs): now supports the "C" command in directory
difference buffer.
(ediff-dir-diff-copy-file): new function that implements copying
of files from one Ediff dir to another
(ediff-bury-dir-diffs-buffer): kills the buffer instead.
(ediff-append-custom-diff): better error msgs.

* ediff-util.el (ediff-compute-custom-diffs-maybe): buglet fix.
(ediff-inferior-compare-regions): Use
ediff-clone-buffer-for-region-comparison. Better and simpler interface.

* ediff.el (ediff-directories-internal): Cleanup.
(ediff-clone-buffer-for-region-comparison): Better interface.

  • Participants
  • Parent commits 2e346f6

Comments (0)

Files changed (7)

+2002-03-05  Michael Kifer  <kifer@cs.stonybrook.edu>
+	
+	* ediff-init.el: comments.
+	
+	* ediff-hook.el: Got rid of autoloads. Not needed, since the hook
+	is loaded.
+	
+	* ediff-mult.el (ediff-intersect-directories,
+	ediff-prepare-meta-buffer,ediff-get-directory-files-under-revision):
+	cleanup.
+	(ediff-draw-dir-diffs): now supports the "C" command in directory
+	difference buffer.
+	(ediff-dir-diff-copy-file): new function that implements copying
+	of files from one Ediff dir to another
+	(ediff-bury-dir-diffs-buffer): kills the buffer instead.
+	(ediff-append-custom-diff): better error msgs.
+	
+	* ediff-util.el (ediff-compute-custom-diffs-maybe): buglet fix.
+	(ediff-inferior-compare-regions): Use
+	ediff-clone-buffer-for-region-comparison. Better and simpler interface.
+	
+	* ediff.el (ediff-directories-internal): Cleanup.
+	(ediff-clone-buffer-for-region-comparison): Better interface.
+	
 2002-02-09  Michael Kifer  <kifer@cs.stonybrook.edu>
 	
         * ediff-init.el (ediff-has-gutter-support): Steven Turnbull's patch.

File ediff-hook.el

 ;; end pacifier
 
 ;; allow menus to be set up without ediff-wind.el being loaded
-;;;###autoload
 (defvar ediff-window-setup-function)
 
 ;; This is used to avoid compilation warnings. When emacs/xemacs forms can
 ;; In this case, the macro will expand into the form that is appropriate to the
 ;; compiler at hand.
 ;; Suggested by rms.
-;;;###autoload
 (defmacro ediff-cond-compile-for-xemacs-or-emacs (xemacs-form emacs-form)
   (if (string-match "XEmacs" emacs-version)
       xemacs-form emacs-form))
 ;; Note we wrap this in a progn so that we pick up the whole def
 ;; for auto-autoload. That way we do not load ediff-hook.el when defining
 ;; the menus.
-;;;###autoload
 (progn
   (ediff-cond-compile-for-xemacs-or-emacs
    ;; xemacs form
    ))
 
 
-;;;###autoload
 (ediff-cond-compile-for-xemacs-or-emacs
  (progn
    (defvar ediff-menu

File ediff-init.el

       (ediff-patch-metajob metajob)
       ;; add more here
       ))
+;; jobs suitable for the operation of collecting diffs into a multifile patch
 (defsubst ediff-collect-diffs-metajob (&optional metajob)
   (memq (or metajob ediff-metajob-name)
 	'(ediff-directories
-	  ediff-directory-revisions
 	  ediff-merge-directories
 	  ediff-merge-directories-with-ancestor
+	  ediff-directory-revisions
 	  ediff-merge-directory-revisions
 	  ediff-merge-directory-revisions-with-ancestor
 	  ;; add more here

File ediff-mult.el

 ;;	3. Provide a list of pairs or triples of file names (or buffers,
 ;;	   depending on the particular Ediff operation you want to invoke)
 ;;	   in the following format:
-;;	  	(descriptor (obj1 obj2 obj3) (...) ...)
+;;	  	(descriptor-header (nil nil (obj1 nil) (obj2 nil) (obj3 nil))
+;;                                 (...) ...)
+;;         The function ediff-make-new-meta-list-element can be used to create
+;;         2nd and subsequent elements of that list.
 ;;	   Actually, the format of this list is pretty much up to the
 ;;	   developer.  The only thing is that it must be a list of lists,
 ;;	   and the first list must describe the meta session, and subsequent
 ;;	   elements must describe individual sessions.
-;;	   This descriptor must be a list of two, three, or four elements (nil
-;;	   or string).  The function ediff-redraw-registry-buffer displays the
-;;	   second through last of these in the registry buffer. 
+;;	   This descriptor-header must be a list of SIX elements (nil or
+;;         string).  The function ediff-redraw-registry-buffer displays the 
+;;	   1st - 4th of these in the registry buffer. 
 ;;	   Also, keep in mind that the function ediff-prepare-meta-buffer
-;;	   (which see) prepends the session group buffer to the descriptor, and
-;;	   nil in front of each subsequent list (i.e., the above list 
-;;	   will become
-;;              ((meta-buf descriptor) (nil obj1 obj2 obj3) (nil ...) ...)
+;;	   (which see) prepends the session group buffer to the descriptor, so
+;;	   the descriptor becomes 7-long.
 ;;	   Ediff expects that your function (in 2 above) will arrange to
 ;;	   replace this prepended nil (via setcar) with the actual ediff
 ;;	   control buffer associated with an appropriate Ediff session.
 
 ;;; API for ediff-meta-list
 
+;; A meta-list is either ediff-meta-list, which contains a header and the list
+;; of ediff sessions or ediff-dir-difference-list, which is a header followed
+;; by the list of differences among the directories (i.e., files that are not
+;; in all directories). The header is the same in all meta lists, but the rest
+;; is different.
 ;; Structure of the meta-list:
 ;; (HEADER SESSION1 SESSION2 ...)
-;;    HEADER: (GROUP-BUF REGEXP OBJA OBJB OBJC SAVE-DIR)
+;;    HEADER: (GROUP-BUF REGEXP OBJA OBJB OBJC SAVE-DIR COMPARISON-FUNC)
 ;;               OBJA - first directory
 ;;               OBJB - second directory
 ;;               OBJC - third directory
   (nth 4 (car meta-list)))
 (defsubst ediff-get-group-merge-autostore-dir (meta-list)
   (nth 5 (car meta-list)))
+(defsubst ediff-get-group-comparison-func (meta-list)
+  (nth 6 (car meta-list)))
 
 ;; ELT is a session meta descriptor (what is being preserved as
 ;; 'ediff-meta-info)
 ;;  The structure is:  (SESSION-CTL-BUFFER STATUS OBJA OBJB OBJC)
-;;   STATUS is ?I, ?*, ?H
+;;   STATUS is ?I (hidden or invalid), ?* (marked for operation), ?H (hidden)
+;;             nil (nothing)
 ;;   OBJA/B/C is (FILENAME EQSTATUS)
 ;;     EQSTATUS is ?= or nil (?= means that this file is equal to some other
 ;;     	       	       	       file in this session)
 (defsubst ediff-set-file-eqstatus (elt value)
   (setcar (cdr elt) value))
 
+;; Create a new element for the meta list out of obj1/2/3, which usually are
+;; files
+;;
+;; The first nil in such an is later replaced with the session buffer.  The
+;; second nil is reserved for session status.
+;;
+;; Also, session objects A/B/C are turned into lists of the form (obj nil).
+;; This nill is a placeholder for eq-indicator. It is either nil or =.
+;; If it is discovered that this file is = to some other
+;; file in the same session, eq-indicator is changed to `='.
+;; Curently, the eq-indicator is used only for 2 and 3-file jobs.
+(defun ediff-make-new-meta-list-element (obj1 obj2 obj3)
+  (list nil nil (list obj1 nil) (list obj2 nil) (list obj3 nil)))
+
 ;; The activity marker is either or + (active session, i.e., ediff is currently
 ;; run in it), or - (finished session, i.e., we've ran ediff in it and then
 ;; exited).  Return nil, if session is neither active nor finished
   ;; modify ediff-meta-buffer-map here
   (run-hooks 'ediff-meta-buffer-keymap-setup-hook))
 
+
 (defun ediff-meta-mode ()
   "This mode controls all operations on Ediff session groups.
 It is entered through one of the following commands:
 (define-key ediff-dir-diffs-buffer-map "n" 'next-line)
 (define-key ediff-dir-diffs-buffer-map "\C-?" 'previous-line)
 (define-key ediff-dir-diffs-buffer-map "p" 'previous-line)
+(define-key ediff-dir-diffs-buffer-map "C" 'ediff-dir-diff-copy-file)
+(if ediff-emacs-p
+    (define-key ediff-dir-diffs-buffer-map [mouse-2] 'ediff-dir-diff-copy-file)
+  (define-key ediff-dir-diffs-buffer-map [button2] 'ediff-dir-diff-copy-file))
 (define-key ediff-dir-diffs-buffer-map [delete] 'previous-line)
 (define-key ediff-dir-diffs-buffer-map [backspace] 'previous-line)
 
   (ediff-update-meta-buffer (current-buffer) 'must-redraw))
 
 
+;; These are used to encode membership of files in directory1/2/3
+;; Membership code of a file is a product of codes for the directories where
+;; this file is in
+(defvar ediff-membership-code1 2)
+(defvar ediff-membership-code2 3)
+(defvar ediff-membership-code3 5)
+(defvar ediff-product-of-memcodes (* ediff-membership-code1
+				     ediff-membership-code2
+				     ediff-membership-code3))
+
 ;; DIR1, DIR2, DIR3 are directories.  DIR3 can be nil.
 ;; OUTPUT-DIR is a directory for auto-storing the results of merge jobs.
 ;;	      Can be nil.
 ;; included in the intersection.  However, a regular file that is a dir in dir3
 ;; is included, since dir3 files are supposed to be ancestors for merging.
 ;; Returns a list of the form:
-;;	((dir1 dir2 dir3) (f1 f2 f3) (f1 f2 f3) ...)
+;;	(DIFF-LIST META-HEADER (f1 f2 f3) (f1 f2 f3) ...)
 ;; dir3, f3 can be nil if intersecting only 2 directories.
 ;; If COMPARISON-FUNC is given, use it.  Otherwise, use string=
-;; DIFF-VAR contains the name of the variable in which to return the
-;; difference list (which represents the differences among the contents of
-;; directories).  The diff list is of the form:
-;;	((dir1 dir2 dir3) (file . num) (file . num)...)
+;; DIFF-LIST is of the form:
+;;	(META-HEADER (file . num) (file . num)...)
 ;; where num encodes the set of dirs where the file is found:
 ;; 2 - only dir1; 3 - only dir2; 5 - only dir3; 6 - dir1&2; 10 - dir1&3; etc.
+;; META-HEADER is of the form
+;;       It contains the meta info about this ediff operation
+;;       (regexp dir1 dir2 dir3 merge-auto-store-dir comparison-func)
+;;       Later the meta-buffer is prepended to this list.
 (defun ediff-intersect-directories (jobname
-				    diff-var regexp dir1 dir2
+				    regexp dir1 dir2
 				    &optional
 				    dir3 merge-autostore-dir comparison-func)
   (setq comparison-func (or comparison-func 'string=))
-  (let (lis1 lis2 lis3 common auxdir1 auxdir2 auxdir3 difflist)
+  (let (lis1 lis2 lis3 common auxdir1 auxdir2 auxdir3 common-part difflist)
 
     (setq auxdir1	(file-name-as-directory dir1)
 	  lis1		(directory-files auxdir1 nil regexp)
     (setq difflist (mapcar (lambda (elt) (cons elt 1)) difflist))
 
     ;; check for files belonging to lis1/2/3
+    ;; Each elt is of the norm (file . number)
+    ;; Number encodes the directories to which file belongs.
+    ;; It is a product of a subset of ediff-membership-code1=2,
+    ;; ediff-membership-code2=3, and ediff-membership-code3=5.
+    ;; If file belongs to dir 1 only, the membership code is 2.
+    ;; If it is in dir1 and dir3, then the membership code is 2*5=10;
+    ;; if it is in dir1 and dir2, then the membership code is 2*3=6, etc.
     (mapcar (lambda (elt)
 	      (if (member (car elt) lis1)
-		  (setcdr elt (* (cdr elt) 2)))
+		  (setcdr elt (* (cdr elt) ediff-membership-code1)))
 	      (if (member (car elt) lis2)
-		  (setcdr elt (* (cdr elt) 3)))
+		  (setcdr elt (* (cdr elt) ediff-membership-code2)))
 	      (if (member (car elt) lis3)
-		  (setcdr elt (* (cdr elt) 5)))
+		  (setcdr elt (* (cdr elt) ediff-membership-code3)))
 	      )
 	    difflist)
-    (setq difflist (cons (list regexp auxdir1 auxdir2 auxdir3) difflist))
+    (setq difflist (cons
+		    ;; diff metalist header
+		    (list regexp
+			  auxdir1 auxdir2 auxdir3
+			  merge-autostore-dir
+			  comparison-func)
+		    difflist))
     
-    ;; return the difference list back to the calling function
-    (set diff-var difflist)
-
+    (setq common-part
+	  (cons 
+	   ;; metalist header
+	   (list regexp
+		 auxdir1 auxdir2 auxdir3
+		 merge-autostore-dir
+		 comparison-func)
+	   (mapcar
+	    (lambda (elt) 
+	      (ediff-make-new-meta-list-element
+	       (concat auxdir1 elt)
+	       (concat auxdir2 elt)
+	       (if lis3
+		   (progn
+		     ;; The following is done because: In merging with
+		     ;; ancestor, we don't intersect with lis3.  So, it is
+		     ;; possible that elt is a file in auxdir1/2 but a
+		     ;; directory in auxdir3 Or elt may not exist in auxdir3 at
+		     ;; all.  In the first case, we add a slash at the end.  In
+		     ;; the second case, we insert nil.
+		     (setq elt (ediff-add-slash-if-directory auxdir3 elt))
+		     (if (file-exists-p (concat auxdir3 elt))
+			 (concat auxdir3 elt))))))
+	    common)))
     ;; return result
-    (cons (list regexp auxdir1 auxdir2 auxdir3 merge-autostore-dir)
-	  (mapcar
-	   (lambda (elt) 
-	     (list (concat auxdir1 elt)
-		   (concat auxdir2 elt)
-		   (if lis3
-		       (progn
-			 ;; The following is done because:
-			 ;;   In merging with ancestor, we don't intersect
-			 ;;   with lis3.  So, it is possible that elt is a
-			 ;;   file in auxdir1/2 but a directory in auxdir3
-			 ;;   Or elt may not exist in auxdir3 at all.
-			 ;;   In the first case, we add a slash at the end.
-			 ;;   In the second case, we insert nil.
-			 (setq elt (ediff-add-slash-if-directory auxdir3 elt))
-			 (if (file-exists-p (concat auxdir3 elt))
-			     (concat auxdir3 elt))))))
-	   common))
+    (cons common-part difflist)
     ))
 
 ;; find directory files that are under revision.  Include subdirectories, since
     (setq common (sort (ediff-copy-list common) 'string-lessp))
 
     ;; return result
-    (cons (list regexp auxdir1 nil nil merge-autostore-dir)
-	  (mapcar (lambda (elt) (list (concat auxdir1 elt) nil nil))
-		  common))
+    (cons 
+     ;; header -- has 6 elements. Meta buffer is prepended later by
+     ;; ediff-prepare-meta-buffer 
+     (list regexp auxdir1 nil nil merge-autostore-dir nil)
+     (mapcar (lambda (elt) (ediff-make-new-meta-list-element
+			    (concat auxdir1 elt) nil nil))
+	     common))
     ))
       
 
 	    ;; meta-buffs.
 	    (define-key
 	      ediff-meta-buffer-map "M" 'ediff-show-meta-buff-from-registry))
-	;; Initialize the meta list -- don't do this for registry.
-	;;
-	;; We prepend '(nil nil) to all elts of meta-list, except the first.
-	;; The first nil will later be replaced by the session buffer.  The
-	;; second is reserved for session status.
-	;;
-	;; (car ediff-meta-list) gets cons'ed with the session group buffer.
-	;; Also, session objects A/B/C are turned into lists of the form
-	;; (obj eq-indicator).  Eq-indicator is either nil or =. Initialized to
-	;; nil.  If later it is discovered that this file is = to some other
-	;; file in the same session, eq-indicator is changed to `='.
-	;; For now, the eq-indicator is used only for 2 and 3-file jobs.
+	;; Initialize the meta list -- we don't do this for registry.
 	(setq ediff-meta-list
+	      ;; add meta-buffer to the list header
 	      (cons (cons meta-buffer (car meta-list))
-		    (mapcar
-		     (lambda (elt)
-		       (cons nil
-			     (cons nil
-				   ;; convert each obj to (obj nil),
-				   ;; where nil is the initial value
-				   ;; for eq-indicator -- see above
-				   (mapcar (lambda (obj) (list obj nil))
-					   elt))))
-		     (cdr meta-list)))))
+		    (cdr meta-list))))
 	
       (or (eq meta-buffer ediff-registry-buffer)
 	  (setq ediff-session-registry
     (if dir3 (insert "*** Directory C: " dir3 "\n"))
     (insert "\n")))
 
-(defun ediff-draw-dir-diffs (diff-list)
+(defun ediff-draw-dir-diffs (diff-list &optional buf-name)
   (if (null diff-list) (error "Lost difference info on these directories"))
-  (let* ((buf-name (ediff-unique-buffer-name
-		    "*Ediff File Group Differences" "*"))
-	 (regexp (ediff-get-group-regexp diff-list))
+  (setq buf-name
+	(or buf-name
+	    (ediff-unique-buffer-name "*Ediff File Group Differences" "*")))
+  (let* ((regexp (ediff-get-group-regexp diff-list))
 	 (dir1 (ediff-abbreviate-file-name (ediff-get-group-objA diff-list)))
 	 (dir2 (ediff-abbreviate-file-name (ediff-get-group-objB diff-list)))
 	 (dir3 (ediff-get-group-objC diff-list))
 	 (dir3 (if (stringp dir3) (ediff-abbreviate-file-name dir3)))
 	 (meta-buf (ediff-get-group-buffer diff-list))
 	 (underline (make-string 26 ?-))
-	 file code 
+	 file membership-code saved-point
 	 buffer-read-only)
     ;; skip the directory part
     (setq diff-list (cdr diff-list))
     (setq ediff-dir-diffs-buffer (get-buffer-create buf-name))
     (ediff-with-current-buffer ediff-dir-diffs-buffer
+      (setq saved-point (point))
       (use-local-map ediff-dir-diffs-buffer-map)
       (erase-buffer)
       (setq ediff-meta-buffer meta-buf)
       (insert "\t\t*** Directory Differences ***\n")
       (insert "
 Useful commands:
-     `q': hide this buffer
-   n,SPC: next line
-   p,DEL: previous line\n\n")
+  C,button2: over file name -- copy this file to directory that doesn't have it
+          q: hide this buffer
+      n,SPC: next line
+      p,DEL: previous line\n\n")
 
+      (insert (format "\n*** Directory A: %s\n" dir1))
+      (if dir2 (insert (format "*** Directory B: %s\n" dir2)))
+      (if dir3 (insert (format "*** Directory C: %s\n" dir3)))
       (if (and (stringp regexp) (> (length regexp) 0))
 	  (insert
-	   (format "\n*** Filter-through regular expression: %s\n" regexp)))
+	   (format "*** Filter-through regular expression: %s\n" regexp)))
       (insert "\n")
-      (insert (format "\n%-27s%-26s"
-		      (ediff-truncate-string-left
-		       (ediff-abbreviate-file-name
-			(file-name-as-directory dir1))
-		       25)
-		      (ediff-truncate-string-left
-		       (ediff-abbreviate-file-name
-			(file-name-as-directory dir2))
-		       25)))
+      (insert (format "\n%-27s%-26s" "Directory A" "Directory B"))
       (if dir3
-	  (insert (format " %-25s\n"
-			  (ediff-truncate-string-left
-			   (ediff-abbreviate-file-name
-			    (file-name-as-directory dir3))
-			   25)))
+	  (insert (format " %-25s\n" "Directory C"))
 	(insert "\n"))
       (insert (format "%s%s" underline underline))
       (if (stringp dir3)
 
       (while diff-list
 	(setq file (car (car diff-list))
-	      code (cdr (car diff-list))
+	      membership-code (cdr (car diff-list))
 	      diff-list (cdr diff-list))
-	(if (= (mod code 2) 0) ; dir1
-	    (insert (format "%-27s"
-			    (ediff-truncate-string-left
-			     (ediff-abbreviate-file-name
-			      (if (file-directory-p (concat dir1 file))
-				  (file-name-as-directory file)
-				file))
-			     24)))
+	(if (= (mod membership-code ediff-membership-code1) 0) ; dir1
+	    (let ((beg (point)))
+	      (insert (format "%-27s"
+			      (ediff-truncate-string-left
+			       (ediff-abbreviate-file-name
+				(if (file-directory-p (concat dir1 file))
+				    (file-name-as-directory file)
+				  file))
+			       24)))
+	      ;; format of meta info in the dir-diff-buffer:
+	      ;;    (filename-tail filename-full otherdir1 otherdir2 otherdir3)
+	      (ediff-set-meta-overlay
+	       beg (point)
+	       (list meta-buf file (concat dir1 file) dir1 dir2 dir3)))
 	  (insert (format "%-27s" "---")))
-	(if (= (mod code 3) 0) ; dir2
-	    (insert (format "%-26s"
-			    (ediff-truncate-string-left
-			     (ediff-abbreviate-file-name
-			      (if (file-directory-p (concat dir2 file))
-				  (file-name-as-directory file)
-				file))
-			     24)))
+	(if (= (mod membership-code ediff-membership-code2) 0) ; dir2
+	    (let ((beg (point)))
+	      (insert (format "%-26s"
+			      (ediff-truncate-string-left
+			       (ediff-abbreviate-file-name
+				(if (file-directory-p (concat dir2 file))
+				    (file-name-as-directory file)
+				  file))
+			       24)))
+	      (ediff-set-meta-overlay
+	       beg (point)
+	       (list meta-buf file (concat dir2 file) dir1 dir2 dir3)))
 	  (insert (format "%-26s" "---")))
 	(if (stringp dir3)
-	    (if (= (mod code 5) 0) ; dir3
-		(insert (format " %-25s" 
-				(ediff-truncate-string-left
-				 (ediff-abbreviate-file-name
-				  (if (file-directory-p (concat dir3 file))
-				      (file-name-as-directory file)
-				    file))
-				 24)))
+	    (if (= (mod membership-code ediff-membership-code3) 0) ; dir3
+		(let ((beg (point)))
+		  (insert (format " %-25s" 
+				  (ediff-truncate-string-left
+				   (ediff-abbreviate-file-name
+				    (if (file-directory-p (concat dir3 file))
+					(file-name-as-directory file)
+				      file))
+				   24)))
+		  (ediff-set-meta-overlay
+		   beg (point)
+		   (list meta-buf file (concat dir3 file) dir1 dir2 dir3)))
 	      (insert (format " %-25s" "---"))))
 	(insert "\n"))
       (setq buffer-read-only t)
-      (set-buffer-modified-p nil)) ; eval in diff buffer
+      (set-buffer-modified-p nil)
+      (goto-char saved-point)) ; end eval in diff buffer
   ))
 
 (defun ediff-bury-dir-diffs-buffer ()
   "Bury the directory difference buffer.  Display the meta buffer instead."
   (interactive)
+  ;; ediff-meta-buffer is set in ediff-draw-dir-diffs so the directory
+  ;; difference buffer remembers the meta buffer
   (let ((buf ediff-meta-buffer)
 	wind)
-    (bury-buffer)
+    (ediff-kill-buffer-carefully ediff-dir-diffs-buffer)
     (if (setq wind (ediff-get-visible-buffer-window buf))
 	(select-window wind)
       (set-window-buffer (selected-window) buf))))
     (set-window-buffer (selected-window) buf)
     (goto-char (point-min))))
 
+;; Format of meta info in dir-diff-buffer:
+;;               (filename-tail filename-full otherdir1 otherdir2)
+(defun ediff-dir-diff-copy-file ()
+  "Copy file described at point to directories where this file is missing."
+  (interactive)
+  (let* ((pos (ediff-event-point last-command-event))
+	 (info (ediff-get-meta-info (current-buffer) pos 'noerror))
+	 (meta-buf (car info))
+	 (file-tail (nth 1 info))
+	 (file-abs  (nth 2 info))
+	 (otherdir1 (nth 3 info))
+	 (otherfile1 (if otherdir1 (concat otherdir1 file-tail)))
+	 (otherdir2 (nth 4 info))
+	 (otherfile2 (if otherdir2 (concat otherdir2 file-tail)))
+	 (otherdir3 (nth 5 info))
+	 (otherfile3 (if otherdir3 (concat otherdir3 file-tail)))
+	 meta-list dir-diff-list
+	 )
+    (if (null info)
+	(error "No file suitable for copying described at this location"))
+    (ediff-with-current-buffer meta-buf
+      (setq meta-list ediff-meta-list
+	    dir-diff-list ediff-dir-difference-list))
+
+    ;; copy file to directories where it doesn't exist, update
+    ;; ediff-dir-difference-list and redisplay
+    (mapcar
+     (lambda (otherfile-struct)
+       (let ((otherfile (car otherfile-struct))
+	     (file-mem-code (cdr otherfile-struct)))
+	 (if otherfile
+	     (or (file-exists-p otherfile)
+		 (if (y-or-n-p
+		      (format "Copy %s to %s ? " file-abs otherfile))
+		     (let* ((file-diff-record (assoc file-tail dir-diff-list))
+			    (new-mem-code
+			     (* (cdr file-diff-record) file-mem-code)))
+		       (copy-file file-abs otherfile)
+		       (setcdr file-diff-record new-mem-code)
+		       (ediff-draw-dir-diffs dir-diff-list (buffer-name))
+		       (sit-for 0)
+		       ;; if file is in all three dirs or in two dirs and only
+		       ;; two dirs are involved, delete this file's record
+		       (if (or (= new-mem-code ediff-product-of-memcodes)
+			       (and (> new-mem-code ediff-membership-code3)
+				    (null otherfile3)))
+			   (delq file-diff-record dir-diff-list))
+		       ))))
+	 ))
+     ;; 2,3,5 are numbers used to encode membership of a file in
+     ;;       dir1/2/3. See ediff-intersect-directories.
+     (list (cons otherfile1 2) (cons otherfile2 3) (cons otherfile3 5)))
+
+    (if (and (file-exists-p otherfile1)
+	     (file-exists-p otherfile2)
+	     (or (not otherfile3) (file-exists-p otherfile3)))
+	;; update ediff-meta-list by direct modification
+	(nconc meta-list
+	       (list (ediff-make-new-meta-list-element
+		      otherfile1 otherfile2 otherfile3)))
+      )
+    (ediff-update-meta-buffer meta-buf 'must-redraw)
+  ))
+
 (defun ediff-up-meta-hierarchy ()
   "Go to the parent session group buffer."
   (interactive)
 	     (save-excursion
 	       (setq numMarked (1+ numMarked))
 	       (funcall operation elt sessionNum)))
+	    ;; The following goes into a session represented by a subdirectory
+	    ;; and applies operation to marked sessions there
 	    ((and  (ediff-meta-session-p elt) 
 		   (ediff-buffer-live-p 
 		    (setq session-buf (ediff-get-session-buffer elt))))
 
 (defun ediff-append-custom-diff (session sessionNum)
   (or (ediff-collect-diffs-metajob)
-      (error "Hmm, I'd hate to do it to you ..."))
+      (error "Can't compute multifile patch in this context"))
   (let ((session-buf (ediff-get-session-buffer session))
 	(meta-diff-buff ediff-meta-diff-buffer)
 	(metajob ediff-metajob-name)
 		(setq custom-diff-buf ediff-custom-diff-buffer)))))
 
     (or (ediff-buffer-live-p meta-diff-buff)
-	(error "Ediff: something wrong--no multiple diffs buffer"))
+	(error "Ediff: something wrong--killed multiple diff's buffer"))
 
     (cond ((ediff-buffer-live-p custom-diff-buf)
+	   ;; for live session buffers we do them first because the user may
+	   ;; have changed them with respect to the underlying files
 	   (save-excursion
 	     (set-buffer meta-diff-buff)
 	     (goto-char (point-max))
 	     (insert-buffer custom-diff-buf)
 	     (insert "\n")))
+	  ;; if ediff session is not live, run diff directly on the files
 	  ((memq metajob '(ediff-directories 
 			   ediff-merge-directories
 			   ediff-merge-directories-with-ancestor))
 	     (insert-buffer tmp-buf)
 	     (insert "\n")))
 	  (t
-	   (error "Can't make context diff for Session %d" sessionNum )))
+	   (ediff-kill-buffer-carefully meta-diff-buff)
+	   (error "Session %d compares versions of file. Such session must be active to enable multifile patch collection" sessionNum )))
     ))
 
 (defun ediff-collect-custom-diffs ()
 	    (get-buffer-create
 	     (ediff-unique-buffer-name "*Ediff Multifile Diffs" "*"))))
   (ediff-with-current-buffer ediff-meta-diff-buffer
+    (setq buffer-read-only nil)
     (erase-buffer))
   (if (> (ediff-operate-on-marked-sessions 'ediff-append-custom-diff) 0)
       ;; did something

File ediff-util.el

 	    )
     (save-buffer)))
 
+;; This function operates only from an ediff control buffer
 (defun ediff-compute-custom-diffs-maybe ()
   (let ((buf-A-file-name (buffer-file-name ediff-buffer-A))
 	(buf-B-file-name (buffer-file-name ediff-buffer-B))
 	(setq ediff-custom-diff-buffer
 	      (get-buffer-create
 	       (ediff-unique-buffer-name "*ediff-custom-diff" "*"))))
+    (ediff-with-current-buffer ediff-custom-diff-buffer
+      (setq buffer-read-only nil)
+      (erase-buffer))
     (ediff-exec-process
      ediff-custom-diff-program ediff-custom-diff-buffer 'synchronize
      ediff-custom-diff-options file-A file-B)
 
     (cond ((ediff-merge-job)
 	   (setq bufB ediff-buffer-C)
+	   ;; ask which buffer to compare to the merge buffer
 	   (while (cond ((eq answer ?A)
 			 (setq bufA ediff-buffer-A
 			       possibilities '(?B))
 			   (sit-for 2)
 			   t))
 	     (let ((cursor-in-echo-area t))
-	       (message "Which buffer to compare to the merge buffer (A/B)? ")
+	       (message
+		"Which buffer to compare to the merge buffer (A or B)? ")
 	       (setq answer (capitalize (read-char-exclusive))))))
 
 	  ((ediff-3way-comparison-job)
+	   ;; ask which two buffers to compare
 	   (while (cond ((memq answer possibilities)
 			 (setq possibilities (delq answer possibilities))
 			 (setq bufA
 			   t))
 	     (let ((cursor-in-echo-area t))
 	       (message "Enter the 1st buffer you want to compare (%s): "
-			(mapconcat 'char-to-string possibilities "/"))
+			(mapconcat 'char-to-string possibilities " or "))
 	       (setq answer (capitalize (read-char-exclusive)))))
 	   (setq answer "") ; silence error msg
 	   (while (cond ((memq answer possibilities)
 		 bufB ediff-buffer-B
 		 possibilities nil)))
 
+    (setq bufA (ediff-clone-buffer-for-region-comparison
+		(buffer-name bufA) "-Region.A-"))
     (ediff-with-current-buffer bufA
-      (or (mark t)
-	  (error "You forgot to specify a region in buffer %s" (buffer-name)))
       (setq begA (region-beginning)
 	    endA (region-end))
       (goto-char begA)
       (end-of-line)
       (or (eobp) (forward-char)) ; include the newline char
       (setq endA (point)))
+
+    (setq bufB (ediff-clone-buffer-for-region-comparison
+		(buffer-name bufB) "-Region.B-"))
     (ediff-with-current-buffer bufB
-      (or (mark t)
-	  (error "You forgot to specify a region in buffer %s" (buffer-name)))
       (setq begB (region-beginning)
 	    endB (region-end))
       (goto-char begB)
       (or (eobp) (forward-char)) ; include the newline char
       (setq endB (point)))
 
-    (ediff-unselect-and-select-difference
-     ediff-current-difference 'unselect-only)
-    (ediff-paint-background-regions 'unhighlight)
-
-    (ediff-with-current-buffer bufA
-      (goto-char begA)
-      (set-mark endA)
-      (narrow-to-region begA endA)
-      ;; (ediff-activate-mark)
-      )
-    ;; (sit-for 0)
-    (ediff-with-current-buffer bufB
-      (goto-char begB)
-      (set-mark endB)
-      (narrow-to-region begB endB)
-      ;; (ediff-activate-mark)
-      )
-    ;; (sit-for 0)
-    
-    ;; At this point, possibilities contains either the window char A/B/C
-    ;; that was not selected, or it is nil.  We delete the window that is not
-    ;; selected.
-    (if possibilities
-	(ediff-with-current-buffer ctl-buf
-	  (let* ((wind-to-delete (eval
-				  (ediff-get-symbol-from-alist
-				   (car possibilities)
-				    ediff-window-alist)))
-		 (frame (window-frame wind-to-delete)))
-	    (delete-window wind-to-delete)
-	    (select-frame frame)
-	    (balance-windows))))
-    (or (y-or-n-p 
-	 "Please check regions selected for comparison.  Continue? ")
-	(setq quit-now t))
-    
-    (ediff-with-current-buffer bufA
-      (widen))
-    (ediff-with-current-buffer bufB
-      (widen))
-    (if quit-now
-	(ediff-with-current-buffer ctl-buf
-	  (ediff-recenter)
-	  (sit-for 0)
-	  (error "All right.  Make up your mind and come back...")))
-
     (ediff-regions-internal
      bufA begA endA bufB begB endB
      nil			; setup-hook
 ;; Created: February 2, 1994
 ;; Keywords: comparing, merging, patching, tools, unix
 
-(defconst ediff-version "2.76.1" "The current version of Ediff")
-(defconst ediff-date "January 4, 2002" "Date of last update")  
+(defconst ediff-version "2.77" "The current version of Ediff")
+(defconst ediff-date "March 5, 2002" "Date of last update")  
 
 
 ;; This file is part of GNU Emacs.
 ;; 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-last-merge-autostore-dir nil)
 
 
 ;; Used as a startup hook to set `_orig' patch file read-only.
       (or (stringp merge-autostore-dir)
 	  (error "%s: Directory for storing merged files must be a string"
 		 jobname)))
-  (let (diffs ; var where ediff-intersect-directories returns the diff list
-	file-list meta-buf)
+  (let (;; dir-diff-struct is of the form (common-list diff-list) 
+	;; It is a structure where ediff-intersect-directories returns
+	;; commonalities and differences among directories
+	dir-diff-struct
+	meta-buf)
     (if (and ediff-autostore-merges
 	     (ediff-merge-metajob jobname)
 	     (not merge-autostore-dir))
 		    "Directory for saving merged files = Ancestor Directory.  Sure? ")
 		   (error "Directory merge aborted")))))
     
-    (setq file-list (ediff-intersect-directories 
-		     jobname 'diffs
-		     regexp dir1 dir2 dir3 merge-autostore-dir))
+    (setq dir-diff-struct (ediff-intersect-directories 
+			   jobname
+			   regexp dir1 dir2 dir3 merge-autostore-dir))
     (setq startup-hooks
 	  ;; this sets various vars in the meta buffer inside
 	  ;; ediff-prepare-meta-buffer
 		   ;; 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)))
+		   (setq ediff-dir-difference-list
+			 (cdr (quote ,dir-diff-struct))))
 		startup-hooks))
     (setq meta-buf (ediff-prepare-meta-buffer 
 		    'ediff-filegroup-action
-		    file-list
+		    (car dir-diff-struct)
 		    "*Ediff Session Group Panel"
 		    'ediff-redraw-directory-group-buffer
 		    jobname
 	      end-B (window-end))))
     (setq buffer-A
 	  (ediff-clone-buffer-for-window-comparison
-	   buffer-A wind-A "-Window1-")
+	   buffer-A wind-A "-Window.A-")
 	  buffer-B
 	  (ediff-clone-buffer-for-window-comparison
-	   buffer-B wind-B "-Window2-"))
+	   buffer-B wind-B "-Window.B-"))
     (ediff-regions-internal
      buffer-A beg-A end-A buffer-B beg-B end-B
      startup-hooks job-name word-mode nil)))
 		      buff-name
 		      (concat buff-name region-name
 			      (symbol-name (gensym)))))
-	(wind (ediff-get-visible-buffer-window buff-name)))
+	(wind (ediff-get-visible-buffer-window buff-name))
+	(pop-up-windows t)
+	other-wind
+	msg-buf)
     (ediff-with-current-buffer cloned-buff
       (setq ediff-temp-indirect-buffer t))
     (if (window-live-p wind)
 	(set-window-buffer wind cloned-buff))
     (pop-to-buffer cloned-buff)
-    (message 
-     "Mark a region in buffer %s; then type %s. Use %s to abort."
-     (buffer-name cloned-buff)
-     (ediff-format-bindings-of 'exit-recursive-edit)
-     (ediff-format-bindings-of 'abort-recursive-edit))
-    (recursive-edit)
+    (with-temp-buffer
+      (erase-buffer)
+      (insert
+       (format "\n   *******  Mark a region in buffer %s  *******\n"
+	       (buffer-name cloned-buff)))
+      (insert
+       (format "\n\t      When done, type %s       Use %s to abort\n    "
+	       (ediff-format-bindings-of 'exit-recursive-edit)
+	       (ediff-format-bindings-of 'abort-recursive-edit)))
+      (goto-char (point-min))
+      (setq msg-buf (current-buffer))
+      (other-window 1)
+      (set-window-buffer (selected-window) msg-buf)
+      (shrink-window-if-larger-than-buffer)
+      (select-window wind)
+      (recursive-edit)
+      )
     cloned-buff))
 
 (defun ediff-clone-buffer-for-window-comparison (buff wind region-name)
   
   
   (let ((buffer-A
-         (ediff-clone-buffer-for-region-comparison buffer-A "-Region1-"))
+         (ediff-clone-buffer-for-region-comparison buffer-A "-Region.A-"))
 	(buffer-B
-         (ediff-clone-buffer-for-region-comparison buffer-B "-Region2-"))
+         (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-"))
         reg-A-beg reg-A-end reg-B-beg reg-B-end)
     (save-excursion
       (set-buffer buffer-A)
       (error "Buffer %S doesn't exist" buffer-B))
   
   (let ((buffer-A
-         (ediff-clone-buffer-for-region-comparison buffer-A "-Region1-"))
+         (ediff-clone-buffer-for-region-comparison buffer-A "-Region.A-"))
 	(buffer-B
-         (ediff-clone-buffer-for-region-comparison buffer-B "-Region2-"))
+         (ediff-clone-buffer-for-region-comparison buffer-B "-Region.B-"))
         reg-A-beg reg-A-end reg-B-beg reg-B-end)
     (save-excursion
       (set-buffer buffer-A)
 C as follows:
 
 @emph{If you are comparing 2 files or buffers:}
-Ediff would compare current Emacs regions in buffers A and B.
+Ediff will ask you to select regions in buffers A and B.
 
-@emph{If you are comparing 3 files or buffers simultaneously:} Ediff would
-compare the current Emacs regions in the buffers of your choice (you will
-be asked which two of the three buffers to use).
+@emph{If you are comparing 3 files or buffers simultaneously:} Ediff will
+ask you to choose buffers and then select regions inside those buffers.
 
 @emph{If you are merging files or buffers (with or without ancestor):}
-Ediff would take the current region in the merge buffer and compare
-it to the current region in the buffer of your choice (A or B).
-
-@emph{Note: In all these cases you must first switch to the appropriate
-Emacs buffers and manually set the regions that you want to compare.}
-
-Highlighting set by the parent Ediff session is removed, to avoid interference
-with highlighting of the child session.  When done with the child session, type
-@kbd{C-l} in the parent's control panel to restore the original highlighting.
-
-If you temporarily switch to the parent session, parent highlighting will be
-restored.  If you then come back to the child session, you may want to remove
-parent highlighting, so it won't interfere.  Typing @kbd{h} may help here.
+Ediff will ask you to choose which buffer (A or B) to compare with the
+merge buffer and then select regions in those buffers.
 
 @end table
 
 
 In directory comparison or merging, a session group panel displays only the
 files common to all directories involved.  The differences are kept in a
-separate buffer and are conveniently displayed by typing @kbd{D} to the
-corresponding session group panel.  Thus, as an added benefit, Ediff can be
-used to compare the contents of up to three directories.
+separate @emph{directory difference buffer} and are conveniently displayed
+by typing @kbd{D} to the corresponding session group panel.  Thus, as an
+added benefit, Ediff can be used to compare the contents of up to three
+directories.
+
+@cindex Directory difference buffer
+Sometimes it is desirable to copy some files from one directory to another
+without exiting Ediff. The @emph{directory difference buffer}, which is
+displayed by typing @kbd{D} as discussed above, can be used for this
+purpose. If a file is, say, in Ediff's Directory A, but is missing in
+Ediff's Directory B (Ediff will refuse to override existing files), then
+typing @kbd{C} or clicking mouse button 2 over that file (which must be
+displayed in directory difference buffer) will copy that file from
+Directory A to Directory B.
 
 Session records in session group panels are also marked with @kbd{+}, for
 active sessions, and with @kbd{-}, for finished sessions.
 Tibor Polgar (tlp00@@spg.amdahl.com),
 David Prince (dave0d@@fegs.co.uk),
 Paul Raines (raines@@slac.stanford.edu),
+Stefan Reicher (xsteve@@riic.at),
 Bill Richter (richter@@math.nwu.edu),
 C.S.@: Roberson (roberson@@aur.alcatel.com),
 Kevin Rodgers (kevin.rodgers@@ihs.com),