]> Git — Sourcephile - julm/julm-nix.git/blob - home-manager/profiles/jujutsu.nix
role(julm)(jj): move to julm.nix what is specific to julm
[julm/julm-nix.git] / home-manager / profiles / jujutsu.nix
1 {
2 config,
3 lib,
4 pkgs,
5 pkgs-unstable,
6 ...
7 }:
8 {
9 programs.jujutsu = {
10 enable = true;
11 package = pkgs-unstable.jujutsu;
12 settings = {
13 fsmonitor = {
14 backend = "watchman";
15 fsmonitor.watchman.register-snapshot-trigger = true;
16 };
17
18 snapshot = {
19 max-new-file-size = "2MiB";
20 auto-update-stale = true;
21 };
22
23 aliases = {
24 a = [ "absorb" ];
25 dlog = [
26 "log"
27 "-r"
28 ];
29 l = [
30 "log"
31 "-r"
32 "(trunk()..@):: | (trunk()..@)-"
33 ];
34 #l = ["log" "-r" "more()"];
35 fresh = [
36 "new"
37 "trunk()"
38 ];
39 # Figures out the closest bookmark
40 # and pulls it up to the latest change that can be pushed.
41 tug = [
42 "bookmark"
43 "move"
44 "--from"
45 "closest_bookmark(@)"
46 "--to"
47 "closest_pushable(@)"
48 ];
49 bl = [
50 "bookmark"
51 "list"
52 ];
53 blr = [
54 "bookmark"
55 "list"
56 "--revisions"
57 "recent()"
58 ];
59 c = [ "commit" ];
60 ci = [
61 "commit"
62 "--interactive"
63 ]; # commit interactive
64 m = [ "describe" ];
65 mm = [
66 "describe"
67 "--message"
68 ];
69 d = [
70 "diff"
71 "--stat"
72 "--revisions"
73 ];
74 dd = [
75 "diff"
76 "--revisions"
77 ];
78 files = [
79 "diff"
80 "--name-only"
81 "--revisions"
82 ];
83 review-files = [
84 "diff"
85 "--name-only"
86 "--revisions"
87 "review()"
88 ];
89 dp = [
90 "describe"
91 "@-"
92 ]; # describe previous
93 e = [ "edit" ];
94 para = [
95 "parallelize"
96 "closest_tip()..@-"
97 ];
98 patch = [
99 "show"
100 "--git"
101 "--template"
102 "git_format_patch_email_headers"
103 ];
104 pull = [
105 "git"
106 "fetch"
107 ];
108 push = [
109 "git"
110 "push"
111 ];
112 gf = [
113 "git"
114 "fetch"
115 ]; # git fetchq
116 gi = [
117 "git"
118 "init"
119 "--colocate"
120 ];
121 gp = [
122 "git"
123 "push"
124 ]; # git push
125 i = [
126 "git"
127 "init"
128 "--colocate"
129 ];
130 nb = [
131 "bookmark"
132 "create"
133 "-r @-"
134 ]; # new bookmark (for creating bookmark to push)
135 nc = [
136 "new"
137 "-B"
138 "@"
139 "--no-edit"
140 ]; # new change before
141 r = [ "rebase" ];
142 rm = [
143 "rebase"
144 "-d"
145 "main"
146 ]; # "re-main"
147 s = [
148 "show"
149 "--stat"
150 ];
151 ss = [ "show" ];
152 sq = [ "squash" ];
153 sqi = [
154 "squash"
155 "--interactive"
156 ];
157 si = [
158 "squash"
159 "--interactive"
160 ];
161 sp = [
162 "show"
163 "@-"
164 ];
165 up = [
166 "util"
167 "exec"
168 "--"
169 "sh"
170 "-c"
171 ''
172 if [ $# == 0 ]; then
173 jj bookmark move --from "closest_bookmark(@)" --to "closest_pushable(@)"
174 else
175 jj bookmark move --to "closest_pushable(@)" "$@"
176 fi
177 ''
178 ""
179 ];
180 upp = [
181 "util"
182 "exec"
183 "--"
184 "sh"
185 "-c"
186 "jj up && jj git push"
187 ""
188 ];
189 track-github-PR = [
190 "util"
191 "exec"
192 "--"
193 "sh"
194 "-euxc"
195 ''
196 PR=$1
197 gh pr view --json headRepository,headRefName,headRepositoryOwner $PR --jq '"
198 set_remote() {
199 jj 2>/dev/null git remote add \"$1\" \"$2\" ||
200 jj git remote set-url \"$1\" \"$2\"
201 }
202 set_remote \(.headRepositoryOwner.login) git@github.com:\(.headRepositoryOwner.login)/\(.headRepository.name)
203 jj git fetch --remote \(.headRepositoryOwner.login) --branch \(.headRefName)
204 jj bookmark track --remote=\(.headRepositoryOwner.login) \(.headRefName)
205 origin=$(gh repo view --json owner,name --jq .owner.login)
206 set_remote \"$origin\" \"$(gh repo view --json sshUrl --jq .sshUrl)\"
207 git fetch --force --update-head-ok \"$origin\" \"refs/pull/'"$PR"'/head:refs/remotes/$origin/pull/'"$PR"'\"
208 "' |
209 sh -euxs
210 ''
211 "jj-track-github-PR"
212 ];
213 untrack-github-PR = [
214 "util"
215 "exec"
216 "--"
217 "sh"
218 "-euxc"
219 ''
220 PR=$1
221 gh pr view --json headRepository,headRefName,headRepositoryOwner $PR --jq '"
222 jj bookmark forget \(.headRefName) || true
223 origin=$(gh repo view --json owner,name --jq .owner.login)
224 git for-each-ref --format \"delete %(refname)\" \\
225 \"refs/remotes/\(.headRepositoryOwner.login)/\(.headRefName)\" \\
226 \"refs/remotes/$origin/pull/'"$PR"'\" |
227 tee /dev/stderr |
228 git update-ref --stdin
229 "' |
230 sh -euxs
231 ''
232 "jj-untrack-github-PR"
233 ];
234 # Get all open stacks of work.
235 open = [
236 "log"
237 "-r"
238 "open()"
239 ];
240 # Better name IMO.
241 credit = [
242 "file"
243 "annotate"
244 ];
245 # Retrunk a series. Typically used as `jj retrunk -s ...` and notably can be
246 # used with open:
247 # - jj retrunk -s 'all:roots(open())'
248 retrunk = [
249 "rebase"
250 "-d"
251 "trunk()"
252 ];
253 # Retrunk the current stack of work.
254 reheat = [
255 "rebase"
256 "-d"
257 "trunk()"
258 "-s"
259 "all:roots(trunk()..stack(@))"
260 ];
261 # Take content from any change and move it into @.
262 # - jj consume xyz path/to/file`
263 consume = [
264 "squash"
265 "--into"
266 "@"
267 "--from"
268 ];
269 # Eject content from @ into any other change.
270 # - jj eject xyz --interactive
271 eject = [
272 "squash"
273 "--from"
274 "@"
275 "--into"
276 ];
277 # All operations
278 o = [
279 "op"
280 "log"
281 ];
282 # All operations, with more whitespace
283 oo = [
284 "op"
285 "log"
286 "-T"
287 "builtin_op_log_comfortable"
288 ];
289
290 abandon-empties = [
291 "abandon"
292 "-r"
293 "description(exact:'') ~ root()"
294 ];
295 # Move $commit just after the bookmark, and then move $bookmark on top of it.
296 #
297 # "$bookmark+" works, because after rebase -A, the only child of $bookmark is $commit.
298 # I do not use $commit itself because it might point to a
299 # different commit after the rebase (e.g. @-).
300 #
301 # My workflow uses mega-merge: I write a commit or two,
302 # and then move them to relevant branches with jj to.
303 # As a bonus, bookmarks are kept up-to-date for easy pushing.
304 # https://github.com/jj-vcs/jj/discussions/5568#discussioncomment-14289564
305 to = [
306 "util"
307 "exec"
308 "--"
309 "sh"
310 "-c"
311 ''
312 set -eux
313 bookmark=$1
314 commit=''${2-}
315 # default to latest non-ephemeral commit
316 if [ -z "$commit" ]; then
317 commit=$(jj log --no-graph -T change_id -r '@ ~ ephemeral')
318 if [ -n "$commit" ]; then
319 # Moving current commit, create a new one to preserve
320 # current position in the commit DAG
321 jj new
322 else
323 commit=@-
324 fi
325 fi
326 jj rebase -r "$commit" -A "$bookmark"
327 jj bookmark move -f "$bookmark" -t "$bookmark+"
328 ''
329 "jj-to"
330 ];
331 };
332
333 revsets = {
334 log = "default()";
335 };
336
337 revset-aliases = {
338 # Focus current commit, trunk(), on local work and remote work by removing ancestors of trunk()
339 "default()" =
340 "@ | trunk() | ancestors_and_children(bookmarks() | tracked_remote_bookmarks()) ~ ..trunk()";
341
342 "active(rev)" = "(ancestors(rev) | descendants(rev)) ~ immutable()";
343 "ancestors_and_children(x)" = "..x | x::";
344
345 # Remote commits not merged in trunk()
346 "review()" = "ancestors_and_children(@) ~ ..trunk()";
347 "review(x)" = "ancestors_and_children(remote_bookmarks(x)) ~ ..trunk()";
348 "reviews()" = "ancestors_and_children(remote_bookmarks()) ~ ..trunk()";
349
350 #
351 "wip()" = "ancestors_and_children(bookmarks()) ~ ..(remote_bookmarks() | trunk())";
352 "wip(x)" = "trunk() | ..bookmarks(x) ~ ..(remote_bookmarks() | trunk())";
353
354 # Authored or committed by specified user
355 "user(x)" = "author(x) | committer(x)";
356
357 "closest_tip()" = "heads(::@ & remote_bookmarks())";
358
359 # Those who would be abandoned if you check out something else
360 ephemeral = "(empty() ~ merges() ~ root()) & description(exact:\" \")";
361
362 # show everything in the current set of branches off of trunk():
363 branch = "(coalesce(trunk(),root())..@)- | (coalesce(trunk(),root())..@)::";
364
365 # radicle
366 "closest_bookmark(to)" = "heads(::to & bookmarks())";
367 "closest_pushable(to)" =
368 "heads(::to & mutable() & ~description(exact:\" \") & (~empty() | merges()))";
369
370 "desc(x)" = "description(x)";
371 "pending()" = ".. ~ ::tags() ~ ::remote_bookmarks() ~ @ ~ private()";
372 "private()" =
373 "description(glob:'wip:*') | description(glob:'private:*') | description(glob:'WIP:*') | description(glob:'PRIVATE:*') | conflicts() | (empty() ~ merges()) | description('substring-i:\"DO NOT MAIL\"')";
374 #"trunk()" = "main@rad";
375 #"immutable_heads()" = "tags()";
376 "immutable_heads()" =
377 "present(trunk()) | tags() | ( untracked_remote_bookmarks() ~ untracked_remote_bookmarks(remote=glob:'rad') ~ untracked_remote_bookmarks(regex:'^patch(es)/',remote=glob:'rad'))";
378 #'default()' = 'coalesce(trunk(), root())::present(@) | ancestors(visible_heads(), 2)'
379
380 # dev
381 "bases" = "present(main@rad) | present(master@origin) | present(main@origin)";
382 "downstream(x,y)" = "(x::y) & y";
383 "branches" = "downstream(trunk(), bookmarks()) & mine()";
384 "branchesandheads" = "branches | (heads(trunk()::) & mine())";
385 "curbranch" = "latest(branches::@- & branches)";
386 "nextbranch" = "roots(@:: & branchesandheads)";
387
388 "more" = "log | ancestors(visible_heads(), 2)";
389 "unmerged()" = "bookmarks() & ~(trunk():: | trunk())";
390 "recent()" = "committer_date(after:\"1 months ago\")";
391 "recent(revset)" = "revset & recent()";
392
393 # stack(x, n) is the set of mutable commits reachable from 'x', with 'n'
394 # parents. 'n' is often useful to customize the display and return set for
395 # certain operations. 'x' can be used to target the set of 'roots' to traverse,
396 # e.g. @ is the current stack.
397 "stack()" = "ancestors(reachable(@, mutable()), 2)";
398 "stack(x)" = "ancestors(reachable(x, mutable()), 2)";
399 "stack(x, n)" = "ancestors(reachable(x, mutable()), n)";
400
401 # The current set of "open" works. It is defined as:
402 #
403 # - given the set of commits not in trunk, that are written by me,
404 # - calculate the given stack() for each of those commits
405 #
406 # n = 1, meaning that nothing from `trunk()` is included, so all resulting
407 # commits are mutable by definition.
408 "open()" = "stack(trunk().. & mine(), 1)";
409
410 # the set of 'ready()' commits. defined as the set of open commits, but nothing
411 # that is blacklisted or any of their children.
412 #
413 # often used with gerrit, which you can use to submit whole stacks at once:
414 #
415 # - jj gerrit send -r 'ready()' --dry-run
416 "ready()" = "open() ~ blacklist()::";
417 };
418
419 template-aliases = {
420 # Hide unnecessary bits to make the jj log more concise
421 #"format_short_change_id(id)" = "id.shortest(4)"
422 #"format_short_commit_id(id)" = "id.shortest(4)"
423 "format_timestamp(timestamp)" = "timestamp.ago()";
424
425 #"format_short_signature(signature)" = "signature.email()"
426 # Both name and email address
427 #"format_short_signature(signature)" = "signature"
428 # Username part of the email address
429 #"format_short_signature(signature)" = "signature.email().local()"
430 "format_short_signature(signature)" = "signature.name()";
431 };
432
433 git = {
434 write-change-id-header = true;
435 # Prevent pushing work in progress or anything explicitly labeled "private";
436 private-commits = "description(glob:'wip:*') | description(glob:'private:*')";
437 # Don't require --allow-new when pushing a new bookmark
438 fetch = [
439 "origin"
440 "rad"
441 ];
442 push = "origin";
443 };
444
445 remotes = {
446 origin.auto-track-bookmarks = "glob:*";
447 ju1m.auto-track-bookmarks = "glob:*";
448 };
449
450 ui = {
451 paginate = "auto";
452 default-command = "log";
453 diff-editor = ":builtin";
454 # From https://github.com/julienvincent/hunk.nvim
455 #diff-editor = ["nvim", "-c", "DiffEditor $left $right $output"];
456 show-cryptographic-signatures = true;
457 };
458
459 merge-tools.diffconflicts = {
460 program = "nvim";
461 merge-args = [
462 "-c"
463 "let g:jj_diffconflicts_marker_length=$marker_length"
464 "-c"
465 "JJDiffConflicts!"
466 "$output"
467 "$base"
468 "$left"
469 "$right"
470 ];
471 merge-tool-edits-conflict-markers = true;
472 };
473
474 # # via @dubi steinkek in the jj discord
475 # [merge-tools.gitpatch];
476 # program = "sh"
477 # edit-args = ["-c", '''
478 # set -eu
479 # rm -f "$right/JJ-INSTRUCTIONS"
480 # git -C "$left" init -q
481 # git -C "$left" add -A
482 # git -C "$left" commit -q -m baseline --allow-empty
483 # mv "$left/.git" "$right"
484 # git -C "$right" add --intent-to-add --ignore-removal . # tell git to include new files in interactive patch mode
485 # git -C "$right" add -p
486 # git -C "$right" diff-index --quiet --cached HEAD && { echo "No changes done, aborting split."; exit 1; }
487 # git -C "$right" commit -q -m split
488 # git -C "$right" reset -q --hard # undo changes in modified files, remove added files
489 # ''',
490 # ];
491 # merge-args = ["-c", "echo gitpatch cannot be used as a diff tool"];
492 # diff-args = ["-c", "echo gitpatch cannot be used as a diff tool"];
493 };
494 };
495 home.packages = lib.mkIf config.programs.git.enable [
496 pkgs.watchman
497 pkgs-unstable.gg-jj
498 pkgs.jjui
499 pkgs-unstable.lazyjj
500 ];
501 xdg.configFile."jjui".source = jujutsu/jjui;
502 programs.bash = lib.mkIf config.programs.git.enable {
503 shellAliases = {
504 je = "jj-edit";
505 jer = "jj-edit-review";
506 };
507 initExtra = ''
508 jj-edit () { local revs=$1; shift; $EDITOR $(jj diff -r "$revs" --name-only "$@"); }
509 jj-edit-review () { local revs=$1; shift; $EDITOR $(jj diff -r "review($revs)" --name-only "$@"); }
510 '';
511 };
512 }