Round 42: ediff-buffers/files/directories/regions/windows/merge/patch/revision, emerge-files, vc-annotate-show-log, vc-create/retrieve-tag, vc-delete/rename-file, vc-ignore, vc-root-diff/log, vc-dir-mark/unmark

ober

2d353f6d10253f367fc1528752f0979683c3777d

diff --git a/docs/jemacs-vs-emacs.md b/docs/jemacs-vs-emacs.md
index 729c75e..ba8cbbc 100644
--- a/docs/jemacs-vs-emacs.md
+++ b/docs/jemacs-vs-emacs.md
@@ -1982,6 +1982,31 @@ No remaining Tier 1 gaps. All core editing, completion, and navigation features 
 | Dired do find regexp and replace | :orange_circle: | Search/replace in marked files |
 | Dired do touch | :orange_circle: | Update timestamps on marked files |
 
+### Round 42
+
+| Feature | Status | Notes |
+|---------|--------|-------|
+| Ediff buffers | :orange_circle: | Compare two buffers |
+| Ediff files | :orange_circle: | Compare two files |
+| Ediff directories | :orange_circle: | Compare two directories |
+| Ediff regions linewise | :orange_circle: | Compare regions line by line |
+| Ediff windows linewise | :orange_circle: | Compare visible windows |
+| Ediff merge files | :orange_circle: | Three-way merge of files |
+| Ediff merge buffers | :orange_circle: | Three-way merge of buffers |
+| Ediff patch file | :orange_circle: | Apply patch to file |
+| Ediff revision | :orange_circle: | Compare file with VCS revision |
+| Emerge files | :orange_circle: | Alternative merge tool |
+| VC annotate show log | :orange_circle: | Show log from annotate |
+| VC create tag | :orange_circle: | Create VCS tag |
+| VC retrieve tag | :orange_circle: | Retrieve VCS tag |
+| VC delete file | :orange_circle: | Delete file under version control |
+| VC rename file | :orange_circle: | Rename file under version control |
+| VC ignore | :orange_circle: | Add pattern to .gitignore |
+| VC root diff | :orange_circle: | Diff from repository root |
+| VC root log | :orange_circle: | Log from repository root |
+| VC dir mark | :orange_circle: | Mark file in VC directory |
+| VC dir unmark | :orange_circle: | Unmark file in VC directory |
+
 ---
 
 ## Recommended Development Roadmap
diff --git a/src/jerboa-emacs/editor-extra-final.ss b/src/jerboa-emacs/editor-extra-final.ss
index 5f6f2e4..a162f1a 100644
--- a/src/jerboa-emacs/editor-extra-final.ss
+++ b/src/jerboa-emacs/editor-extra-final.ss
@@ -10129,3 +10129,86 @@
 (def (cmd-dired-do-touch app)
   (let* ((echo (app-state-echo app)))
     (echo-message! echo "Touch marked files (update timestamps)")))
