branch: externals/vc-jj commit ac1f798dcdcde50c8d4008b97dce788b45e83fa4 Author: Kristoffer Balintona <krisbalint...@gmail.com> Commit: Kristoffer Balintona <krisbalint...@gmail.com>
fix: Support "removed" VC file state Previously, deleted files would be incorrectly reported as "ignored" by vc-jj. Now, they are properly reported as "removed," both in `vc-jj-state` and `vc-jj-dir-status-files`. Additionally, this commit includes a few other ancillary changes: - More informative docstrings. - Updated comments documenting the logic of the functions modified by this commit. - Use symbol names more consistent with the names of the VC states. - Update NEWS file with a new entry announcing this bug fix. --- NEWS.org | 2 ++ vc-jj-tests.el | 13 ++++++++++ vc-jj.el | 75 ++++++++++++++++++++++++++++++++++++++++++---------------- 3 files changed, 69 insertions(+), 21 deletions(-) diff --git a/NEWS.org b/NEWS.org index c096e773c5..98d3c72460 100644 --- a/NEWS.org +++ b/NEWS.org @@ -29,6 +29,8 @@ - Fix error when calling =vc-find-revision= due to erroneous argument to =erase-buffer=, which accepts no arguments. Commands that use =vc-find-revision=, like =vc-revision-other-window=, now do not error. +- Identify the "removed" vc file state. Previously, removed files + would be incorrectly labeled as "ignored." ** [[https://codeberg.org/emacs-jj-vc/vc-jj.el/compare/v0.2...v0.3][0.3]] - 2025-06-15 diff --git a/vc-jj-tests.el b/vc-jj-tests.el index fc963fe66e..1261124b1d 100644 --- a/vc-jj-tests.el +++ b/vc-jj-tests.el @@ -97,6 +97,19 @@ is needed." (should (equal (vc-jj-dir-status-files repo nil (lambda (x y) x)) '(("second-file" added) ("first-file" up-to-date)))))) +(ert-deftest vc-jj-delete-file () + "Test \"removed\" vc state and `vc-jj-delete-file'." + (vc-jj-test-with-repo repo + (write-region "First file" nil "first-file") + (should (eq (vc-jj-state "first-file") 'added)) + (vc-jj-checkin '("first-file") "Commit") + (vc-jj-delete-file "first-file") + (should (eq (vc-jj-state "first-file") 'removed)) + (write-region "Second file" nil "second-file") + (should (eq (vc-jj-state "second-file") 'added)) + (should (equal (vc-jj-dir-status-files repo nil (lambda (x y) x)) + '(("second-file" added) ("first-file" removed)))))) + (ert-deftest vc-jj-test-conflict () (vc-jj-test-with-repo repo (let (branch-1 branch-2 branch-merged) diff --git a/vc-jj.el b/vc-jj.el index 126da39eff..45a2806e74 100644 --- a/vc-jj.el +++ b/vc-jj.el @@ -195,33 +195,61 @@ stderr and1 `vc-do-command' cannot separate output to stdout and stderr." (/= (point-min) (point-max)))))) (defun vc-jj-state (file) - "JJ implementation of `vc-state' for FILE." - ;; We need to run two commands for the complete state: + "JJ implementation of `vc-state' for FILE. +There are several file states recognized by vc (see the docstring of +`vc-state' for the full list). Several of these are relevant to +jujutsu. They are: +- Added (new file) +- Removed (deleted file) +- Edited (modified file) +- Up-to-date (unmodified file) +- Conflict (merge conflict) +- Ignored (ignored by repository) + +Other VCS backends would also recognize the \"unregistered\" state, but +there is no such state in jj since jj automatically registers new files." + ;; We can deduce all of the vc states listed above with two + ;; commands: ;; - ;; - "jj file list -T 'conflict' FILE" gets us conflicted (output - ;; "true"), ignored (no output) or tracked (output "false", but - ;; could be added or modified) + ;; - "jj diff --summary FILE" gets us the "added" state (when output + ;; starts with "A "), the "removed" state (when output starts with + ;; "D ") and "edited" state (when output starts with "M "). No + ;; output is also possible (this could mean the "conflict", + ;; "ignored" or "unchanged" state), but we deduce these with the + ;; command below instead. + ;; + ;; - "jj file list -T 'conflict' FILE" gets us the "conflict" state + ;; (when output is "true"), the "ignored" state (when there is no + ;; output) and states for tracked files (when output is "false", + ;; either the "added", "removed", "edited" or "up-to-date" state). + ;; We deduce the "added", "removed" and "edited" states with the + ;; command above. ;; - ;; - "jj diff --summary FILE" gets us modified (output starts with - ;; "M ") or added (output starts with "A "), but no output could - ;; be conflicted, ignored or unchanged + ;; The only remaining possibility is the "up-to-date" state, which + ;; we can deduce because when all other states have not been + ;; matched against. (let* ((default-directory (vc-jj-root file)) (file (vc-jj--filename-to-fileset file)) + (changed + (vc-jj--command-parseable "diff" "--summary" "--" file)) (conflicted-ignored - (vc-jj--command-parseable "file" "list" "-T" "conflict" "--" file)) - (modified-added - (vc-jj--command-parseable "diff" "--summary" "--" file))) + (vc-jj--command-parseable "file" "list" "-T" "conflict" "--" file))) (cond - ((string-empty-p conflicted-ignored) 'ignored) + ((string-prefix-p "A " changed) 'added) + ((string-prefix-p "D " changed) 'removed) + ((string-prefix-p "M " changed) 'edited) ((string= conflicted-ignored "true") 'conflict) - ((string-prefix-p "M " modified-added) 'edited) - ((string-prefix-p "A " modified-added) 'added) + ((string-empty-p conflicted-ignored) 'ignored) + ;; If the file hasn't matched anything yet, this leaves only one + ;; possible state: up-to-date ((string= conflicted-ignored "false") 'up-to-date) - (t nil)))) + (t (error "VC state of %s is not recognized" file))))) (defun vc-jj-dir-status-files (dir _files update-function) "Calculate a list of (FILE STATE EXTRA) entries for DIR. -Return the result of applying UPDATE-FUNCTION to that list." +Return the result of applying UPDATE-FUNCTION to that list. + +For a description of the states relevant to jj, see `vc-jj-state'." ;; 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 @@ -252,7 +280,11 @@ Return the result of applying UPDATE-FUNCTION to that list." (and (string-prefix-p "A " entry) (list (substring entry 2)))) changed)) - (modified-files (mapcan (lambda (entry) + (removed-files (mapcan (lambda (entry) + (and (string-prefix-p "D " entry) + (list (substring entry 2)))) + changed)) + (edited-files (mapcan (lambda (entry) (and (string-prefix-p "M " entry) (list (substring entry 2)))) changed)) @@ -263,17 +295,18 @@ Return the result of applying UPDATE-FUNCTION to that list." (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) + (up-to-date-files (cl-remove-if (lambda (entry) (or (member entry conflicted-files) + (member entry edited-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 'removed)) removed-files) + (mapcar (lambda (entry) (list entry 'edited)) edited-files) (mapcar (lambda (entry) (list entry 'ignored)) ignored-files) - (mapcar (lambda (entry) (list entry 'up-to-date)) unchanged-files)))) + (mapcar (lambda (entry) (list entry 'up-to-date)) up-to-date-files)))) (funcall update-function result nil)))) (defun vc-jj-dir-extra-headers (dir)