branch: externals/vc-jj
commit 79740251a096154fd95297b7494ff586eea8436b
Author: Rudi Schlatte <r...@constantly.at>
Commit: Rudi Schlatte <r...@constantly.at>

    Make vc-jj robust against corrupt repository
    
    Catch and report errors in 'vc-jj-dir-status-files'.
    
    Fixes https://codeberg.org/emacs-jj-vc/vc-jj.el/issues/63
---
 NEWS.org       |  5 ++++
 vc-jj-tests.el |  9 ++++++++
 vc-jj.el       | 73 +++++++++++++++++++++++++++++-----------------------------
 3 files changed, 51 insertions(+), 36 deletions(-)

diff --git a/NEWS.org b/NEWS.org
index fdea58eada..65a1cfd103 100644
--- a/NEWS.org
+++ b/NEWS.org
@@ -10,6 +10,11 @@
 
 *** Fixed
 
+- vc-jj now does not prevent non-vc Emacs operations (dired, editing
+  files) in a corrupted jj repository.  Blocking this was never
+  intended, but was a consequence of letting errors escape from
+  =vc-jj-dir-status-files=.
+
 ** [[https://codeberg.org/emacs-jj-vc/vc-jj.el/compare/v0.2...v0.3][0.3]] - 
2025-06-15
 
 *** Changed
diff --git a/vc-jj-tests.el b/vc-jj-tests.el
index 9675fa429e..fc963fe66e 100644
--- a/vc-jj-tests.el
+++ b/vc-jj-tests.el
@@ -219,5 +219,14 @@ is needed."
     (should (eq (vc-jj-state "numbers.txt") 'added))
     (should (eq (vc-jj-state "alphabet.txt") 'ignored))))
 
+(ert-deftest vc-jj-tolerate-repo-corruption ()
+  ;; https://codeberg.org/emacs-jj-vc/vc-jj.el/issues/63
+  (let ((current-prefix-arg 4))         ; create git co-located repo
+    (vc-jj-test-with-repo repo
+      (write-region "Hello!" nil "README")
+      (shell-command "rm -r .git")
+      (should (eq (vc-jj-dir-status-files repo nil (lambda (x y) x))
+                  nil)))))
+
 (provide 'vc-jj-tests)
 ;;; vc-jj-tests.el ends here
diff --git a/vc-jj.el b/vc-jj.el
index ad4cf834f1..0c29e7579f 100644
--- a/vc-jj.el
+++ b/vc-jj.el
@@ -221,7 +221,7 @@ stderr and1 `vc-do-command' cannot separate output to 
stdout and stderr."
 
 (defun vc-jj-dir-status-files (dir _files update-function)
   "Calculate a list of (FILE STATE EXTRA) entries for DIR.
-Return the result result of applying UPDATE-FUNCTION to that list."
+Return the result of applying UPDATE-FUNCTION to that list."
   ;; This function is specified below the STATE-QUERYING FUNCTIONS
   ;; header in the comments at the beginning of vc.el.  The
   ;; specification says the 'dir-status-files' backend function
@@ -239,41 +239,42 @@ Return the result result of applying UPDATE-FUNCTION to 
that list."
   ;;
   ;; TODO: we should use hash tables, since we're doing a lot of set
   ;; operations, which are slow on lists.
-  (let* ((dir (expand-file-name dir))
-         (default-directory dir)
-         (project-root (vc-jj-root dir))
-         (registered-files (vc-jj--process-lines "file" "list" "--" dir))
-         (ignored-files (seq-difference (cl-delete-if #'file-directory-p
-                                                      (directory-files dir nil 
nil t))
-                                        registered-files))
-         (changed (vc-jj--process-lines "diff" "--summary" "--" dir))
-         (added-files (mapcan (lambda (entry)
-                                (and (string-prefix-p "A " entry)
-                                     (list (substring entry 2))))
-                              changed))
-         (modified-files (mapcan (lambda (entry)
-                                   (and (string-prefix-p "M " entry)
-                                        (list (substring entry 2))))
-                                 changed))
-         ;; The command below only prints conflicted files in DIR, but
-         ;; relative to project-root, hence the dance with
-         ;; expand-file-name / file-relative-name
-         (conflicted-files (mapcar (lambda (entry)
-                                     (file-relative-name (expand-file-name 
entry project-root) dir))
-                                   (vc-jj--process-lines "file" "list"
-                                                         "-T" "if(conflict, 
path ++ \"\\n\")" "--" dir)))
-         (unchanged-files (cl-remove-if (lambda (entry) (or (member entry 
conflicted-files)
-                                                            (member entry 
modified-files)
-                                                            (member entry 
added-files)
-                                                            (member entry 
ignored-files)))
-                                        registered-files))
-         (result
-          (nconc (mapcar (lambda (entry) (list entry 'conflict)) 
conflicted-files)
-                 (mapcar (lambda (entry) (list entry 'added)) added-files)
-                 (mapcar (lambda (entry) (list entry 'edited)) modified-files)
-                 (mapcar (lambda (entry) (list entry 'ignored)) ignored-files)
-                 (mapcar (lambda (entry) (list entry 'up-to-date)) 
unchanged-files))))
-    (funcall update-function result nil)))
+  (with-demoted-errors "JJ error during `vc-dir-status-files': %S"
+    (let* ((dir (expand-file-name dir))
+           (default-directory dir)
+           (project-root (vc-jj-root dir))
+           (registered-files (vc-jj--process-lines "file" "list" "--" dir))
+           (ignored-files (seq-difference (cl-delete-if #'file-directory-p
+                                                        (directory-files dir 
nil nil t))
+                                          registered-files))
+           (changed (vc-jj--process-lines "diff" "--summary" "--" dir))
+           (added-files (mapcan (lambda (entry)
+                                  (and (string-prefix-p "A " entry)
+                                       (list (substring entry 2))))
+                                changed))
+           (modified-files (mapcan (lambda (entry)
+                                     (and (string-prefix-p "M " entry)
+                                          (list (substring entry 2))))
+                                   changed))
+           ;; The command below only prints conflicted files in DIR, but
+           ;; relative to project-root, hence the dance with
+           ;; expand-file-name / file-relative-name
+           (conflicted-files (mapcar (lambda (entry)
+                                       (file-relative-name (expand-file-name 
entry project-root) dir))
+                                     (vc-jj--process-lines "file" "list"
+                                                           "-T" "if(conflict, 
path ++ \"\\n\")" "--" dir)))
+           (unchanged-files (cl-remove-if (lambda (entry) (or (member entry 
conflicted-files)
+                                                              (member entry 
modified-files)
+                                                              (member entry 
added-files)
+                                                              (member entry 
ignored-files)))
+                                          registered-files))
+           (result
+            (nconc (mapcar (lambda (entry) (list entry 'conflict)) 
conflicted-files)
+                   (mapcar (lambda (entry) (list entry 'added)) added-files)
+                   (mapcar (lambda (entry) (list entry 'edited)) 
modified-files)
+                   (mapcar (lambda (entry) (list entry 'ignored)) 
ignored-files)
+                   (mapcar (lambda (entry) (list entry 'up-to-date)) 
unchanged-files))))
+      (funcall update-function result nil))))
 
 (defun vc-jj-dir-extra-headers (dir)
   "Return extra headers for `vc-dir' when executed inside DIR.

Reply via email to