just playing with tangled
at diffedit3 556 lines 19 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 std::path::Path; 16 17use crate::common::TestEnvironment; 18 19#[test] 20fn test_new() { 21 let test_env = TestEnvironment::default(); 22 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 23 let repo_path = test_env.env_root().join("repo"); 24 25 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "add a file"]); 26 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "a new commit"]); 27 28 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 29 @ 4f2d6e0a3482a6a34e4856a4a63869c0df109e79 a new commit 30 ◉ 5d5c60b2aa96b8dbf55710656c50285c66cdcd74 add a file 31 ◉ 0000000000000000000000000000000000000000 32 "###); 33 34 // Start a new change off of a specific commit (the root commit in this case). 35 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "off of root", "root()"]); 36 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 37 @ 026537ddb96b801b9cb909985d5443aab44616c1 off of root 38 │ ◉ 4f2d6e0a3482a6a34e4856a4a63869c0df109e79 a new commit 39 │ ◉ 5d5c60b2aa96b8dbf55710656c50285c66cdcd74 add a file 40 ├─╯ 41 ◉ 0000000000000000000000000000000000000000 42 "###); 43 44 // --edit is a no-op 45 test_env.jj_cmd_ok(&repo_path, &["new", "--edit", "-m", "yet another commit"]); 46 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 47 @ 101cbec5cae8049cb9850a906ef3675631ed48fa yet another commit 48 ◉ 026537ddb96b801b9cb909985d5443aab44616c1 off of root 49 │ ◉ 4f2d6e0a3482a6a34e4856a4a63869c0df109e79 a new commit 50 │ ◉ 5d5c60b2aa96b8dbf55710656c50285c66cdcd74 add a file 51 ├─╯ 52 ◉ 0000000000000000000000000000000000000000 53 "###); 54 55 // --edit cannot be used with --no-edit 56 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["new", "--edit", "B", "--no-edit", "D"]); 57 insta::assert_snapshot!(stderr, @r###" 58 error: the argument '--edit' cannot be used with '--no-edit' 59 60 Usage: jj new <REVISIONS>... 61 62 For more information, try '--help'. 63 "###); 64} 65 66#[test] 67fn test_new_merge() { 68 let test_env = TestEnvironment::default(); 69 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 70 let repo_path = test_env.env_root().join("repo"); 71 72 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "main"]); 73 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "add file1"]); 74 std::fs::write(repo_path.join("file1"), "a").unwrap(); 75 test_env.jj_cmd_ok(&repo_path, &["new", "root()", "-m", "add file2"]); 76 std::fs::write(repo_path.join("file2"), "b").unwrap(); 77 78 // Create a merge commit 79 test_env.jj_cmd_ok(&repo_path, &["new", "main", "@"]); 80 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 81 @ 0c4e5b9b68ae0cbe7ce3c61042619513d09005bf 82 ├─╮ 83 │ ◉ f399209d9dda06e8a25a0c8e9a0cde9f421ff35d add file2 84 ◉ │ 38e8e2f6c92ffb954961fc391b515ff551b41636 add file1 85 ├─╯ 86 ◉ 0000000000000000000000000000000000000000 87 "###); 88 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1"]); 89 insta::assert_snapshot!(stdout, @"a"); 90 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]); 91 insta::assert_snapshot!(stdout, @"b"); 92 93 // Same test with `--no-edit` 94 test_env.jj_cmd_ok(&repo_path, &["undo"]); 95 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["new", "main", "@", "--no-edit"]); 96 insta::assert_snapshot!(stdout, @""); 97 insta::assert_snapshot!(stderr, @r###" 98 Created new commit znkkpsqq 200ed1a1 (empty) (no description set) 99 "###); 100 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 101 ◉ 200ed1a14c8acf09783dafefe5bebf2ff58f12fd 102 ├─╮ 103 │ @ f399209d9dda06e8a25a0c8e9a0cde9f421ff35d add file2 104 ◉ │ 38e8e2f6c92ffb954961fc391b515ff551b41636 add file1 105 ├─╯ 106 ◉ 0000000000000000000000000000000000000000 107 "###); 108 109 // Same test with `jj merge` 110 test_env.jj_cmd_ok(&repo_path, &["undo"]); 111 test_env.jj_cmd_ok(&repo_path, &["merge", "main", "@"]); 112 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 113 @ 3a44e52b073cbb5deb11bb8fa0763a369e96427a 114 ├─╮ 115 │ ◉ f399209d9dda06e8a25a0c8e9a0cde9f421ff35d add file2 116 ◉ │ 38e8e2f6c92ffb954961fc391b515ff551b41636 add file1 117 ├─╯ 118 ◉ 0000000000000000000000000000000000000000 119 "###); 120 121 // `jj merge` with less than two arguments is an error 122 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["merge"]); 123 insta::assert_snapshot!(stderr, @r###" 124 Warning: `jj merge` is deprecated; use `jj new` instead, which is equivalent 125 Warning: `jj merge` will be removed in a future version, and this will be a hard error 126 Error: Merge requires at least two revisions 127 "###); 128 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["merge", "main"]); 129 insta::assert_snapshot!(stderr, @r###" 130 Warning: `jj merge` is deprecated; use `jj new` instead, which is equivalent 131 Warning: `jj merge` will be removed in a future version, and this will be a hard error 132 Error: Merge requires at least two revisions 133 "###); 134 135 // merge with non-unique revisions 136 let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "@", "3a44e"]); 137 insta::assert_snapshot!(stderr, @r###" 138 Error: More than one revset resolved to revision 3a44e52b073c 139 "###); 140 // if prefixed with all:, duplicates are allowed 141 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["new", "@", "all:visible_heads()"]); 142 insta::assert_snapshot!(stdout, @""); 143 insta::assert_snapshot!(stderr, @r###" 144 Working copy now at: xznxytkn dddeb489 (empty) (no description set) 145 Parent commit : wqnwkozp 3a44e52b (empty) (no description set) 146 "###); 147 148 // merge with root 149 let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "@", "root()"]); 150 insta::assert_snapshot!(stderr, @r###" 151 Error: The Git backend does not support creating merge commits with the root commit as one of the parents. 152 "###); 153} 154 155#[test] 156fn test_new_insert_after() { 157 let test_env = TestEnvironment::default(); 158 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 159 let repo_path = test_env.env_root().join("repo"); 160 setup_before_insertion(&test_env, &repo_path); 161 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 162 @ F 163 ├─╮ 164 │ ◉ E 165 ◉ │ D 166 ├─╯ 167 │ ◉ C 168 │ ◉ B 169 │ ◉ A 170 ├─╯ 171 ◉ root 172 "###); 173 174 // --insert-after can be repeated (this does not affect the outcome); --after is 175 // an alias 176 let (stdout, stderr) = test_env.jj_cmd_ok( 177 &repo_path, 178 &[ 179 "new", 180 "--insert-after", 181 "-m", 182 "G", 183 "--after", 184 "B", 185 "--after", 186 "D", 187 ], 188 ); 189 insta::assert_snapshot!(stdout, @""); 190 insta::assert_snapshot!(stderr, @r###" 191 Rebased 2 descendant commits 192 Working copy now at: kxryzmor ca7c6481 (empty) G 193 Parent commit : kkmpptxz 6041917c B | (empty) B 194 Parent commit : vruxwmqv c9257eff D | (empty) D 195 "###); 196 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 197 ◉ C 198 │ ◉ F 199 ╭─┤ 200 @ │ G 201 ├───╮ 202 │ │ ◉ D 203 ◉ │ │ B 204 ◉ │ │ A 205 ├───╯ 206 │ ◉ E 207 ├─╯ 208 ◉ root 209 "###); 210 211 let (stdout, stderr) = 212 test_env.jj_cmd_ok(&repo_path, &["new", "--insert-after", "-m", "H", "D"]); 213 insta::assert_snapshot!(stdout, @""); 214 insta::assert_snapshot!(stderr, @r###" 215 Rebased 3 descendant commits 216 Working copy now at: uyznsvlq fcf8281b (empty) H 217 Parent commit : vruxwmqv c9257eff D | (empty) D 218 "###); 219 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 220 ◉ C 221 │ ◉ F 222 ╭─┤ 223 ◉ │ G 224 ├───╮ 225 │ │ @ H 226 │ │ ◉ D 227 ◉ │ │ B 228 ◉ │ │ A 229 ├───╯ 230 │ ◉ E 231 ├─╯ 232 ◉ root 233 "###); 234 235 // --after cannot be used with --before 236 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["new", "--after", "B", "--before", "D"]); 237 insta::assert_snapshot!(stderr, @r###" 238 error: the argument '--insert-after' cannot be used with '--insert-before' 239 240 Usage: jj new --insert-after <REVISIONS>... 241 242 For more information, try '--help'. 243 "###); 244} 245 246#[test] 247fn test_new_insert_after_children() { 248 let test_env = TestEnvironment::default(); 249 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 250 let repo_path = test_env.env_root().join("repo"); 251 setup_before_insertion(&test_env, &repo_path); 252 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 253 @ F 254 ├─╮ 255 │ ◉ E 256 ◉ │ D 257 ├─╯ 258 │ ◉ C 259 │ ◉ B 260 │ ◉ A 261 ├─╯ 262 ◉ root 263 "###); 264 265 // Check that inserting G after A and C doesn't try to rebase B (which is 266 // initially a child of A) onto G as that would create a cycle since B is 267 // a parent of C which is a parent G. 268 let (stdout, stderr) = 269 test_env.jj_cmd_ok(&repo_path, &["new", "--insert-after", "-m", "G", "A", "C"]); 270 insta::assert_snapshot!(stdout, @""); 271 insta::assert_snapshot!(stderr, @r###" 272 Working copy now at: kxryzmor b48d4d73 (empty) G 273 Parent commit : qpvuntsm 65b1ef43 A | (empty) A 274 Parent commit : mzvwutvl ec18c57d C | (empty) C 275 "###); 276 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 277 @ G 278 ├─╮ 279 │ ◉ C 280 │ ◉ B 281 ├─╯ 282 ◉ A 283 │ ◉ F 284 │ ├─╮ 285 │ │ ◉ E 286 ├───╯ 287 │ ◉ D 288 ├─╯ 289 ◉ root 290 "###); 291} 292 293#[test] 294fn test_new_insert_before() { 295 let test_env = TestEnvironment::default(); 296 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 297 let repo_path = test_env.env_root().join("repo"); 298 setup_before_insertion(&test_env, &repo_path); 299 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 300 @ F 301 ├─╮ 302 │ ◉ E 303 ◉ │ D 304 ├─╯ 305 │ ◉ C 306 │ ◉ B 307 │ ◉ A 308 ├─╯ 309 ◉ root 310 "###); 311 312 let (stdout, stderr) = 313 test_env.jj_cmd_ok(&repo_path, &["new", "--insert-before", "-m", "G", "C", "F"]); 314 insta::assert_snapshot!(stdout, @""); 315 insta::assert_snapshot!(stderr, @r###" 316 Rebased 2 descendant commits 317 Working copy now at: kxryzmor ff6bbbc7 (empty) G 318 Parent commit : znkkpsqq 41a89ffc E | (empty) E 319 Parent commit : vruxwmqv c9257eff D | (empty) D 320 Parent commit : kkmpptxz 6041917c B | (empty) B 321 "###); 322 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 323 ◉ F 324 │ ◉ C 325 ├─╯ 326 @ G 327 ├─┬─╮ 328 │ │ ◉ B 329 │ │ ◉ A 330 │ ◉ │ D 331 │ ├─╯ 332 ◉ │ E 333 ├─╯ 334 ◉ root 335 "###); 336} 337 338#[test] 339fn test_new_insert_before_root_successors() { 340 let test_env = TestEnvironment::default(); 341 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 342 let repo_path = test_env.env_root().join("repo"); 343 setup_before_insertion(&test_env, &repo_path); 344 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 345 @ F 346 ├─╮ 347 │ ◉ E 348 ◉ │ D 349 ├─╯ 350 │ ◉ C 351 │ ◉ B 352 │ ◉ A 353 ├─╯ 354 ◉ root 355 "###); 356 357 let (stdout, stderr) = 358 test_env.jj_cmd_ok(&repo_path, &["new", "--insert-before", "-m", "G", "A", "D"]); 359 insta::assert_snapshot!(stdout, @""); 360 insta::assert_snapshot!(stderr, @r###" 361 Rebased 5 descendant commits 362 Working copy now at: kxryzmor 36541977 (empty) G 363 Parent commit : zzzzzzzz 00000000 (empty) (no description set) 364 "###); 365 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 366 ◉ F 367 ├─╮ 368 │ ◉ E 369 ◉ │ D 370 │ │ ◉ C 371 │ │ ◉ B 372 │ │ ◉ A 373 ├───╯ 374 @ │ G 375 ├─╯ 376 ◉ root 377 "###); 378} 379 380#[test] 381fn test_new_insert_before_no_loop() { 382 let test_env = TestEnvironment::default(); 383 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 384 let repo_path = test_env.env_root().join("repo"); 385 setup_before_insertion(&test_env, &repo_path); 386 let template = r#"commit_id.short() ++ " " ++ if(description, description, "root")"#; 387 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-T", template]); 388 insta::assert_snapshot!(stdout, @r###" 389 @ 7705d353bf5d F 390 ├─╮ 391 │ ◉ 41a89ffcbba2 E 392 ◉ │ c9257eff5bf9 D 393 ├─╯ 394 │ ◉ ec18c57d72d8 C 395 │ ◉ 6041917ceeb5 B 396 │ ◉ 65b1ef43c737 A 397 ├─╯ 398 ◉ 000000000000 root 399 "###); 400 401 let stderr = 402 test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "-m", "G", "A", "C"]); 403 insta::assert_snapshot!(stderr, @r###" 404 Error: Refusing to create a loop: commit 6041917ceeb5 would be both an ancestor and a descendant of the new commit 405 "###); 406} 407 408#[test] 409fn test_new_insert_before_no_root_merge() { 410 let test_env = TestEnvironment::default(); 411 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 412 let repo_path = test_env.env_root().join("repo"); 413 setup_before_insertion(&test_env, &repo_path); 414 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 415 @ F 416 ├─╮ 417 │ ◉ E 418 ◉ │ D 419 ├─╯ 420 │ ◉ C 421 │ ◉ B 422 │ ◉ A 423 ├─╯ 424 ◉ root 425 "###); 426 427 let stderr = 428 test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "-m", "G", "B", "D"]); 429 insta::assert_snapshot!(stderr, @r###" 430 Error: The Git backend does not support creating merge commits with the root commit as one of the parents. 431 "###); 432} 433 434#[test] 435fn test_new_insert_before_root() { 436 let test_env = TestEnvironment::default(); 437 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 438 let repo_path = test_env.env_root().join("repo"); 439 setup_before_insertion(&test_env, &repo_path); 440 insta::assert_snapshot!(get_short_log_output(&test_env, &repo_path), @r###" 441 @ F 442 ├─╮ 443 │ ◉ E 444 ◉ │ D 445 ├─╯ 446 │ ◉ C 447 │ ◉ B 448 │ ◉ A 449 ├─╯ 450 ◉ root 451 "###); 452 453 let stderr = 454 test_env.jj_cmd_failure(&repo_path, &["new", "--insert-before", "-m", "G", "root()"]); 455 insta::assert_snapshot!(stderr, @r###" 456 Error: The root commit 000000000000 is immutable 457 "###); 458} 459 460#[test] 461fn test_new_conflicting_branches() { 462 let test_env = TestEnvironment::default(); 463 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 464 let repo_path = test_env.env_root().join("repo"); 465 466 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "one"]); 467 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "two", "@-"]); 468 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "foo"]); 469 test_env.jj_cmd_ok( 470 &repo_path, 471 &[ 472 "--at-op=@-", 473 "branch", 474 "create", 475 "foo", 476 "-r", 477 r#"description("one")"#, 478 ], 479 ); 480 481 // Trigger resolution of concurrent operations 482 test_env.jj_cmd_ok(&repo_path, &["st"]); 483 484 let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "foo"]); 485 insta::assert_snapshot!(stderr, @r###" 486 Error: Revset "foo" resolved to more than one revision 487 Hint: Branch foo resolved to multiple revisions because it's conflicted. 488 It resolved to these revisions: 489 kkmpptxz 66c6502d foo?? | (empty) two 490 qpvuntsm a9330854 foo?? | (empty) one 491 Hint: Set which revision the branch points to with `jj branch set foo -r <REVISION>`. 492 "###); 493} 494 495#[test] 496fn test_new_conflicting_change_ids() { 497 let test_env = TestEnvironment::default(); 498 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 499 let repo_path = test_env.env_root().join("repo"); 500 501 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "one"]); 502 test_env.jj_cmd_ok(&repo_path, &["--at-op=@-", "describe", "-m", "two"]); 503 504 // Trigger resolution of concurrent operations 505 test_env.jj_cmd_ok(&repo_path, &["st"]); 506 507 let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "qpvuntsm"]); 508 insta::assert_snapshot!(stderr, @r###" 509 Error: Revset "qpvuntsm" resolved to more than one revision 510 Hint: The revset "qpvuntsm" resolved to these revisions: 511 qpvuntsm?? d2ae6806 (empty) two 512 qpvuntsm?? a9330854 (empty) one 513 Hint: Some of these commits have the same change id. Abandon one of them with `jj abandon -r <REVISION>`. 514 "###); 515} 516 517#[test] 518fn test_new_error_revision_does_not_exist() { 519 let test_env = TestEnvironment::default(); 520 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 521 let repo_path = test_env.env_root().join("repo"); 522 523 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "one"]); 524 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "two"]); 525 526 let stderr = test_env.jj_cmd_failure(&repo_path, &["new", "this"]); 527 insta::assert_snapshot!(stderr, @r###" 528 Error: Revision "this" doesn't exist 529 "###); 530} 531 532fn setup_before_insertion(test_env: &TestEnvironment, repo_path: &Path) { 533 test_env.jj_cmd_ok(repo_path, &["branch", "create", "A"]); 534 test_env.jj_cmd_ok(repo_path, &["commit", "-m", "A"]); 535 test_env.jj_cmd_ok(repo_path, &["branch", "create", "B"]); 536 test_env.jj_cmd_ok(repo_path, &["commit", "-m", "B"]); 537 test_env.jj_cmd_ok(repo_path, &["branch", "create", "C"]); 538 test_env.jj_cmd_ok(repo_path, &["describe", "-m", "C"]); 539 test_env.jj_cmd_ok(repo_path, &["new", "-m", "D", "root()"]); 540 test_env.jj_cmd_ok(repo_path, &["branch", "create", "D"]); 541 test_env.jj_cmd_ok(repo_path, &["new", "-m", "E", "root()"]); 542 test_env.jj_cmd_ok(repo_path, &["branch", "create", "E"]); 543 // Any number of -r's is ignored 544 test_env.jj_cmd_ok(repo_path, &["new", "-m", "F", "-r", "D", "-r", "E"]); 545 test_env.jj_cmd_ok(repo_path, &["branch", "create", "F"]); 546} 547 548fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String { 549 let template = r#"commit_id ++ " " ++ description"#; 550 test_env.jj_cmd_success(repo_path, &["log", "-T", template]) 551} 552 553fn get_short_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String { 554 let template = r#"if(description, description, "root")"#; 555 test_env.jj_cmd_success(repo_path, &["log", "-T", template]) 556}