just playing with tangled
at main 1675 lines 49 kB view raw
1// Copyright 2022 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::to_toml_value; 16use crate::common::TestEnvironment; 17 18#[test] 19fn test_log_with_empty_revision() { 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 let output = work_dir.run_jj(["log", "-r="]); 25 insta::assert_snapshot!(output, @r" 26 ------- stderr ------- 27 error: a value is required for '--revisions <REVSETS>' but none was supplied 28 29 For more information, try '--help'. 30 [EOF] 31 [exit status: 2] 32 "); 33} 34 35#[test] 36fn test_log_with_no_template() { 37 let test_env = TestEnvironment::default(); 38 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 39 let work_dir = test_env.work_dir("repo"); 40 41 let output = work_dir.run_jj(["log", "-T"]); 42 insta::assert_snapshot!(output, @r" 43 ------- stderr ------- 44 error: a value is required for '--template <TEMPLATE>' but none was supplied 45 46 For more information, try '--help'. 47 Hint: The following template aliases are defined: 48 - builtin_config_list 49 - builtin_config_list_detailed 50 - builtin_draft_commit_description 51 - builtin_log_comfortable 52 - builtin_log_compact 53 - builtin_log_compact_full_description 54 - builtin_log_detailed 55 - builtin_log_node 56 - builtin_log_node_ascii 57 - builtin_log_oneline 58 - builtin_op_log_comfortable 59 - builtin_op_log_compact 60 - builtin_op_log_node 61 - builtin_op_log_node_ascii 62 - builtin_op_log_oneline 63 - commit_summary_separator 64 - default_commit_description 65 - description_placeholder 66 - email_placeholder 67 - git_format_patch_email_headers 68 - name_placeholder 69 [EOF] 70 [exit status: 2] 71 "); 72} 73 74#[test] 75fn test_log_with_or_without_diff() { 76 let test_env = TestEnvironment::default(); 77 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 78 let work_dir = test_env.work_dir("repo"); 79 80 work_dir.write_file("file1", "foo\n"); 81 work_dir.run_jj(["describe", "-m", "add a file"]).success(); 82 work_dir.run_jj(["new", "-m", "a new commit"]).success(); 83 work_dir.write_file("file1", "foo\nbar\n"); 84 85 let output = work_dir.run_jj(["log", "-T", "description"]); 86 insta::assert_snapshot!(output, @r" 87 @ a new commit 88 ○ add a file 89 90 [EOF] 91 "); 92 93 let output = work_dir.run_jj(["log", "-T", "description", "-p"]); 94 insta::assert_snapshot!(output, @r" 95 @ a new commit 96 │ Modified regular file file1: 97 │ 1 1: foo 98 │ 2: bar 99 ○ add a file 100 │ Added regular file file1: 101 │ 1: foo 102 103 [EOF] 104 "); 105 106 let output = work_dir.run_jj(["log", "-T", "description", "--no-graph"]); 107 insta::assert_snapshot!(output, @r" 108 a new commit 109 add a file 110 [EOF] 111 "); 112 113 // `-p` for default diff output, `-s` for summary 114 let output = work_dir.run_jj(["log", "-T", "description", "-p", "-s"]); 115 insta::assert_snapshot!(output, @r" 116 @ a new commit 117 │ M file1 118 │ Modified regular file file1: 119 │ 1 1: foo 120 │ 2: bar 121 ○ add a file 122 │ A file1 123 │ Added regular file file1: 124 │ 1: foo 125 126 [EOF] 127 "); 128 129 // `-s` for summary, `--git` for git diff (which implies `-p`) 130 let output = work_dir.run_jj(["log", "-T", "description", "-s", "--git"]); 131 insta::assert_snapshot!(output, @r" 132 @ a new commit 133 │ M file1 134 │ diff --git a/file1 b/file1 135 │ index 257cc5642c..3bd1f0e297 100644 136 │ --- a/file1 137 │ +++ b/file1 138 │ @@ -1,1 +1,2 @@ 139 │ foo 140 │ +bar 141 ○ add a file 142 │ A file1 143 │ diff --git a/file1 b/file1 144 │ new file mode 100644 145 │ index 0000000000..257cc5642c 146 │ --- /dev/null 147 │ +++ b/file1 148 │ @@ -0,0 +1,1 @@ 149 │ +foo 150 151 [EOF] 152 "); 153 154 // `-p` for default diff output, `--stat` for diff-stat 155 let output = work_dir.run_jj(["log", "-T", "description", "-p", "--stat"]); 156 insta::assert_snapshot!(output, @r" 157 @ a new commit 158 │ file1 | 1 + 159 │ 1 file changed, 1 insertion(+), 0 deletions(-) 160 │ Modified regular file file1: 161 │ 1 1: foo 162 │ 2: bar 163 ○ add a file 164 │ file1 | 1 + 165 │ 1 file changed, 1 insertion(+), 0 deletions(-) 166 │ Added regular file file1: 167 │ 1: foo 168 169 0 files changed, 0 insertions(+), 0 deletions(-) 170 [EOF] 171 "); 172 173 // `--stat` is short format, which should be printed first 174 let output = work_dir.run_jj(["log", "-T", "description", "--git", "--stat"]); 175 insta::assert_snapshot!(output, @r" 176 @ a new commit 177 │ file1 | 1 + 178 │ 1 file changed, 1 insertion(+), 0 deletions(-) 179 │ diff --git a/file1 b/file1 180 │ index 257cc5642c..3bd1f0e297 100644 181 │ --- a/file1 182 │ +++ b/file1 183 │ @@ -1,1 +1,2 @@ 184 │ foo 185 │ +bar 186 ○ add a file 187 │ file1 | 1 + 188 │ 1 file changed, 1 insertion(+), 0 deletions(-) 189 │ diff --git a/file1 b/file1 190 │ new file mode 100644 191 │ index 0000000000..257cc5642c 192 │ --- /dev/null 193 │ +++ b/file1 194 │ @@ -0,0 +1,1 @@ 195 │ +foo 196 197 0 files changed, 0 insertions(+), 0 deletions(-) 198 [EOF] 199 "); 200 201 // `-p` enables default "summary" output, so `-s` is noop 202 let output = work_dir.run_jj([ 203 "log", 204 "-T", 205 "description", 206 "-p", 207 "-s", 208 "--config=ui.diff-formatter=:summary", 209 ]); 210 insta::assert_snapshot!(output, @r" 211 @ a new commit 212 │ M file1 213 ○ add a file 214 │ A file1 215 216 [EOF] 217 "); 218 219 // `-p` enables default "color-words" diff output, so `--color-words` is noop 220 let output = work_dir.run_jj(["log", "-T", "description", "-p", "--color-words"]); 221 insta::assert_snapshot!(output, @r" 222 @ a new commit 223 │ Modified regular file file1: 224 │ 1 1: foo 225 │ 2: bar 226 ○ add a file 227 │ Added regular file file1: 228 │ 1: foo 229 230 [EOF] 231 "); 232 233 // `--git` enables git diff, so `-p` is noop 234 let output = work_dir.run_jj(["log", "-T", "description", "--no-graph", "-p", "--git"]); 235 insta::assert_snapshot!(output, @r" 236 a new commit 237 diff --git a/file1 b/file1 238 index 257cc5642c..3bd1f0e297 100644 239 --- a/file1 240 +++ b/file1 241 @@ -1,1 +1,2 @@ 242 foo 243 +bar 244 add a file 245 diff --git a/file1 b/file1 246 new file mode 100644 247 index 0000000000..257cc5642c 248 --- /dev/null 249 +++ b/file1 250 @@ -0,0 +1,1 @@ 251 +foo 252 [EOF] 253 "); 254 255 // Cannot use both `--git` and `--color-words` 256 let output = work_dir.run_jj([ 257 "log", 258 "-T", 259 "description", 260 "--no-graph", 261 "-p", 262 "--git", 263 "--color-words", 264 ]); 265 insta::assert_snapshot!(output, @r" 266 ------- stderr ------- 267 error: the argument '--git' cannot be used with '--color-words' 268 269 Usage: jj log --template <TEMPLATE> --no-graph --patch --git [FILESETS]... 270 271 For more information, try '--help'. 272 [EOF] 273 [exit status: 2] 274 "); 275 276 // `-s` with or without graph 277 let output = work_dir.run_jj(["log", "-T", "description", "-s"]); 278 insta::assert_snapshot!(output, @r" 279 @ a new commit 280 │ M file1 281 ○ add a file 282 │ A file1 283 284 [EOF] 285 "); 286 let output = work_dir.run_jj(["log", "-T", "description", "--no-graph", "-s"]); 287 insta::assert_snapshot!(output, @r" 288 a new commit 289 M file1 290 add a file 291 A file1 292 [EOF] 293 "); 294 295 // `--git` implies `-p`, with or without graph 296 let output = work_dir.run_jj(["log", "-T", "description", "-r", "@", "--git"]); 297 insta::assert_snapshot!(output, @r" 298 @ a new commit 299 │ diff --git a/file1 b/file1 300 ~ index 257cc5642c..3bd1f0e297 100644 301 --- a/file1 302 +++ b/file1 303 @@ -1,1 +1,2 @@ 304 foo 305 +bar 306 [EOF] 307 "); 308 let output = work_dir.run_jj(["log", "-T", "description", "-r", "@", "--no-graph", "--git"]); 309 insta::assert_snapshot!(output, @r" 310 a new commit 311 diff --git a/file1 b/file1 312 index 257cc5642c..3bd1f0e297 100644 313 --- a/file1 314 +++ b/file1 315 @@ -1,1 +1,2 @@ 316 foo 317 +bar 318 [EOF] 319 "); 320 321 // `--color-words` implies `-p`, with or without graph 322 let output = work_dir.run_jj(["log", "-T", "description", "-r", "@", "--color-words"]); 323 insta::assert_snapshot!(output, @r" 324 @ a new commit 325 │ Modified regular file file1: 326 ~ 1 1: foo 327 2: bar 328 [EOF] 329 "); 330 let output = work_dir.run_jj([ 331 "log", 332 "-T", 333 "description", 334 "-r", 335 "@", 336 "--no-graph", 337 "--color-words", 338 ]); 339 insta::assert_snapshot!(output, @r" 340 a new commit 341 Modified regular file file1: 342 1 1: foo 343 2: bar 344 [EOF] 345 "); 346} 347 348#[test] 349fn test_log_null_terminate_multiline_descriptions() { 350 let test_env = TestEnvironment::default(); 351 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 352 let work_dir = test_env.work_dir("repo"); 353 354 work_dir 355 .run_jj(["commit", "-m", "commit 1 line 1", "-m", "commit 1 line 2"]) 356 .success(); 357 work_dir 358 .run_jj(["commit", "-m", "commit 2 line 1", "-m", "commit 2 line 2"]) 359 .success(); 360 work_dir 361 .run_jj(["describe", "-m", "commit 3 line 1", "-m", "commit 3 line 2"]) 362 .success(); 363 364 let output = work_dir 365 .run_jj([ 366 "log", 367 "-r", 368 "~root()", 369 "-T", 370 r#"description ++ "\0""#, 371 "--no-graph", 372 ]) 373 .success(); 374 insta::assert_debug_snapshot!( 375 output.stdout.normalized(), 376 @r#""commit 3 line 1\n\ncommit 3 line 2\n\0commit 2 line 1\n\ncommit 2 line 2\n\0commit 1 line 1\n\ncommit 1 line 2\n\0""# 377 ); 378} 379 380#[test] 381fn test_log_shortest_accessors() { 382 let test_env = TestEnvironment::default(); 383 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 384 let work_dir = test_env.work_dir("repo"); 385 let render = |rev, template| work_dir.run_jj(["log", "--no-graph", "-r", rev, "-T", template]); 386 test_env.add_config( 387 r#" 388 [template-aliases] 389 'format_id(id)' = 'id.shortest(12).prefix() ++ "[" ++ id.shortest(12).rest() ++ "]"' 390 "#, 391 ); 392 393 work_dir.write_file("file", "original file\n"); 394 work_dir.run_jj(["describe", "-m", "initial"]).success(); 395 work_dir 396 .run_jj(["bookmark", "c", "-r@", "original"]) 397 .success(); 398 insta::assert_snapshot!( 399 render("original", r#"format_id(change_id) ++ " " ++ format_id(commit_id)"#), 400 @"q[pvuntsmwlqt] 8[216f646c36d][EOF]"); 401 402 // Create a chain of 10 commits 403 for i in 1..10 { 404 work_dir 405 .run_jj(["new", "-m", &format!("commit{i}")]) 406 .success(); 407 work_dir.write_file("file", format!("file {i}\n")); 408 } 409 // Create 2^3 duplicates of the chain 410 for _ in 0..3 { 411 work_dir 412 .run_jj(["duplicate", "description(commit)"]) 413 .success(); 414 } 415 416 insta::assert_snapshot!( 417 render("original", r#"format_id(change_id) ++ " " ++ format_id(commit_id)"#), 418 @"qpv[untsmwlqt] 82[16f646c36d][EOF]"); 419 420 insta::assert_snapshot!( 421 render("::@", r#"change_id.shortest() ++ " " ++ commit_id.shortest() ++ "\n""#), @r" 422 wq c2 423 km 74 424 kp 97 425 zn 78 426 yo 40 427 vr bc9 428 yq 28 429 ro af 430 mz 04 431 qpv 82 432 zzz 00 433 [EOF] 434 "); 435 436 insta::assert_snapshot!( 437 render("::@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), @r" 438 wq[nwkozpkust] c2[b4c0bb3362] 439 km[kuslswpqwq] 74[fcd50c0643] 440 kp[qxywonksrl] 97[dcaada9b8d] 441 zn[kkpsqqskkl] 78[c03ab2235b] 442 yo[stqsxwqrlt] 40[1119280761] 443 vr[uxwmqvtpmx] bc9[e8942b459] 444 yq[osqzytrlsw] 28[edbc9658ef] 445 ro[yxmykxtrkr] af[3e6a27a1d0] 446 mz[vwutvlkqwt] 04[6c6a1df762] 447 qpv[untsmwlqt] 82[16f646c36d] 448 zzz[zzzzzzzzz] 00[0000000000] 449 [EOF] 450 "); 451 452 // Can get shorter prefixes in configured revset 453 test_env.add_config(r#"revsets.short-prefixes = "(@----)::""#); 454 insta::assert_snapshot!( 455 render("::@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), @r" 456 w[qnwkozpkust] c[2b4c0bb3362] 457 km[kuslswpqwq] 74[fcd50c0643] 458 kp[qxywonksrl] 9[7dcaada9b8d] 459 z[nkkpsqqskkl] 78[c03ab2235b] 460 y[ostqsxwqrlt] 4[01119280761] 461 vr[uxwmqvtpmx] bc9[e8942b459] 462 yq[osqzytrlsw] 28[edbc9658ef] 463 ro[yxmykxtrkr] af[3e6a27a1d0] 464 mz[vwutvlkqwt] 04[6c6a1df762] 465 qpv[untsmwlqt] 82[16f646c36d] 466 zzz[zzzzzzzzz] 00[0000000000] 467 [EOF] 468 "); 469 470 // Can disable short prefixes by setting to empty string 471 test_env.add_config(r#"revsets.short-prefixes = """#); 472 insta::assert_snapshot!( 473 render("::@", r#"format_id(change_id) ++ " " ++ format_id(commit_id) ++ "\n""#), @r" 474 wq[nwkozpkust] c2[b4c0bb3362] 475 km[kuslswpqwq] 74[fcd50c0643] 476 kp[qxywonksrl] 97[dcaada9b8d] 477 zn[kkpsqqskkl] 78[c03ab2235b] 478 yo[stqsxwqrlt] 401[119280761] 479 vr[uxwmqvtpmx] bc9[e8942b459] 480 yq[osqzytrlsw] 28[edbc9658ef] 481 ro[yxmykxtrkr] af[3e6a27a1d0] 482 mz[vwutvlkqwt] 04[6c6a1df762] 483 qpv[untsmwlqt] 82[16f646c36d] 484 zzz[zzzzzzzzz] 00[0000000000] 485 [EOF] 486 "); 487} 488 489#[test] 490fn test_log_bad_short_prefixes() { 491 let test_env = TestEnvironment::default(); 492 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 493 let work_dir = test_env.work_dir("repo"); 494 495 // Suppress warning in the commit summary template 496 test_env.add_config("template-aliases.'format_short_id(id)' = 'id.short(8)'"); 497 498 // Error on bad config of short prefixes 499 test_env.add_config(r#"revsets.short-prefixes = "!nval!d""#); 500 let output = work_dir.run_jj(["status"]); 501 insta::assert_snapshot!(output, @r" 502 ------- stderr ------- 503 Config error: Invalid `revsets.short-prefixes` 504 Caused by: --> 1:1 505 | 506 1 | !nval!d 507 | ^--- 508 | 509 = expected <strict_identifier> or <expression> 510 For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`. 511 [EOF] 512 [exit status: 1] 513 "); 514 515 // Warn on resolution of short prefixes 516 test_env.add_config("revsets.short-prefixes = 'missing'"); 517 let output = work_dir.run_jj(["log", "-Tcommit_id.shortest()"]); 518 insta::assert_snapshot!(output, @r" 519 @ e 520 ◆ 0 521 [EOF] 522 ------- stderr ------- 523 Warning: In template expression 524 --> 1:11 525 | 526 1 | commit_id.shortest() 527 | ^------^ 528 | 529 = Failed to load short-prefixes index 530 Failed to resolve short-prefixes disambiguation revset 531 Revision `missing` doesn't exist 532 [EOF] 533 "); 534 535 // Error on resolution of short prefixes 536 test_env.add_config("revsets.short-prefixes = 'missing'"); 537 let output = work_dir.run_jj(["log", "-r0"]); 538 insta::assert_snapshot!(output, @r" 539 ------- stderr ------- 540 Error: Failed to resolve short-prefixes disambiguation revset 541 Caused by: Revision `missing` doesn't exist 542 [EOF] 543 [exit status: 1] 544 "); 545} 546 547#[test] 548fn test_log_prefix_highlight_styled() { 549 let test_env = TestEnvironment::default(); 550 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 551 let work_dir = test_env.work_dir("repo"); 552 553 fn prefix_format(len: Option<usize>) -> String { 554 format!( 555 r###" 556 separate(" ", 557 "Change", 558 change_id.shortest({0}), 559 description.first_line(), 560 commit_id.shortest({0}), 561 bookmarks, 562 ) 563 "###, 564 len.map(|l| l.to_string()).unwrap_or_default() 565 ) 566 } 567 568 work_dir.write_file("file", "original file\n"); 569 work_dir.run_jj(["describe", "-m", "initial"]).success(); 570 work_dir 571 .run_jj(["bookmark", "c", "-r@", "original"]) 572 .success(); 573 insta::assert_snapshot!( 574 work_dir.run_jj(["log", "-r", "original", "-T", &prefix_format(Some(12))]), @r" 575 @ Change qpvuntsmwlqt initial 8216f646c36d original 576 577 ~ 578 [EOF] 579 "); 580 581 // Create a chain of 10 commits 582 for i in 1..10 { 583 work_dir 584 .run_jj(["new", "-m", &format!("commit{i}")]) 585 .success(); 586 work_dir.write_file("file", format!("file {i}\n")); 587 } 588 // Create 2^3 duplicates of the chain 589 for _ in 0..3 { 590 work_dir 591 .run_jj(["duplicate", "description(commit)"]) 592 .success(); 593 } 594 595 insta::assert_snapshot!( 596 work_dir.run_jj(["log", "-r", "original", "-T", &prefix_format(Some(12))]), @r" 597 ○ Change qpvuntsmwlqt initial 8216f646c36d original 598 599 ~ 600 [EOF] 601 "); 602 let output = work_dir.run_jj([ 603 "--color=always", 604 "log", 605 "-r", 606 "@-----------..@", 607 "-T", 608 &prefix_format(Some(12)), 609 ]); 610 insta::assert_snapshot!(output, @r" 611 @ Change wqnwkozpkust commit9 c2b4c0bb3362 612 ○ Change kmkuslswpqwq commit8 74fcd50c0643 613 ○ Change kpqxywonksrl commit7 97dcaada9b8d 614 ○ Change znkkpsqqskkl commit6 78c03ab2235b 615 ○ Change yostqsxwqrlt commit5 401119280761 616 ○ Change vruxwmqvtpmx commit4 bc9e8942b459 617 ○ Change yqosqzytrlsw commit3 28edbc9658ef 618 ○ Change royxmykxtrkr commit2 af3e6a27a1d0 619 ○ Change mzvwutvlkqwt commit1 046c6a1df762 620 ○ Change qpvuntsmwlqt initial 8216f646c36d original 621 ◆ Change zzzzzzzzzzzz 000000000000 622 [EOF] 623 "); 624 let output = work_dir.run_jj([ 625 "--color=always", 626 "log", 627 "-r", 628 "@-----------..@", 629 "-T", 630 &prefix_format(Some(3)), 631 ]); 632 insta::assert_snapshot!(output, @r" 633 @ Change wqn commit9 c2b 634 ○ Change kmk commit8 74f 635 ○ Change kpq commit7 97d 636 ○ Change znk commit6 78c 637 ○ Change yos commit5 401 638 ○ Change vru commit4 bc9 639 ○ Change yqo commit3 28e 640 ○ Change roy commit2 af3 641 ○ Change mzv commit1 046 642 ○ Change qpv initial 821 original 643 ◆ Change zzz 000 644 [EOF] 645 "); 646 let output = work_dir.run_jj([ 647 "--color=always", 648 "log", 649 "-r", 650 "@-----------..@", 651 "-T", 652 &prefix_format(None), 653 ]); 654 insta::assert_snapshot!(output, @r" 655 @ Change wq commit9 c2 656 ○ Change km commit8 74 657 ○ Change kp commit7 97 658 ○ Change zn commit6 78 659 ○ Change yo commit5 40 660 ○ Change vr commit4 bc9 661 ○ Change yq commit3 28 662 ○ Change ro commit2 af 663 ○ Change mz commit1 04 664 ○ Change qpv initial 82 original 665 ◆ Change zzz 00 666 [EOF] 667 "); 668} 669 670#[test] 671fn test_log_prefix_highlight_counts_hidden_commits() { 672 let test_env = TestEnvironment::default(); 673 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 674 let work_dir = test_env.work_dir("repo"); 675 test_env.add_config( 676 r#" 677 [revsets] 678 short-prefixes = "" # Disable short prefixes 679 [template-aliases] 680 'format_id(id)' = 'id.shortest(12).prefix() ++ "[" ++ id.shortest(12).rest() ++ "]"' 681 "#, 682 ); 683 684 let prefix_format = r#" 685 separate(" ", 686 "Change", 687 format_id(change_id), 688 description.first_line(), 689 format_id(commit_id), 690 bookmarks, 691 ) 692 "#; 693 694 work_dir.write_file("file", "original file\n"); 695 work_dir.run_jj(["describe", "-m", "initial"]).success(); 696 work_dir 697 .run_jj(["bookmark", "c", "-r@", "original"]) 698 .success(); 699 insta::assert_snapshot!(work_dir.run_jj(["log", "-r", "all()", "-T", prefix_format]), @r" 700 @ Change q[pvuntsmwlqt] initial 8[216f646c36d] original 701 ◆ Change z[zzzzzzzzzzz] 00[0000000000] 702 [EOF] 703 "); 704 705 // Create 2^7 hidden commits 706 work_dir.run_jj(["new", "root()", "-m", "extra"]).success(); 707 for _ in 0..7 { 708 work_dir 709 .run_jj(["duplicate", "description(extra)"]) 710 .success(); 711 } 712 work_dir.run_jj(["abandon", "description(extra)"]).success(); 713 714 // The unique prefixes became longer. 715 insta::assert_snapshot!(work_dir.run_jj(["log", "-T", prefix_format]), @r" 716 @ Change wq[nwkozpkust] 88[e8407a4f0a] 717 │ ○ Change qpv[untsmwlqt] initial 82[16f646c36d] original 718 ├─╯ 719 ◆ Change zzz[zzzzzzzzz] 00[0000000000] 720 [EOF] 721 "); 722 insta::assert_snapshot!(work_dir.run_jj(["log", "-r", "8", "-T", prefix_format]), @r" 723 ------- stderr ------- 724 Error: Commit ID prefix `8` is ambiguous 725 [EOF] 726 [exit status: 1] 727 "); 728 insta::assert_snapshot!(work_dir.run_jj(["log", "-r", "88", "-T", prefix_format]), @r" 729 @ Change wq[nwkozpkust] 88[e8407a4f0a] 730 731 ~ 732 [EOF] 733 "); 734} 735 736#[test] 737fn test_log_author_format() { 738 let test_env = TestEnvironment::default(); 739 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 740 let work_dir = test_env.work_dir("repo"); 741 742 insta::assert_snapshot!(work_dir.run_jj(["log", "--revisions=@"]), @r" 743 @ qpvuntsm test.user@example.com 2001-02-03 08:05:07 e8849ae1 744 │ (empty) (no description set) 745 ~ 746 [EOF] 747 "); 748 749 let decl = "template-aliases.'format_short_signature(signature)'"; 750 insta::assert_snapshot!(work_dir.run_jj([ 751 "--config", 752 &format!("{decl}='signature.email().local()'"), 753 "log", 754 "--revisions=@", 755 ]), @r" 756 @ qpvuntsm test.user 2001-02-03 08:05:07 e8849ae1 757 │ (empty) (no description set) 758 ~ 759 [EOF] 760 "); 761} 762 763#[test] 764fn test_log_divergence() { 765 let test_env = TestEnvironment::default(); 766 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 767 let work_dir = test_env.work_dir("repo"); 768 let template = r#"description.first_line() ++ if(divergent, " !divergence!")"#; 769 770 work_dir.write_file("file", "foo\n"); 771 work_dir 772 .run_jj(["describe", "-m", "description 1"]) 773 .success(); 774 // No divergence 775 let output = work_dir.run_jj(["log", "-T", template]); 776 insta::assert_snapshot!(output, @r" 777 @ description 1 778 779 [EOF] 780 "); 781 782 // Create divergence 783 work_dir 784 .run_jj(["describe", "-m", "description 2", "--at-operation", "@-"]) 785 .success(); 786 let output = work_dir.run_jj(["log", "-T", template]); 787 insta::assert_snapshot!(output, @r" 788 @ description 1 !divergence! 789 │ ○ description 2 !divergence! 790 ├─╯ 791 792 [EOF] 793 ------- stderr ------- 794 Concurrent modification detected, resolving automatically. 795 [EOF] 796 "); 797} 798 799#[test] 800fn test_log_reversed() { 801 let test_env = TestEnvironment::default(); 802 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 803 let work_dir = test_env.work_dir("repo"); 804 805 work_dir.run_jj(["describe", "-m", "first"]).success(); 806 work_dir.run_jj(["new", "-m", "second"]).success(); 807 808 let output = work_dir.run_jj(["log", "-T", "description", "--reversed"]); 809 insta::assert_snapshot!(output, @r" 810 811 ○ first 812 @ second 813 [EOF] 814 "); 815 816 let output = work_dir.run_jj(["log", "-T", "description", "--reversed", "--no-graph"]); 817 insta::assert_snapshot!(output, @r" 818 first 819 second 820 [EOF] 821 "); 822} 823 824#[test] 825fn test_log_filtered_by_path() { 826 let test_env = TestEnvironment::default(); 827 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 828 let work_dir = test_env.work_dir("repo"); 829 830 work_dir.write_file("file1", "foo\n"); 831 work_dir.run_jj(["describe", "-m", "first"]).success(); 832 work_dir.run_jj(["new", "-m", "second"]).success(); 833 work_dir.write_file("file1", "foo\nbar\n"); 834 work_dir.write_file("file2", "baz\n"); 835 836 let output = work_dir.run_jj(["log", "-T", "description", "file1"]); 837 insta::assert_snapshot!(output, @r" 838 @ second 839 ○ first 840 841 ~ 842 [EOF] 843 "); 844 845 let output = work_dir.run_jj(["log", "-T", "description", "file2"]); 846 insta::assert_snapshot!(output, @r" 847 @ second 848 849 ~ 850 [EOF] 851 "); 852 853 let output = work_dir.run_jj(["log", "-T", "description", "-s", "file1"]); 854 insta::assert_snapshot!(output, @r" 855 @ second 856 │ M file1 857 ○ first 858 │ A file1 859 ~ 860 [EOF] 861 "); 862 863 let output = work_dir.run_jj(["log", "-T", "description", "-s", "file2", "--no-graph"]); 864 insta::assert_snapshot!(output, @r" 865 second 866 A file2 867 [EOF] 868 "); 869 870 // empty revisions are filtered out by "all()" fileset. 871 let output = work_dir.run_jj(["log", "-Tdescription", "-s", "all()"]); 872 insta::assert_snapshot!(output, @r" 873 @ second 874 │ M file1 875 │ A file2 876 ○ first 877 │ A file1 878 ~ 879 [EOF] 880 "); 881 882 // "root:<path>" is resolved relative to the workspace root. 883 let output = test_env.run_jj_in( 884 ".", 885 [ 886 "log", 887 "-R", 888 work_dir.root().to_str().unwrap(), 889 "-Tdescription", 890 "-s", 891 "root:file1", 892 ], 893 ); 894 insta::assert_snapshot!(output.normalize_backslash(), @r" 895 @ second 896 │ M repo/file1 897 ○ first 898 │ A repo/file1 899 ~ 900 [EOF] 901 "); 902 903 // files() revset doesn't filter the diff. 904 let output = work_dir.run_jj([ 905 "log", 906 "-T", 907 "description", 908 "-s", 909 "-rfiles(file2)", 910 "--no-graph", 911 ]); 912 insta::assert_snapshot!(output, @r" 913 second 914 M file1 915 A file2 916 [EOF] 917 "); 918} 919 920#[test] 921fn test_log_limit() { 922 let test_env = TestEnvironment::default(); 923 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 924 let work_dir = test_env.work_dir("repo"); 925 926 work_dir.run_jj(["describe", "-m", "a"]).success(); 927 work_dir.write_file("a", ""); 928 work_dir.run_jj(["new", "-m", "b"]).success(); 929 work_dir.write_file("b", ""); 930 work_dir 931 .run_jj(["new", "-m", "c", "description(a)"]) 932 .success(); 933 work_dir.write_file("c", ""); 934 work_dir 935 .run_jj(["new", "-m", "d", "description(c)", "description(b)"]) 936 .success(); 937 938 let output = work_dir.run_jj(["log", "-T", "description", "--limit=3"]); 939 insta::assert_snapshot!(output, @r" 940 @ d 941 ├─╮ 942 │ ○ b 943 ○ │ c 944 ├─╯ 945 [EOF] 946 "); 947 948 // Applied on sorted DAG 949 let output = work_dir.run_jj(["log", "-T", "description", "--limit=2"]); 950 insta::assert_snapshot!(output, @r" 951 @ d 952 ├─╮ 953 │ ○ b 954 [EOF] 955 "); 956 957 let output = work_dir.run_jj(["log", "-T", "description", "--limit=2", "--no-graph"]); 958 insta::assert_snapshot!(output, @r" 959 d 960 c 961 [EOF] 962 "); 963 964 // Applied on reversed DAG: Because the node "a" is omitted, "b" and "c" are 965 // rendered as roots. 966 let output = work_dir.run_jj(["log", "-T", "description", "--limit=3", "--reversed"]); 967 insta::assert_snapshot!(output, @r" 968 ○ c 969 │ ○ b 970 ├─╯ 971 @ d 972 [EOF] 973 "); 974 let output = work_dir.run_jj([ 975 "log", 976 "-T", 977 "description", 978 "--limit=3", 979 "--reversed", 980 "--no-graph", 981 ]); 982 insta::assert_snapshot!(output, @r" 983 b 984 c 985 d 986 [EOF] 987 "); 988 989 // Applied on filtered commits 990 let output = work_dir.run_jj(["log", "-T", "description", "--limit=1", "b", "c"]); 991 insta::assert_snapshot!(output, @r" 992 ○ c 993 994 ~ 995 [EOF] 996 "); 997} 998 999#[test] 1000fn test_log_warn_path_might_be_revset() { 1001 let test_env = TestEnvironment::default(); 1002 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1003 let work_dir = test_env.work_dir("repo"); 1004 1005 work_dir.write_file("file1", "foo\n"); 1006 1007 // Don't warn if the file actually exists. 1008 let output = work_dir.run_jj(["log", "file1", "-T", "description"]); 1009 insta::assert_snapshot!(output, @r" 1010 @ 10111012 ~ 1013 [EOF] 1014 "); 1015 1016 // Warn for `jj log .` specifically, for former Mercurial users. 1017 let output = work_dir.run_jj(["log", ".", "-T", "description"]); 1018 insta::assert_snapshot!(output, @r#" 1019 @ 10201021 ~ 1022 [EOF] 1023 ------- stderr ------- 1024 Warning: The argument "." is being interpreted as a fileset expression, but this is often not useful because all non-empty commits touch '.'. If you meant to show the working copy commit, pass -r '@' instead. 1025 [EOF] 1026 "#); 1027 1028 // ...but checking `jj log .` makes sense in a subdirectory. 1029 let sub_dir = work_dir.create_dir_all("dir"); 1030 let output = sub_dir.run_jj(["log", "."]); 1031 insta::assert_snapshot!(output, @""); 1032 1033 // Warn for `jj log @` instead of `jj log -r @`. 1034 let output = work_dir.run_jj(["log", "@", "-T", "description"]); 1035 insta::assert_snapshot!(output, @r#" 1036 ------- stderr ------- 1037 Warning: The argument "@" is being interpreted as a fileset expression. To specify a revset, pass -r "@" instead. 1038 [EOF] 1039 "#); 1040 1041 // Warn when there's no path with the provided name. 1042 let output = work_dir.run_jj(["log", "file2", "-T", "description"]); 1043 insta::assert_snapshot!(output, @r#" 1044 ------- stderr ------- 1045 Warning: The argument "file2" is being interpreted as a fileset expression. To specify a revset, pass -r "file2" instead. 1046 [EOF] 1047 "#); 1048 1049 // If an explicit revision is provided, then suppress the warning. 1050 let output = work_dir.run_jj(["log", "@", "-r", "@", "-T", "description"]); 1051 insta::assert_snapshot!(output, @""); 1052} 1053 1054#[test] 1055fn test_default_revset() { 1056 let test_env = TestEnvironment::default(); 1057 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1058 let work_dir = test_env.work_dir("repo"); 1059 1060 work_dir.write_file("file1", "foo\n"); 1061 work_dir.run_jj(["describe", "-m", "add a file"]).success(); 1062 1063 // Set configuration to only show the root commit. 1064 test_env.add_config(r#"revsets.log = "root()""#); 1065 1066 // Log should only contain one line (for the root commit), and not show the 1067 // commit created above. 1068 insta::assert_snapshot!(work_dir.run_jj(["log", "-T", "commit_id"]), @r" 1069 ◆ 0000000000000000000000000000000000000000 1070 [EOF] 1071 "); 1072 1073 // The default revset is not used if a path is specified 1074 insta::assert_snapshot!(work_dir.run_jj(["log", "file1", "-T", "description"]), @r" 1075 @ add a file 10761077 ~ 1078 [EOF] 1079 "); 1080} 1081 1082#[test] 1083fn test_default_revset_per_repo() { 1084 let test_env = TestEnvironment::default(); 1085 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1086 let work_dir = test_env.work_dir("repo"); 1087 1088 work_dir.write_file("file1", "foo\n"); 1089 work_dir.run_jj(["describe", "-m", "add a file"]).success(); 1090 1091 // Set configuration to only show the root commit. 1092 work_dir.write_file(".jj/repo/config.toml", r#"revsets.log = "root()""#); 1093 1094 // Log should only contain one line (for the root commit), and not show the 1095 // commit created above. 1096 insta::assert_snapshot!(work_dir.run_jj(["log", "-T", "commit_id"]), @r" 1097 ◆ 0000000000000000000000000000000000000000 1098 [EOF] 1099 "); 1100} 1101 1102#[test] 1103fn test_multiple_revsets() { 1104 let test_env = TestEnvironment::default(); 1105 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1106 let work_dir = test_env.work_dir("repo"); 1107 for name in ["foo", "bar", "baz"] { 1108 work_dir.run_jj(["new", "-m", name]).success(); 1109 work_dir 1110 .run_jj(["bookmark", "create", "-r@", name]) 1111 .success(); 1112 } 1113 1114 // Default revset should be overridden if one or more -r options are specified. 1115 test_env.add_config(r#"revsets.log = "root()""#); 1116 1117 insta::assert_snapshot!( 1118 work_dir.run_jj(["log", "-T", "bookmarks", "-rfoo"]), @r" 1119 ○ foo 11201121 ~ 1122 [EOF] 1123 "); 1124 insta::assert_snapshot!( 1125 work_dir.run_jj(["log", "-T", "bookmarks", "-rfoo", "-rbar", "-rbaz"]), @r" 1126 @ baz 1127 ○ bar 1128 ○ foo 11291130 ~ 1131 [EOF] 1132 "); 1133 insta::assert_snapshot!( 1134 work_dir.run_jj(["log", "-T", "bookmarks", "-rfoo", "-rfoo"]), @r" 1135 ○ foo 11361137 ~ 1138 [EOF] 1139 "); 1140} 1141 1142#[test] 1143fn test_graph_template_color() { 1144 // Test that color codes from a multi-line template don't span the graph lines. 1145 let test_env = TestEnvironment::default(); 1146 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1147 let work_dir = test_env.work_dir("repo"); 1148 1149 work_dir 1150 .run_jj(["describe", "-m", "first line\nsecond line\nthird line"]) 1151 .success(); 1152 work_dir.run_jj(["new", "-m", "single line"]).success(); 1153 1154 test_env.add_config( 1155 r#"[colors] 1156 description = "red" 1157 "working_copy description" = "green" 1158 "#, 1159 ); 1160 1161 // First test without color for comparison 1162 let template = r#"label(if(current_working_copy, "working_copy"), description)"#; 1163 let output = work_dir.run_jj(["log", "-T", template]); 1164 insta::assert_snapshot!(output, @r" 1165 @ single line 1166 ○ first line 1167 │ second line 1168 │ third line 11691170 [EOF] 1171 "); 1172 let output = work_dir.run_jj(["--color=always", "log", "-T", template]); 1173 insta::assert_snapshot!(output, @r" 1174 @ single line 1175 ○ first line 1176 │ second line 1177 │ third line 1178 ◆ 1179 [EOF] 1180 "); 1181 let output = work_dir.run_jj(["--color=debug", "log", "-T", template]); 1182 insta::assert_snapshot!(output, @r" 1183 <<log commit node working_copy::@>> <<log commit working_copy description::single line>> 1184 <<log commit node::○>> <<log commit description::first line>> 1185 │ <<log commit description::second line>> 1186 │ <<log commit description::third line>> 1187 <<log commit node immutable::◆>> 1188 [EOF] 1189 "); 1190} 1191 1192#[test] 1193fn test_graph_styles() { 1194 // Test that different graph styles are available. 1195 let test_env = TestEnvironment::default(); 1196 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1197 let work_dir = test_env.work_dir("repo"); 1198 1199 work_dir.run_jj(["commit", "-m", "initial"]).success(); 1200 work_dir 1201 .run_jj(["commit", "-m", "main bookmark 1"]) 1202 .success(); 1203 work_dir 1204 .run_jj(["describe", "-m", "main bookmark 2"]) 1205 .success(); 1206 work_dir 1207 .run_jj(["new", "-m", "side bookmark\nwith\nlong\ndescription"]) 1208 .success(); 1209 work_dir 1210 .run_jj([ 1211 "new", 1212 "-m", 1213 "merge", 1214 r#"description("main bookmark 1")"#, 1215 "@", 1216 ]) 1217 .success(); 1218 1219 // Default (curved) style 1220 let output = work_dir.run_jj(["log", "-T=description"]); 1221 insta::assert_snapshot!(output, @r" 1222 @ merge 1223 ├─╮ 1224 │ ○ side bookmark 1225 │ │ with 1226 │ │ long 1227 │ │ description 1228 │ ○ main bookmark 2 1229 ├─╯ 1230 ○ main bookmark 1 1231 ○ initial 12321233 [EOF] 1234 "); 1235 1236 // ASCII style 1237 test_env.add_config(r#"ui.graph.style = "ascii""#); 1238 let output = work_dir.run_jj(["log", "-T=description"]); 1239 insta::assert_snapshot!(output, @r" 1240 @ merge 1241 |\ 1242 | o side bookmark 1243 | | with 1244 | | long 1245 | | description 1246 | o main bookmark 2 1247 |/ 1248 o main bookmark 1 1249 o initial 1250 + 1251 [EOF] 1252 "); 1253 1254 // Large ASCII style 1255 test_env.add_config(r#"ui.graph.style = "ascii-large""#); 1256 let output = work_dir.run_jj(["log", "-T=description"]); 1257 insta::assert_snapshot!(output, @r" 1258 @ merge 1259 |\ 1260 | \ 1261 | o side bookmark 1262 | | with 1263 | | long 1264 | | description 1265 | o main bookmark 2 1266 | / 1267 |/ 1268 o main bookmark 1 1269 o initial 1270 + 1271 [EOF] 1272 "); 1273 1274 // Curved style 1275 test_env.add_config(r#"ui.graph.style = "curved""#); 1276 let output = work_dir.run_jj(["log", "-T=description"]); 1277 insta::assert_snapshot!(output, @r" 1278 @ merge 1279 ├─╮ 1280 │ ○ side bookmark 1281 │ │ with 1282 │ │ long 1283 │ │ description 1284 │ ○ main bookmark 2 1285 ├─╯ 1286 ○ main bookmark 1 1287 ○ initial 12881289 [EOF] 1290 "); 1291 1292 // Square style 1293 test_env.add_config(r#"ui.graph.style = "square""#); 1294 let output = work_dir.run_jj(["log", "-T=description"]); 1295 insta::assert_snapshot!(output, @r" 1296 @ merge 1297 ├─┐ 1298 │ ○ side bookmark 1299 │ │ with 1300 │ │ long 1301 │ │ description 1302 │ ○ main bookmark 2 1303 ├─┘ 1304 ○ main bookmark 1 1305 ○ initial 13061307 [EOF] 1308 "); 1309 1310 // Invalid style name 1311 let output = work_dir.run_jj(["log", "--config=ui.graph.style=unknown"]); 1312 insta::assert_snapshot!(output, @r" 1313 ------- stderr ------- 1314 Config error: Invalid type or value for ui.graph.style 1315 Caused by: unknown variant `unknown`, expected one of `ascii`, `ascii-large`, `curved`, `square` 1316 1317 For help, see https://jj-vcs.github.io/jj/latest/config/ or use `jj help -k config`. 1318 [EOF] 1319 [exit status: 1] 1320 "); 1321} 1322 1323#[test] 1324fn test_log_word_wrap() { 1325 let test_env = TestEnvironment::default(); 1326 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1327 let work_dir = test_env.work_dir("repo"); 1328 let render = |args: &[&str], columns: u32, word_wrap: bool| { 1329 let word_wrap = to_toml_value(word_wrap); 1330 work_dir.run_jj_with(|cmd| { 1331 cmd.args(args) 1332 .arg(format!("--config=ui.log-word-wrap={word_wrap}")) 1333 .env("COLUMNS", columns.to_string()) 1334 }) 1335 }; 1336 1337 work_dir 1338 .run_jj(["commit", "-m", "main bookmark 1"]) 1339 .success(); 1340 work_dir 1341 .run_jj(["describe", "-m", "main bookmark 2"]) 1342 .success(); 1343 work_dir.run_jj(["new", "-m", "side"]).success(); 1344 work_dir 1345 .run_jj(["new", "-m", "merge", "@--", "@"]) 1346 .success(); 1347 1348 // ui.log-word-wrap option applies to both graph/no-graph outputs 1349 insta::assert_snapshot!(render(&["log", "-r@"], 40, false), @r" 1350 @ mzvwutvl test.user@example.com 2001-02-03 08:05:11 bafb1ee5 1351 │ (empty) merge 1352 ~ 1353 [EOF] 1354 "); 1355 insta::assert_snapshot!(render(&["log", "-r@"], 40, true), @r" 1356 @ mzvwutvl test.user@example.com 1357 │ 2001-02-03 08:05:11 bafb1ee5 1358 ~ (empty) merge 1359 [EOF] 1360 "); 1361 insta::assert_snapshot!(render(&["log", "--no-graph", "-r@"], 40, false), @r" 1362 mzvwutvl test.user@example.com 2001-02-03 08:05:11 bafb1ee5 1363 (empty) merge 1364 [EOF] 1365 "); 1366 insta::assert_snapshot!(render(&["log", "--no-graph", "-r@"], 40, true), @r" 1367 mzvwutvl test.user@example.com 1368 2001-02-03 08:05:11 bafb1ee5 1369 (empty) merge 1370 [EOF] 1371 "); 1372 1373 // Color labels should be preserved 1374 insta::assert_snapshot!(render(&["log", "-r@", "--color=always"], 40, true), @r" 1375 @ mzvwutvl test.user@example.com 1376 │ 2001-02-03 08:05:11 bafb1ee5 1377 ~ (empty) merge 1378 [EOF] 1379 "); 1380 1381 // Graph width should be subtracted from the term width 1382 let template = r#""0 1 2 3 4 5 6 7 8 9""#; 1383 insta::assert_snapshot!(render(&["log", "-T", template], 10, true), @r" 1384 @ 0 1 2 1385 ├─╮ 3 4 5 1386 │ │ 6 7 8 1387 │ │ 9 1388 │ ○ 0 1 2 1389 │ │ 3 4 5 1390 │ │ 6 7 8 1391 │ │ 9 1392 │ ○ 0 1 2 1393 ├─╯ 3 4 5 1394 │ 6 7 8 1395 │ 9 1396 ○ 0 1 2 3 1397 │ 4 5 6 7 1398 │ 8 9 1399 ◆ 0 1 2 3 1400 4 5 6 7 1401 8 9 1402 [EOF] 1403 "); 1404 1405 // Shouldn't panic with $COLUMNS < graph_width 1406 insta::assert_snapshot!(render(&["log", "-r@"], 0, true), @r" 1407 @ mzvwutvl 1408 │ test.user@example.com 1409 ~ 2001-02-03 1410 08:05:11 1411 bafb1ee5 1412 (empty) 1413 merge 1414 [EOF] 1415 "); 1416 insta::assert_snapshot!(render(&["log", "-r@"], 1, true), @r" 1417 @ mzvwutvl 1418 │ test.user@example.com 1419 ~ 2001-02-03 1420 08:05:11 1421 bafb1ee5 1422 (empty) 1423 merge 1424 [EOF] 1425 "); 1426} 1427 1428#[test] 1429fn test_log_diff_stat_width() { 1430 let test_env = TestEnvironment::default(); 1431 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1432 let work_dir = test_env.work_dir("repo"); 1433 let render = |args: &[&str], columns: u32| { 1434 work_dir.run_jj_with(|cmd| cmd.args(args).env("COLUMNS", columns.to_string())) 1435 }; 1436 1437 work_dir.write_file("file1", "foo\n".repeat(100)); 1438 work_dir.run_jj(["new", "root()"]).success(); 1439 work_dir.write_file("file2", "foo\n".repeat(100)); 1440 1441 insta::assert_snapshot!(render(&["log", "--stat", "--no-graph"], 30), @r" 1442 rlvkpnrz test.user@example.com 2001-02-03 08:05:09 9490cfd3 1443 (no description set) 1444 file2 | 100 +++++++++++++++ 1445 1 file changed, 100 insertions(+), 0 deletions(-) 1446 qpvuntsm test.user@example.com 2001-02-03 08:05:08 79f0968d 1447 (no description set) 1448 file1 | 100 +++++++++++++++ 1449 1 file changed, 100 insertions(+), 0 deletions(-) 1450 zzzzzzzz root() 00000000 1451 0 files changed, 0 insertions(+), 0 deletions(-) 1452 [EOF] 1453 "); 1454 1455 // Graph width should be subtracted 1456 insta::assert_snapshot!(render(&["log", "--stat"], 30), @r" 1457 @ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 9490cfd3 1458 │ (no description set) 1459 │ file2 | 100 ++++++++++++ 1460 │ 1 file changed, 100 insertions(+), 0 deletions(-) 1461 │ ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 79f0968d 1462 ├─╯ (no description set) 1463 │ file1 | 100 ++++++++++ 1464 │ 1 file changed, 100 insertions(+), 0 deletions(-) 1465 ◆ zzzzzzzz root() 00000000 1466 0 files changed, 0 insertions(+), 0 deletions(-) 1467 [EOF] 1468 "); 1469} 1470 1471#[test] 1472fn test_elided() { 1473 // Test that elided commits are shown as synthetic nodes. 1474 let test_env = TestEnvironment::default(); 1475 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1476 let work_dir = test_env.work_dir("repo"); 1477 1478 work_dir.run_jj(["describe", "-m", "initial"]).success(); 1479 work_dir.run_jj(["new", "-m", "main bookmark 1"]).success(); 1480 work_dir.run_jj(["new", "-m", "main bookmark 2"]).success(); 1481 work_dir 1482 .run_jj(["new", "@--", "-m", "side bookmark 1"]) 1483 .success(); 1484 work_dir.run_jj(["new", "-m", "side bookmark 2"]).success(); 1485 work_dir 1486 .run_jj([ 1487 "new", 1488 "-m", 1489 "merge", 1490 r#"description("main bookmark 2")"#, 1491 "@", 1492 ]) 1493 .success(); 1494 1495 let get_log = |revs: &str| work_dir.run_jj(["log", "-T", r#"description ++ "\n""#, "-r", revs]); 1496 1497 // Test the setup 1498 insta::assert_snapshot!(get_log("::"), @r" 1499 @ merge 1500 ├─╮ 1501 │ ○ side bookmark 2 1502 │ │ 1503 │ ○ side bookmark 1 1504 │ │ 1505 ○ │ main bookmark 2 1506 │ │ 1507 ○ │ main bookmark 1 1508 ├─╯ 1509 ○ initial 151015111512 [EOF] 1513 "); 1514 1515 // Elide some commits from each side of the merge. It's unclear that a revision 1516 // was skipped on the left side. 1517 test_env.add_config("ui.log-synthetic-elided-nodes = false"); 1518 insta::assert_snapshot!(get_log("@ | @- | description(initial)"), @r" 1519 @ merge 1520 ├─╮ 1521 │ ○ side bookmark 2 1522 │ ╷ 1523 ○ ╷ main bookmark 2 1524 ├─╯ 1525 ○ initial 15261527 ~ 1528 [EOF] 1529 "); 1530 1531 // Elide shared commits. It's unclear that a revision was skipped on the right 1532 // side (#1252). 1533 insta::assert_snapshot!(get_log("@-- | root()"), @r" 1534 ○ side bookmark 1 15351536 ╷ ○ main bookmark 1 1537 ╭─╯ 15381539 [EOF] 1540 "); 1541 1542 // Now test the same thing with synthetic nodes for elided commits 1543 1544 // Elide some commits from each side of the merge 1545 test_env.add_config("ui.log-synthetic-elided-nodes = true"); 1546 insta::assert_snapshot!(get_log("@ | @- | description(initial)"), @r" 1547 @ merge 1548 ├─╮ 1549 │ ○ side bookmark 2 1550 │ │ 1551 │ ~ (elided revisions) 1552 ○ │ main bookmark 2 1553 │ │ 1554 ~ │ (elided revisions) 1555 ├─╯ 1556 ○ initial 15571558 ~ 1559 [EOF] 1560 "); 1561 1562 // Elide shared commits. To keep the implementation simple, it still gets 1563 // rendered as two synthetic nodes. 1564 insta::assert_snapshot!(get_log("@-- | root()"), @r" 1565 ○ side bookmark 1 15661567 ~ (elided revisions) 1568 │ ○ main bookmark 1 1569 │ │ 1570 │ ~ (elided revisions) 1571 ├─╯ 15721573 [EOF] 1574 "); 1575} 1576 1577#[test] 1578fn test_log_with_custom_symbols() { 1579 // Test that elided commits are shown as synthetic nodes. 1580 let test_env = TestEnvironment::default(); 1581 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1582 let work_dir = test_env.work_dir("repo"); 1583 1584 work_dir.run_jj(["describe", "-m", "initial"]).success(); 1585 work_dir.run_jj(["new", "-m", "main bookmark 1"]).success(); 1586 work_dir.run_jj(["new", "-m", "main bookmark 2"]).success(); 1587 work_dir 1588 .run_jj(["new", "@--", "-m", "side bookmark 1"]) 1589 .success(); 1590 work_dir.run_jj(["new", "-m", "side bookmark 2"]).success(); 1591 work_dir 1592 .run_jj([ 1593 "new", 1594 "-m", 1595 "merge", 1596 r#"description("main bookmark 2")"#, 1597 "@", 1598 ]) 1599 .success(); 1600 1601 let get_log = |revs: &str| work_dir.run_jj(["log", "-T", r#"description ++ "\n""#, "-r", revs]); 1602 1603 // Simple test with showing default and elided nodes. 1604 test_env.add_config( 1605 r###" 1606 ui.log-synthetic-elided-nodes = true 1607 templates.log_node = 'if(self, if(current_working_copy, "$", if(root, "┴", "┝")), "🮀")' 1608 "###, 1609 ); 1610 insta::assert_snapshot!(get_log("@ | @- | description(initial) | root()"), @r" 1611 $ merge 1612 ├─╮ 1613 │ ┝ side bookmark 2 1614 │ │ 1615 │ 🮀 (elided revisions) 1616 ┝ │ main bookmark 2 1617 │ │ 1618 🮀 │ (elided revisions) 1619 ├─╯ 1620 ┝ initial 162116221623 [EOF] 1624 "); 1625 1626 // Simple test with showing default and elided nodes, ascii style. 1627 test_env.add_config( 1628 r###" 1629 ui.log-synthetic-elided-nodes = true 1630 ui.graph.style = 'ascii' 1631 templates.log_node = 'if(self, if(current_working_copy, "$", if(root, "^", "*")), ":")' 1632 "###, 1633 ); 1634 insta::assert_snapshot!(get_log("@ | @- | description(initial) | root()"), @r" 1635 $ merge 1636 |\ 1637 | * side bookmark 2 1638 | | 1639 | : (elided revisions) 1640 * | main bookmark 2 1641 | | 1642 : | (elided revisions) 1643 |/ 1644 * initial 1645 | 1646 ^ 1647 [EOF] 1648 "); 1649} 1650 1651#[test] 1652fn test_log_full_description_template() { 1653 let test_env = TestEnvironment::default(); 1654 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1655 let work_dir = test_env.work_dir("repo"); 1656 1657 work_dir 1658 .run_jj([ 1659 "describe", 1660 "-m", 1661 "this is commit with a multiline description\n\n<full description>", 1662 ]) 1663 .success(); 1664 1665 let output = work_dir.run_jj(["log", "-T", "builtin_log_compact_full_description"]); 1666 insta::assert_snapshot!(output, @r" 1667 @ qpvuntsm test.user@example.com 2001-02-03 08:05:08 37b69cda 1668 │ (empty) this is commit with a multiline description 16691670 │ <full description> 16711672 ◆ zzzzzzzz root() 00000000 1673 [EOF] 1674 "); 1675}