just playing with tangled
at main 2230 lines 79 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 testutils::git; 16 17use crate::common::CommandOutput; 18use crate::common::TestEnvironment; 19use crate::common::TestWorkDir; 20 21fn git_repo_dir_for_jj_repo(work_dir: &TestWorkDir<'_>) -> std::path::PathBuf { 22 work_dir 23 .root() 24 .join(".jj") 25 .join("repo") 26 .join("store") 27 .join("git") 28} 29 30fn set_up(test_env: &TestEnvironment) { 31 test_env.run_jj_in(".", ["git", "init", "origin"]).success(); 32 let origin_dir = test_env.work_dir("origin"); 33 let origin_git_repo_path = git_repo_dir_for_jj_repo(&origin_dir); 34 35 origin_dir 36 .run_jj(["describe", "-m=description 1"]) 37 .success(); 38 origin_dir 39 .run_jj(["bookmark", "create", "-r@", "bookmark1"]) 40 .success(); 41 origin_dir 42 .run_jj(["new", "root()", "-m=description 2"]) 43 .success(); 44 origin_dir 45 .run_jj(["bookmark", "create", "-r@", "bookmark2"]) 46 .success(); 47 origin_dir.run_jj(["git", "export"]).success(); 48 49 test_env 50 .run_jj_in( 51 ".", 52 [ 53 "git", 54 "clone", 55 "--config=git.auto-local-bookmark=true", 56 origin_git_repo_path.to_str().unwrap(), 57 "local", 58 ], 59 ) 60 .success(); 61} 62 63#[test] 64fn test_git_push_nothing() { 65 let test_env = TestEnvironment::default(); 66 set_up(&test_env); 67 let work_dir = test_env.work_dir("local"); 68 // Show the setup. `insta` has trouble if this is done inside `set_up()` 69 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 70 bookmark1: qpvuntsm 9b2e76de (empty) description 1 71 @origin: qpvuntsm 9b2e76de (empty) description 1 72 bookmark2: zsuskuln 38a20473 (empty) description 2 73 @origin: zsuskuln 38a20473 (empty) description 2 74 [EOF] 75 "); 76 // No bookmarks to push yet 77 let output = work_dir.run_jj(["git", "push", "--all"]); 78 insta::assert_snapshot!(output, @r" 79 ------- stderr ------- 80 Nothing changed. 81 [EOF] 82 "); 83} 84 85#[test] 86fn test_git_push_current_bookmark() { 87 let test_env = TestEnvironment::default(); 88 set_up(&test_env); 89 let work_dir = test_env.work_dir("local"); 90 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 91 // Update some bookmarks. `bookmark1` is not a current bookmark, but 92 // `bookmark2` and `my-bookmark` are. 93 work_dir 94 .run_jj(["describe", "bookmark1", "-m", "modified bookmark1 commit"]) 95 .success(); 96 work_dir.run_jj(["new", "bookmark2"]).success(); 97 work_dir 98 .run_jj(["bookmark", "set", "bookmark2", "-r@"]) 99 .success(); 100 work_dir 101 .run_jj(["bookmark", "create", "-r@", "my-bookmark"]) 102 .success(); 103 work_dir.run_jj(["describe", "-m", "foo"]).success(); 104 // Check the setup 105 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 106 bookmark1: qpvuntsm e5ce6d9a (empty) modified bookmark1 commit 107 @origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 9b2e76de (empty) description 1 108 bookmark2: yostqsxw 88ca14a7 (empty) foo 109 @origin (behind by 1 commits): zsuskuln 38a20473 (empty) description 2 110 my-bookmark: yostqsxw 88ca14a7 (empty) foo 111 [EOF] 112 "); 113 // First dry-run. `bookmark1` should not get pushed. 114 let output = work_dir.run_jj(["git", "push", "--allow-new", "--dry-run"]); 115 insta::assert_snapshot!(output, @r" 116 ------- stderr ------- 117 Changes to push to origin: 118 Move forward bookmark bookmark2 from 38a204733702 to 88ca14a7d46f 119 Add bookmark my-bookmark to 88ca14a7d46f 120 Dry-run requested, not pushing. 121 [EOF] 122 "); 123 let output = work_dir.run_jj(["git", "push", "--allow-new"]); 124 insta::assert_snapshot!(output, @r" 125 ------- stderr ------- 126 Changes to push to origin: 127 Move forward bookmark bookmark2 from 38a204733702 to 88ca14a7d46f 128 Add bookmark my-bookmark to 88ca14a7d46f 129 [EOF] 130 "); 131 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 132 bookmark1: qpvuntsm e5ce6d9a (empty) modified bookmark1 commit 133 @origin (ahead by 1 commits, behind by 1 commits): qpvuntsm hidden 9b2e76de (empty) description 1 134 bookmark2: yostqsxw 88ca14a7 (empty) foo 135 @origin: yostqsxw 88ca14a7 (empty) foo 136 my-bookmark: yostqsxw 88ca14a7 (empty) foo 137 @origin: yostqsxw 88ca14a7 (empty) foo 138 [EOF] 139 "); 140 141 // Try pushing backwards 142 work_dir 143 .run_jj([ 144 "bookmark", 145 "set", 146 "bookmark2", 147 "-rbookmark2-", 148 "--allow-backwards", 149 ]) 150 .success(); 151 // This behavior is a strangeness of our definition of the default push revset. 152 // We could consider changing it. 153 let output = work_dir.run_jj(["git", "push"]); 154 insta::assert_snapshot!(output, @r" 155 ------- stderr ------- 156 Warning: No bookmarks found in the default push revset: remote_bookmarks(remote=origin)..@ 157 Nothing changed. 158 [EOF] 159 "); 160 // We can move a bookmark backwards 161 let output = work_dir.run_jj(["git", "push", "-bbookmark2"]); 162 insta::assert_snapshot!(output, @r" 163 ------- stderr ------- 164 Changes to push to origin: 165 Move backward bookmark bookmark2 from 88ca14a7d46f to 38a204733702 166 [EOF] 167 "); 168} 169 170#[test] 171fn test_git_push_parent_bookmark() { 172 let test_env = TestEnvironment::default(); 173 set_up(&test_env); 174 let work_dir = test_env.work_dir("local"); 175 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 176 work_dir.run_jj(["edit", "bookmark1"]).success(); 177 work_dir 178 .run_jj(["describe", "-m", "modified bookmark1 commit"]) 179 .success(); 180 work_dir 181 .run_jj(["new", "-m", "non-empty description"]) 182 .success(); 183 work_dir.write_file("file", "file"); 184 let output = work_dir.run_jj(["git", "push"]); 185 insta::assert_snapshot!(output, @r" 186 ------- stderr ------- 187 Changes to push to origin: 188 Move sideways bookmark bookmark1 from 9b2e76de3920 to 80560a3e08e2 189 [EOF] 190 "); 191} 192 193#[test] 194fn test_git_push_no_matching_bookmark() { 195 let test_env = TestEnvironment::default(); 196 set_up(&test_env); 197 let work_dir = test_env.work_dir("local"); 198 work_dir.run_jj(["new"]).success(); 199 let output = work_dir.run_jj(["git", "push"]); 200 insta::assert_snapshot!(output, @r" 201 ------- stderr ------- 202 Warning: No bookmarks found in the default push revset: remote_bookmarks(remote=origin)..@ 203 Nothing changed. 204 [EOF] 205 "); 206} 207 208#[test] 209fn test_git_push_matching_bookmark_unchanged() { 210 let test_env = TestEnvironment::default(); 211 set_up(&test_env); 212 let work_dir = test_env.work_dir("local"); 213 work_dir.run_jj(["new", "bookmark1"]).success(); 214 let output = work_dir.run_jj(["git", "push"]); 215 insta::assert_snapshot!(output, @r" 216 ------- stderr ------- 217 Warning: No bookmarks found in the default push revset: remote_bookmarks(remote=origin)..@ 218 Nothing changed. 219 [EOF] 220 "); 221} 222 223/// Test that `jj git push` without arguments pushes a bookmark to the specified 224/// remote even if it's already up to date on another remote 225/// (`remote_bookmarks(remote=<remote>)..@` vs. `remote_bookmarks()..@`). 226#[test] 227fn test_git_push_other_remote_has_bookmark() { 228 let test_env = TestEnvironment::default(); 229 set_up(&test_env); 230 let work_dir = test_env.work_dir("local"); 231 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 232 // Create another remote (but actually the same) 233 let other_remote_path = test_env 234 .env_root() 235 .join("origin") 236 .join(".jj") 237 .join("repo") 238 .join("store") 239 .join("git"); 240 work_dir 241 .run_jj([ 242 "git", 243 "remote", 244 "add", 245 "other", 246 other_remote_path.to_str().unwrap(), 247 ]) 248 .success(); 249 // Modify bookmark1 and push it to `origin` 250 work_dir.run_jj(["edit", "bookmark1"]).success(); 251 work_dir.run_jj(["describe", "-m=modified"]).success(); 252 let output = work_dir.run_jj(["git", "push"]); 253 insta::assert_snapshot!(output, @r" 254 ------- stderr ------- 255 Changes to push to origin: 256 Move sideways bookmark bookmark1 from 9b2e76de3920 to a843bfad2abb 257 [EOF] 258 "); 259 // Since it's already pushed to origin, nothing will happen if push again 260 let output = work_dir.run_jj(["git", "push"]); 261 insta::assert_snapshot!(output, @r" 262 ------- stderr ------- 263 Warning: No bookmarks found in the default push revset: remote_bookmarks(remote=origin)..@ 264 Nothing changed. 265 [EOF] 266 "); 267 // The bookmark was moved on the "other" remote as well (since it's actually the 268 // same remote), but `jj` is not aware of that since it thinks this is a 269 // different remote. So, the push should fail. 270 // 271 // But it succeeds! That's because the bookmark is created at the same location 272 // as it is on the remote. This would also work for a descendant. 273 // 274 // TODO: Saner test? 275 let output = work_dir.run_jj(["git", "push", "--allow-new", "--remote=other"]); 276 insta::assert_snapshot!(output, @r" 277 ------- stderr ------- 278 Changes to push to other: 279 Add bookmark bookmark1 to a843bfad2abb 280 [EOF] 281 "); 282} 283 284#[test] 285fn test_git_push_forward_unexpectedly_moved() { 286 let test_env = TestEnvironment::default(); 287 set_up(&test_env); 288 let work_dir = test_env.work_dir("local"); 289 290 // Move bookmark1 forward on the remote 291 let origin_dir = test_env.work_dir("origin"); 292 origin_dir 293 .run_jj(["new", "bookmark1", "-m=remote"]) 294 .success(); 295 origin_dir.write_file("remote", "remote"); 296 origin_dir 297 .run_jj(["bookmark", "set", "bookmark1", "-r@"]) 298 .success(); 299 origin_dir.run_jj(["git", "export"]).success(); 300 301 // Move bookmark1 forward to another commit locally 302 work_dir.run_jj(["new", "bookmark1", "-m=local"]).success(); 303 work_dir.write_file("local", "local"); 304 work_dir 305 .run_jj(["bookmark", "set", "bookmark1", "-r@"]) 306 .success(); 307 308 // Pushing should fail 309 let output = work_dir.run_jj(["git", "push"]); 310 insta::assert_snapshot!(output, @r" 311 ------- stderr ------- 312 Changes to push to origin: 313 Move forward bookmark bookmark1 from 9b2e76de3920 to 624f94a35f00 314 Error: Failed to push some bookmarks 315 Hint: The following references unexpectedly moved on the remote: 316 refs/heads/bookmark1 (reason: stale info) 317 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 318 [EOF] 319 [exit status: 1] 320 "); 321 322 // The ref name should be colorized 323 let output = work_dir.run_jj(["git", "push", "--color=always"]); 324 insta::assert_snapshot!(output, @r" 325 ------- stderr ------- 326 Changes to push to origin: 327 Move forward bookmark bookmark1 from 9b2e76de3920 to 624f94a35f00 328 Error: Failed to push some bookmarks 329 Hint: The following references unexpectedly moved on the remote: 330  refs/heads/bookmark1 (reason: stale info) 331 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 332 [EOF] 333 [exit status: 1] 334 "); 335} 336 337#[test] 338fn test_git_push_sideways_unexpectedly_moved() { 339 let test_env = TestEnvironment::default(); 340 set_up(&test_env); 341 let work_dir = test_env.work_dir("local"); 342 343 // Move bookmark1 forward on the remote 344 let origin_dir = test_env.work_dir("origin"); 345 origin_dir 346 .run_jj(["new", "bookmark1", "-m=remote"]) 347 .success(); 348 origin_dir.write_file("remote", "remote"); 349 origin_dir 350 .run_jj(["bookmark", "set", "bookmark1", "-r@"]) 351 .success(); 352 insta::assert_snapshot!(get_bookmark_output(&origin_dir), @r" 353 bookmark1: vruxwmqv 7ce4029e remote 354 @git (behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1 355 bookmark2: zsuskuln 38a20473 (empty) description 2 356 @git: zsuskuln 38a20473 (empty) description 2 357 [EOF] 358 "); 359 origin_dir.run_jj(["git", "export"]).success(); 360 361 // Move bookmark1 sideways to another commit locally 362 work_dir.run_jj(["new", "root()", "-m=local"]).success(); 363 work_dir.write_file("local", "local"); 364 work_dir 365 .run_jj(["bookmark", "set", "bookmark1", "--allow-backwards", "-r@"]) 366 .success(); 367 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 368 bookmark1: kmkuslsw 827b8a38 local 369 @origin (ahead by 1 commits, behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1 370 bookmark2: zsuskuln 38a20473 (empty) description 2 371 @origin: zsuskuln 38a20473 (empty) description 2 372 [EOF] 373 "); 374 375 let output = work_dir.run_jj(["git", "push"]); 376 insta::assert_snapshot!(output, @r" 377 ------- stderr ------- 378 Changes to push to origin: 379 Move sideways bookmark bookmark1 from 9b2e76de3920 to 827b8a385853 380 Error: Failed to push some bookmarks 381 Hint: The following references unexpectedly moved on the remote: 382 refs/heads/bookmark1 (reason: stale info) 383 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 384 [EOF] 385 [exit status: 1] 386 "); 387 388 // The ref name should be colorized 389 let output = work_dir.run_jj(["git", "push", "--color=always"]); 390 insta::assert_snapshot!(output, @r" 391 ------- stderr ------- 392 Changes to push to origin: 393 Move sideways bookmark bookmark1 from 9b2e76de3920 to 827b8a385853 394 Error: Failed to push some bookmarks 395 Hint: The following references unexpectedly moved on the remote: 396  refs/heads/bookmark1 (reason: stale info) 397 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 398 [EOF] 399 [exit status: 1] 400 "); 401} 402 403// This tests whether the push checks that the remote bookmarks are in expected 404// positions. 405#[test] 406fn test_git_push_deletion_unexpectedly_moved() { 407 let test_env = TestEnvironment::default(); 408 set_up(&test_env); 409 let work_dir = test_env.work_dir("local"); 410 411 // Move bookmark1 forward on the remote 412 let origin_dir = test_env.work_dir("origin"); 413 origin_dir 414 .run_jj(["new", "bookmark1", "-m=remote"]) 415 .success(); 416 origin_dir.write_file("remote", "remote"); 417 origin_dir 418 .run_jj(["bookmark", "set", "bookmark1", "-r@"]) 419 .success(); 420 insta::assert_snapshot!(get_bookmark_output(&origin_dir), @r" 421 bookmark1: vruxwmqv 7ce4029e remote 422 @git (behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1 423 bookmark2: zsuskuln 38a20473 (empty) description 2 424 @git: zsuskuln 38a20473 (empty) description 2 425 [EOF] 426 "); 427 origin_dir.run_jj(["git", "export"]).success(); 428 429 // Delete bookmark1 locally 430 work_dir 431 .run_jj(["bookmark", "delete", "bookmark1"]) 432 .success(); 433 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 434 bookmark1 (deleted) 435 @origin: qpvuntsm 9b2e76de (empty) description 1 436 bookmark2: zsuskuln 38a20473 (empty) description 2 437 @origin: zsuskuln 38a20473 (empty) description 2 438 [EOF] 439 "); 440 441 let output = work_dir.run_jj(["git", "push", "--bookmark", "bookmark1"]); 442 insta::assert_snapshot!(output, @r" 443 ------- stderr ------- 444 Changes to push to origin: 445 Delete bookmark bookmark1 from 9b2e76de3920 446 Error: Failed to push some bookmarks 447 Hint: The following references unexpectedly moved on the remote: 448 refs/heads/bookmark1 (reason: stale info) 449 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 450 [EOF] 451 [exit status: 1] 452 "); 453} 454 455#[test] 456fn test_git_push_unexpectedly_deleted() { 457 let test_env = TestEnvironment::default(); 458 set_up(&test_env); 459 let work_dir = test_env.work_dir("local"); 460 461 // Delete bookmark1 forward on the remote 462 let origin_dir = test_env.work_dir("origin"); 463 origin_dir 464 .run_jj(["bookmark", "delete", "bookmark1"]) 465 .success(); 466 insta::assert_snapshot!(get_bookmark_output(&origin_dir), @r" 467 bookmark1 (deleted) 468 @git: qpvuntsm 9b2e76de (empty) description 1 469 bookmark2: zsuskuln 38a20473 (empty) description 2 470 @git: zsuskuln 38a20473 (empty) description 2 471 [EOF] 472 "); 473 origin_dir.run_jj(["git", "export"]).success(); 474 475 // Move bookmark1 sideways to another commit locally 476 work_dir.run_jj(["new", "root()", "-m=local"]).success(); 477 work_dir.write_file("local", "local"); 478 work_dir 479 .run_jj(["bookmark", "set", "bookmark1", "--allow-backwards", "-r@"]) 480 .success(); 481 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 482 bookmark1: kpqxywon 09919fb0 local 483 @origin (ahead by 1 commits, behind by 1 commits): qpvuntsm 9b2e76de (empty) description 1 484 bookmark2: zsuskuln 38a20473 (empty) description 2 485 @origin: zsuskuln 38a20473 (empty) description 2 486 [EOF] 487 "); 488 489 // Pushing a moved bookmark fails if deleted on remote 490 let output = work_dir.run_jj(["git", "push"]); 491 insta::assert_snapshot!(output, @r" 492 ------- stderr ------- 493 Changes to push to origin: 494 Move sideways bookmark bookmark1 from 9b2e76de3920 to 09919fb051bf 495 Error: Failed to push some bookmarks 496 Hint: The following references unexpectedly moved on the remote: 497 refs/heads/bookmark1 (reason: stale info) 498 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 499 [EOF] 500 [exit status: 1] 501 "); 502 503 work_dir 504 .run_jj(["bookmark", "delete", "bookmark1"]) 505 .success(); 506 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 507 bookmark1 (deleted) 508 @origin: qpvuntsm 9b2e76de (empty) description 1 509 bookmark2: zsuskuln 38a20473 (empty) description 2 510 @origin: zsuskuln 38a20473 (empty) description 2 511 [EOF] 512 "); 513 514 // git does not allow to push a deleted bookmark if we expect it to exist even 515 // though it was already deleted 516 let output = work_dir.run_jj(["git", "push", "-bbookmark1"]); 517 insta::assert_snapshot!(output, @r" 518 ------- stderr ------- 519 Changes to push to origin: 520 Delete bookmark bookmark1 from 9b2e76de3920 521 Error: Failed to push some bookmarks 522 Hint: The following references unexpectedly moved on the remote: 523 refs/heads/bookmark1 (reason: stale info) 524 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 525 [EOF] 526 [exit status: 1] 527 "); 528} 529 530#[test] 531fn test_git_push_creation_unexpectedly_already_exists() { 532 let test_env = TestEnvironment::default(); 533 set_up(&test_env); 534 let work_dir = test_env.work_dir("local"); 535 536 // Forget bookmark1 locally 537 work_dir 538 .run_jj(["bookmark", "forget", "--include-remotes", "bookmark1"]) 539 .success(); 540 541 // Create a new branh1 542 work_dir 543 .run_jj(["new", "root()", "-m=new bookmark1"]) 544 .success(); 545 work_dir.write_file("local", "local"); 546 work_dir 547 .run_jj(["bookmark", "create", "-r@", "bookmark1"]) 548 .success(); 549 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 550 bookmark1: yostqsxw a43cb801 new bookmark1 551 bookmark2: zsuskuln 38a20473 (empty) description 2 552 @origin: zsuskuln 38a20473 (empty) description 2 553 [EOF] 554 "); 555 556 let output = work_dir.run_jj(["git", "push", "--allow-new"]); 557 insta::assert_snapshot!(output, @r" 558 ------- stderr ------- 559 Changes to push to origin: 560 Add bookmark bookmark1 to a43cb8011c85 561 Error: Failed to push some bookmarks 562 Hint: The following references unexpectedly moved on the remote: 563 refs/heads/bookmark1 (reason: stale info) 564 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 565 [EOF] 566 [exit status: 1] 567 "); 568} 569 570#[test] 571fn test_git_push_locally_created_and_rewritten() { 572 let test_env = TestEnvironment::default(); 573 set_up(&test_env); 574 let work_dir = test_env.work_dir("local"); 575 // Ensure that remote bookmarks aren't tracked automatically 576 test_env.add_config("git.auto-local-bookmark = false"); 577 578 // Push locally-created bookmark 579 work_dir.run_jj(["new", "root()", "-mlocal 1"]).success(); 580 work_dir 581 .run_jj(["bookmark", "create", "-r@", "my"]) 582 .success(); 583 let output = work_dir.run_jj(["git", "push"]); 584 insta::assert_snapshot!(output, @r" 585 ------- stderr ------- 586 Warning: Refusing to create new remote bookmark my@origin 587 Hint: Use --allow-new to push new bookmark. Use --remote to specify the remote to push to. 588 Nothing changed. 589 [EOF] 590 "); 591 // Either --allow-new or git.push-new-bookmarks=true should work 592 let output = work_dir.run_jj(["git", "push", "--allow-new", "--dry-run"]); 593 insta::assert_snapshot!(output, @r" 594 ------- stderr ------- 595 Changes to push to origin: 596 Add bookmark my to e0cba5e497ee 597 Dry-run requested, not pushing. 598 [EOF] 599 "); 600 let output = work_dir.run_jj(["git", "push", "--config=git.push-new-bookmarks=true"]); 601 insta::assert_snapshot!(output, @r" 602 ------- stderr ------- 603 Changes to push to origin: 604 Add bookmark my to e0cba5e497ee 605 [EOF] 606 "); 607 608 // Rewrite it and push again, which would fail if the pushed bookmark weren't 609 // set to "tracking" 610 work_dir.run_jj(["describe", "-mlocal 2"]).success(); 611 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 612 bookmark1: qpvuntsm 9b2e76de (empty) description 1 613 @origin: qpvuntsm 9b2e76de (empty) description 1 614 bookmark2: zsuskuln 38a20473 (empty) description 2 615 @origin: zsuskuln 38a20473 (empty) description 2 616 my: vruxwmqv 5eb416c1 (empty) local 2 617 @origin (ahead by 1 commits, behind by 1 commits): vruxwmqv hidden e0cba5e4 (empty) local 1 618 [EOF] 619 "); 620 let output = work_dir.run_jj(["git", "push"]); 621 insta::assert_snapshot!(output, @r" 622 ------- stderr ------- 623 Changes to push to origin: 624 Move sideways bookmark my from e0cba5e497ee to 5eb416c1ff97 625 [EOF] 626 "); 627} 628 629#[test] 630fn test_git_push_multiple() { 631 let test_env = TestEnvironment::default(); 632 set_up(&test_env); 633 let work_dir = test_env.work_dir("local"); 634 work_dir 635 .run_jj(["bookmark", "delete", "bookmark1"]) 636 .success(); 637 work_dir 638 .run_jj(["bookmark", "set", "--allow-backwards", "bookmark2", "-r@"]) 639 .success(); 640 work_dir 641 .run_jj(["bookmark", "create", "-r@", "my-bookmark"]) 642 .success(); 643 work_dir.run_jj(["describe", "-m", "foo"]).success(); 644 // Check the setup 645 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 646 bookmark1 (deleted) 647 @origin: qpvuntsm 9b2e76de (empty) description 1 648 bookmark2: yqosqzyt 352fa187 (empty) foo 649 @origin (ahead by 1 commits, behind by 1 commits): zsuskuln 38a20473 (empty) description 2 650 my-bookmark: yqosqzyt 352fa187 (empty) foo 651 [EOF] 652 "); 653 // First dry-run 654 let output = work_dir.run_jj(["git", "push", "--all", "--deleted", "--dry-run"]); 655 insta::assert_snapshot!(output, @r" 656 ------- stderr ------- 657 Changes to push to origin: 658 Delete bookmark bookmark1 from 9b2e76de3920 659 Move sideways bookmark bookmark2 from 38a204733702 to 352fa1879f75 660 Add bookmark my-bookmark to 352fa1879f75 661 Dry-run requested, not pushing. 662 [EOF] 663 "); 664 // Dry run requesting two specific bookmarks 665 let output = work_dir.run_jj([ 666 "git", 667 "push", 668 "--allow-new", 669 "-b=bookmark1", 670 "-b=my-bookmark", 671 "--dry-run", 672 ]); 673 insta::assert_snapshot!(output, @r" 674 ------- stderr ------- 675 Changes to push to origin: 676 Delete bookmark bookmark1 from 9b2e76de3920 677 Add bookmark my-bookmark to 352fa1879f75 678 Dry-run requested, not pushing. 679 [EOF] 680 "); 681 // Dry run requesting two specific bookmarks twice 682 let output = work_dir.run_jj([ 683 "git", 684 "push", 685 "--allow-new", 686 "-b=bookmark1", 687 "-b=my-bookmark", 688 "-b=bookmark1", 689 "-b=glob:my-*", 690 "--dry-run", 691 ]); 692 insta::assert_snapshot!(output, @r" 693 ------- stderr ------- 694 Changes to push to origin: 695 Delete bookmark bookmark1 from 9b2e76de3920 696 Add bookmark my-bookmark to 352fa1879f75 697 Dry-run requested, not pushing. 698 [EOF] 699 "); 700 // Dry run with glob pattern 701 let output = work_dir.run_jj(["git", "push", "-b=glob:bookmark?", "--dry-run"]); 702 insta::assert_snapshot!(output, @r" 703 ------- stderr ------- 704 Changes to push to origin: 705 Delete bookmark bookmark1 from 9b2e76de3920 706 Move sideways bookmark bookmark2 from 38a204733702 to 352fa1879f75 707 Dry-run requested, not pushing. 708 [EOF] 709 "); 710 711 // Unmatched bookmark name is error 712 let output = work_dir.run_jj(["git", "push", "-b=foo"]); 713 insta::assert_snapshot!(output, @r" 714 ------- stderr ------- 715 Error: No such bookmark: foo 716 [EOF] 717 [exit status: 1] 718 "); 719 let output = work_dir.run_jj(["git", "push", "-b=foo", "-b=glob:?bookmark"]); 720 insta::assert_snapshot!(output, @r" 721 ------- stderr ------- 722 Error: No matching bookmarks for patterns: foo, ?bookmark 723 [EOF] 724 [exit status: 1] 725 "); 726 727 // --deleted is required to push deleted bookmarks even with --all 728 let output = work_dir.run_jj(["git", "push", "--all", "--dry-run"]); 729 insta::assert_snapshot!(output, @r" 730 ------- stderr ------- 731 Warning: Refusing to push deleted bookmark bookmark1 732 Hint: Push deleted bookmarks with --deleted or forget the bookmark to suppress this warning. 733 Changes to push to origin: 734 Move sideways bookmark bookmark2 from 38a204733702 to 352fa1879f75 735 Add bookmark my-bookmark to 352fa1879f75 736 Dry-run requested, not pushing. 737 [EOF] 738 "); 739 let output = work_dir.run_jj(["git", "push", "--all", "--deleted", "--dry-run"]); 740 insta::assert_snapshot!(output, @r" 741 ------- stderr ------- 742 Changes to push to origin: 743 Delete bookmark bookmark1 from 9b2e76de3920 744 Move sideways bookmark bookmark2 from 38a204733702 to 352fa1879f75 745 Add bookmark my-bookmark to 352fa1879f75 746 Dry-run requested, not pushing. 747 [EOF] 748 "); 749 750 let output = work_dir.run_jj(["git", "push", "--all", "--deleted"]); 751 insta::assert_snapshot!(output, @r" 752 ------- stderr ------- 753 Changes to push to origin: 754 Delete bookmark bookmark1 from 9b2e76de3920 755 Move sideways bookmark bookmark2 from 38a204733702 to 352fa1879f75 756 Add bookmark my-bookmark to 352fa1879f75 757 [EOF] 758 "); 759 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 760 bookmark2: yqosqzyt 352fa187 (empty) foo 761 @origin: yqosqzyt 352fa187 (empty) foo 762 my-bookmark: yqosqzyt 352fa187 (empty) foo 763 @origin: yqosqzyt 352fa187 (empty) foo 764 [EOF] 765 "); 766 let output = work_dir.run_jj(["log", "-rall()"]); 767 insta::assert_snapshot!(output, @r" 768 @ yqosqzyt test.user@example.com 2001-02-03 08:05:17 bookmark2 my-bookmark 352fa187 769 │ (empty) foo 770 │ ○ zsuskuln test.user@example.com 2001-02-03 08:05:10 38a20473 771 ├─╯ (empty) description 2 772 │ ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 9b2e76de 773 ├─╯ (empty) description 1 774 ◆ zzzzzzzz root() 00000000 775 [EOF] 776 "); 777} 778 779#[test] 780fn test_git_push_changes() { 781 let test_env = TestEnvironment::default(); 782 set_up(&test_env); 783 let work_dir = test_env.work_dir("local"); 784 work_dir.run_jj(["describe", "-m", "foo"]).success(); 785 work_dir.write_file("file", "contents"); 786 work_dir.run_jj(["new", "-m", "bar"]).success(); 787 work_dir.write_file("file", "modified"); 788 789 let output = work_dir.run_jj(["git", "push", "--change", "@"]); 790 insta::assert_snapshot!(output, @r" 791 ------- stderr ------- 792 Creating bookmark push-yostqsxwqrlt for revision yostqsxwqrlt 793 Changes to push to origin: 794 Add bookmark push-yostqsxwqrlt to 916414184c47 795 [EOF] 796 "); 797 // test pushing two changes at once 798 work_dir.write_file("file", "modified2"); 799 let output = work_dir.run_jj(["git", "push", "-c=(@|@-)"]); 800 insta::assert_snapshot!(output, @r" 801 ------- stderr ------- 802 Error: Revset `(@|@-)` resolved to more than one revision 803 Hint: The revset `(@|@-)` resolved to these revisions: 804 yostqsxw 2723f611 push-yostqsxwqrlt* | bar 805 yqosqzyt 0f8164cd foo 806 Hint: Prefix the expression with `all:` to allow any number of revisions (i.e. `all:(@|@-)`). 807 [EOF] 808 [exit status: 1] 809 "); 810 // test pushing two changes at once, part 2 811 let output = work_dir.run_jj(["git", "push", "-c=all:(@|@-)"]); 812 insta::assert_snapshot!(output, @r" 813 ------- stderr ------- 814 Creating bookmark push-yqosqzytrlsw for revision yqosqzytrlsw 815 Changes to push to origin: 816 Move sideways bookmark push-yostqsxwqrlt from 916414184c47 to 2723f6111cb9 817 Add bookmark push-yqosqzytrlsw to 0f8164cd580b 818 [EOF] 819 "); 820 // specifying the same change twice doesn't break things 821 work_dir.write_file("file", "modified3"); 822 let output = work_dir.run_jj(["git", "push", "-c=all:(@|@)"]); 823 insta::assert_snapshot!(output, @r" 824 ------- stderr ------- 825 Changes to push to origin: 826 Move sideways bookmark push-yostqsxwqrlt from 2723f6111cb9 to 7436a8a600a4 827 [EOF] 828 "); 829 830 // specifying the same bookmark with --change/--bookmark doesn't break things 831 work_dir.write_file("file", "modified4"); 832 let output = work_dir.run_jj(["git", "push", "-c=@", "-b=push-yostqsxwqrlt"]); 833 insta::assert_snapshot!(output, @r" 834 ------- stderr ------- 835 Changes to push to origin: 836 Move sideways bookmark push-yostqsxwqrlt from 7436a8a600a4 to a8b93bdd0f68 837 [EOF] 838 "); 839 840 // try again with --change that could move the bookmark forward 841 work_dir.write_file("file", "modified5"); 842 work_dir 843 .run_jj([ 844 "bookmark", 845 "set", 846 "-r=@-", 847 "--allow-backwards", 848 "push-yostqsxwqrlt", 849 ]) 850 .success(); 851 let output = work_dir.run_jj(["status"]); 852 insta::assert_snapshot!(output, @r" 853 Working copy changes: 854 M file 855 Working copy (@) : yostqsxw 4b18f5ea bar 856 Parent commit (@-): yqosqzyt 0f8164cd push-yostqsxwqrlt* push-yqosqzytrlsw | foo 857 [EOF] 858 "); 859 let output = work_dir.run_jj(["git", "push", "-c=@", "-b=push-yostqsxwqrlt"]); 860 insta::assert_snapshot!(output, @r" 861 ------- stderr ------- 862 Error: Bookmark already exists: push-yostqsxwqrlt 863 Hint: Use 'jj bookmark move' to move it, and 'jj git push -b push-yostqsxwqrlt [--allow-new]' to push it 864 [EOF] 865 [exit status: 1] 866 "); 867 let output = work_dir.run_jj(["status"]); 868 insta::assert_snapshot!(output, @r" 869 Working copy changes: 870 M file 871 Working copy (@) : yostqsxw 4b18f5ea bar 872 Parent commit (@-): yqosqzyt 0f8164cd push-yostqsxwqrlt* push-yqosqzytrlsw | foo 873 [EOF] 874 "); 875 876 // Test changing `git.push-bookmark-prefix`. It causes us to push again. 877 let output = work_dir.run_jj([ 878 "git", 879 "push", 880 "--config=git.push-bookmark-prefix=test-", 881 "--change=@", 882 ]); 883 insta::assert_snapshot!(output, @r" 884 ------- stderr ------- 885 Creating bookmark test-yostqsxwqrlt for revision yostqsxwqrlt 886 Changes to push to origin: 887 Add bookmark test-yostqsxwqrlt to 4b18f5ea2994 888 [EOF] 889 "); 890} 891 892#[test] 893fn test_git_push_changes_with_name() { 894 let test_env = TestEnvironment::default(); 895 set_up(&test_env); 896 let work_dir = test_env.work_dir("local"); 897 work_dir.run_jj(["describe", "-m", "foo"]).success(); 898 work_dir.write_file("file", "contents"); 899 work_dir.run_jj(["new", "-m", "pushed"]).success(); 900 work_dir.write_file("file", "modified"); 901 902 // Normal behavior. 903 let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]); 904 insta::assert_snapshot!(output, @r" 905 ------- stderr ------- 906 Changes to push to origin: 907 Add bookmark b1 to 5f4f9a466c96 908 [EOF] 909 "); 910 // Spaces before the = sign are treated like part of the bookmark name and such 911 // bookmarks cannot be pushed. 912 let output = work_dir.run_jj(["git", "push", "--named", "b1 = @"]); 913 insta::assert_snapshot!(output, @r" 914 ------- stderr ------- 915 Error: Could not parse 'b1 ' as a bookmark name 916 Caused by: 917 1: Failed to parse bookmark name: Syntax error 918 2: --> 1:3 919 | 920 1 | b1 921 | ^--- 922 | 923 = expected <EOI> 924 Hint: For example, `--named myfeature=@` is valid syntax 925 [EOF] 926 [exit status: 2] 927 "); 928 // test pushing a change with an empty name 929 let output = work_dir.run_jj(["git", "push", "--named", "=@"]); 930 insta::assert_snapshot!(output, @r" 931 ------- stderr ------- 932 Error: Argument '=@' must have the form NAME=REVISION, with both NAME and REVISION non-empty 933 Hint: For example, `--named myfeature=@` is valid syntax 934 [EOF] 935 [exit status: 2] 936 "); 937 // Unparsable name 938 let output = work_dir.run_jj(["git", "push", "--named", ":!:=@"]); 939 insta::assert_snapshot!(output, @r" 940 ------- stderr ------- 941 Error: Could not parse ':!:' as a bookmark name 942 Caused by: 943 1: Failed to parse bookmark name: Syntax error 944 2: --> 1:1 945 | 946 1 | :!: 947 | ^--- 948 | 949 = expected <identifier>, <string_literal>, or <raw_string_literal> 950 Hint: For example, `--named myfeature=@` is valid syntax 951 [EOF] 952 [exit status: 2] 953 "); 954 // test pushing a change with an empty revision 955 let output = work_dir.run_jj(["git", "push", "--named", "b2="]); 956 insta::assert_snapshot!(output, @r" 957 ------- stderr ------- 958 Error: Argument 'b2=' must have the form NAME=REVISION, with both NAME and REVISION non-empty 959 Hint: For example, `--named myfeature=@` is valid syntax 960 [EOF] 961 [exit status: 2] 962 "); 963 // test pushing a change with no equals sign 964 let output = work_dir.run_jj(["git", "push", "--named", "b2"]); 965 insta::assert_snapshot!(output, @r" 966 ------- stderr ------- 967 Error: Argument 'b2' must include '=' and have the form NAME=REVISION 968 Hint: For example, `--named myfeature=@` is valid syntax 969 [EOF] 970 [exit status: 2] 971 "); 972 973 // test pushing the same change with the same name again 974 let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]); 975 insta::assert_snapshot!(output, @r" 976 ------- stderr ------- 977 Error: Bookmark already exists: b1 978 Hint: Use 'jj bookmark move' to move it, and 'jj git push -b b1 [--allow-new]' to push it 979 [EOF] 980 [exit status: 1] 981 "); 982 // test pushing two changes at once 983 work_dir.write_file("file", "modified2"); 984 let output = work_dir.run_jj(["git", "push", "--named=b2=all:(@|@-)"]); 985 insta::assert_snapshot!(output, @r" 986 ------- stderr ------- 987 Error: Revset `all:(@|@-)` resolved to more than one revision 988 Hint: The revset `all:(@|@-)` resolved to these revisions: 989 yostqsxw 1b2bd869 b1* | pushed 990 yqosqzyt 0f8164cd foo 991 [EOF] 992 [exit status: 1] 993 "); 994 995 // specifying the same bookmark with --named/--bookmark 996 work_dir.write_file("file", "modified4"); 997 let output = work_dir.run_jj(["git", "push", "--named=b2=@", "-b=b2"]); 998 insta::assert_snapshot!(output, @r" 999 ------- stderr ------- 1000 Changes to push to origin: 1001 Add bookmark b2 to 95ba7bdacb38 1002 [EOF] 1003 "); 1004} 1005 1006#[test] 1007fn test_git_push_changes_with_name_deleted_tracked() { 1008 let test_env = TestEnvironment::default(); 1009 set_up(&test_env); 1010 // Unset immutable_heads so that untracking branches does not move the working 1011 // copy 1012 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 1013 let work_dir = test_env.work_dir("local"); 1014 // Create a second empty remote `another_remote` 1015 test_env 1016 .run_jj_in(".", ["git", "init", "another_remote"]) 1017 .success(); 1018 let another_remote_git_repo_path = 1019 git_repo_dir_for_jj_repo(&test_env.work_dir("another_remote")); 1020 work_dir 1021 .run_jj([ 1022 "git", 1023 "remote", 1024 "add", 1025 "another_remote", 1026 another_remote_git_repo_path.to_str().unwrap(), 1027 ]) 1028 .success(); 1029 work_dir.run_jj(["describe", "-m", "foo"]).success(); 1030 work_dir.write_file("file", "contents"); 1031 work_dir.run_jj(["new", "-m", "pushed"]).success(); 1032 work_dir.write_file("file", "modified"); 1033 // Normal push as part of the test setup 1034 let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]); 1035 insta::assert_snapshot!(output, @r" 1036 ------- stderr ------- 1037 Changes to push to origin: 1038 Add bookmark b1 to 08f401c17d51 1039 [EOF] 1040 "); 1041 work_dir.run_jj(["bookmark", "delete", "b1"]).success(); 1042 1043 // Test the setup 1044 let output = work_dir 1045 .run_jj(["bookmark", "list", "--all", "b1"]) 1046 .success(); 1047 insta::assert_snapshot!(output, @r" 1048 b1 (deleted) 1049 @origin: kpqxywon 08f401c1 pushed 1050 [EOF] 1051 ------- stderr ------- 1052 Hint: Bookmarks marked as deleted can be *deleted permanently* on the remote by running `jj git push --deleted`. Use `jj bookmark forget` if you don't want that. 1053 [EOF] 1054 "); 1055 1056 // Can't push `b1` with --named to the same or another remote if it's deleted 1057 // locally and still tracked on `origin` 1058 let output = work_dir.run_jj(["git", "push", "--named", "b1=@", "--remote=another_remote"]); 1059 insta::assert_snapshot!(output, @r" 1060 ------- stderr ------- 1061 Error: Tracked remote bookmarks exist for deleted bookmark: b1 1062 Hint: Use `jj bookmark set` to recreate the local bookmark. Run `jj bookmark untrack 'glob:b1@*'` to disassociate them. 1063 [EOF] 1064 [exit status: 1] 1065 "); 1066 let output = work_dir.run_jj(["git", "push", "--named", "b1=@", "--remote=origin"]); 1067 insta::assert_snapshot!(output, @r" 1068 ------- stderr ------- 1069 Error: Tracked remote bookmarks exist for deleted bookmark: b1 1070 Hint: Use `jj bookmark set` to recreate the local bookmark. Run `jj bookmark untrack 'glob:b1@*'` to disassociate them. 1071 [EOF] 1072 [exit status: 1] 1073 "); 1074 1075 // OK to push to a different remote once the bookmark is no longer tracked on 1076 // `origin` 1077 work_dir 1078 .run_jj(["bookmark", "untrack", "b1@origin"]) 1079 .success(); 1080 let output = work_dir 1081 .run_jj(["bookmark", "list", "--all", "b1"]) 1082 .success(); 1083 insta::assert_snapshot!(output, @r" 1084 b1@origin: kpqxywon 08f401c1 pushed 1085 [EOF] 1086 "); 1087 let output = work_dir.run_jj(["git", "push", "--named", "b1=@", "--remote=another_remote"]); 1088 insta::assert_snapshot!(output, @r" 1089 ------- stderr ------- 1090 Changes to push to another_remote: 1091 Add bookmark b1 to 08f401c17d51 1092 [EOF] 1093 "); 1094 let output = work_dir 1095 .run_jj(["bookmark", "list", "--all", "b1"]) 1096 .success(); 1097 insta::assert_snapshot!(output, @r" 1098 b1: kpqxywon 08f401c1 pushed 1099 @another_remote: kpqxywon 08f401c1 pushed 1100 b1@origin: kpqxywon 08f401c1 pushed 1101 [EOF] 1102 "); 1103} 1104 1105#[test] 1106fn test_git_push_changes_with_name_untracked_or_forgotten() { 1107 let test_env = TestEnvironment::default(); 1108 set_up(&test_env); 1109 let work_dir = test_env.work_dir("local"); 1110 // Unset immutable_heads so that untracking branches does not move the working 1111 // copy 1112 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 1113 work_dir.run_jj(["describe", "-m", "parent"]).success(); 1114 work_dir.run_jj(["new", "-m", "pushed_to_remote"]).success(); 1115 work_dir.write_file("file", "contents"); 1116 work_dir 1117 .run_jj(["new", "-m", "child", "--no-edit"]) 1118 .success(); 1119 work_dir.write_file("file", "modified"); 1120 1121 // Push a branch to a remote, but forget the local branch 1122 work_dir 1123 .run_jj(["git", "push", "--named", "b1=@"]) 1124 .success(); 1125 work_dir 1126 .run_jj(["bookmark", "untrack", "b1@origin"]) 1127 .success(); 1128 work_dir.run_jj(["bookmark", "delete", "b1"]).success(); 1129 1130 let output = work_dir 1131 .run_jj(&[ 1132 "log", 1133 "-r=::@+", 1134 r#"-T=separate(" ", commit_id.shortest(3), bookmarks, description)"#, 1135 ]) 1136 .success(); 1137 insta::assert_snapshot!(output, @r" 1138 ○ 9a0 child 1139 @ 767 b1@origin pushed_to_remote 1140 ○ aa9 parent 1141 ◆ 000 1142 [EOF] 1143 "); 1144 let output = work_dir 1145 .run_jj(["bookmark", "list", "--all", "b1"]) 1146 .success(); 1147 insta::assert_snapshot!(output, @r" 1148 b1@origin: yostqsxw 767b63a5 pushed_to_remote 1149 [EOF] 1150 "); 1151 1152 let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]); 1153 insta::assert_snapshot!(output, @r" 1154 ------- stderr ------- 1155 Error: Non-tracking remote bookmark b1@origin exists 1156 Hint: Run `jj bookmark track b1@origin` to import the remote bookmark. 1157 [EOF] 1158 [exit status: 1] 1159 "); 1160 1161 let output = work_dir.run_jj(["git", "push", "--named", "b1=@+"]); 1162 insta::assert_snapshot!(output, @r" 1163 ------- stderr ------- 1164 Error: Non-tracking remote bookmark b1@origin exists 1165 Hint: Run `jj bookmark track b1@origin` to import the remote bookmark. 1166 [EOF] 1167 [exit status: 1] 1168 "); 1169 1170 // The bookmarked is still pushed to the remote, but let's entirely forget 1171 // it. In other words, let's forget the remote-tracking bookmarks. 1172 work_dir 1173 .run_jj(&["bookmark", "forget", "b1", "--include-remotes"]) 1174 .success(); 1175 let output = work_dir 1176 .run_jj(["bookmark", "list", "--all", "b1"]) 1177 .success(); 1178 insta::assert_snapshot!(output, @""); 1179 1180 // Make sure push still errors if we try to push a bookmark with the same name 1181 // to a different location. 1182 let output = work_dir.run_jj(["git", "push", "--named", "b1=@-"]); 1183 insta::assert_snapshot!(output, @r" 1184 ------- stderr ------- 1185 Changes to push to origin: 1186 Add bookmark b1 to aa9ad64cb4ce 1187 Error: Failed to push some bookmarks 1188 Hint: The following references unexpectedly moved on the remote: 1189 refs/heads/b1 (reason: stale info) 1190 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 1191 [EOF] 1192 [exit status: 1] 1193 "); 1194 1195 // The bookmark is still forgotten 1196 let output = work_dir.run_jj(["bookmark", "list", "--all", "b1"]); 1197 insta::assert_snapshot!(output, @""); 1198 let output = work_dir.run_jj(["git", "push", "--named", "b1=@+"]); 1199 insta::assert_snapshot!(output, @r" 1200 ------- stderr ------- 1201 Changes to push to origin: 1202 Add bookmark b1 to 9a0f76645905 1203 Error: Failed to push some bookmarks 1204 Hint: The following references unexpectedly moved on the remote: 1205 refs/heads/b1 (reason: stale info) 1206 Hint: Try fetching from the remote, then make the bookmark point to where you want it to be, and push again. 1207 [EOF] 1208 [exit status: 1] 1209 "); 1210 // In this case, pushing the bookmark to the same location where it already is 1211 // succeeds. TODO: This seems pretty safe, but perhaps it should still show 1212 // an error or some sort of warning? 1213 let output = work_dir.run_jj(["git", "push", "--named", "b1=@"]); 1214 insta::assert_snapshot!(output, @r" 1215 ------- stderr ------- 1216 Changes to push to origin: 1217 Add bookmark b1 to 767b63a598e1 1218 [EOF] 1219 "); 1220} 1221 1222#[test] 1223fn test_git_push_revisions() { 1224 let test_env = TestEnvironment::default(); 1225 set_up(&test_env); 1226 let work_dir = test_env.work_dir("local"); 1227 work_dir.run_jj(["describe", "-m", "foo"]).success(); 1228 work_dir.write_file("file", "contents"); 1229 work_dir.run_jj(["new", "-m", "bar"]).success(); 1230 work_dir 1231 .run_jj(["bookmark", "create", "-r@", "bookmark-1"]) 1232 .success(); 1233 work_dir.write_file("file", "modified"); 1234 work_dir.run_jj(["new", "-m", "baz"]).success(); 1235 work_dir 1236 .run_jj(["bookmark", "create", "-r@", "bookmark-2a"]) 1237 .success(); 1238 work_dir 1239 .run_jj(["bookmark", "create", "-r@", "bookmark-2b"]) 1240 .success(); 1241 work_dir.write_file("file", "modified again"); 1242 1243 // Push an empty set 1244 let output = work_dir.run_jj(["git", "push", "--allow-new", "-r=none()"]); 1245 insta::assert_snapshot!(output, @r" 1246 ------- stderr ------- 1247 Warning: No bookmarks point to the specified revisions: none() 1248 Nothing changed. 1249 [EOF] 1250 "); 1251 // Push a revision with no bookmarks 1252 let output = work_dir.run_jj(["git", "push", "--allow-new", "-r=@--"]); 1253 insta::assert_snapshot!(output, @r" 1254 ------- stderr ------- 1255 Warning: No bookmarks point to the specified revisions: @-- 1256 Nothing changed. 1257 [EOF] 1258 "); 1259 // Push a revision with a single bookmark 1260 let output = work_dir.run_jj(["git", "push", "--allow-new", "-r=@-", "--dry-run"]); 1261 insta::assert_snapshot!(output, @r" 1262 ------- stderr ------- 1263 Changes to push to origin: 1264 Add bookmark bookmark-1 to e76139e55e1e 1265 Dry-run requested, not pushing. 1266 [EOF] 1267 "); 1268 // Push multiple revisions of which some have bookmarks 1269 let output = work_dir.run_jj(["git", "push", "--allow-new", "-r=@--", "-r=@-", "--dry-run"]); 1270 insta::assert_snapshot!(output, @r" 1271 ------- stderr ------- 1272 Warning: No bookmarks point to the specified revisions: @-- 1273 Changes to push to origin: 1274 Add bookmark bookmark-1 to e76139e55e1e 1275 Dry-run requested, not pushing. 1276 [EOF] 1277 "); 1278 // Push a revision with a multiple bookmarks 1279 let output = work_dir.run_jj(["git", "push", "--allow-new", "-r=@", "--dry-run"]); 1280 insta::assert_snapshot!(output, @r" 1281 ------- stderr ------- 1282 Changes to push to origin: 1283 Add bookmark bookmark-2a to 57d822f901bb 1284 Add bookmark bookmark-2b to 57d822f901bb 1285 Dry-run requested, not pushing. 1286 [EOF] 1287 "); 1288 // Repeating a commit doesn't result in repeated messages about the bookmark 1289 let output = work_dir.run_jj(["git", "push", "--allow-new", "-r=@-", "-r=@-", "--dry-run"]); 1290 insta::assert_snapshot!(output, @r" 1291 ------- stderr ------- 1292 Changes to push to origin: 1293 Add bookmark bookmark-1 to e76139e55e1e 1294 Dry-run requested, not pushing. 1295 [EOF] 1296 "); 1297} 1298 1299#[test] 1300fn test_git_push_mixed() { 1301 let test_env = TestEnvironment::default(); 1302 set_up(&test_env); 1303 let work_dir = test_env.work_dir("local"); 1304 work_dir.run_jj(["describe", "-m", "foo"]).success(); 1305 work_dir.write_file("file", "contents"); 1306 work_dir.run_jj(["new", "-m", "bar"]).success(); 1307 work_dir 1308 .run_jj(["bookmark", "create", "-r@", "bookmark-1"]) 1309 .success(); 1310 work_dir.write_file("file", "modified"); 1311 work_dir.run_jj(["new", "-m", "baz"]).success(); 1312 work_dir 1313 .run_jj(["bookmark", "create", "-r@", "bookmark-2a"]) 1314 .success(); 1315 work_dir 1316 .run_jj(["bookmark", "create", "-r@", "bookmark-2b"]) 1317 .success(); 1318 work_dir.write_file("file", "modified again"); 1319 1320 // --allow-new is not implied for --bookmark=.. and -r=.. 1321 let output = work_dir.run_jj([ 1322 "git", 1323 "push", 1324 "--change=@--", 1325 "--bookmark=bookmark-1", 1326 "-r=@", 1327 ]); 1328 insta::assert_snapshot!(output, @r" 1329 ------- stderr ------- 1330 Creating bookmark push-yqosqzytrlsw for revision yqosqzytrlsw 1331 Error: Refusing to create new remote bookmark bookmark-1@origin 1332 Hint: Use --allow-new to push new bookmark. Use --remote to specify the remote to push to. 1333 [EOF] 1334 [exit status: 1] 1335 "); 1336 1337 let output = work_dir.run_jj([ 1338 "git", 1339 "push", 1340 "--allow-new", 1341 "--change=@--", 1342 "--bookmark=bookmark-1", 1343 "-r=@", 1344 ]); 1345 insta::assert_snapshot!(output, @r" 1346 ------- stderr ------- 1347 Creating bookmark push-yqosqzytrlsw for revision yqosqzytrlsw 1348 Changes to push to origin: 1349 Add bookmark push-yqosqzytrlsw to 0f8164cd580b 1350 Add bookmark bookmark-1 to e76139e55e1e 1351 Add bookmark bookmark-2a to 57d822f901bb 1352 Add bookmark bookmark-2b to 57d822f901bb 1353 [EOF] 1354 "); 1355} 1356 1357#[test] 1358fn test_git_push_unsnapshotted_change() { 1359 let test_env = TestEnvironment::default(); 1360 set_up(&test_env); 1361 let work_dir = test_env.work_dir("local"); 1362 work_dir.run_jj(["describe", "-m", "foo"]).success(); 1363 work_dir.write_file("file", "contents"); 1364 work_dir.run_jj(["git", "push", "--change", "@"]).success(); 1365 work_dir.write_file("file", "modified"); 1366 work_dir.run_jj(["git", "push", "--change", "@"]).success(); 1367} 1368 1369#[test] 1370fn test_git_push_conflict() { 1371 let test_env = TestEnvironment::default(); 1372 set_up(&test_env); 1373 let work_dir = test_env.work_dir("local"); 1374 work_dir.write_file("file", "first"); 1375 work_dir.run_jj(["commit", "-m", "first"]).success(); 1376 work_dir.write_file("file", "second"); 1377 work_dir.run_jj(["commit", "-m", "second"]).success(); 1378 work_dir.write_file("file", "third"); 1379 work_dir 1380 .run_jj(["rebase", "-r", "@", "-d", "@--"]) 1381 .success(); 1382 work_dir 1383 .run_jj(["bookmark", "create", "-r@", "my-bookmark"]) 1384 .success(); 1385 work_dir.run_jj(["describe", "-m", "third"]).success(); 1386 let output = work_dir.run_jj(["git", "push", "--all"]); 1387 insta::assert_snapshot!(output, @r" 1388 ------- stderr ------- 1389 Error: Won't push commit 654e715becca since it has conflicts 1390 Hint: Rejected commit: yostqsxw 654e715b my-bookmark | (conflict) third 1391 [EOF] 1392 [exit status: 1] 1393 "); 1394} 1395 1396#[test] 1397fn test_git_push_no_description() { 1398 let test_env = TestEnvironment::default(); 1399 set_up(&test_env); 1400 let work_dir = test_env.work_dir("local"); 1401 work_dir 1402 .run_jj(["bookmark", "create", "-r@", "my-bookmark"]) 1403 .success(); 1404 work_dir.run_jj(["describe", "-m="]).success(); 1405 let output = work_dir.run_jj(["git", "push", "--allow-new", "--bookmark", "my-bookmark"]); 1406 insta::assert_snapshot!(output, @r" 1407 ------- stderr ------- 1408 Error: Won't push commit 8d23abddc924 since it has no description 1409 Hint: Rejected commit: yqosqzyt 8d23abdd my-bookmark | (empty) (no description set) 1410 [EOF] 1411 [exit status: 1] 1412 "); 1413 work_dir 1414 .run_jj([ 1415 "git", 1416 "push", 1417 "--allow-new", 1418 "--bookmark", 1419 "my-bookmark", 1420 "--allow-empty-description", 1421 ]) 1422 .success(); 1423} 1424 1425#[test] 1426fn test_git_push_no_description_in_immutable() { 1427 let test_env = TestEnvironment::default(); 1428 set_up(&test_env); 1429 let work_dir = test_env.work_dir("local"); 1430 work_dir 1431 .run_jj(["bookmark", "create", "-r@", "imm"]) 1432 .success(); 1433 work_dir.run_jj(["describe", "-m="]).success(); 1434 work_dir.run_jj(["new", "-m", "foo"]).success(); 1435 work_dir.write_file("file", "contents"); 1436 work_dir 1437 .run_jj(["bookmark", "create", "-r@", "my-bookmark"]) 1438 .success(); 1439 1440 let output = work_dir.run_jj([ 1441 "git", 1442 "push", 1443 "--allow-new", 1444 "--bookmark=my-bookmark", 1445 "--dry-run", 1446 ]); 1447 insta::assert_snapshot!(output, @r" 1448 ------- stderr ------- 1449 Error: Won't push commit 8d23abddc924 since it has no description 1450 Hint: Rejected commit: yqosqzyt 8d23abdd imm | (empty) (no description set) 1451 [EOF] 1452 [exit status: 1] 1453 "); 1454 1455 test_env.add_config(r#"revset-aliases."immutable_heads()" = "imm""#); 1456 let output = work_dir.run_jj([ 1457 "git", 1458 "push", 1459 "--allow-new", 1460 "--bookmark=my-bookmark", 1461 "--dry-run", 1462 ]); 1463 insta::assert_snapshot!(output, @r" 1464 ------- stderr ------- 1465 Changes to push to origin: 1466 Add bookmark my-bookmark to 240e2e89abb2 1467 Dry-run requested, not pushing. 1468 [EOF] 1469 "); 1470} 1471 1472#[test] 1473fn test_git_push_missing_author() { 1474 let test_env = TestEnvironment::default(); 1475 set_up(&test_env); 1476 let work_dir = test_env.work_dir("local"); 1477 let run_without_var = |var: &str, args: &[&str]| { 1478 work_dir 1479 .run_jj_with(|cmd| cmd.args(args).env_remove(var)) 1480 .success(); 1481 }; 1482 run_without_var("JJ_USER", &["new", "root()", "-m=initial"]); 1483 run_without_var("JJ_USER", &["bookmark", "create", "-r@", "missing-name"]); 1484 let output = work_dir.run_jj(["git", "push", "--allow-new", "--bookmark", "missing-name"]); 1485 insta::assert_snapshot!(output, @r" 1486 ------- stderr ------- 1487 Error: Won't push commit 613adaba9d49 since it has no author and/or committer set 1488 Hint: Rejected commit: vruxwmqv 613adaba missing-name | (empty) initial 1489 [EOF] 1490 [exit status: 1] 1491 "); 1492 run_without_var("JJ_EMAIL", &["new", "root()", "-m=initial"]); 1493 run_without_var("JJ_EMAIL", &["bookmark", "create", "-r@", "missing-email"]); 1494 let output = work_dir.run_jj(["git", "push", "--allow-new", "--bookmark=missing-email"]); 1495 insta::assert_snapshot!(output, @r" 1496 ------- stderr ------- 1497 Error: Won't push commit bb4ea60fc9ba since it has no author and/or committer set 1498 Hint: Rejected commit: kpqxywon bb4ea60f missing-email | (empty) initial 1499 [EOF] 1500 [exit status: 1] 1501 "); 1502} 1503 1504#[test] 1505fn test_git_push_missing_author_in_immutable() { 1506 let test_env = TestEnvironment::default(); 1507 set_up(&test_env); 1508 let work_dir = test_env.work_dir("local"); 1509 let run_without_var = |var: &str, args: &[&str]| { 1510 work_dir 1511 .run_jj_with(|cmd| cmd.args(args).env_remove(var)) 1512 .success(); 1513 }; 1514 run_without_var("JJ_USER", &["new", "root()", "-m=no author name"]); 1515 run_without_var("JJ_EMAIL", &["new", "-m=no author email"]); 1516 work_dir 1517 .run_jj(["bookmark", "create", "-r@", "imm"]) 1518 .success(); 1519 work_dir.run_jj(["new", "-m", "foo"]).success(); 1520 work_dir.write_file("file", "contents"); 1521 work_dir 1522 .run_jj(["bookmark", "create", "-r@", "my-bookmark"]) 1523 .success(); 1524 1525 let output = work_dir.run_jj([ 1526 "git", 1527 "push", 1528 "--allow-new", 1529 "--bookmark=my-bookmark", 1530 "--dry-run", 1531 ]); 1532 insta::assert_snapshot!(output, @r" 1533 ------- stderr ------- 1534 Error: Won't push commit 5c3cc711907f since it has no author and/or committer set 1535 Hint: Rejected commit: yostqsxw 5c3cc711 imm | (empty) no author email 1536 [EOF] 1537 [exit status: 1] 1538 "); 1539 1540 test_env.add_config(r#"revset-aliases."immutable_heads()" = "imm""#); 1541 let output = work_dir.run_jj([ 1542 "git", 1543 "push", 1544 "--allow-new", 1545 "--bookmark=my-bookmark", 1546 "--dry-run", 1547 ]); 1548 insta::assert_snapshot!(output, @r" 1549 ------- stderr ------- 1550 Changes to push to origin: 1551 Add bookmark my-bookmark to 96080b93b4ce 1552 Dry-run requested, not pushing. 1553 [EOF] 1554 "); 1555} 1556 1557#[test] 1558fn test_git_push_missing_committer() { 1559 let test_env = TestEnvironment::default(); 1560 set_up(&test_env); 1561 let work_dir = test_env.work_dir("local"); 1562 let run_without_var = |var: &str, args: &[&str]| { 1563 work_dir 1564 .run_jj_with(|cmd| cmd.args(args).env_remove(var)) 1565 .success(); 1566 }; 1567 work_dir 1568 .run_jj(["bookmark", "create", "-r@", "missing-name"]) 1569 .success(); 1570 run_without_var("JJ_USER", &["describe", "-m=no committer name"]); 1571 let output = work_dir.run_jj(["git", "push", "--allow-new", "--bookmark=missing-name"]); 1572 insta::assert_snapshot!(output, @r" 1573 ------- stderr ------- 1574 Error: Won't push commit e8a77cb24da9 since it has no author and/or committer set 1575 Hint: Rejected commit: yqosqzyt e8a77cb2 missing-name | (empty) no committer name 1576 [EOF] 1577 [exit status: 1] 1578 "); 1579 work_dir.run_jj(["new", "root()"]).success(); 1580 work_dir 1581 .run_jj(["bookmark", "create", "-r@", "missing-email"]) 1582 .success(); 1583 run_without_var("JJ_EMAIL", &["describe", "-m=no committer email"]); 1584 let output = work_dir.run_jj(["git", "push", "--allow-new", "--bookmark=missing-email"]); 1585 insta::assert_snapshot!(output, @r" 1586 ------- stderr ------- 1587 Error: Won't push commit 971c50fd8d1d since it has no author and/or committer set 1588 Hint: Rejected commit: kpqxywon 971c50fd missing-email | (empty) no committer email 1589 [EOF] 1590 [exit status: 1] 1591 "); 1592 1593 // Test message when there are multiple reasons (missing committer and 1594 // description) 1595 run_without_var("JJ_EMAIL", &["describe", "-m=", "missing-email"]); 1596 let output = work_dir.run_jj(["git", "push", "--allow-new", "--bookmark=missing-email"]); 1597 insta::assert_snapshot!(output, @r" 1598 ------- stderr ------- 1599 Error: Won't push commit 4bd3b55c7759 since it has no description and it has no author and/or committer set 1600 Hint: Rejected commit: kpqxywon 4bd3b55c missing-email | (empty) (no description set) 1601 [EOF] 1602 [exit status: 1] 1603 "); 1604} 1605 1606#[test] 1607fn test_git_push_missing_committer_in_immutable() { 1608 let test_env = TestEnvironment::default(); 1609 set_up(&test_env); 1610 let work_dir = test_env.work_dir("local"); 1611 let run_without_var = |var: &str, args: &[&str]| { 1612 work_dir 1613 .run_jj_with(|cmd| cmd.args(args).env_remove(var)) 1614 .success(); 1615 }; 1616 run_without_var("JJ_USER", &["describe", "-m=no committer name"]); 1617 work_dir.run_jj(["new"]).success(); 1618 run_without_var("JJ_EMAIL", &["describe", "-m=no committer email"]); 1619 work_dir 1620 .run_jj(["bookmark", "create", "-r@", "imm"]) 1621 .success(); 1622 work_dir.run_jj(["new", "-m", "foo"]).success(); 1623 work_dir.write_file("file", "contents"); 1624 work_dir 1625 .run_jj(["bookmark", "create", "-r@", "my-bookmark"]) 1626 .success(); 1627 1628 let output = work_dir.run_jj([ 1629 "git", 1630 "push", 1631 "--allow-new", 1632 "--bookmark=my-bookmark", 1633 "--dry-run", 1634 ]); 1635 insta::assert_snapshot!(output, @r" 1636 ------- stderr ------- 1637 Error: Won't push commit ab230f98c812 since it has no author and/or committer set 1638 Hint: Rejected commit: yostqsxw ab230f98 imm | (empty) no committer email 1639 [EOF] 1640 [exit status: 1] 1641 "); 1642 1643 test_env.add_config(r#"revset-aliases."immutable_heads()" = "imm""#); 1644 let output = work_dir.run_jj([ 1645 "git", 1646 "push", 1647 "--allow-new", 1648 "--bookmark=my-bookmark", 1649 "--dry-run", 1650 ]); 1651 insta::assert_snapshot!(output, @r" 1652 ------- stderr ------- 1653 Changes to push to origin: 1654 Add bookmark my-bookmark to e0dff9c29479 1655 Dry-run requested, not pushing. 1656 [EOF] 1657 "); 1658} 1659 1660#[test] 1661fn test_git_push_deleted() { 1662 let test_env = TestEnvironment::default(); 1663 set_up(&test_env); 1664 let work_dir = test_env.work_dir("local"); 1665 1666 work_dir 1667 .run_jj(["bookmark", "delete", "bookmark1"]) 1668 .success(); 1669 let output = work_dir.run_jj(["git", "push", "--deleted"]); 1670 insta::assert_snapshot!(output, @r" 1671 ------- stderr ------- 1672 Changes to push to origin: 1673 Delete bookmark bookmark1 from 9b2e76de3920 1674 [EOF] 1675 "); 1676 let output = work_dir.run_jj(["log", "-rall()"]); 1677 insta::assert_snapshot!(output, @r" 1678 @ yqosqzyt test.user@example.com 2001-02-03 08:05:13 8d23abdd 1679 │ (empty) (no description set) 1680 │ ○ zsuskuln test.user@example.com 2001-02-03 08:05:10 bookmark2 38a20473 1681 ├─╯ (empty) description 2 1682 │ ○ qpvuntsm test.user@example.com 2001-02-03 08:05:08 9b2e76de 1683 ├─╯ (empty) description 1 1684 ◆ zzzzzzzz root() 00000000 1685 [EOF] 1686 "); 1687 let output = work_dir.run_jj(["git", "push", "--deleted"]); 1688 insta::assert_snapshot!(output, @r" 1689 ------- stderr ------- 1690 Nothing changed. 1691 [EOF] 1692 "); 1693} 1694 1695#[test] 1696fn test_git_push_conflicting_bookmarks() { 1697 let test_env = TestEnvironment::default(); 1698 set_up(&test_env); 1699 let work_dir = test_env.work_dir("local"); 1700 test_env.add_config("git.auto-local-bookmark = true"); 1701 let git_repo = { 1702 let mut git_repo_path = work_dir.root().to_owned(); 1703 git_repo_path.extend([".jj", "repo", "store", "git"]); 1704 git::open(&git_repo_path) 1705 }; 1706 1707 // Forget remote ref, move local ref, then fetch to create conflict. 1708 git_repo 1709 .find_reference("refs/remotes/origin/bookmark2") 1710 .unwrap() 1711 .delete() 1712 .unwrap(); 1713 work_dir.run_jj(["git", "import"]).success(); 1714 work_dir 1715 .run_jj(["new", "root()", "-m=description 3"]) 1716 .success(); 1717 work_dir 1718 .run_jj(["bookmark", "create", "-r@", "bookmark2"]) 1719 .success(); 1720 work_dir.run_jj(["git", "fetch"]).success(); 1721 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1722 bookmark1: qpvuntsm 9b2e76de (empty) description 1 1723 @origin: qpvuntsm 9b2e76de (empty) description 1 1724 bookmark2 (conflicted): 1725 + yostqsxw ebedbe63 (empty) description 3 1726 + zsuskuln 38a20473 (empty) description 2 1727 @origin (behind by 1 commits): zsuskuln 38a20473 (empty) description 2 1728 [EOF] 1729 "); 1730 1731 let bump_bookmark1 = || { 1732 work_dir.run_jj(["new", "bookmark1", "-m=bump"]).success(); 1733 work_dir 1734 .run_jj(["bookmark", "set", "bookmark1", "-r@"]) 1735 .success(); 1736 }; 1737 1738 // Conflicting bookmark at @ 1739 let output = work_dir.run_jj(["git", "push", "--allow-new"]); 1740 insta::assert_snapshot!(output, @r" 1741 ------- stderr ------- 1742 Warning: Bookmark bookmark2 is conflicted 1743 Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up. 1744 Nothing changed. 1745 [EOF] 1746 "); 1747 1748 // --bookmark should be blocked by conflicting bookmark 1749 let output = work_dir.run_jj(["git", "push", "--allow-new", "--bookmark", "bookmark2"]); 1750 insta::assert_snapshot!(output, @r" 1751 ------- stderr ------- 1752 Error: Bookmark bookmark2 is conflicted 1753 Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up. 1754 [EOF] 1755 [exit status: 1] 1756 "); 1757 1758 // --all shouldn't be blocked by conflicting bookmark 1759 bump_bookmark1(); 1760 let output = work_dir.run_jj(["git", "push", "--all"]); 1761 insta::assert_snapshot!(output, @r" 1762 ------- stderr ------- 1763 Warning: Bookmark bookmark2 is conflicted 1764 Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up. 1765 Changes to push to origin: 1766 Move forward bookmark bookmark1 from 9b2e76de3920 to 749c2e6d999f 1767 [EOF] 1768 "); 1769 1770 // --revisions shouldn't be blocked by conflicting bookmark 1771 bump_bookmark1(); 1772 let output = work_dir.run_jj(["git", "push", "--allow-new", "-rall()"]); 1773 insta::assert_snapshot!(output, @r" 1774 ------- stderr ------- 1775 Warning: Bookmark bookmark2 is conflicted 1776 Hint: Run `jj bookmark list` to inspect, and use `jj bookmark set` to fix it up. 1777 Changes to push to origin: 1778 Move forward bookmark bookmark1 from 749c2e6d999f to 9bb0f427b517 1779 [EOF] 1780 "); 1781} 1782 1783#[test] 1784fn test_git_push_deleted_untracked() { 1785 let test_env = TestEnvironment::default(); 1786 set_up(&test_env); 1787 let work_dir = test_env.work_dir("local"); 1788 1789 // Absent local bookmark shouldn't be considered "deleted" compared to 1790 // non-tracking remote bookmark. 1791 work_dir 1792 .run_jj(["bookmark", "delete", "bookmark1"]) 1793 .success(); 1794 work_dir 1795 .run_jj(["bookmark", "untrack", "bookmark1@origin"]) 1796 .success(); 1797 let output = work_dir.run_jj(["git", "push", "--deleted"]); 1798 insta::assert_snapshot!(output, @r" 1799 ------- stderr ------- 1800 Nothing changed. 1801 [EOF] 1802 "); 1803 let output = work_dir.run_jj(["git", "push", "--bookmark=bookmark1"]); 1804 insta::assert_snapshot!(output, @r" 1805 ------- stderr ------- 1806 Error: No such bookmark: bookmark1 1807 [EOF] 1808 [exit status: 1] 1809 "); 1810} 1811 1812#[test] 1813fn test_git_push_tracked_vs_all() { 1814 let test_env = TestEnvironment::default(); 1815 set_up(&test_env); 1816 let work_dir = test_env.work_dir("local"); 1817 work_dir 1818 .run_jj(["new", "bookmark1", "-mmoved bookmark1"]) 1819 .success(); 1820 work_dir 1821 .run_jj(["bookmark", "set", "bookmark1", "-r@"]) 1822 .success(); 1823 work_dir 1824 .run_jj(["new", "bookmark2", "-mmoved bookmark2"]) 1825 .success(); 1826 work_dir 1827 .run_jj(["bookmark", "delete", "bookmark2"]) 1828 .success(); 1829 work_dir 1830 .run_jj(["bookmark", "untrack", "bookmark1@origin"]) 1831 .success(); 1832 work_dir 1833 .run_jj(["bookmark", "create", "-r@", "bookmark3"]) 1834 .success(); 1835 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1836 bookmark1: vruxwmqv d7607a25 (empty) moved bookmark1 1837 bookmark1@origin: qpvuntsm 9b2e76de (empty) description 1 1838 bookmark2 (deleted) 1839 @origin: zsuskuln 38a20473 (empty) description 2 1840 bookmark3: znkkpsqq 0004a65e (empty) moved bookmark2 1841 [EOF] 1842 "); 1843 1844 // At this point, only bookmark2 is still tracked. 1845 // `jj git push --tracked --deleted` would try to push it and no other 1846 // bookmarks. 1847 let output = work_dir.run_jj(["git", "push", "--tracked", "--dry-run"]); 1848 insta::assert_snapshot!(output, @r" 1849 ------- stderr ------- 1850 Warning: Refusing to push deleted bookmark bookmark2 1851 Hint: Push deleted bookmarks with --deleted or forget the bookmark to suppress this warning. 1852 Nothing changed. 1853 [EOF] 1854 "); 1855 let output = work_dir.run_jj(["git", "push", "--tracked", "--deleted", "--dry-run"]); 1856 insta::assert_snapshot!(output, @r" 1857 ------- stderr ------- 1858 Changes to push to origin: 1859 Delete bookmark bookmark2 from 38a204733702 1860 Dry-run requested, not pushing. 1861 [EOF] 1862 "); 1863 1864 // Untrack the last remaining tracked bookmark. 1865 work_dir 1866 .run_jj(["bookmark", "untrack", "bookmark2@origin"]) 1867 .success(); 1868 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1869 bookmark1: vruxwmqv d7607a25 (empty) moved bookmark1 1870 bookmark1@origin: qpvuntsm 9b2e76de (empty) description 1 1871 bookmark2@origin: zsuskuln 38a20473 (empty) description 2 1872 bookmark3: znkkpsqq 0004a65e (empty) moved bookmark2 1873 [EOF] 1874 "); 1875 1876 // Now, no bookmarks are tracked. --tracked does not push anything 1877 let output = work_dir.run_jj(["git", "push", "--tracked"]); 1878 insta::assert_snapshot!(output, @r" 1879 ------- stderr ------- 1880 Nothing changed. 1881 [EOF] 1882 "); 1883 1884 // All bookmarks are still untracked. 1885 // - --all tries to push bookmark1, but fails because a bookmark with the same 1886 // name exist on the remote. 1887 // - --all succeeds in pushing bookmark3, since there is no bookmark of the same 1888 // name on the remote. 1889 // - It does not try to push bookmark2. 1890 // 1891 // TODO: Not trying to push bookmark2 could be considered correct, or perhaps 1892 // we want to consider this as a deletion of the bookmark that failed because 1893 // the bookmark was untracked. In the latter case, an error message should be 1894 // printed. Some considerations: 1895 // - Whatever we do should be consistent with what `jj bookmark list` does; it 1896 // currently does *not* list bookmarks like bookmark2 as "about to be 1897 // deleted", as can be seen above. 1898 // - We could consider showing some hint on `jj bookmark untrack 1899 // bookmark2@origin` instead of showing an error here. 1900 let output = work_dir.run_jj(["git", "push", "--all"]); 1901 insta::assert_snapshot!(output, @r" 1902 ------- stderr ------- 1903 Warning: Non-tracking remote bookmark bookmark1@origin exists 1904 Hint: Run `jj bookmark track bookmark1@origin` to import the remote bookmark. 1905 Changes to push to origin: 1906 Add bookmark bookmark3 to 0004a65e1d28 1907 [EOF] 1908 "); 1909} 1910 1911#[test] 1912fn test_git_push_moved_forward_untracked() { 1913 let test_env = TestEnvironment::default(); 1914 set_up(&test_env); 1915 let work_dir = test_env.work_dir("local"); 1916 1917 work_dir 1918 .run_jj(["new", "bookmark1", "-mmoved bookmark1"]) 1919 .success(); 1920 work_dir 1921 .run_jj(["bookmark", "set", "bookmark1", "-r@"]) 1922 .success(); 1923 work_dir 1924 .run_jj(["bookmark", "untrack", "bookmark1@origin"]) 1925 .success(); 1926 let output = work_dir.run_jj(["git", "push", "--allow-new"]); 1927 insta::assert_snapshot!(output, @r" 1928 ------- stderr ------- 1929 Warning: Non-tracking remote bookmark bookmark1@origin exists 1930 Hint: Run `jj bookmark track bookmark1@origin` to import the remote bookmark. 1931 Nothing changed. 1932 [EOF] 1933 "); 1934} 1935 1936#[test] 1937fn test_git_push_moved_sideways_untracked() { 1938 let test_env = TestEnvironment::default(); 1939 set_up(&test_env); 1940 let work_dir = test_env.work_dir("local"); 1941 1942 work_dir 1943 .run_jj(["new", "root()", "-mmoved bookmark1"]) 1944 .success(); 1945 work_dir 1946 .run_jj(["bookmark", "set", "--allow-backwards", "bookmark1", "-r@"]) 1947 .success(); 1948 work_dir 1949 .run_jj(["bookmark", "untrack", "bookmark1@origin"]) 1950 .success(); 1951 let output = work_dir.run_jj(["git", "push", "--allow-new"]); 1952 insta::assert_snapshot!(output, @r" 1953 ------- stderr ------- 1954 Warning: Non-tracking remote bookmark bookmark1@origin exists 1955 Hint: Run `jj bookmark track bookmark1@origin` to import the remote bookmark. 1956 Nothing changed. 1957 [EOF] 1958 "); 1959} 1960 1961#[test] 1962fn test_git_push_to_remote_named_git() { 1963 let test_env = TestEnvironment::default(); 1964 set_up(&test_env); 1965 let work_dir = test_env.work_dir("local"); 1966 let git_repo_path = { 1967 let mut git_repo_path = work_dir.root().to_owned(); 1968 git_repo_path.extend([".jj", "repo", "store", "git"]); 1969 git_repo_path 1970 }; 1971 git::rename_remote(&git_repo_path, "origin", "git"); 1972 1973 let output = work_dir.run_jj(["git", "push", "--all", "--remote=git"]); 1974 insta::assert_snapshot!(output, @r" 1975 ------- stderr ------- 1976 Changes to push to git: 1977 Add bookmark bookmark1 to 9b2e76de3920 1978 Add bookmark bookmark2 to 38a204733702 1979 Error: Git remote named 'git' is reserved for local Git repository 1980 Hint: Run `jj git remote rename` to give a different name. 1981 [EOF] 1982 [exit status: 1] 1983 "); 1984} 1985 1986#[test] 1987fn test_git_push_to_remote_with_slashes() { 1988 let test_env = TestEnvironment::default(); 1989 set_up(&test_env); 1990 let work_dir = test_env.work_dir("local"); 1991 let git_repo_path = { 1992 let mut git_repo_path = work_dir.root().to_owned(); 1993 git_repo_path.extend([".jj", "repo", "store", "git"]); 1994 git_repo_path 1995 }; 1996 git::rename_remote(&git_repo_path, "origin", "slash/origin"); 1997 1998 let output = work_dir.run_jj(["git", "push", "--all", "--remote=slash/origin"]); 1999 insta::assert_snapshot!(output, @r" 2000 ------- stderr ------- 2001 Changes to push to slash/origin: 2002 Add bookmark bookmark1 to 9b2e76de3920 2003 Add bookmark bookmark2 to 38a204733702 2004 Error: Git remotes with slashes are incompatible with jj: slash/origin 2005 Hint: Run `jj git remote rename` to give a different name. 2006 [EOF] 2007 [exit status: 1] 2008 "); 2009} 2010 2011#[test] 2012fn test_git_push_sign_on_push() { 2013 let test_env = TestEnvironment::default(); 2014 set_up(&test_env); 2015 let work_dir = test_env.work_dir("local"); 2016 let template = r#" 2017 separate("\n", 2018 description.first_line(), 2019 if(signature, 2020 separate(", ", 2021 "Signature: " ++ signature.display(), 2022 "Status: " ++ signature.status(), 2023 "Key: " ++ signature.key(), 2024 ) 2025 ) 2026 ) 2027 "#; 2028 work_dir 2029 .run_jj(["new", "bookmark2", "-m", "commit to be signed 1"]) 2030 .success(); 2031 work_dir 2032 .run_jj(["new", "-m", "commit to be signed 2"]) 2033 .success(); 2034 work_dir 2035 .run_jj(["bookmark", "set", "bookmark2", "-r@"]) 2036 .success(); 2037 work_dir 2038 .run_jj(["new", "-m", "commit which should not be signed 1"]) 2039 .success(); 2040 work_dir 2041 .run_jj(["new", "-m", "commit which should not be signed 2"]) 2042 .success(); 2043 // There should be no signed commits initially 2044 let output = work_dir.run_jj(["log", "-T", template]); 2045 insta::assert_snapshot!(output, @r" 2046 @ commit which should not be signed 2 2047 ○ commit which should not be signed 1 2048 ○ commit to be signed 2 2049 ○ commit to be signed 1 2050 ○ description 2 2051 │ ○ description 1 2052 ├─╯ 20532054 [EOF] 2055 "); 2056 test_env.add_config( 2057 r#" 2058 signing.backend = "test" 2059 signing.key = "impeccable" 2060 git.sign-on-push = true 2061 "#, 2062 ); 2063 let output = work_dir.run_jj(["git", "push", "--dry-run"]); 2064 insta::assert_snapshot!(output, @r" 2065 ------- stderr ------- 2066 Changes to push to origin: 2067 Move forward bookmark bookmark2 from 38a204733702 to 3779ed7f18df 2068 Dry-run requested, not pushing. 2069 [EOF] 2070 "); 2071 // There should be no signed commits after performing a dry run 2072 let output = work_dir.run_jj(["log", "-T", template]); 2073 insta::assert_snapshot!(output, @r" 2074 @ commit which should not be signed 2 2075 ○ commit which should not be signed 1 2076 ○ commit to be signed 2 2077 ○ commit to be signed 1 2078 ○ description 2 2079 │ ○ description 1 2080 ├─╯ 20812082 [EOF] 2083 "); 2084 let output = work_dir.run_jj(["git", "push"]); 2085 insta::assert_snapshot!(output, @r" 2086 ------- stderr ------- 2087 Updated signatures of 2 commits 2088 Rebased 2 descendant commits 2089 Changes to push to origin: 2090 Move forward bookmark bookmark2 from 38a204733702 to d45e2adce0ad 2091 Working copy (@) now at: kmkuslsw 3d5a9465 (empty) commit which should not be signed 2 2092 Parent commit (@-) : kpqxywon 48ea83e9 (empty) commit which should not be signed 1 2093 [EOF] 2094 "); 2095 // Only commits which are being pushed should be signed 2096 let output = work_dir.run_jj(["log", "-T", template]); 2097 insta::assert_snapshot!(output, @r" 2098 @ commit which should not be signed 2 2099 ○ commit which should not be signed 1 2100 ○ commit to be signed 2 2101 │ Signature: test-display, Status: good, Key: impeccable 2102 ○ commit to be signed 1 2103 │ Signature: test-display, Status: good, Key: impeccable 2104 ○ description 2 2105 │ ○ description 1 2106 ├─╯ 21072108 [EOF] 2109 "); 2110 2111 // Immutable commits should not be signed 2112 let output = work_dir.run_jj([ 2113 "bookmark", 2114 "create", 2115 "bookmark3", 2116 "-r", 2117 "description('commit which should not be signed 1')", 2118 ]); 2119 insta::assert_snapshot!(output, @r" 2120 ------- stderr ------- 2121 Created 1 bookmarks pointing to kpqxywon 48ea83e9 bookmark3 | (empty) commit which should not be signed 1 2122 [EOF] 2123 "); 2124 let output = work_dir.run_jj(["bookmark", "move", "bookmark2", "--to", "bookmark3"]); 2125 insta::assert_snapshot!(output, @r" 2126 ------- stderr ------- 2127 Moved 1 bookmarks to kpqxywon 48ea83e9 bookmark2* bookmark3 | (empty) commit which should not be signed 1 2128 [EOF] 2129 "); 2130 test_env.add_config(r#"revset-aliases."immutable_heads()" = "bookmark3""#); 2131 let output = work_dir.run_jj(["git", "push"]); 2132 insta::assert_snapshot!(output, @r" 2133 ------- stderr ------- 2134 Warning: Refusing to create new remote bookmark bookmark3@origin 2135 Hint: Use --allow-new to push new bookmark. Use --remote to specify the remote to push to. 2136 Changes to push to origin: 2137 Move forward bookmark bookmark2 from d45e2adce0ad to 48ea83e9499c 2138 [EOF] 2139 "); 2140 let output = work_dir.run_jj(["log", "-T", template, "-r", "::"]); 2141 insta::assert_snapshot!(output, @r" 2142 @ commit which should not be signed 2 2143 ◆ commit which should not be signed 1 2144 ◆ commit to be signed 2 2145 │ Signature: test-display, Status: good, Key: impeccable 2146 ◆ commit to be signed 1 2147 │ Signature: test-display, Status: good, Key: impeccable 2148 ◆ description 2 2149 │ ○ description 1 2150 ├─╯ 21512152 [EOF] 2153 "); 2154} 2155 2156#[test] 2157fn test_git_push_rejected_by_remote() { 2158 let test_env = TestEnvironment::default(); 2159 set_up(&test_env); 2160 let work_dir = test_env.work_dir("local"); 2161 // show repo state 2162 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 2163 bookmark1: qpvuntsm 9b2e76de (empty) description 1 2164 @origin: qpvuntsm 9b2e76de (empty) description 1 2165 bookmark2: zsuskuln 38a20473 (empty) description 2 2166 @origin: zsuskuln 38a20473 (empty) description 2 2167 [EOF] 2168 "); 2169 2170 // create a hook on the remote that prevents pushing 2171 let hook_path = test_env 2172 .env_root() 2173 .join("origin") 2174 .join(".jj") 2175 .join("repo") 2176 .join("store") 2177 .join("git") 2178 .join("hooks") 2179 .join("update"); 2180 2181 std::fs::write(&hook_path, "#!/bin/sh\nexit 1").unwrap(); 2182 #[cfg(unix)] 2183 { 2184 use std::os::unix::fs::PermissionsExt as _; 2185 2186 std::fs::set_permissions(&hook_path, std::fs::Permissions::from_mode(0o700)).unwrap(); 2187 } 2188 2189 // create new commit on top of bookmark1 2190 work_dir.run_jj(["new", "bookmark1"]).success(); 2191 work_dir.write_file("file", "file"); 2192 work_dir.run_jj(["describe", "-m=update"]).success(); 2193 2194 // update bookmark 2195 work_dir.run_jj(["bookmark", "move", "bookmark1"]).success(); 2196 2197 // push bookmark 2198 let output = work_dir.run_jj(["git", "push"]); 2199 2200 // The git remote sideband adds a dummy suffix of 8 spaces to attempt to clear 2201 // any leftover data. This is done to help with cases where the line is 2202 // rewritten. 2203 // 2204 // However, a common option in a lot of editors removes trailing whitespace. 2205 // This means that anyone with that option that opens this file would make the 2206 // following snapshot fail. Using the insta filter here normalizes the 2207 // output. 2208 let mut settings = insta::Settings::clone_current(); 2209 settings.add_filter(r"\s*\n", "\n"); 2210 settings.bind(|| { 2211 insta::assert_snapshot!(output, @r" 2212 ------- stderr ------- 2213 Changes to push to origin: 2214 Move forward bookmark bookmark1 from 9b2e76de3920 to 0fc4cf312e83 2215 remote: error: hook declined to update refs/heads/bookmark1 2216 Error: Failed to push some bookmarks 2217 Hint: The remote rejected the following updates: 2218 refs/heads/bookmark1 (reason: hook declined) 2219 Hint: Try checking if you have permission to push to all the bookmarks. 2220 [EOF] 2221 [exit status: 1] 2222 "); 2223 }); 2224} 2225 2226#[must_use] 2227fn get_bookmark_output(work_dir: &TestWorkDir) -> CommandOutput { 2228 // --quiet to suppress deleted bookmarks hint 2229 work_dir.run_jj(["bookmark", "list", "--all-remotes", "--quiet"]) 2230}