branch: externals/vc-jj
commit 31ab84e6bf2551a506c45d2d0dda7e3116bf84ee
Author: Rudi Schlatte <[email protected]>
Commit: Rudi Schlatte <[email protected]>
Implement vc-annotate
No support yet for moving between revisions
Also add some test machinery to make timestamps, change ids stable
(currently unused)
---
vc-jj-tests.el | 94 +++++++++++++++++++++++++++++++++++++++++++---------------
vc-jj.el | 39 ++++++++++++++++++++++++
2 files changed, 109 insertions(+), 24 deletions(-)
diff --git a/vc-jj-tests.el b/vc-jj-tests.el
index 3a72ecf254..09feb83465 100644
--- a/vc-jj-tests.el
+++ b/vc-jj-tests.el
@@ -19,7 +19,7 @@
;;; Commentary:
-;;
+;;
;;; Code:
@@ -27,34 +27,56 @@
(require 'vc)
(require 'vc-dir)
(require 'vc-jj)
-
-(defmacro vc-jj-test--with-repo (name &rest body)
+(require 'iso8601)
+(require 'cl-lib)
+(require 'thingatpt)
+
+(defun vc-jj-test-environment (seq)
+ "Create a list suitable for prepending to `process-environment'.
+The purpose is to make tests reproducible by fixing timestamps,
+change ids, author information etc. SEQ is an integer that
+modifies the JJ_RANDOMNESS_SEED, JJ_TIMESTAMP and JJ_OP_TIMESTAMP
+environment variables. Increasing values for SEQ will result in
+increasing timestamps.
+
+Note that it not necessary to use this function, except when
+stably increasing timestamps and stable change ids across test
+runs are necessary."
+ ;; For other potentially relevant variables, see
+ ;;
https://github.com/jj-vcs/jj/blob/d79c7a0dd5b8f9d3d6f9436452dcf0e1600b0b14/cli/tests/common/test_environment.rs#L115
+ (let* ((startdate (iso8601-parse "2001-02-03T04:05:06+07:00"))
+ (timezone (cl-ninth startdate))
+ (offset (time-add (encode-time startdate) seq))
+ (timestring (format-time-string "%FT%T%:z" offset timezone)))
+ (list "[email protected]"
+ "JJ_USER=john"
+ (format "JJ_RANDOMNESS_SEED=%i" (+ 12345 seq))
+ (format "JJ_TIMESTAMP=%s" timestring)
+ (format "JJ_OP_TIMESTAMP=%s" timestring))))
+
+(defmacro vc-jj-test-with-repo (name &rest body)
"Initialize a repository in a temporary directory and evaluate BODY.
-
-The current directory will be set to the top of that repository; NAME
-will be bound to that directory's file name. Once BODY exits, the
-directory will be deleted.
-
-Some environment variables that control jj's behavior will be set
-for the duration of BODY."
+The current directory will be set to the top of that repository;
+NAME will be bound to that directory's file name. Once BODY
+exits, the directory will be deleted.
+
+jj commands are executed with a fixed username and email; augment
+`process-environment' with `vc-jj-test-environment' if control
+over timestamps and random number seed (and thereby change ids)
+is needed."
(declare (indent 1))
`(ert-with-temp-directory ,name
(let ((default-directory ,name)
- ;; Note: if we need reproducible repository state, use
- ;; JJ_RANDOMNESS_SEED=12345 when calling `jj git init', and
- ;; increase its value by 1 or call `jj config set --repo
- ;; debug.randomness-seed 12346' etc. between each jj call
- ;; afterwards -- see
- ;;
https://github.com/jj-vcs/jj/blob/d79c7a0dd5b8f9d3d6f9436452dcf0e1600b0b14/cli/tests/common/test_environment.rs#L115
- ;; for other relevant environment variables.
- (process-environment (append '("[email protected]"
- "JJ_USER=john")
- process-environment)))
- (vc-create-repo 'jj)
+ (process-environment
+ (append (list "[email protected]" "JJ_USER=john")
+ process-environment)))
+ (let ((process-environment
+ (append (vc-jj-test-environment 0) process-environment)))
+ (vc-create-repo 'jj))
,@body)))
(ert-deftest vc-jj-test-add-file ()
- (vc-jj-test--with-repo repo
+ (vc-jj-test-with-repo repo
(write-region "New file" nil "README")
(should (vc-jj--file-tracked "README"))
(should (vc-jj--file-added "README"))
@@ -63,7 +85,7 @@ for the duration of BODY."
(should (eq (vc-state "README" 'jj) 'added))))
(ert-deftest vc-jj-test-added-tracked ()
- (vc-jj-test--with-repo repo
+ (vc-jj-test-with-repo repo
(write-region "In first commit" nil "first-file")
(vc-jj-checkin '("first-file") "First commit")
(write-region "In second commit" nil "second-file")
@@ -71,7 +93,7 @@ for the duration of BODY."
(should (eq (vc-jj-state "first-file") 'up-to-date))))
(ert-deftest vc-jj-test-conflict ()
- (vc-jj-test--with-repo repo
+ (vc-jj-test-with-repo repo
(let (branch-1 branch-2 branch-merged)
;; the root change id is always zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz
(shell-command "jj new zzzzzzzzzzzzzzzzzzzzzzzzzzzzzzzz")
@@ -91,5 +113,29 @@ for the duration of BODY."
(should (eq (vc-jj-state "conflicted.txt") 'conflict))
(should (eq (vc-jj-state "subdir/conflicted.txt") 'conflict)))))
+(ert-deftest vc-jj-test-annotate ()
+ (vc-jj-test-with-repo repo
+ (let ( change-1 change-2
+ readme-buffer annotation-buffer)
+ ;; Create two changes, make sure that the change ids in the
+ ;; annotation buffer match. This test is supposed to detect
+ ;; changes in the output format of `jj annotate'.
+ (write-region "Line 1\n" nil "README")
+ (setq change-1 (vc-jj-working-revision "README"))
+ (shell-command "jj commit -m 'First change'")
+ (write-region "Line 2\n" nil "README" t)
+ (shell-command "jj describe -m 'Second change'")
+ (setq change-2 (vc-jj-working-revision "README"))
+ (find-file "README")
+ (setq readme-buffer (current-buffer))
+ (vc-annotate "README" change-2)
+ (setq annotation-buffer (current-buffer))
+ (goto-char (point-min))
+ (should (string-prefix-p (thing-at-point 'word) change-1))
+ (forward-line)
+ (should (string-prefix-p (thing-at-point 'word) change-2))
+ (kill-buffer readme-buffer)
+ (kill-buffer annotation-buffer))))
+
(provide 'vc-jj-tests)
;;; vc-jj-tests.el ends here
diff --git a/vc-jj.el b/vc-jj.el
index 05ecf9a824..c0c92e94cf 100644
--- a/vc-jj.el
+++ b/vc-jj.el
@@ -32,6 +32,8 @@
(autoload 'vc-switches "vc")
(autoload 'ansi-color-apply-on-region "ansi-color")
+(autoload 'iso8601-parse "iso8601")
+(autoload 'decoded-time-set-defaults "time-date")
(add-to-list 'vc-handled-backends 'JJ)
@@ -346,6 +348,43 @@ For jj, modify `.gitignore' and call `jj untrack' or `jj
track'."
1
0)))
+(defun vc-jj-annotate-command (file buf &optional rev)
+ (with-current-buffer buf
+ (let ((rev (or rev "@")))
+ (call-process "jj" nil t nil "file" "annotate" "-r" rev file))))
+
+(defconst vc-jj--annotation-line-prefix-re
+ (rx (: bol
+ (group (+ (any "a-z"))) ; change id
+ " "
+ (group (+ (any alnum))) ; author
+ (+ " ")
+ (group ; iso 8601-ish datetime
+ (= 4 digit) "-" (= 2 digit) "-" (= 2 digit) " "
+ (= 2 digit) ":" (= 2 digit) ":" (= 2 digit))
+ (+ " ")
+ (group (+ (any "0-9"))) ; line number
+ ": "))
+ ;; TODO: find out if the output changes when the file got renamed
+ ;; somewhere in its history
+ "Regexp for the per-line prefix of the output of 'jj file annotate'.
+The regex captures four groups: change id, author, datetime, line number.")
+
+(defun vc-jj-annotate-time ()
+ (and (re-search-forward vc-jj--annotation-line-prefix-re nil t)
+ (let* ((dt (match-string 3))
+ (dt (and dt (string-replace " " "T" dt)))
+ (decoded (ignore-errors (iso8601-parse dt))))
+ (and decoded
+ (vc-annotate-convert-time
+ (encode-time (decoded-time-set-defaults decoded)))))))
+
+(defun vc-jj-annotate-extract-revision-at-line ()
+ (save-excursion
+ (beginning-of-line)
+ (when (looking-at vc-jj--annotation-line-prefix-re)
+ (match-string-no-properties 1))))
+
(defun vc-jj-revision-completion-table (files)
(let ((revisions
(apply #'process-lines