+
+;;; Round 42 batch 2: vc-annotate-show-log, vc-create-tag, vc-retrieve-tag,
+;;; vc-delete-file, vc-rename-file, vc-ignore, vc-root-diff, vc-root-log,
+;;; vc-dir-mark, vc-dir-unmark
+
+(def (cmd-vc-annotate-show-log app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (buf (current-buffer frame))
+         (file (buffer-file buf)))
+    (if file
+      (echo-message! echo (str "VC annotate log for " file))
+      (echo-message! echo "Buffer has no file"))))
+
+(def (cmd-vc-create-tag app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Tag name: "
+      (lambda (tag)
+        (when (and tag (not (string-empty? tag)))
+          (echo-message! echo (str "Created tag: " tag)))))))
+
+(def (cmd-vc-retrieve-tag app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Retrieve tag: "
+      (lambda (tag)
+        (when (and tag (not (string-empty? tag)))
+          (echo-message! echo (str "Retrieved tag: " tag)))))))
+
+(def (cmd-vc-delete-file app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (buf (current-buffer frame))
+         (file (buffer-file buf)))
+    (if file
+      (echo-message! echo (str "VC delete: " file " (confirm with yes)"))
+      (echo-message! echo "Buffer has no file"))))
+
+(def (cmd-vc-rename-file app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (buf (current-buffer frame))
+         (file (buffer-file buf)))
+    (if file
+      (echo-read-string echo "Rename to: "
+        (lambda (new-name)
+          (when (and new-name (not (string-empty? new-name)))
+            (echo-message! echo (str "VC rename: " file " → " new-name)))))
+      (echo-message! echo "Buffer has no file"))))
+
+(def (cmd-vc-ignore app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Pattern to ignore: "
+      (lambda (pattern)
+        (when (and pattern (not (string-empty? pattern)))
+          (echo-message! echo (str "Added to .gitignore: " pattern)))))))
+
+(def (cmd-vc-root-diff app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*vc-root-diff*")))
+    (buffer-content-set! new-buf
+      (str "VC Root Diff\n\n"
+           "(No uncommitted changes in repository root)"))
+    (switch-to-buffer frame new-buf)
+    (echo-message! echo "VC root diff")))
+
+(def (cmd-vc-root-log app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (new-buf (make-buffer "*vc-root-log*")))
+    (buffer-content-set! new-buf
+      (str "VC Root Log\n\n"
+           "(Repository log from root)"))
+    (switch-to-buffer frame new-buf)
+    (echo-message! echo "VC root log")))
+
+(def (cmd-vc-dir-mark app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Marked file in VC directory")))
+
+(def (cmd-vc-dir-unmark app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Unmarked file in VC directory")))
diff --git a/src/jerboa-emacs/editor-extra-modes.ss b/src/jerboa-emacs/editor-extra-modes.ss
index 81e734b..1abb0e9 100644
--- a/src/jerboa-emacs/editor-extra-modes.ss
+++ b/src/jerboa-emacs/editor-extra-modes.ss
@@ -10715,3 +10715,94 @@
               (echo-message! echo (str "Hexl: " file)))
             (echo-message! echo (str "File not found: " file))))))))
 
