test-diff-git.output
1 diff --git a/README.org b/README.org 2 new file mode 100644 3 index 0000000000..41babfe10a 4 --- /dev/null 5 +++ b/README.org 6 @@ -1,0 +1,32 @@ 7 +#+title: Areas to work on 8 + 9 +* Status 10 +** A/M/D file overview 11 +** Diffs 12 +*** Side-By-Side Diff 13 +**** split based on git and then reconstitute with overlays 14 +**** investigate overlays or how magit did it 15 +*** Segments 16 +**** within the same file the different segments 17 +*** squash action acting only on currently selected segment/file 18 +** Shortlog equivalent 19 +*** make configurable 20 +** Overview (Two liner) 21 +*** conflict marker missing 22 +* Log 23 +** Operation Log 24 +** Obslog 25 +** Commit Log 26 +* Actions 27 +** Squash -i 28 +*** figure out how to call emacs after jj prepares temporary directory 29 +**** uses ui.diff-editor even for squash 30 +**** by default everything is selected for squash, revert needed 31 +** Diff 32 +*** 33 +* Minibuffer integration 34 +** parent change > change > description 35 +** also show change id after each save? 36 +* Multiple workflow 37 +** Squash workflow 38 +** Split workflow 39 diff --git a/jujutsu.el b/jujutsu.el 40 index 5b32d77748...1aa38bdebf 100644 41 --- a/jujutsu.el 42 +++ b/jujutsu.el 43 @@ -26,6 +26,11 @@ 44 "Face used for Jujutsu commit descriptions." 45 :group 'jujutsu) 46 47 +(defcustom jujutsu-log-revset-fallback "@ | ancestors(immutable_heads().., 2) | trunk()" 48 + "Default value when no revset is specified." 49 + :group 'jujutsu 50 + :type 'string) 51 + 52 (defun jj--find-project-root () 53 "Find the root directory of the Jujutsu project." 54 (locate-dominating-file default-directory ".jj")) 55 @@ -56,15 +61,17 @@ 56 rev))) 57 (jj--run-command formatted))) 58 59 -(defun jj--log-w/template (template) 60 - "Run `jj log' command with a custom TEMPLATE. 61 +(defun jj--log-w/template (template &optional revset) 62 + "Run `jj log' command with a custom TEMPLATE and optional REVSET. 63 64 TEMPLATE is a string containing the custom template for the `jj log' command. 65 66 This function constructs and executes a `jj log' command with the given 67 template, disabling graph output and adding newlines between entries. It returns 68 the command's output as a string, with each log entry separated by newlines." 69 - (let* ((formatted (format "log --no-graph --template \"%s ++ \\\"\\n\\n\\\"\"" 70 + (let* ((revset (or revset jujutsu-log-revset-fallback)) 71 + (formatted (format "log --revisions \"%s\" --no-graph --template \"%s ++ \\\"\\n\\n\\\"\"" 72 + revset 73 template))) 74 (jj--run-command formatted))) 75 76 @@ -75,7 +82,7 @@ 77 (s-split "\n" it t))) 78 79 (defun jj--map-to-escaped-string (map) 80 - "Convert MAP (hash-table) to an escaped string." 81 + "Convert MAP (hash-table) to an escaped string for use as a jj template." 82 (->> map 83 (ht-map (lambda (key value) 84 (format "\\\"%s \\\" ++ %s ++ \\\"\\\\n\\\"" 85 @@ -83,14 +90,14 @@ 86 (s-join " ++ "))) 87 88 (defun jj--parse-file-change (line) 89 - "Parse a file change LINE into a cons of (type . filename)." 90 + "Parse a file change LINE into a hash-table." 91 (-let* [(regex (rx bos (group (any "AMD")) " " (group (+ not-newline)) eos) line) 92 ((res m1 m2) (s-match regex line))] 93 (when res 94 (ht (m1 m2))))) 95 96 (defun jj--parse-key-value (line) 97 - "Parse a KEY-VALUE LINE into a cons of (key . value)." 98 + "Parse a KEY-VALUE LINE into a hash-table." 99 (unless (s-matches? (rx bos (any "AMD") " ") line) 100 (-when-let* [(regex (rx bos 101 (group (+ (not (any " ")))) ; key 102 @@ -101,7 +108,7 @@ 103 (ht ((intern m1) m2))))) 104 105 (defun jj--parse-and-group-file-changes (file-changes) 106 - "Parse and group FILE-CHANGES by their change type." 107 + "Parse and group FILE-CHANGES by their change type into a hash-table." 108 (let ((grouped-changes (ht ('files-added nil) 109 ('files-modified nil) 110 ('files-deleted nil)))) 111 @@ -115,7 +122,7 @@ 112 grouped-changes)) 113 114 (defun jj--parse-string-to-map (input-string) 115 - "Parse INPUT-STRING into a map and an organized list of file change." 116 + "Parse INPUT-STRING into a hash-table and an organized list of file change." 117 (let* ((lines (s-split "\n" input-string t)) 118 (file-change-lines (-filter #'jj--parse-file-change lines)) 119 (grouped-file-changes (jj--parse-and-group-file-changes file-change-lines)) 120 @@ -139,11 +146,34 @@ 121 ('commit-id-shortest "commit_id.shortest()") 122 ('empty "empty") 123 ('branches "branches") 124 + ('git-head "git_head") 125 ('description "description")))) 126 (-> (jj--map-to-escaped-string template) 127 (jj--show-w/template rev) 128 jj--parse-string-to-map))) 129 130 +(defun jj--get-log-data (&optional revset) 131 + "Get status data for the given REVSET." 132 + (let ((revset (or revset jujutsu-log-revset-fallback)) 133 + (template (ht ('change-id-short "change_id.short(8)") 134 + ('change-id-shortest "change_id.shortest()") 135 + ('commit-id-short "commit_id.short(8)") 136 + ('commit-id-shortest "commit_id.shortest()") 137 + ('empty "empty") 138 + ('branches "branches") 139 + ('hidden "hidden") 140 + ('author-email "author.email()") 141 + ('timestamp "author.timestamp().format(\\\"%Y-%m-%d %H:%M:%S\\\")") 142 + ('current-working-copy "current_working_copy") 143 + ('remote-branches "remote_branches") 144 + ('git-head "git_head") 145 + ('root "root") 146 + ('description "description")))) 147 + (--> (jj--map-to-escaped-string template) 148 + (jj--log-w/template it revset) 149 + (jj--split-string-on-empty-lines it) 150 + (-map #'jj--parse-string-to-map it)))) 151 + 152 (defun jj--format-id (id-short id-shortest) 153 "Format ID-SHORT with ID-SHORTEST distinguished." 154 (let* ((shortest-length (length id-shortest)) 155 @@ -177,6 +207,88 @@ 156 (propertize "(no description set)" 'face 'warning)))] 157 (format "%s %s %s%s%s" change-id commit-id branches empty desc))) 158 159 +(defun jj--format-log-line (data) 160 + "Format a status line using DATA with fontification." 161 + (-let* [((&hash 'change-id-short chids 'change-id-shortest chidss 162 + 'commit-id-short coids 'commit-id-shortest coidss 163 + 'branches branches 'empty empty 'description desc 164 + 'root root 'author-email author-email 165 + 'timestamp timestamp 166 + 'current-working-copy cwc) 167 + data) 168 + (cwc (if (s-equals? cwc "true") "@" "◉")) 169 + (author-email (if author-email 170 + (propertize author-email 'face 'warning) 171 + "")) 172 + (root (if (s-equals? root "true") t nil)) 173 + (empty (if (s-equals? empty "true") 174 + (propertize "(empty) " 'face 'warning) 175 + "")) 176 + (timestamp (if timestamp (propertize timestamp 'face 'magit-log-date) 177 + "")) 178 + (branches (if branches 179 + (s-concat (propertize branches 'face 'magit-branch-local) " ") 180 + "")) 181 + (change-id (jj--format-id chids chidss)) 182 + (commit-id (jj--format-id coids coidss)) 183 + (desc (if desc 184 + (propertize desc 'face 'jujutsu-description-face) 185 + (propertize "(no description set)" 'face 'warning)))] 186 + (if root 187 + (list (format "%s %s %s %s\n" 188 + cwc 189 + change-id 190 + (propertize "root()" 'face 'magit-keyword) 191 + commit-id) ) 192 + (list 193 + (format "%s %s %s %s %s%s\n" cwc change-id author-email timestamp branches commit-id) 194 + (format "│ %s%s\n" empty desc))))) 195 + 196 +(defun jj--foobar (revset) 197 + "Fooofoofofofo REVSET fooo." 198 + (let* ((log-data (jj--get-log-data revset)) 199 + (includes-root? (-some (lambda (m) (string= (ht-get m 'root) "true")) log-data))) 200 + (-concat (-map #'jj--format-log-line log-data) (when (not includes-root?) (list "~"))))) 201 + 202 +(defun jj--parse-git-diff (diff-output) 203 + "Parse GIT-DIFF-OUTPUT into a hash-table with 'a' and 'b' keys and metadata." 204 + (let ((result (ht ('a '()) ('b '()) ('metadata (ht)))) 205 + (lines (s-split "\n" diff-output)) 206 + (current-section nil)) 207 + (dolist (line lines) 208 + (cond 209 + ;; Metadata 210 + ((s-starts-with? "diff --git " line) 211 + (ht-set! (ht-get result 'metadata) 'diff-git line)) 212 + ((s-starts-with? "index " line) 213 + (ht-set! (ht-get result 'metadata) 'index line)) 214 + ((s-starts-with? "--- " line) 215 + (ht-set! (ht-get result 'metadata) 'file-a (s-chop-prefix "--- " line)) 216 + (setq current-section 'a)) 217 + ((s-starts-with? "+++ " line) 218 + (ht-set! (ht-get result 'metadata) 'file-b (s-chop-prefix "+++ " line)) 219 + (setq current-section 'b)) 220 + ;; Diff content 221 + ((s-starts-with? "-" line) 222 + (ht-set! result 'a (cons line (ht-get result 'a)))) 223 + ((s-starts-with? "+" line) 224 + (ht-set! result 'b (cons line (ht-get result 'b)))) 225 + (t 226 + ;; Lines that don't start with special characters 227 + (ht-set! result 'a (cons line (ht-get result 'a))) 228 + (ht-set! result 'b (cons line (ht-get result 'b)))))) 229 + (ht-set! result 'a (reverse (ht-get result 'a))) 230 + (ht-set! result 'b (reverse (ht-get result 'b))) 231 + result)) 232 + 233 +(comment 234 + (jj--parse-git-diff foobar) 235 + (-> foobar 236 + jj--parse-git-diff 237 + parseedn-print-str) 238 + ) 239 + 240 + 241 (defun jujutsu-status () 242 "Display a summary of the current Jujutsu working copy status." 243 (interactive) 244 @@ -200,7 +312,7 @@ 245 "\n" 246 (if (> (length all-files) 0) 247 (propertize "Working copy changes:\n" 'face 'font-lock-keyword-face) 248 - (propertize "The working copy is clean" 'face 'font-lock-keyword-face)) 249 + (propertize "The working copy is clean\n" 'face 'font-lock-keyword-face)) 250 (-map (lambda (added-file) (propertize (format "A %s\n" added-file) 251 'face 252 'magit-diffstat-added)) 253 @@ -212,7 +324,11 @@ 254 (-map (lambda (deleted-file) (propertize (format "D %s\n" deleted-file) 255 'face 256 'magit-diffstat-removed)) 257 - files-deleted)) 258 + files-deleted) 259 + "\n" 260 + (propertize "Log:\n" 'face 'font-lock-keyword-face) 261 + (jj--foobar jujutsu-log-revset-fallback) 262 + ) 263 -flatten 264 (apply #'s-concat) 265 insert))