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