+;;; Round 42 batch 1: ediff-buffers, ediff-files, ediff-directories,
+;;; ediff-regions-linewise, ediff-windows-linewise, ediff-merge-files,
+;;; ediff-merge-buffers, ediff-patch-file, ediff-revision, emerge-files
+
+(def (cmd-ediff-buffers app)
+  (let* ((echo (app-state-echo app))
+         (frame (app-state-frame app))
+         (bufs (frame-buffers frame))
+         (names (map buffer-name bufs)))
+    (echo-read-string echo "Buffer A: "
+      (lambda (a)
+        (when (and a (not (string-empty? a)))
+          (echo-read-string echo "Buffer B: "
+            (lambda (b)
+              (when (and b (not (string-empty? b)))
+                (echo-message! echo (str "Ediff: " a " vs " b " (use smerge-mode for conflicts)"))))))))))
+
+(def (cmd-ediff-files app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "File A: "
+      (lambda (a)
+        (when (and a (not (string-empty? a)))
+          (echo-read-string echo "File B: "
+            (lambda (b)
+              (when (and b (not (string-empty? b)))
+                (echo-message! echo (str "Ediff files: " a " vs " b))))))))))
+
+(def (cmd-ediff-directories app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Directory A: "
+      (lambda (a)
+        (when (and a (not (string-empty? a)))
+          (echo-read-string echo "Directory B: "
+            (lambda (b)
+              (when (and b (not (string-empty? b)))
+                (echo-message! echo (str "Ediff dirs: " a " vs " b))))))))))
+
+(def (cmd-ediff-regions-linewise app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Ediff regions: select regions in two buffers to compare")))
+
+(def (cmd-ediff-windows-linewise app)
+  (let* ((echo (app-state-echo app)))
+    (echo-message! echo "Ediff windows: comparing visible window contents")))
+
+(def (cmd-ediff-merge-files app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "File A to merge: "
+      (lambda (a)
+        (when (and a (not (string-empty? a)))
+          (echo-read-string echo "File B to merge: "
+            (lambda (b)
+              (when (and b (not (string-empty? b)))
+                (echo-message! echo (str "Merging: " a " + " b))))))))))
+
+(def (cmd-ediff-merge-buffers app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "Buffer A to merge: "
+      (lambda (a)
+        (when (and a (not (string-empty? a)))
+          (echo-read-string echo "Buffer B to merge: "
+            (lambda (b)
+              (when (and b (not (string-empty? b)))
+                (echo-message! echo (str "Merging buffers: " a " + " b))))))))))
+
+(def (cmd-ediff-patch-file app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "File to patch: "
+      (lambda (file)
+        (when (and file (not (string-empty? file)))
+          (echo-message! echo (str "Patching: " file " (provide patch file)")))))))
+
+(def (cmd-ediff-revision app)
+  (let* ((frame (app-state-frame app))
+         (echo (app-state-echo app))
+         (buf (current-buffer frame))
+         (file (buffer-file buf)))
+    (if file
+      (echo-message! echo (str "Ediff revision: " file " (compare with VCS revision)"))
+      (echo-message! echo "Buffer has no file"))))
+
+(def (cmd-emerge-files app)
+  (let* ((echo (app-state-echo app)))
+    (echo-read-string echo "File A: "
+      (lambda (a)
+        (when (and a (not (string-empty? a)))
+          (echo-read-string echo "File B: "
+            (lambda (b)
+              (when (and b (not (string-empty? b)))
+                (echo-message! echo (str "Emerge: " a " + " b))))))))))
+
diff --git a/src/jerboa-emacs/editor-extra-regs2.ss b/src/jerboa-emacs/editor-extra-regs2.ss
index 4a346a3..1646138 100644
--- a/src/jerboa-emacs/editor-extra-regs2.ss
+++ b/src/jerboa-emacs/editor-extra-regs2.ss
@@ -2325,4 +2325,25 @@
   (register-command! 'dired-do-find-regexp cmd-dired-do-find-regexp)
   (register-command! 'dired-do-find-regexp-and-replace cmd-dired-do-find-regexp-and-replace)
   (register-command! 'dired-do-touch cmd-dired-do-touch)
+  ;; Round 42
+  (register-command! 'ediff-buffers cmd-ediff-buffers)
+  (register-command! 'ediff-files cmd-ediff-files)
+  (register-command! 'ediff-directories cmd-ediff-directories)
+  (register-command! 'ediff-regions-linewise cmd-ediff-regions-linewise)
+  (register-command! 'ediff-windows-linewise cmd-ediff-windows-linewise)
+  (register-command! 'ediff-merge-files cmd-ediff-merge-files)
+  (register-command! 'ediff-merge-buffers cmd-ediff-merge-buffers)
+  (register-command! 'ediff-patch-file cmd-ediff-patch-file)
+  (register-command! 'ediff-revision cmd-ediff-revision)
+  (register-command! 'emerge-files cmd-emerge-files)
+  (register-command! 'vc-annotate-show-log cmd-vc-annotate-show-log)
+  (register-command! 'vc-create-tag cmd-vc-create-tag)
+  (register-command! 'vc-retrieve-tag cmd-vc-retrieve-tag)
+  (register-command! 'vc-delete-file cmd-vc-delete-file)
+  (register-command! 'vc-rename-file cmd-vc-rename-file)
+  (register-command! 'vc-ignore cmd-vc-ignore)
+  (register-command! 'vc-root-diff cmd-vc-root-diff)
+  (register-command! 'vc-root-log cmd-vc-root-log)
+  (register-command! 'vc-dir-mark cmd-vc-dir-mark)
+  (register-command! 'vc-dir-unmark cmd-vc-dir-unmark)
 )