I also needed this feature and came up with the following. The function ediff-directories-recursive works like ediff-directories but recurses into sub-directories.
The magic behind that is temporarily replacing the built-in directory-files by a self-made directory-files-recursive before calling ediff-directories.
(eval (let ((directory-files-original (symbol-function 'directory-files))) `(defun directory-files-recursive (directory &optional full match nosort) "Like `directory-files' but recurses into subdirectories. Does not follow symbolic links." (let* ((prefix (or (and full "") directory)) dirs files) (mapc (lambda (p) (let ((fullname (if full p (concat prefix "/" p)))) (when (and (file-directory-p fullname) (null (or (string-match "\\(^\\|/\\).$" p) (string-match "\\(^\\|/\\)..$" p) (file-symlink-p fullname)))) (setq dirs (cons p dirs))))) (funcall ,directory-files-original directory full nil nosort)) (setq dirs (nreverse dirs)) (mapc (lambda (p) (when (null (file-directory-p (if full p (concat prefix "/" p)))) (setq files (cons p files)))) (funcall ,directory-files-original directory full match nosort)) (setq files (nreverse files)) (mapc (lambda (d) (setq files (append files (if full (apply 'directory-files-recursive (list d full match nosort)) (mapcar (lambda (n) (concat d "/" n)) (apply 'directory-files-recursive (list (concat prefix "/" d) full match nosort))))))) dirs) files)))) (eval `(defun ediff-directories-recursive (dir1 dir2 regexp) "Like `ediff-directories' but recurses into sub-directories. Does not follow symbolic links." ,(interactive-form (symbol-function 'ediff-directories)) (let ((directory-files-original (symbol-function 'directory-files))) (unwind-protect (progn (fset 'directory-files (symbol-function 'directory-files-recursive)) (ediff-directories dir1 dir2 regexp) (fset 'directory-files directory-files-original))))))