Function: ediff-intersect-directories

ediff-intersect-directories is a byte-compiled function defined in ediff-mult.el.gz.

Signature

(ediff-intersect-directories JOBNAME REGEXP DIR1 DIR2 &optional DIR3 MERGE-AUTOSTORE-DIR)

Source Code

;; Defined in /usr/src/emacs/lisp/vc/ediff-mult.el.gz
;; 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.
;; REGEXP is nil or a filter regexp; only file names that match the regexp
;; are considered.
;; If a file is a directory in dir1 but not dir2 (or vice versa), it is not
;; 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.
;; If COMPARISON-FUNC is given, use it.  Otherwise, use string=
;;
;; Returns a list of the form:
;;      (COMMON-PART DIFF-LIST)
;; COMMON-PART is car and DIFF-LIST is cdr.
;;
;; COMMON-PART is of the form:
;;	(META-HEADER (f1 f2 f3) (f1 f2 f3) ...)
;; f3 can be nil if intersecting only 2 directories.
;; Each triple (f1 f2 f3) represents the files to be compared in the
;; corresponding ediff subsession.
;;
;; 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:
;;       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.
;;
;; Some operations might use a different meta header. For instance,
;; ediff-multifile-patch doesn't have dir2 and dir3, and regexp,
;; comparison-func don't apply.
;;
(defun ediff-intersect-directories (jobname
				    regexp dir1 dir2
				    &optional
				    dir3 merge-autostore-dir)
  (let (lis1 lis2 lis3 common auxdir1 auxdir2 auxdir3 common-part difflist)

    (setq auxdir1	(file-name-as-directory dir1)
	  lis1		(directory-files auxdir1 nil regexp)
	  lis1          (delete "."  lis1)
	  lis1          (delete ".." lis1)
	  lis1          (mapcar
			 (lambda (elt)
			   (ediff-add-slash-if-directory auxdir1 elt))
			 lis1)
	  auxdir2	(file-name-as-directory dir2)
	  lis2		(directory-files auxdir2 nil regexp)
	  lis2          (delete "."  lis2)
	  lis2          (delete ".." lis2)
	  lis2		(mapcar
			 (lambda (elt)
			   (ediff-add-slash-if-directory auxdir2 elt))
			 lis2))

    (if (stringp dir3)
	(setq auxdir3	(file-name-as-directory dir3)
	      lis3	(directory-files auxdir3 nil regexp)
	      lis3      (delete "."  lis3)
	      lis3      (delete ".." lis3)
	      lis3	(mapcar
			 (lambda (elt)
			   (ediff-add-slash-if-directory auxdir3 elt))
			 lis3)))

    (if (ediff-nonempty-string-p merge-autostore-dir)
	(setq merge-autostore-dir
	      (file-name-as-directory merge-autostore-dir)))
    (setq common (seq-intersection lis1 lis2 #'string=))

    ;; In merge with ancestor jobs, we don't intersect with lis3.
    ;; If there is no ancestor, we'll offer to merge without the ancestor.
    ;; So, we intersect with lis3 only when we are doing 3-way file comparison
    (if (and lis3 (ediff-comparison-metajob3 jobname))
        (setq common (seq-intersection common lis3 #'string=)))

    ;; copying is needed because sort sorts via side effects
    (setq common (sort (copy-sequence common) #'string-lessp))

    ;; compute difference list
    (setq difflist (seq-difference
                    (seq-union (seq-union lis1 lis2 #'string=)
                               lis3
                               #'string=)
		    common
		    #'string=)
	  difflist (delete "."  difflist)
	  ;; copying is needed because sort sorts via side effects
          difflist (sort (copy-sequence (delete ".." difflist))
			 #'string-lessp))

    (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.
    (mapc (lambda (elt)
	    (if (member (car elt) lis1)
		(setcdr elt (* (cdr elt) ediff-membership-code1)))
	    (if (member (car elt) lis2)
		(setcdr elt (* (cdr elt) ediff-membership-code2)))
	    (if (member (car elt) lis3)
		(setcdr elt (* (cdr elt) ediff-membership-code3)))
	    )
	  difflist)
    (setq difflist (cons
		    ;; diff metalist header
		    (ediff-make-new-meta-list-header regexp
						     auxdir1 auxdir2 auxdir3
						     merge-autostore-dir
						     #'string=)
		    difflist))

    (setq common-part
	  (cons
	   ;; metalist header
	   (ediff-make-new-meta-list-header regexp
					    auxdir1 auxdir2 auxdir3
					    merge-autostore-dir
					    #'string=)
	   (mapcar
	    (lambda (elt)
	      (ediff-make-new-meta-list-element
	       (expand-file-name (concat auxdir1 elt))
	       (expand-file-name (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))
			 (expand-file-name (concat auxdir3 elt)))))))
	    common)))
    ;; return result
    (cons common-part difflist)
    ))