just playing with tangled
1// Copyright 2024 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::common::TestEnvironment;
16use crate::common::TestWorkDir;
17
18#[test]
19fn test_bookmark_names() {
20 let test_env = TestEnvironment::default();
21 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
22 let work_dir = test_env.work_dir("repo");
23
24 test_env.run_jj_in(".", ["git", "init", "origin"]).success();
25 let origin_dir = test_env.work_dir("origin");
26 let origin_git_repo_path = origin_dir
27 .root()
28 .join(".jj")
29 .join("repo")
30 .join("store")
31 .join("git");
32
33 work_dir
34 .run_jj(["bookmark", "create", "-r@", "aaa-local"])
35 .success();
36 work_dir
37 .run_jj(["bookmark", "create", "-r@", "bbb-local"])
38 .success();
39
40 // add various remote branches
41 work_dir
42 .run_jj([
43 "git",
44 "remote",
45 "add",
46 "origin",
47 origin_git_repo_path.to_str().unwrap(),
48 ])
49 .success();
50 work_dir
51 .run_jj(["bookmark", "create", "-r@", "aaa-tracked"])
52 .success();
53 work_dir
54 .run_jj(["desc", "-r", "aaa-tracked", "-m", "x"])
55 .success();
56 work_dir
57 .run_jj(["bookmark", "create", "-r@", "bbb-tracked"])
58 .success();
59 work_dir
60 .run_jj(["desc", "-r", "bbb-tracked", "-m", "x"])
61 .success();
62 work_dir
63 .run_jj(["git", "push", "--allow-new", "--bookmark", "glob:*-tracked"])
64 .success();
65
66 origin_dir
67 .run_jj(["bookmark", "create", "-r@", "aaa-untracked"])
68 .success();
69 origin_dir
70 .run_jj(["desc", "-r", "aaa-untracked", "-m", "x"])
71 .success();
72 origin_dir
73 .run_jj(["bookmark", "create", "-r@", "bbb-untracked"])
74 .success();
75 origin_dir
76 .run_jj(["desc", "-r", "bbb-untracked", "-m", "x"])
77 .success();
78 origin_dir.run_jj(["git", "export"]).success();
79 work_dir.run_jj(["git", "fetch"]).success();
80
81 let mut test_env = test_env;
82 // Every shell hook is a little different, e.g. the zsh hooks add some
83 // additional environment variables. But this is irrelevant for the purpose
84 // of testing our own logic, so it's fine to test a single shell only.
85 test_env.add_env_var("COMPLETE", "fish");
86 let test_env = test_env;
87 let work_dir = test_env.work_dir("repo");
88
89 let output = work_dir.run_jj(["--", "jj", "bookmark", "rename", ""]);
90 insta::assert_snapshot!(output, @r"
91 aaa-local x
92 aaa-tracked x
93 bbb-local x
94 bbb-tracked x
95 --repository Path to repository to operate on
96 --ignore-working-copy Don't snapshot the working copy, and don't update it
97 --ignore-immutable Allow rewriting immutable commits
98 --at-operation Operation to load the repo at
99 --debug Enable debug logging
100 --color When to colorize output
101 --quiet Silence non-primary command output
102 --no-pager Disable the pager
103 --config Additional configuration options (can be repeated)
104 --config-file Additional configuration files (can be repeated)
105 --help Print help (see more with '--help')
106 [EOF]
107 ");
108
109 let output = work_dir.run_jj(["--", "jj", "bookmark", "rename", "a"]);
110 insta::assert_snapshot!(output, @r"
111 aaa-local x
112 aaa-tracked x
113 [EOF]
114 ");
115
116 let output = work_dir.run_jj(["--", "jj", "bookmark", "delete", "a"]);
117 insta::assert_snapshot!(output, @r"
118 aaa-local x
119 aaa-tracked x
120 [EOF]
121 ");
122
123 let output = work_dir.run_jj(["--", "jj", "bookmark", "forget", "a"]);
124 insta::assert_snapshot!(output, @r"
125 aaa-local x
126 aaa-tracked x
127 aaa-untracked
128 [EOF]
129 ");
130
131 let output = work_dir.run_jj(["--", "jj", "bookmark", "list", "--bookmark", "a"]);
132 insta::assert_snapshot!(output, @r"
133 aaa-local x
134 aaa-tracked x
135 aaa-untracked
136 [EOF]
137 ");
138
139 let output = work_dir.run_jj(["--", "jj", "bookmark", "move", "a"]);
140 insta::assert_snapshot!(output, @r"
141 aaa-local x
142 aaa-tracked x
143 [EOF]
144 ");
145
146 let output = work_dir.run_jj(["--", "jj", "bookmark", "set", "a"]);
147 insta::assert_snapshot!(output, @r"
148 aaa-local x
149 aaa-tracked x
150 [EOF]
151 ");
152
153 let output = work_dir.run_jj(["--", "jj", "bookmark", "track", "a"]);
154 insta::assert_snapshot!(output, @r"
155 aaa-untracked@origin x
156 [EOF]
157 ");
158
159 let output = work_dir.run_jj(["--", "jj", "bookmark", "untrack", "a"]);
160 insta::assert_snapshot!(output, @r"
161 aaa-tracked@origin x
162 [EOF]
163 ");
164
165 let output = work_dir.run_jj(["--", "jj", "git", "push", "-b", "a"]);
166 insta::assert_snapshot!(output, @r"
167 aaa-local x
168 aaa-tracked x
169 [EOF]
170 ");
171
172 let output = work_dir.run_jj(["--", "jj", "git", "fetch", "-b", "a"]);
173 insta::assert_snapshot!(output, @r"
174 aaa-local x
175 aaa-tracked x
176 aaa-untracked
177 [EOF]
178 ");
179}
180
181#[test]
182fn test_global_arg_repository_is_respected() {
183 let test_env = TestEnvironment::default();
184 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
185 let work_dir = test_env.work_dir("repo");
186
187 work_dir
188 .run_jj(["bookmark", "create", "-r@", "aaa"])
189 .success();
190
191 let mut test_env = test_env;
192 test_env.add_env_var("COMPLETE", "fish");
193 let test_env = test_env;
194
195 let output = test_env.run_jj_in(
196 ".",
197 [
198 "--",
199 "jj",
200 "--repository",
201 "repo",
202 "bookmark",
203 "rename",
204 "a",
205 ],
206 );
207 insta::assert_snapshot!(output, @r"
208 aaa (no description set)
209 [EOF]
210 ");
211}
212
213#[test]
214fn test_aliases_are_resolved() {
215 let test_env = TestEnvironment::default();
216 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
217 let work_dir = test_env.work_dir("repo");
218
219 work_dir
220 .run_jj(["bookmark", "create", "-r@", "aaa"])
221 .success();
222
223 // user config alias
224 test_env.add_config(r#"aliases.b = ["bookmark"]"#);
225 // repo config alias
226 work_dir
227 .run_jj(["config", "set", "--repo", "aliases.b2", "['bookmark']"])
228 .success();
229
230 let mut test_env = test_env;
231 test_env.add_env_var("COMPLETE", "fish");
232 let test_env = test_env;
233 let work_dir = test_env.work_dir("repo");
234
235 let output = work_dir.run_jj(["--", "jj", "b", "rename", "a"]);
236 insta::assert_snapshot!(output, @r"
237 aaa (no description set)
238 [EOF]
239 ");
240
241 let output = work_dir.run_jj(["--", "jj", "b2", "rename", "a"]);
242 insta::assert_snapshot!(output, @r"
243 aaa (no description set)
244 [EOF]
245 ");
246}
247
248#[test]
249fn test_completions_are_generated() {
250 let mut test_env = TestEnvironment::default();
251 test_env.add_env_var("COMPLETE", "fish");
252 let mut insta_settings = insta::Settings::clone_current();
253 insta_settings.add_filter(r"(--arguments) .*", "$1 .."); // omit path to jj binary
254 let _guard = insta_settings.bind_to_scope();
255
256 let output = test_env.run_jj_in(".", [""; 0]);
257 insta::assert_snapshot!(output, @r"
258 complete --keep-order --exclusive --command jj --arguments ..
259 [EOF]
260 ");
261 let output = test_env.run_jj_in(".", ["--"]);
262 insta::assert_snapshot!(output, @r"
263 complete --keep-order --exclusive --command jj --arguments ..
264 [EOF]
265 ");
266}
267
268#[test]
269fn test_zsh_completion() {
270 let mut test_env = TestEnvironment::default();
271 test_env.add_env_var("COMPLETE", "zsh");
272
273 // ["--", "jj"]
274 // ^^^^ index = 0
275 let complete_at = |index: usize, args: &[&str]| {
276 test_env.run_jj_with(|cmd| {
277 cmd.args(args)
278 .env("_CLAP_COMPLETE_INDEX", index.to_string())
279 })
280 };
281
282 // Command names should be suggested. If the default command were expanded,
283 // only "log" would be listed.
284 let output = complete_at(1, &["--", "jj"]);
285 insta::assert_snapshot!(
286 output.normalize_stdout_with(|s| s.split_inclusive('\n').take(2).collect()), @r"
287 abandon:Abandon a revision
288 absorb:Move changes from a revision into the stack of mutable revisions
289 [EOF]
290 ");
291 let output = complete_at(2, &["--", "jj", "--no-pager"]);
292 insta::assert_snapshot!(
293 output.normalize_stdout_with(|s| s.split_inclusive('\n').take(2).collect()), @r"
294 abandon:Abandon a revision
295 absorb:Move changes from a revision into the stack of mutable revisions
296 [EOF]
297 ");
298
299 let output = complete_at(1, &["--", "jj", "b"]);
300 insta::assert_snapshot!(output, @"bookmark:Manage bookmarks [default alias: b][EOF]");
301}
302
303#[test]
304fn test_remote_names() {
305 let mut test_env = TestEnvironment::default();
306 test_env.run_jj_in(".", ["git", "init"]).success();
307
308 test_env
309 .run_jj_in(
310 ".",
311 ["git", "remote", "add", "origin", "git@git.local:user/repo"],
312 )
313 .success();
314
315 test_env.add_env_var("COMPLETE", "fish");
316
317 let output = test_env.run_jj_in(".", ["--", "jj", "git", "remote", "remove", "o"]);
318 insta::assert_snapshot!(output, @r"
319 origin
320 [EOF]
321 ");
322
323 let output = test_env.run_jj_in(".", ["--", "jj", "git", "remote", "rename", "o"]);
324 insta::assert_snapshot!(output, @r"
325 origin
326 [EOF]
327 ");
328
329 let output = test_env.run_jj_in(".", ["--", "jj", "git", "remote", "set-url", "o"]);
330 insta::assert_snapshot!(output, @r"
331 origin
332 [EOF]
333 ");
334
335 let output = test_env.run_jj_in(".", ["--", "jj", "git", "push", "--remote", "o"]);
336 insta::assert_snapshot!(output, @r"
337 origin
338 [EOF]
339 ");
340
341 let output = test_env.run_jj_in(".", ["--", "jj", "git", "fetch", "--remote", "o"]);
342 insta::assert_snapshot!(output, @r"
343 origin
344 [EOF]
345 ");
346
347 let output = test_env.run_jj_in(".", ["--", "jj", "bookmark", "list", "--remote", "o"]);
348 insta::assert_snapshot!(output, @r"
349 origin
350 [EOF]
351 ");
352}
353
354#[test]
355fn test_aliases_are_completed() {
356 let test_env = TestEnvironment::default();
357 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
358 let work_dir = test_env.work_dir("repo");
359
360 // user config alias
361 test_env.add_config(r#"aliases.user-alias = ["bookmark"]"#);
362 // repo config alias
363 work_dir
364 .run_jj([
365 "config",
366 "set",
367 "--repo",
368 "aliases.repo-alias",
369 "['bookmark']",
370 ])
371 .success();
372
373 let mut test_env = test_env;
374 test_env.add_env_var("COMPLETE", "fish");
375 let test_env = test_env;
376 let work_dir = test_env.work_dir("repo");
377
378 let output = work_dir.run_jj(["--", "jj", "user-al"]);
379 insta::assert_snapshot!(output, @r"
380 user-alias
381 [EOF]
382 ");
383
384 // make sure --repository flag is respected
385 let output = test_env.run_jj_in(
386 ".",
387 [
388 "--",
389 "jj",
390 "--repository",
391 work_dir.root().to_str().unwrap(),
392 "repo-al",
393 ],
394 );
395 insta::assert_snapshot!(output, @r"
396 repo-alias
397 [EOF]
398 ");
399
400 // cannot load aliases from --config flag
401 let output = test_env.run_jj_in(
402 ".",
403 [
404 "--",
405 "jj",
406 "--config=aliases.cli-alias=['bookmark']",
407 "cli-al",
408 ],
409 );
410 insta::assert_snapshot!(output, @"");
411}
412
413#[test]
414fn test_revisions() {
415 let test_env = TestEnvironment::default();
416 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
417 let work_dir = test_env.work_dir("repo");
418
419 // create remote to test remote branches
420 test_env.run_jj_in(".", ["git", "init", "origin"]).success();
421 let origin_dir = test_env.work_dir("origin");
422 let origin_git_repo_path = origin_dir
423 .root()
424 .join(".jj")
425 .join("repo")
426 .join("store")
427 .join("git");
428 work_dir
429 .run_jj([
430 "git",
431 "remote",
432 "add",
433 "origin",
434 origin_git_repo_path.to_str().unwrap(),
435 ])
436 .success();
437 origin_dir
438 .run_jj(["b", "c", "-r@", "remote_bookmark"])
439 .success();
440 origin_dir
441 .run_jj(["commit", "-m", "remote_commit"])
442 .success();
443 origin_dir.run_jj(["git", "export"]).success();
444 work_dir.run_jj(["git", "fetch"]).success();
445
446 work_dir
447 .run_jj(["b", "c", "-r@", "immutable_bookmark"])
448 .success();
449 work_dir.run_jj(["commit", "-m", "immutable"]).success();
450 test_env.add_config(r#"revset-aliases."immutable_heads()" = "immutable_bookmark""#);
451 test_env.add_config(r#"revset-aliases."siblings" = "@-+ ~@""#);
452 test_env.add_config(
453 r#"revset-aliases."alias_with_newline" = '''
454 roots(
455 conflicts()
456 )
457 '''"#,
458 );
459
460 work_dir
461 .run_jj(["b", "c", "-r@", "mutable_bookmark"])
462 .success();
463 work_dir.run_jj(["commit", "-m", "mutable"]).success();
464
465 work_dir
466 .run_jj(["describe", "-m", "working_copy"])
467 .success();
468
469 let mut test_env = test_env;
470 test_env.add_env_var("COMPLETE", "fish");
471 let test_env = test_env;
472 let work_dir = test_env.work_dir("repo");
473
474 // There are _a lot_ of commands and arguments accepting revisions.
475 // Let's not test all of them. Having at least one test per variation of
476 // completion function should be sufficient.
477
478 // complete all revisions
479 let output = work_dir.run_jj(["--", "jj", "diff", "--from", ""]);
480 insta::assert_snapshot!(output, @r"
481 immutable_bookmark immutable
482 mutable_bookmark mutable
483 k working_copy
484 y mutable
485 q immutable
486 zq remote_commit
487 zz (no description set)
488 remote_bookmark@origin remote_commit
489 alias_with_newline roots(
490 siblings @-+ ~@
491 [EOF]
492 ");
493
494 // complete only mutable revisions
495 let output = work_dir.run_jj(["--", "jj", "squash", "--into", ""]);
496 insta::assert_snapshot!(output, @r"
497 mutable_bookmark mutable
498 k working_copy
499 y mutable
500 zq remote_commit
501 alias_with_newline roots(
502 siblings @-+ ~@
503 [EOF]
504 ");
505
506 // complete args of the default command
507 test_env.add_config("ui.default-command = 'log'");
508 let output = work_dir.run_jj(["--", "jj", "-r", ""]);
509 insta::assert_snapshot!(output, @r"
510 immutable_bookmark immutable
511 mutable_bookmark mutable
512 k working_copy
513 y mutable
514 q immutable
515 zq remote_commit
516 zz (no description set)
517 remote_bookmark@origin remote_commit
518 alias_with_newline roots(
519 siblings @-+ ~@
520 [EOF]
521 ");
522
523 // Begin testing `jj git push --named`
524
525 // The name of a bookmark does not get completed, since we want to create a new
526 // bookmark
527 let output = work_dir.run_jj(["--", "jj", "git", "push", "--named", ""]);
528 insta::assert_snapshot!(output, @"");
529 let output = work_dir.run_jj(["--", "jj", "git", "push", "--named", "a"]);
530 insta::assert_snapshot!(output, @"");
531
532 let output = work_dir.run_jj(["--", "jj", "git", "push", "--named", "a="]);
533 insta::assert_snapshot!(output, @r"
534 a=immutable_bookmark immutable
535 a=mutable_bookmark mutable
536 a=k working_copy
537 a=y mutable
538 a=q immutable
539 a=zq remote_commit
540 a=zz (no description set)
541 a=remote_bookmark@origin remote_commit
542 a=alias_with_newline roots(
543 a=siblings @-+ ~@
544 [EOF]
545 ");
546
547 let output = work_dir.run_jj(["--", "jj", "git", "push", "--named", "a=a"]);
548 insta::assert_snapshot!(output, @r"
549 a=alias_with_newline roots(
550 [EOF]
551 ");
552}
553
554#[test]
555fn test_operations() {
556 let test_env = TestEnvironment::default();
557
558 // suppress warnings on stderr of completions for invalid args
559 test_env.add_config("ui.default-command = 'log'");
560
561 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
562 let work_dir = test_env.work_dir("repo");
563 work_dir
564 .run_jj(["describe", "-m", "description 0"])
565 .success();
566 work_dir
567 .run_jj(["describe", "-m", "description 1"])
568 .success();
569 work_dir
570 .run_jj(["describe", "-m", "description 2"])
571 .success();
572 work_dir
573 .run_jj(["describe", "-m", "description 3"])
574 .success();
575 work_dir
576 .run_jj(["describe", "-m", "description 4"])
577 .success();
578
579 let mut test_env = test_env;
580 test_env.add_env_var("COMPLETE", "fish");
581 let test_env = test_env;
582 let work_dir = test_env.work_dir("repo");
583
584 let output = work_dir.run_jj(["--", "jj", "op", "show", ""]).success();
585 let add_workspace_id = output
586 .stdout
587 .raw()
588 .lines()
589 .nth(5)
590 .unwrap()
591 .split('\t')
592 .next()
593 .unwrap();
594 insta::assert_snapshot!(add_workspace_id, @"eac759b9ab75");
595
596 let output = work_dir.run_jj(["--", "jj", "op", "show", "5"]);
597 insta::assert_snapshot!(output, @r"
598 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
599 518b588abbc6 (2001-02-03 08:05:09) describe commit 19611c995a342c01f525583e5fcafdd211f6d009
600 [EOF]
601 ");
602 // make sure global --at-op flag is respected
603 let output = work_dir.run_jj(["--", "jj", "--at-op", "518b588abbc6", "op", "show", "5"]);
604 insta::assert_snapshot!(output, @r"
605 518b588abbc6 (2001-02-03 08:05:09) describe commit 19611c995a342c01f525583e5fcafdd211f6d009
606 [EOF]
607 ");
608
609 let output = work_dir.run_jj(["--", "jj", "--at-op", "5b"]);
610 insta::assert_snapshot!(output, @r"
611 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
612 [EOF]
613 ");
614
615 let output = work_dir.run_jj(["--", "jj", "op", "abandon", "5b"]);
616 insta::assert_snapshot!(output, @r"
617 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
618 [EOF]
619 ");
620
621 let output = work_dir.run_jj(["--", "jj", "op", "diff", "--op", "5b"]);
622 insta::assert_snapshot!(output, @r"
623 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
624 [EOF]
625 ");
626 let output = work_dir.run_jj(["--", "jj", "op", "diff", "--from", "5b"]);
627 insta::assert_snapshot!(output, @r"
628 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
629 [EOF]
630 ");
631 let output = work_dir.run_jj(["--", "jj", "op", "diff", "--to", "5b"]);
632 insta::assert_snapshot!(output, @r"
633 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
634 [EOF]
635 ");
636
637 let output = work_dir.run_jj(["--", "jj", "op", "restore", "5b"]);
638 insta::assert_snapshot!(output, @r"
639 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
640 [EOF]
641 ");
642
643 let output = work_dir.run_jj(["--", "jj", "op", "undo", "5b"]);
644 insta::assert_snapshot!(output, @r"
645 5bbb4ca536a8 (2001-02-03 08:05:12) describe commit 968261075dddabf4b0e333c1cc9a49ce26a3f710
646 [EOF]
647 ");
648}
649
650#[test]
651fn test_workspaces() {
652 let test_env = TestEnvironment::default();
653 test_env.run_jj_in(".", ["git", "init", "main"]).success();
654 let main_dir = test_env.work_dir("main");
655
656 main_dir.write_file("file", "contents");
657 main_dir.run_jj(["describe", "-m", "initial"]).success();
658
659 // same prefix as "default" workspace
660 main_dir
661 .run_jj(["workspace", "add", "--name", "def-second", "../secondary"])
662 .success();
663
664 let mut test_env = test_env;
665 test_env.add_env_var("COMPLETE", "fish");
666 let test_env = test_env;
667 let main_dir = test_env.work_dir("main");
668
669 let output = main_dir.run_jj(["--", "jj", "workspace", "forget", "def"]);
670 insta::assert_snapshot!(output, @r"
671 def-second (no description set)
672 default initial
673 [EOF]
674 ");
675}
676
677#[test]
678fn test_config() {
679 let mut test_env = TestEnvironment::default();
680 test_env.add_env_var("COMPLETE", "fish");
681 let dir = test_env.env_root();
682
683 let output = test_env.run_jj_in(dir, ["--", "jj", "config", "get", "c"]);
684 insta::assert_snapshot!(output, @r"
685 core.fsmonitor Whether to use an external filesystem monitor, useful for large repos
686 core.watchman.register-snapshot-trigger Whether to use triggers to monitor for changes in the background.
687 [EOF]
688 ");
689
690 let output = test_env.run_jj_in(dir, ["--", "jj", "config", "list", "c"]);
691 insta::assert_snapshot!(output, @r"
692 colors Mapping from jj formatter labels to colors
693 core
694 core.fsmonitor Whether to use an external filesystem monitor, useful for large repos
695 core.watchman
696 core.watchman.register-snapshot-trigger Whether to use triggers to monitor for changes in the background.
697 [EOF]
698 ");
699
700 let output = test_env.run_jj_in(dir, ["--", "jj", "log", "--config", "c"]);
701 insta::assert_snapshot!(output, @r"
702 core.fsmonitor= Whether to use an external filesystem monitor, useful for large repos
703 core.watchman.register-snapshot-trigger= Whether to use triggers to monitor for changes in the background.
704 [EOF]
705 ");
706
707 let output = test_env.run_jj_in(
708 dir,
709 ["--", "jj", "log", "--config", "ui.conflict-marker-style="],
710 );
711 insta::assert_snapshot!(output, @r"
712 ui.conflict-marker-style=diff
713 ui.conflict-marker-style=snapshot
714 ui.conflict-marker-style=git
715 [EOF]
716 ");
717 let output = test_env.run_jj_in(
718 dir,
719 ["--", "jj", "log", "--config", "ui.conflict-marker-style=g"],
720 );
721 insta::assert_snapshot!(output, @r"
722 ui.conflict-marker-style=git
723 [EOF]
724 ");
725
726 let output = test_env.run_jj_in(
727 dir,
728 [
729 "--",
730 "jj",
731 "log",
732 "--config",
733 "git.abandon-unreachable-commits=",
734 ],
735 );
736 insta::assert_snapshot!(output, @r"
737 git.abandon-unreachable-commits=false
738 git.abandon-unreachable-commits=true
739 [EOF]
740 ");
741}
742
743#[test]
744fn test_template_alias() {
745 let mut test_env = TestEnvironment::default();
746 test_env.add_env_var("COMPLETE", "fish");
747 let dir = test_env.env_root();
748
749 let output = test_env.run_jj_in(dir, ["--", "jj", "log", "-T", ""]);
750 insta::assert_snapshot!(output, @r"
751 builtin_config_list
752 builtin_config_list_detailed
753 builtin_draft_commit_description
754 builtin_log_comfortable
755 builtin_log_compact
756 builtin_log_compact_full_description
757 builtin_log_detailed
758 builtin_log_node
759 builtin_log_node_ascii
760 builtin_log_oneline
761 builtin_op_log_comfortable
762 builtin_op_log_compact
763 builtin_op_log_node
764 builtin_op_log_node_ascii
765 builtin_op_log_oneline
766 commit_summary_separator
767 description_placeholder
768 email_placeholder
769 name_placeholder
770 [EOF]
771 ");
772}
773
774fn create_commit(
775 work_dir: &TestWorkDir,
776 name: &str,
777 parents: &[&str],
778 files: &[(&str, Option<&str>)],
779) {
780 let parents = match parents {
781 [] => &["root()"],
782 parents => parents,
783 };
784 work_dir
785 .run_jj_with(|cmd| cmd.args(["new", "-m", name]).args(parents))
786 .success();
787 for (name, content) in files {
788 if let Some((dir, _)) = name.rsplit_once('/') {
789 work_dir.create_dir_all(dir);
790 }
791 match content {
792 Some(content) => work_dir.write_file(name, content),
793 None => work_dir.remove_file(name),
794 }
795 }
796 work_dir
797 .run_jj(["bookmark", "create", "-r@", name])
798 .success();
799}
800
801#[test]
802fn test_files() {
803 let test_env = TestEnvironment::default();
804 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
805 let work_dir = test_env.work_dir("repo");
806
807 create_commit(
808 &work_dir,
809 "first",
810 &[],
811 &[
812 ("f_unchanged", Some("unchanged\n")),
813 ("f_modified", Some("not_yet_modified\n")),
814 ("f_not_yet_renamed", Some("renamed\n")),
815 ("f_deleted", Some("not_yet_deleted\n")),
816 // not yet: "added" file
817 ],
818 );
819 create_commit(
820 &work_dir,
821 "second",
822 &["first"],
823 &[
824 // "unchanged" file
825 ("f_modified", Some("modified\n")),
826 ("f_not_yet_renamed", None),
827 ("f_renamed", Some("renamed\n")),
828 ("f_deleted", None),
829 ("f_added", Some("added\n")),
830 ("f_dir/dir_file_1", Some("foo\n")),
831 ("f_dir/dir_file_2", Some("foo\n")),
832 ("f_dir/dir_file_3", Some("foo\n")),
833 ],
834 );
835
836 // create a conflicted commit to check the completions of `jj restore`
837 create_commit(
838 &work_dir,
839 "conflicted",
840 &["second"],
841 &[
842 ("f_modified", Some("modified_again\n")),
843 ("f_added_2", Some("added_2\n")),
844 ("f_dir/dir_file_1", Some("bar\n")),
845 ("f_dir/dir_file_2", Some("bar\n")),
846 ("f_dir/dir_file_3", Some("bar\n")),
847 ],
848 );
849 work_dir.run_jj(["rebase", "-r=@", "-d=first"]).success();
850
851 // two commits that are similar but not identical, for `jj interdiff`
852 create_commit(
853 &work_dir,
854 "interdiff_from",
855 &[],
856 &[
857 ("f_interdiff_same", Some("same in both commits\n")),
858 (("f_interdiff_only_from"), Some("only from\n")),
859 ],
860 );
861 create_commit(
862 &work_dir,
863 "interdiff_to",
864 &[],
865 &[
866 ("f_interdiff_same", Some("same in both commits\n")),
867 (("f_interdiff_only_to"), Some("only to\n")),
868 ],
869 );
870
871 // "dirty worktree"
872 create_commit(
873 &work_dir,
874 "working_copy",
875 &["second"],
876 &[
877 ("f_modified", Some("modified_again\n")),
878 ("f_added_2", Some("added_2\n")),
879 ],
880 );
881
882 let output = work_dir.run_jj(["log", "-r", "all()", "--summary"]);
883 insta::assert_snapshot!(output.normalize_backslash(), @r"
884 @ wqnwkozp test.user@example.com 2001-02-03 08:05:20 working_copy cb594eba
885 │ working_copy
886 │ A f_added_2
887 │ M f_modified
888 ○ zsuskuln test.user@example.com 2001-02-03 08:05:11 second 24242473
889 │ second
890 │ A f_added
891 │ D f_deleted
892 │ A f_dir/dir_file_1
893 │ A f_dir/dir_file_2
894 │ A f_dir/dir_file_3
895 │ M f_modified
896 │ R {f_not_yet_renamed => f_renamed}
897 │ × royxmykx test.user@example.com 2001-02-03 08:05:14 conflicted 0ba6786b conflict
898 ├─╯ conflicted
899 │ A f_added_2
900 │ A f_dir/dir_file_1
901 │ A f_dir/dir_file_2
902 │ A f_dir/dir_file_3
903 │ M f_modified
904 ○ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 first 2a2f433c
905 │ first
906 │ A f_deleted
907 │ A f_modified
908 │ A f_not_yet_renamed
909 │ A f_unchanged
910 │ ○ kpqxywon test.user@example.com 2001-02-03 08:05:18 interdiff_to 302c4041
911 ├─╯ interdiff_to
912 │ A f_interdiff_only_to
913 │ A f_interdiff_same
914 │ ○ yostqsxw test.user@example.com 2001-02-03 08:05:16 interdiff_from 083d1cc6
915 ├─╯ interdiff_from
916 │ A f_interdiff_only_from
917 │ A f_interdiff_same
918 ◆ zzzzzzzz root() 00000000
919 [EOF]
920 ");
921
922 let mut test_env = test_env;
923 test_env.add_env_var("COMPLETE", "fish");
924 let test_env = test_env;
925 let work_dir = test_env.work_dir("repo");
926
927 let output = work_dir.run_jj(["--", "jj", "file", "show", "f_"]);
928 insta::assert_snapshot!(output.normalize_backslash(), @r"
929 f_added
930 f_added_2
931 f_dir/
932 f_modified
933 f_renamed
934 f_unchanged
935 [EOF]
936 ");
937
938 let output = work_dir.run_jj(["--", "jj", "file", "annotate", "-r@-", "f_"]);
939 insta::assert_snapshot!(output.normalize_backslash(), @r"
940 f_added
941 f_dir/
942 f_modified
943 f_renamed
944 f_unchanged
945 [EOF]
946 ");
947 let output = work_dir.run_jj(["--", "jj", "diff", "-r", "@-", "f_"]);
948 insta::assert_snapshot!(output.normalize_backslash(), @r"
949 f_added Added
950 f_deleted Deleted
951 f_dir/
952 f_modified Modified
953 f_not_yet_renamed Renamed
954 f_renamed Renamed
955 [EOF]
956 ");
957
958 let output = work_dir.run_jj([
959 "--",
960 "jj",
961 "diff",
962 "-r",
963 "@-",
964 &format!("f_dir{}", std::path::MAIN_SEPARATOR),
965 ]);
966 insta::assert_snapshot!(output.normalize_backslash(), @r"
967 f_dir/dir_file_1 Added
968 f_dir/dir_file_2 Added
969 f_dir/dir_file_3 Added
970 [EOF]
971 ");
972
973 let output = work_dir.run_jj(["--", "jj", "diff", "--from", "root()", "--to", "@-", "f_"]);
974 insta::assert_snapshot!(output.normalize_backslash(), @r"
975 f_added Added
976 f_dir/
977 f_modified Added
978 f_renamed Added
979 f_unchanged Added
980 [EOF]
981 ");
982
983 // interdiff has a different behavior with --from and --to flags
984 let output = work_dir.run_jj([
985 "--",
986 "jj",
987 "interdiff",
988 "--to=interdiff_to",
989 "--from=interdiff_from",
990 "f_",
991 ]);
992 insta::assert_snapshot!(output.normalize_backslash(), @r"
993 f_interdiff_only_from Added
994 f_interdiff_same Added
995 f_interdiff_only_to Added
996 f_interdiff_same Added
997 [EOF]
998 ");
999
1000 // squash has a different behavior with --from and --to flags
1001 let output = work_dir.run_jj(["--", "jj", "squash", "-f=first", "f_"]);
1002 insta::assert_snapshot!(output.normalize_backslash(), @r"
1003 f_deleted Added
1004 f_modified Added
1005 f_not_yet_renamed Added
1006 f_unchanged Added
1007 [EOF]
1008 ");
1009
1010 let output = work_dir.run_jj(["--", "jj", "resolve", "-r=conflicted", "f_"]);
1011 insta::assert_snapshot!(output.normalize_backslash(), @r"
1012 f_dir/
1013 f_modified
1014 [EOF]
1015 ");
1016
1017 let output = work_dir.run_jj(["--", "jj", "log", "f_"]);
1018 insta::assert_snapshot!(output.normalize_backslash(), @r"
1019 f_added
1020 f_added_2
1021 f_dir/
1022 f_modified
1023 f_renamed
1024 f_unchanged
1025 [EOF]
1026 ");
1027 let output = work_dir.run_jj([
1028 "--",
1029 "jj",
1030 "log",
1031 "-r=first",
1032 "--revisions",
1033 "conflicted",
1034 "f_",
1035 ]);
1036 insta::assert_snapshot!(output.normalize_backslash(), @r"
1037 f_added_2
1038 f_deleted
1039 f_dir/
1040 f_modified
1041 f_not_yet_renamed
1042 f_unchanged
1043 [EOF]
1044 ");
1045
1046 let outside_repo = test_env.env_root();
1047 let output = test_env.run_jj_in(outside_repo, ["--", "jj", "log", "f_"]);
1048 insta::assert_snapshot!(output, @"");
1049}