just playing with tangled
at main 1758 lines 58 kB view raw
1// Copyright 2023 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::create_commit; 18use crate::common::CommandOutput; 19use crate::common::TestEnvironment; 20use crate::common::TestWorkDir; 21 22fn add_commit_to_branch(git_repo: &gix::Repository, branch: &str) -> gix::ObjectId { 23 git::add_commit( 24 git_repo, 25 &format!("refs/heads/{branch}"), 26 branch, // filename 27 branch.as_bytes(), // content 28 "message", 29 &[], 30 ) 31 .commit_id 32} 33 34/// Creates a remote Git repo containing a bookmark with the same name 35fn init_git_remote(test_env: &TestEnvironment, remote: &str) -> gix::Repository { 36 let git_repo_path = test_env.env_root().join(remote); 37 let git_repo = git::init(git_repo_path); 38 add_commit_to_branch(&git_repo, remote); 39 40 git_repo 41} 42 43/// Add a remote containing a bookmark with the same name 44fn add_git_remote( 45 test_env: &TestEnvironment, 46 work_dir: &TestWorkDir, 47 remote: &str, 48) -> gix::Repository { 49 let repo = init_git_remote(test_env, remote); 50 work_dir 51 .run_jj(["git", "remote", "add", remote, &format!("../{remote}")]) 52 .success(); 53 54 repo 55} 56 57#[must_use] 58fn get_bookmark_output(work_dir: &TestWorkDir) -> CommandOutput { 59 // --quiet to suppress deleted bookmarks hint 60 work_dir.run_jj(["bookmark", "list", "--all-remotes", "--quiet"]) 61} 62 63#[must_use] 64fn get_log_output(work_dir: &TestWorkDir) -> CommandOutput { 65 let template = 66 r#"commit_id.short() ++ " \"" ++ description.first_line() ++ "\" " ++ bookmarks"#; 67 work_dir.run_jj(["log", "-T", template, "-r", "all()"]) 68} 69 70fn clone_git_remote_into( 71 test_env: &TestEnvironment, 72 upstream: &str, 73 fork: &str, 74) -> gix::Repository { 75 let upstream_path = test_env.env_root().join(upstream); 76 let fork_path = test_env.env_root().join(fork); 77 let fork_repo = git::clone(&fork_path, upstream_path.to_str().unwrap(), Some(upstream)); 78 79 // create local branch mirroring the upstream 80 let upstream_head = fork_repo 81 .find_reference(&format!("refs/remotes/{upstream}/{upstream}")) 82 .unwrap() 83 .peel_to_id_in_place() 84 .unwrap() 85 .detach(); 86 87 fork_repo 88 .reference( 89 format!("refs/heads/{upstream}"), 90 upstream_head, 91 gix::refs::transaction::PreviousValue::MustNotExist, 92 "create tracking head", 93 ) 94 .unwrap(); 95 96 fork_repo 97} 98 99#[test] 100fn test_git_fetch_with_default_config() { 101 let test_env = TestEnvironment::default(); 102 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 103 let work_dir = test_env.work_dir("repo"); 104 add_git_remote(&test_env, &work_dir, "origin"); 105 106 work_dir.run_jj(["git", "fetch"]).success(); 107 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 108 origin@origin: qmyrypzk ab8b299e message 109 [EOF] 110 "); 111} 112 113#[test] 114fn test_git_fetch_default_remote() { 115 let test_env = TestEnvironment::default(); 116 test_env.add_config("git.auto-local-bookmark = true"); 117 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 118 let work_dir = test_env.work_dir("repo"); 119 add_git_remote(&test_env, &work_dir, "origin"); 120 121 work_dir.run_jj(["git", "fetch"]).success(); 122 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 123 origin: qmyrypzk ab8b299e message 124 @origin: qmyrypzk ab8b299e message 125 [EOF] 126 "); 127} 128 129#[test] 130fn test_git_fetch_single_remote() { 131 let test_env = TestEnvironment::default(); 132 test_env.add_config("git.auto-local-bookmark = true"); 133 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 134 let work_dir = test_env.work_dir("repo"); 135 add_git_remote(&test_env, &work_dir, "rem1"); 136 137 let output = work_dir.run_jj(["git", "fetch"]); 138 insta::assert_snapshot!(output, @r" 139 ------- stderr ------- 140 Hint: Fetching from the only existing remote: rem1 141 bookmark: rem1@rem1 [new] tracked 142 [EOF] 143 "); 144 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 145 rem1: ppspxspk 4acd0343 message 146 @rem1: ppspxspk 4acd0343 message 147 [EOF] 148 "); 149} 150 151#[test] 152fn test_git_fetch_single_remote_all_remotes_flag() { 153 let test_env = TestEnvironment::default(); 154 test_env.add_config("git.auto-local-bookmark = true"); 155 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 156 let work_dir = test_env.work_dir("repo"); 157 add_git_remote(&test_env, &work_dir, "rem1"); 158 159 work_dir.run_jj(["git", "fetch", "--all-remotes"]).success(); 160 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 161 rem1: ppspxspk 4acd0343 message 162 @rem1: ppspxspk 4acd0343 message 163 [EOF] 164 "); 165} 166 167#[test] 168fn test_git_fetch_single_remote_from_arg() { 169 let test_env = TestEnvironment::default(); 170 test_env.add_config("git.auto-local-bookmark = true"); 171 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 172 let work_dir = test_env.work_dir("repo"); 173 add_git_remote(&test_env, &work_dir, "rem1"); 174 175 work_dir 176 .run_jj(["git", "fetch", "--remote", "rem1"]) 177 .success(); 178 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 179 rem1: ppspxspk 4acd0343 message 180 @rem1: ppspxspk 4acd0343 message 181 [EOF] 182 "); 183} 184 185#[test] 186fn test_git_fetch_single_remote_from_config() { 187 let test_env = TestEnvironment::default(); 188 test_env.add_config("git.auto-local-bookmark = true"); 189 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 190 let work_dir = test_env.work_dir("repo"); 191 add_git_remote(&test_env, &work_dir, "rem1"); 192 test_env.add_config(r#"git.fetch = "rem1""#); 193 194 work_dir.run_jj(["git", "fetch"]).success(); 195 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 196 rem1: ppspxspk 4acd0343 message 197 @rem1: ppspxspk 4acd0343 message 198 [EOF] 199 "); 200} 201 202#[test] 203fn test_git_fetch_multiple_remotes() { 204 let test_env = TestEnvironment::default(); 205 test_env.add_config("git.auto-local-bookmark = true"); 206 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 207 let work_dir = test_env.work_dir("repo"); 208 add_git_remote(&test_env, &work_dir, "rem1"); 209 add_git_remote(&test_env, &work_dir, "rem2"); 210 211 work_dir 212 .run_jj(["git", "fetch", "--remote", "rem1", "--remote", "rem2"]) 213 .success(); 214 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 215 rem1: ppspxspk 4acd0343 message 216 @rem1: ppspxspk 4acd0343 message 217 rem2: pzqqpnpo 44c57802 message 218 @rem2: pzqqpnpo 44c57802 message 219 [EOF] 220 "); 221} 222 223#[test] 224fn test_git_fetch_with_glob() { 225 let test_env = TestEnvironment::default(); 226 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 227 let work_dir = test_env.work_dir("repo"); 228 add_git_remote(&test_env, &work_dir, "rem1"); 229 add_git_remote(&test_env, &work_dir, "rem2"); 230 231 let output = work_dir.run_jj(["git", "fetch", "--remote", "glob:*"]); 232 insta::assert_snapshot!(output, @r" 233 ------- stderr ------- 234 bookmark: rem1@rem1 [new] untracked 235 bookmark: rem2@rem2 [new] untracked 236 [EOF] 237 "); 238} 239 240#[test] 241fn test_git_fetch_with_glob_and_exact_match() { 242 let test_env = TestEnvironment::default(); 243 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 244 let work_dir = test_env.work_dir("repo"); 245 add_git_remote(&test_env, &work_dir, "rem1"); 246 add_git_remote(&test_env, &work_dir, "rem2"); 247 add_git_remote(&test_env, &work_dir, "upstream1"); 248 add_git_remote(&test_env, &work_dir, "upstream2"); 249 add_git_remote(&test_env, &work_dir, "origin"); 250 251 let output = work_dir.run_jj(["git", "fetch", "--remote=glob:rem*", "--remote=origin"]); 252 insta::assert_snapshot!(output, @r" 253 ------- stderr ------- 254 bookmark: origin@origin [new] untracked 255 bookmark: rem1@rem1 [new] untracked 256 bookmark: rem2@rem2 [new] untracked 257 [EOF] 258 "); 259} 260 261#[test] 262fn test_git_fetch_with_glob_from_config() { 263 let test_env = TestEnvironment::default(); 264 test_env.add_config(r#"git.fetch = "glob:rem*""#); 265 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 266 let work_dir = test_env.work_dir("repo"); 267 add_git_remote(&test_env, &work_dir, "rem1"); 268 add_git_remote(&test_env, &work_dir, "rem2"); 269 add_git_remote(&test_env, &work_dir, "upstream"); 270 271 let output = work_dir.run_jj(["git", "fetch"]); 272 insta::assert_snapshot!(output, @r" 273 ------- stderr ------- 274 bookmark: rem1@rem1 [new] untracked 275 bookmark: rem2@rem2 [new] untracked 276 [EOF] 277 "); 278} 279 280#[test] 281fn test_git_fetch_with_glob_with_no_matching_remotes() { 282 let test_env = TestEnvironment::default(); 283 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 284 let work_dir = test_env.work_dir("repo"); 285 add_git_remote(&test_env, &work_dir, "upstream"); 286 287 let output = work_dir.run_jj(["git", "fetch", "--remote=glob:rem*"]); 288 insta::assert_snapshot!(output, @r" 289 ------- stderr ------- 290 Warning: No git remotes matching 'rem*' 291 Error: No git remotes to push 292 [EOF] 293 [exit status: 1] 294 "); 295 // No remote should have been fetched as part of the failing transaction 296 insta::assert_snapshot!(get_bookmark_output(&work_dir), @""); 297} 298 299#[test] 300fn test_git_fetch_all_remotes() { 301 let test_env = TestEnvironment::default(); 302 test_env.add_config("git.auto-local-bookmark = true"); 303 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 304 let work_dir = test_env.work_dir("repo"); 305 add_git_remote(&test_env, &work_dir, "rem1"); 306 add_git_remote(&test_env, &work_dir, "rem2"); 307 308 // add empty [remote "rem3"] section to .git/config, which should be ignored 309 work_dir 310 .run_jj(["git", "remote", "add", "rem3", "../unknown"]) 311 .success(); 312 work_dir 313 .run_jj(["git", "remote", "remove", "rem3"]) 314 .success(); 315 316 work_dir.run_jj(["git", "fetch", "--all-remotes"]).success(); 317 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 318 rem1: ppspxspk 4acd0343 message 319 @rem1: ppspxspk 4acd0343 message 320 rem2: pzqqpnpo 44c57802 message 321 @rem2: pzqqpnpo 44c57802 message 322 [EOF] 323 "); 324} 325 326#[test] 327fn test_git_fetch_multiple_remotes_from_config() { 328 let test_env = TestEnvironment::default(); 329 test_env.add_config("git.auto-local-bookmark = true"); 330 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 331 let work_dir = test_env.work_dir("repo"); 332 add_git_remote(&test_env, &work_dir, "rem1"); 333 add_git_remote(&test_env, &work_dir, "rem2"); 334 test_env.add_config(r#"git.fetch = ["rem1", "rem2"]"#); 335 336 work_dir.run_jj(["git", "fetch"]).success(); 337 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 338 rem1: ppspxspk 4acd0343 message 339 @rem1: ppspxspk 4acd0343 message 340 rem2: pzqqpnpo 44c57802 message 341 @rem2: pzqqpnpo 44c57802 message 342 [EOF] 343 "); 344} 345 346#[test] 347fn test_git_fetch_no_matching_remote() { 348 let test_env = TestEnvironment::default(); 349 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 350 let work_dir = test_env.work_dir("repo"); 351 352 let output = work_dir.run_jj(["git", "fetch", "--remote", "rem1"]); 353 insta::assert_snapshot!(output, @r" 354 ------- stderr ------- 355 Warning: No git remotes matching 'rem1' 356 Error: No git remotes to push 357 [EOF] 358 [exit status: 1] 359 "); 360 insta::assert_snapshot!(get_bookmark_output(&work_dir), @""); 361} 362 363#[test] 364fn test_git_fetch_nonexistent_remote() { 365 let test_env = TestEnvironment::default(); 366 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 367 let work_dir = test_env.work_dir("repo"); 368 add_git_remote(&test_env, &work_dir, "rem1"); 369 370 let output = work_dir.run_jj(["git", "fetch", "--remote", "rem1", "--remote", "rem2"]); 371 insta::assert_snapshot!(output, @r" 372 ------- stderr ------- 373 Warning: No git remotes matching 'rem2' 374 bookmark: rem1@rem1 [new] untracked 375 [EOF] 376 "); 377 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 378 rem1@rem1: ppspxspk 4acd0343 message 379 [EOF] 380 "); 381} 382 383#[test] 384fn test_git_fetch_nonexistent_remote_from_config() { 385 let test_env = TestEnvironment::default(); 386 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 387 let work_dir = test_env.work_dir("repo"); 388 add_git_remote(&test_env, &work_dir, "rem1"); 389 test_env.add_config(r#"git.fetch = ["rem1", "rem2"]"#); 390 391 let output = work_dir.run_jj(["git", "fetch"]); 392 insta::assert_snapshot!(output, @r" 393 ------- stderr ------- 394 Warning: No git remotes matching 'rem2' 395 bookmark: rem1@rem1 [new] untracked 396 [EOF] 397 "); 398 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 399 rem1@rem1: ppspxspk 4acd0343 message 400 [EOF] 401 "); 402} 403 404#[test] 405fn test_git_fetch_from_remote_named_git() { 406 let test_env = TestEnvironment::default(); 407 test_env.add_config("git.auto-local-bookmark = true"); 408 let work_dir = test_env.work_dir("repo"); 409 init_git_remote(&test_env, "git"); 410 411 git::init(work_dir.root()); 412 git::add_remote(work_dir.root(), "git", "../git"); 413 414 // Existing remote named 'git' shouldn't block the repo initialization. 415 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 416 417 // Try fetching from the remote named 'git'. 418 let output = work_dir.run_jj(["git", "fetch", "--remote=git"]); 419 insta::assert_snapshot!(output, @r" 420 ------- stderr ------- 421 Error: Git remote named 'git' is reserved for local Git repository 422 Hint: Run `jj git remote rename` to give a different name. 423 [EOF] 424 [exit status: 1] 425 "); 426 427 // Fetch remote refs by using the git CLI. 428 git::fetch(work_dir.root(), "git"); 429 430 // Implicit import shouldn't fail because of the remote ref. 431 let output = work_dir.run_jj(["bookmark", "list", "--all-remotes"]); 432 insta::assert_snapshot!(output, @r" 433 ------- stderr ------- 434 Warning: Failed to import some Git refs: 435 refs/remotes/git/git 436 Hint: Git remote named 'git' is reserved for local Git repository. 437 Use `jj git remote rename` to give a different name. 438 [EOF] 439 "); 440 441 // Explicit import also works. Warnings are printed twice because this is a 442 // colocated repo. That should be fine since "jj git import" wouldn't be 443 // used in colocated environment. 444 insta::assert_snapshot!(work_dir.run_jj(["git", "import"]), @r" 445 ------- stderr ------- 446 Warning: Failed to import some Git refs: 447 refs/remotes/git/git 448 Hint: Git remote named 'git' is reserved for local Git repository. 449 Use `jj git remote rename` to give a different name. 450 Warning: Failed to import some Git refs: 451 refs/remotes/git/git 452 Hint: Git remote named 'git' is reserved for local Git repository. 453 Use `jj git remote rename` to give a different name. 454 Nothing changed. 455 [EOF] 456 "); 457 458 // The remote can be renamed, and the ref can be imported. 459 work_dir 460 .run_jj(["git", "remote", "rename", "git", "bar"]) 461 .success(); 462 let output = work_dir.run_jj(["bookmark", "list", "--all-remotes"]); 463 insta::assert_snapshot!(output, @r" 464 git: vkponlun 400c483d message 465 @bar: vkponlun 400c483d message 466 @git: vkponlun 400c483d message 467 [EOF] 468 ------- stderr ------- 469 Done importing changes from the underlying Git repo. 470 [EOF] 471 "); 472} 473 474#[test] 475fn test_git_fetch_from_remote_with_slashes() { 476 let test_env = TestEnvironment::default(); 477 test_env.add_config("git.auto-local-bookmark = true"); 478 let work_dir = test_env.work_dir("repo"); 479 init_git_remote(&test_env, "source"); 480 481 git::init(work_dir.root()); 482 git::add_remote(work_dir.root(), "slash/origin", "../source"); 483 484 // Existing remote with slash shouldn't block the repo initialization. 485 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 486 487 // Try fetching from the remote named 'git'. 488 let output = work_dir.run_jj(["git", "fetch", "--remote=slash/origin"]); 489 insta::assert_snapshot!(output, @r" 490 ------- stderr ------- 491 Error: Git remotes with slashes are incompatible with jj: slash/origin 492 Hint: Run `jj git remote rename` to give a different name. 493 [EOF] 494 [exit status: 1] 495 "); 496} 497 498#[test] 499fn test_git_fetch_prune_before_updating_tips() { 500 let test_env = TestEnvironment::default(); 501 test_env.add_config("git.auto-local-bookmark = true"); 502 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 503 let work_dir = test_env.work_dir("repo"); 504 let git_repo = add_git_remote(&test_env, &work_dir, "origin"); 505 work_dir.run_jj(["git", "fetch"]).success(); 506 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 507 origin: qmyrypzk ab8b299e message 508 @origin: qmyrypzk ab8b299e message 509 [EOF] 510 "); 511 512 // Remove origin bookmark in git repo and create origin/subname 513 let mut origin_reference = git_repo.find_reference("refs/heads/origin").unwrap(); 514 let commit_id = origin_reference.peel_to_commit().unwrap().id().detach(); 515 origin_reference.delete().unwrap(); 516 git_repo 517 .reference( 518 "refs/heads/origin/subname", 519 commit_id, 520 gix::refs::transaction::PreviousValue::MustNotExist, 521 "create new reference", 522 ) 523 .unwrap(); 524 525 work_dir.run_jj(["git", "fetch"]).success(); 526 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 527 origin/subname: qmyrypzk ab8b299e message 528 @origin: qmyrypzk ab8b299e message 529 [EOF] 530 "); 531} 532 533#[test] 534fn test_git_fetch_conflicting_bookmarks() { 535 let test_env = TestEnvironment::default(); 536 test_env.add_config("git.auto-local-bookmark = true"); 537 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 538 let work_dir = test_env.work_dir("repo"); 539 add_git_remote(&test_env, &work_dir, "rem1"); 540 541 // Create a rem1 bookmark locally 542 work_dir.run_jj(["new", "root()"]).success(); 543 work_dir 544 .run_jj(["bookmark", "create", "-r@", "rem1"]) 545 .success(); 546 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 547 rem1: kkmpptxz 2b17ac71 (empty) (no description set) 548 [EOF] 549 "); 550 551 work_dir 552 .run_jj(["git", "fetch", "--remote", "rem1", "--branch", "glob:*"]) 553 .success(); 554 // This should result in a CONFLICTED bookmark 555 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 556 rem1 (conflicted): 557 + kkmpptxz 2b17ac71 (empty) (no description set) 558 + ppspxspk 4acd0343 message 559 @rem1 (behind by 1 commits): ppspxspk 4acd0343 message 560 [EOF] 561 "); 562} 563 564#[test] 565fn test_git_fetch_conflicting_bookmarks_colocated() { 566 let test_env = TestEnvironment::default(); 567 test_env.add_config("git.auto-local-bookmark = true"); 568 let work_dir = test_env.work_dir("repo"); 569 git::init(work_dir.root()); 570 // create_colocated_repo_and_bookmarks_from_trunk1(&test_env, &repo_path); 571 work_dir 572 .run_jj(["git", "init", "--git-repo", "."]) 573 .success(); 574 add_git_remote(&test_env, &work_dir, "rem1"); 575 insta::assert_snapshot!(get_bookmark_output(&work_dir), @""); 576 577 // Create a rem1 bookmark locally 578 work_dir.run_jj(["new", "root()"]).success(); 579 work_dir 580 .run_jj(["bookmark", "create", "-r@", "rem1"]) 581 .success(); 582 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 583 rem1: zsuskuln c2934cfb (empty) (no description set) 584 @git: zsuskuln c2934cfb (empty) (no description set) 585 [EOF] 586 "); 587 588 work_dir 589 .run_jj(["git", "fetch", "--remote", "rem1", "--branch", "rem1"]) 590 .success(); 591 // This should result in a CONFLICTED bookmark 592 // See https://github.com/jj-vcs/jj/pull/1146#discussion_r1112372340 for the bug this tests for. 593 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 594 rem1 (conflicted): 595 + zsuskuln c2934cfb (empty) (no description set) 596 + ppspxspk 4acd0343 message 597 @git (behind by 1 commits): zsuskuln c2934cfb (empty) (no description set) 598 @rem1 (behind by 1 commits): ppspxspk 4acd0343 message 599 [EOF] 600 "); 601} 602 603// Helper functions to test obtaining multiple bookmarks at once and changed 604// bookmarks 605fn create_colocated_repo_and_bookmarks_from_trunk1(work_dir: &TestWorkDir) -> String { 606 // Create a colocated repo in `source` to populate it more easily 607 work_dir 608 .run_jj(["git", "init", "--git-repo", "."]) 609 .success(); 610 create_commit(work_dir, "trunk1", &[]); 611 create_commit(work_dir, "a1", &["trunk1"]); 612 create_commit(work_dir, "a2", &["trunk1"]); 613 create_commit(work_dir, "b", &["trunk1"]); 614 format!( 615 " ===== Source git repo contents =====\n{}", 616 get_log_output(work_dir) 617 ) 618} 619 620fn create_trunk2_and_rebase_bookmarks(work_dir: &TestWorkDir) -> String { 621 create_commit(work_dir, "trunk2", &["trunk1"]); 622 for br in ["a1", "a2", "b"] { 623 work_dir 624 .run_jj(["rebase", "-b", br, "-d", "trunk2"]) 625 .success(); 626 } 627 format!( 628 " ===== Source git repo contents =====\n{}", 629 get_log_output(work_dir) 630 ) 631} 632 633#[test] 634fn test_git_fetch_all() { 635 let test_env = TestEnvironment::default(); 636 test_env.add_config("git.auto-local-bookmark = true"); 637 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 638 let source_dir = test_env.work_dir("source"); 639 git::init(source_dir.root()); 640 641 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 642 let output = test_env.run_jj_in(".", ["git", "clone", "source", "target"]); 643 insta::assert_snapshot!(output, @r#" 644 ------- stderr ------- 645 Fetching into new repo in "$TEST_ENV/target" 646 Nothing changed. 647 [EOF] 648 "#); 649 let target_dir = test_env.work_dir("target"); 650 651 let source_log = create_colocated_repo_and_bookmarks_from_trunk1(&source_dir); 652 insta::assert_snapshot!(source_log, @r#" 653 ===== Source git repo contents ===== 654 @ bc83465a3090 "b" b 655 │ ○ d4d535f1d579 "a2" a2 656 ├─╯ 657 │ ○ c8303692b8e2 "a1" a1 658 ├─╯ 659 ○ 382881770501 "trunk1" trunk1 660 ◆ 000000000000 "" 661 [EOF] 662 "#); 663 664 // Nothing in our repo before the fetch 665 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 666 @ e8849ae12c70 "" 667 ◆ 000000000000 "" 668 [EOF] 669 "#); 670 insta::assert_snapshot!(get_bookmark_output(&target_dir), @""); 671 let output = target_dir.run_jj(["git", "fetch"]); 672 insta::assert_snapshot!(output, @r" 673 ------- stderr ------- 674 bookmark: a1@origin [new] tracked 675 bookmark: a2@origin [new] tracked 676 bookmark: b@origin [new] tracked 677 bookmark: trunk1@origin [new] tracked 678 [EOF] 679 "); 680 insta::assert_snapshot!(get_bookmark_output(&target_dir), @r" 681 a1: mzvwutvl c8303692 a1 682 @origin: mzvwutvl c8303692 a1 683 a2: yqosqzyt d4d535f1 a2 684 @origin: yqosqzyt d4d535f1 a2 685 b: yostqsxw bc83465a b 686 @origin: yostqsxw bc83465a b 687 trunk1: kkmpptxz 38288177 trunk1 688 @origin: kkmpptxz 38288177 trunk1 689 [EOF] 690 "); 691 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 692 @ e8849ae12c70 "" 693 │ ○ bc83465a3090 "b" b 694 │ │ ○ d4d535f1d579 "a2" a2 695 │ ├─╯ 696 │ │ ○ c8303692b8e2 "a1" a1 697 │ ├─╯ 698 │ ○ 382881770501 "trunk1" trunk1 699 ├─╯ 700 ◆ 000000000000 "" 701 [EOF] 702 "#); 703 704 // ==== Change both repos ==== 705 // First, change the target repo: 706 let source_log = create_trunk2_and_rebase_bookmarks(&source_dir); 707 insta::assert_snapshot!(source_log, @r#" 708 ===== Source git repo contents ===== 709 ○ 6fc6fe17dbee "b" b 710 │ ○ baad96fead6c "a2" a2 711 ├─╯ 712 │ ○ 798c5e2435e1 "a1" a1 713 ├─╯ 714 @ e80d998ab04b "trunk2" trunk2 715 ○ 382881770501 "trunk1" trunk1 716 ◆ 000000000000 "" 717 [EOF] 718 "#); 719 // Change a bookmark in the source repo as well, so that it becomes conflicted. 720 target_dir 721 .run_jj(["describe", "b", "-m=new_descr_for_b_to_create_conflict"]) 722 .success(); 723 724 // Our repo before and after fetch 725 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 726 @ e8849ae12c70 "" 727 │ ○ 0fbbc495357c "new_descr_for_b_to_create_conflict" b* 728 │ │ ○ d4d535f1d579 "a2" a2 729 │ ├─╯ 730 │ │ ○ c8303692b8e2 "a1" a1 731 │ ├─╯ 732 │ ○ 382881770501 "trunk1" trunk1 733 ├─╯ 734 ◆ 000000000000 "" 735 [EOF] 736 "#); 737 insta::assert_snapshot!(get_bookmark_output(&target_dir), @r" 738 a1: mzvwutvl c8303692 a1 739 @origin: mzvwutvl c8303692 a1 740 a2: yqosqzyt d4d535f1 a2 741 @origin: yqosqzyt d4d535f1 a2 742 b: yostqsxw 0fbbc495 new_descr_for_b_to_create_conflict 743 @origin (ahead by 1 commits, behind by 1 commits): yostqsxw hidden bc83465a b 744 trunk1: kkmpptxz 38288177 trunk1 745 @origin: kkmpptxz 38288177 trunk1 746 [EOF] 747 "); 748 let output = target_dir.run_jj(["git", "fetch"]); 749 insta::assert_snapshot!(output, @r" 750 ------- stderr ------- 751 bookmark: a1@origin [updated] tracked 752 bookmark: a2@origin [updated] tracked 753 bookmark: b@origin [updated] tracked 754 bookmark: trunk2@origin [new] tracked 755 Abandoned 2 commits that are no longer reachable. 756 [EOF] 757 "); 758 insta::assert_snapshot!(get_bookmark_output(&target_dir), @r" 759 a1: mzvwutvl 798c5e24 a1 760 @origin: mzvwutvl 798c5e24 a1 761 a2: yqosqzyt baad96fe a2 762 @origin: yqosqzyt baad96fe a2 763 b (conflicted): 764 - yostqsxw hidden bc83465a b 765 + yostqsxw?? 0fbbc495 new_descr_for_b_to_create_conflict 766 + yostqsxw?? 6fc6fe17 b 767 @origin (behind by 1 commits): yostqsxw?? 6fc6fe17 b 768 trunk1: kkmpptxz 38288177 trunk1 769 @origin: kkmpptxz 38288177 trunk1 770 trunk2: uyznsvlq e80d998a trunk2 771 @origin: uyznsvlq e80d998a trunk2 772 [EOF] 773 "); 774 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 775 @ e8849ae12c70 "" 776 │ ○ 6fc6fe17dbee "b" b?? b@origin 777 │ │ ○ baad96fead6c "a2" a2 778 │ ├─╯ 779 │ │ ○ 798c5e2435e1 "a1" a1 780 │ ├─╯ 781 │ ○ e80d998ab04b "trunk2" trunk2 782 │ │ ○ 0fbbc495357c "new_descr_for_b_to_create_conflict" b?? 783 │ ├─╯ 784 │ ○ 382881770501 "trunk1" trunk1 785 ├─╯ 786 ◆ 000000000000 "" 787 [EOF] 788 "#); 789} 790 791#[test] 792fn test_git_fetch_some_of_many_bookmarks() { 793 let test_env = TestEnvironment::default(); 794 test_env.add_config("git.auto-local-bookmark = true"); 795 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 796 let source_dir = test_env.work_dir("source"); 797 git::init(source_dir.root()); 798 799 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 800 let output = test_env.run_jj_in(".", ["git", "clone", "source", "target"]); 801 insta::assert_snapshot!(output, @r#" 802 ------- stderr ------- 803 Fetching into new repo in "$TEST_ENV/target" 804 Nothing changed. 805 [EOF] 806 "#); 807 let target_dir = test_env.work_dir("target"); 808 809 let source_log = create_colocated_repo_and_bookmarks_from_trunk1(&source_dir); 810 insta::assert_snapshot!(source_log, @r#" 811 ===== Source git repo contents ===== 812 @ bc83465a3090 "b" b 813 │ ○ d4d535f1d579 "a2" a2 814 ├─╯ 815 │ ○ c8303692b8e2 "a1" a1 816 ├─╯ 817 ○ 382881770501 "trunk1" trunk1 818 ◆ 000000000000 "" 819 [EOF] 820 "#); 821 822 // Test an error message 823 let output = target_dir.run_jj(["git", "fetch", "--branch", "glob:^:a*"]); 824 insta::assert_snapshot!(output, @r" 825 ------- stderr ------- 826 Error: Invalid branch pattern provided. When fetching, branch names and globs may not contain the characters `:`, `^`, `?`, `[`, `]` 827 [EOF] 828 [exit status: 1] 829 "); 830 let output = target_dir.run_jj(["git", "fetch", "--branch", "a*"]); 831 insta::assert_snapshot!(output, @r" 832 ------- stderr ------- 833 Error: Branch names may not include `*`. 834 Hint: Prefix the pattern with `glob:` to expand `*` as a glob 835 [EOF] 836 [exit status: 1] 837 "); 838 839 // Nothing in our repo before the fetch 840 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 841 @ e8849ae12c70 "" 842 ◆ 000000000000 "" 843 [EOF] 844 "#); 845 // Fetch one bookmark... 846 let output = target_dir.run_jj(["git", "fetch", "--branch", "b"]); 847 insta::assert_snapshot!(output, @r" 848 ------- stderr ------- 849 bookmark: b@origin [new] tracked 850 [EOF] 851 "); 852 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 853 @ e8849ae12c70 "" 854 │ ○ bc83465a3090 "b" b 855 │ ○ 382881770501 "trunk1" 856 ├─╯ 857 ◆ 000000000000 "" 858 [EOF] 859 "#); 860 // ...check what the intermediate state looks like... 861 insta::assert_snapshot!(get_bookmark_output(&target_dir), @r" 862 b: yostqsxw bc83465a b 863 @origin: yostqsxw bc83465a b 864 [EOF] 865 "); 866 // ...then fetch two others with a glob. 867 let output = target_dir.run_jj(["git", "fetch", "--branch", "glob:a*"]); 868 insta::assert_snapshot!(output, @r" 869 ------- stderr ------- 870 bookmark: a1@origin [new] tracked 871 bookmark: a2@origin [new] tracked 872 [EOF] 873 "); 874 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 875 @ e8849ae12c70 "" 876 │ ○ d4d535f1d579 "a2" a2 877 │ │ ○ c8303692b8e2 "a1" a1 878 │ ├─╯ 879 │ │ ○ bc83465a3090 "b" b 880 │ ├─╯ 881 │ ○ 382881770501 "trunk1" 882 ├─╯ 883 ◆ 000000000000 "" 884 [EOF] 885 "#); 886 // Fetching the same bookmark again 887 let output = target_dir.run_jj(["git", "fetch", "--branch", "a1"]); 888 insta::assert_snapshot!(output, @r" 889 ------- stderr ------- 890 Nothing changed. 891 [EOF] 892 "); 893 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 894 @ e8849ae12c70 "" 895 │ ○ d4d535f1d579 "a2" a2 896 │ │ ○ c8303692b8e2 "a1" a1 897 │ ├─╯ 898 │ │ ○ bc83465a3090 "b" b 899 │ ├─╯ 900 │ ○ 382881770501 "trunk1" 901 ├─╯ 902 ◆ 000000000000 "" 903 [EOF] 904 "#); 905 906 // ==== Change both repos ==== 907 // First, change the target repo: 908 let source_log = create_trunk2_and_rebase_bookmarks(&source_dir); 909 insta::assert_snapshot!(source_log, @r#" 910 ===== Source git repo contents ===== 911 ○ 2b30dbc93959 "b" b 912 │ ○ 841140b152fc "a2" a2 913 ├─╯ 914 │ ○ bc7e74c21d43 "a1" a1 915 ├─╯ 916 @ 756be1d31c41 "trunk2" trunk2 917 ○ 382881770501 "trunk1" trunk1 918 ◆ 000000000000 "" 919 [EOF] 920 "#); 921 // Change a bookmark in the source repo as well, so that it becomes conflicted. 922 target_dir 923 .run_jj(["describe", "b", "-m=new_descr_for_b_to_create_conflict"]) 924 .success(); 925 926 // Our repo before and after fetch of two bookmarks 927 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 928 @ e8849ae12c70 "" 929 │ ○ c62db3119722 "new_descr_for_b_to_create_conflict" b* 930 │ │ ○ d4d535f1d579 "a2" a2 931 │ ├─╯ 932 │ │ ○ c8303692b8e2 "a1" a1 933 │ ├─╯ 934 │ ○ 382881770501 "trunk1" 935 ├─╯ 936 ◆ 000000000000 "" 937 [EOF] 938 "#); 939 let output = target_dir.run_jj(["git", "fetch", "--branch", "b", "--branch", "a1"]); 940 insta::assert_snapshot!(output, @r" 941 ------- stderr ------- 942 bookmark: a1@origin [updated] tracked 943 bookmark: b@origin [updated] tracked 944 Abandoned 1 commits that are no longer reachable. 945 [EOF] 946 "); 947 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 948 @ e8849ae12c70 "" 949 │ ○ 2b30dbc93959 "b" b?? b@origin 950 │ │ ○ bc7e74c21d43 "a1" a1 951 │ ├─╯ 952 │ ○ 756be1d31c41 "trunk2" 953 │ │ ○ c62db3119722 "new_descr_for_b_to_create_conflict" b?? 954 │ ├─╯ 955 │ │ ○ d4d535f1d579 "a2" a2 956 │ ├─╯ 957 │ ○ 382881770501 "trunk1" 958 ├─╯ 959 ◆ 000000000000 "" 960 [EOF] 961 "#); 962 963 // We left a2 where it was before, let's see how `jj bookmark list` sees this. 964 insta::assert_snapshot!(get_bookmark_output(&target_dir), @r" 965 a1: mzvwutvl bc7e74c2 a1 966 @origin: mzvwutvl bc7e74c2 a1 967 a2: yqosqzyt d4d535f1 a2 968 @origin: yqosqzyt d4d535f1 a2 969 b (conflicted): 970 - yostqsxw hidden bc83465a b 971 + yostqsxw?? c62db311 new_descr_for_b_to_create_conflict 972 + yostqsxw?? 2b30dbc9 b 973 @origin (behind by 1 commits): yostqsxw?? 2b30dbc9 b 974 [EOF] 975 "); 976 // Now, let's fetch a2 and double-check that fetching a1 and b again doesn't do 977 // anything. 978 let output = target_dir.run_jj(["git", "fetch", "--branch", "b", "--branch", "glob:a*"]); 979 insta::assert_snapshot!(output, @r" 980 ------- stderr ------- 981 bookmark: a2@origin [updated] tracked 982 Abandoned 1 commits that are no longer reachable. 983 [EOF] 984 "); 985 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 986 @ e8849ae12c70 "" 987 │ ○ 841140b152fc "a2" a2 988 │ │ ○ 2b30dbc93959 "b" b?? b@origin 989 │ ├─╯ 990 │ │ ○ bc7e74c21d43 "a1" a1 991 │ ├─╯ 992 │ ○ 756be1d31c41 "trunk2" 993 │ │ ○ c62db3119722 "new_descr_for_b_to_create_conflict" b?? 994 │ ├─╯ 995 │ ○ 382881770501 "trunk1" 996 ├─╯ 997 ◆ 000000000000 "" 998 [EOF] 999 "#); 1000 insta::assert_snapshot!(get_bookmark_output(&target_dir), @r" 1001 a1: mzvwutvl bc7e74c2 a1 1002 @origin: mzvwutvl bc7e74c2 a1 1003 a2: yqosqzyt 841140b1 a2 1004 @origin: yqosqzyt 841140b1 a2 1005 b (conflicted): 1006 - yostqsxw hidden bc83465a b 1007 + yostqsxw?? c62db311 new_descr_for_b_to_create_conflict 1008 + yostqsxw?? 2b30dbc9 b 1009 @origin (behind by 1 commits): yostqsxw?? 2b30dbc9 b 1010 [EOF] 1011 "); 1012} 1013 1014#[test] 1015fn test_git_fetch_bookmarks_some_missing() { 1016 let test_env = TestEnvironment::default(); 1017 test_env.add_config("git.auto-local-bookmark = true"); 1018 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1019 let work_dir = test_env.work_dir("repo"); 1020 add_git_remote(&test_env, &work_dir, "origin"); 1021 add_git_remote(&test_env, &work_dir, "rem1"); 1022 add_git_remote(&test_env, &work_dir, "rem2"); 1023 add_git_remote(&test_env, &work_dir, "rem3"); 1024 1025 // single missing bookmark, implicit remotes (@origin) 1026 let output = work_dir.run_jj(["git", "fetch", "--branch", "noexist"]); 1027 insta::assert_snapshot!(output, @r" 1028 ------- stderr ------- 1029 Warning: No branch matching `noexist` found on any specified/configured remote 1030 Nothing changed. 1031 [EOF] 1032 "); 1033 insta::assert_snapshot!(get_bookmark_output(&work_dir), @""); 1034 1035 // multiple missing bookmarks, implicit remotes (@origin) 1036 let output = work_dir.run_jj([ 1037 "git", "fetch", "--branch", "noexist1", "--branch", "noexist2", 1038 ]); 1039 insta::assert_snapshot!(output, @r" 1040 ------- stderr ------- 1041 Warning: No branch matching `noexist1` found on any specified/configured remote 1042 Warning: No branch matching `noexist2` found on any specified/configured remote 1043 Nothing changed. 1044 [EOF] 1045 "); 1046 insta::assert_snapshot!(get_bookmark_output(&work_dir), @""); 1047 1048 // single existing bookmark, implicit remotes (@origin) 1049 let output = work_dir.run_jj(["git", "fetch", "--branch", "origin"]); 1050 insta::assert_snapshot!(output, @r" 1051 ------- stderr ------- 1052 bookmark: origin@origin [new] tracked 1053 [EOF] 1054 "); 1055 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1056 origin: qmyrypzk ab8b299e message 1057 @origin: qmyrypzk ab8b299e message 1058 [EOF] 1059 "); 1060 1061 // multiple existing bookmark, explicit remotes, each bookmark is only in one 1062 // remote. 1063 let output = work_dir.run_jj([ 1064 "git", "fetch", "--branch", "rem1", "--branch", "rem2", "--branch", "rem3", "--remote", 1065 "rem1", "--remote", "rem2", "--remote", "rem3", 1066 ]); 1067 insta::assert_snapshot!(output, @r" 1068 ------- stderr ------- 1069 bookmark: rem1@rem1 [new] tracked 1070 bookmark: rem2@rem2 [new] tracked 1071 bookmark: rem3@rem3 [new] tracked 1072 [EOF] 1073 "); 1074 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1075 origin: qmyrypzk ab8b299e message 1076 @origin: qmyrypzk ab8b299e message 1077 rem1: ppspxspk 4acd0343 message 1078 @rem1: ppspxspk 4acd0343 message 1079 rem2: pzqqpnpo 44c57802 message 1080 @rem2: pzqqpnpo 44c57802 message 1081 rem3: wrzwlmys 45a3faef message 1082 @rem3: wrzwlmys 45a3faef message 1083 [EOF] 1084 "); 1085 1086 // multiple bookmarks, one exists, one doesn't 1087 let output = work_dir.run_jj([ 1088 "git", "fetch", "--branch", "rem1", "--branch", "notexist", "--remote", "rem1", 1089 ]); 1090 insta::assert_snapshot!(output, @r" 1091 ------- stderr ------- 1092 Warning: No branch matching `notexist` found on any specified/configured remote 1093 Nothing changed. 1094 [EOF] 1095 "); 1096 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1097 origin: qmyrypzk ab8b299e message 1098 @origin: qmyrypzk ab8b299e message 1099 rem1: ppspxspk 4acd0343 message 1100 @rem1: ppspxspk 4acd0343 message 1101 rem2: pzqqpnpo 44c57802 message 1102 @rem2: pzqqpnpo 44c57802 message 1103 rem3: wrzwlmys 45a3faef message 1104 @rem3: wrzwlmys 45a3faef message 1105 [EOF] 1106 "); 1107} 1108 1109#[test] 1110fn test_git_fetch_bookmarks_missing_with_subprocess_localized_message() { 1111 let test_env = TestEnvironment::default(); 1112 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1113 let work_dir = test_env.work_dir("repo"); 1114 add_git_remote(&test_env, &work_dir, "origin"); 1115 1116 // "fatal: couldn't find remote ref %s" shouldn't be localized. 1117 let output = work_dir.run_jj_with(|cmd| { 1118 cmd.args(["git", "fetch", "--branch=unknown"]) 1119 // Initialize locale as "en_US" which is the most common. 1120 .env("LC_ALL", "en_US.UTF-8") 1121 // Set some other locale variables for testing. 1122 .env("LC_MESSAGES", "en_US.UTF-8") 1123 .env("LANG", "en_US.UTF-8") 1124 // GNU gettext prioritizes LANGUAGE if translation is enabled. It works 1125 // no matter if system locale exists or not. 1126 .env("LANGUAGE", "zh_TW") 1127 }); 1128 insta::assert_snapshot!(output, @r" 1129 ------- stderr ------- 1130 Warning: No branch matching `unknown` found on any specified/configured remote 1131 Nothing changed. 1132 [EOF] 1133 "); 1134} 1135 1136// See `test_undo_restore_commands.rs` for fetch-undo-push and fetch-undo-fetch 1137// of the same bookmarks for various kinds of undo. 1138#[test] 1139fn test_git_fetch_undo() { 1140 let test_env = TestEnvironment::default(); 1141 test_env.add_config("git.auto-local-bookmark = true"); 1142 let source_dir = test_env.work_dir("source"); 1143 git::init(source_dir.root()); 1144 1145 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 1146 let output = test_env.run_jj_in(".", ["git", "clone", "source", "target"]); 1147 insta::assert_snapshot!(output, @r#" 1148 ------- stderr ------- 1149 Fetching into new repo in "$TEST_ENV/target" 1150 Nothing changed. 1151 [EOF] 1152 "#); 1153 let target_dir = test_env.work_dir("target"); 1154 1155 let source_log = create_colocated_repo_and_bookmarks_from_trunk1(&source_dir); 1156 insta::assert_snapshot!(source_log, @r#" 1157 ===== Source git repo contents ===== 1158 @ bc83465a3090 "b" b 1159 │ ○ d4d535f1d579 "a2" a2 1160 ├─╯ 1161 │ ○ c8303692b8e2 "a1" a1 1162 ├─╯ 1163 ○ 382881770501 "trunk1" trunk1 1164 ◆ 000000000000 "" 1165 [EOF] 1166 "#); 1167 1168 // Fetch 2 bookmarks 1169 let output = target_dir.run_jj(["git", "fetch", "--branch", "b", "--branch", "a1"]); 1170 insta::assert_snapshot!(output, @r" 1171 ------- stderr ------- 1172 bookmark: a1@origin [new] tracked 1173 bookmark: b@origin [new] tracked 1174 [EOF] 1175 "); 1176 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1177 @ e8849ae12c70 "" 1178 │ ○ bc83465a3090 "b" b 1179 │ │ ○ c8303692b8e2 "a1" a1 1180 │ ├─╯ 1181 │ ○ 382881770501 "trunk1" 1182 ├─╯ 1183 ◆ 000000000000 "" 1184 [EOF] 1185 "#); 1186 let output = target_dir.run_jj(["undo"]); 1187 insta::assert_snapshot!(output, @r" 1188 ------- stderr ------- 1189 Undid operation: 158b589e0e15 (2001-02-03 08:05:18) fetch from git remote(s) origin 1190 [EOF] 1191 "); 1192 // The undo works as expected 1193 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1194 @ e8849ae12c70 "" 1195 ◆ 000000000000 "" 1196 [EOF] 1197 "#); 1198 // Now try to fetch just one bookmark 1199 let output = target_dir.run_jj(["git", "fetch", "--branch", "b"]); 1200 insta::assert_snapshot!(output, @r" 1201 ------- stderr ------- 1202 bookmark: b@origin [new] tracked 1203 [EOF] 1204 "); 1205 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1206 @ e8849ae12c70 "" 1207 │ ○ bc83465a3090 "b" b 1208 │ ○ 382881770501 "trunk1" 1209 ├─╯ 1210 ◆ 000000000000 "" 1211 [EOF] 1212 "#); 1213} 1214 1215// Compare to `test_git_import_undo` in test_git_import_export 1216// TODO: Explain why these behaviors are useful 1217#[test] 1218fn test_fetch_undo_what() { 1219 let test_env = TestEnvironment::default(); 1220 test_env.add_config("git.auto-local-bookmark = true"); 1221 let source_dir = test_env.work_dir("source"); 1222 git::init(source_dir.root()); 1223 1224 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 1225 let output = test_env.run_jj_in(".", ["git", "clone", "source", "target"]); 1226 insta::assert_snapshot!(output, @r#" 1227 ------- stderr ------- 1228 Fetching into new repo in "$TEST_ENV/target" 1229 Nothing changed. 1230 [EOF] 1231 "#); 1232 let work_dir = test_env.work_dir("target"); 1233 1234 let source_log = create_colocated_repo_and_bookmarks_from_trunk1(&source_dir); 1235 insta::assert_snapshot!(source_log, @r#" 1236 ===== Source git repo contents ===== 1237 @ bc83465a3090 "b" b 1238 │ ○ d4d535f1d579 "a2" a2 1239 ├─╯ 1240 │ ○ c8303692b8e2 "a1" a1 1241 ├─╯ 1242 ○ 382881770501 "trunk1" trunk1 1243 ◆ 000000000000 "" 1244 [EOF] 1245 "#); 1246 1247 // Initial state we will try to return to after `op restore`. There are no 1248 // bookmarks. 1249 insta::assert_snapshot!(get_bookmark_output(&work_dir), @""); 1250 let base_operation_id = work_dir.current_operation_id(); 1251 1252 // Fetch a bookmark 1253 let output = work_dir.run_jj(["git", "fetch", "--branch", "b"]); 1254 insta::assert_snapshot!(output, @r" 1255 ------- stderr ------- 1256 bookmark: b@origin [new] tracked 1257 [EOF] 1258 "); 1259 insta::assert_snapshot!(get_log_output(&work_dir), @r#" 1260 @ e8849ae12c70 "" 1261 │ ○ bc83465a3090 "b" b 1262 │ ○ 382881770501 "trunk1" 1263 ├─╯ 1264 ◆ 000000000000 "" 1265 [EOF] 1266 "#); 1267 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1268 b: yostqsxw bc83465a b 1269 @origin: yostqsxw bc83465a b 1270 [EOF] 1271 "); 1272 1273 // We can undo the change in the repo without moving the remote-tracking 1274 // bookmark 1275 let output = work_dir.run_jj(["op", "restore", "--what", "repo", &base_operation_id]); 1276 insta::assert_snapshot!(output, @r" 1277 ------- stderr ------- 1278 Restored to operation: 8f47435a3990 (2001-02-03 08:05:07) add workspace 'default' 1279 [EOF] 1280 "); 1281 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1282 b (deleted) 1283 @origin: yostqsxw hidden bc83465a b 1284 [EOF] 1285 "); 1286 1287 // Now, let's demo restoring just the remote-tracking bookmark. First, let's 1288 // change our local repo state... 1289 work_dir 1290 .run_jj(["bookmark", "c", "-r@", "newbookmark"]) 1291 .success(); 1292 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1293 b (deleted) 1294 @origin: yostqsxw hidden bc83465a b 1295 newbookmark: qpvuntsm e8849ae1 (empty) (no description set) 1296 [EOF] 1297 "); 1298 // Restoring just the remote-tracking state will not affect `newbookmark`, but 1299 // will eliminate `b@origin`. 1300 let output = work_dir.run_jj([ 1301 "op", 1302 "restore", 1303 "--what", 1304 "remote-tracking", 1305 &base_operation_id, 1306 ]); 1307 insta::assert_snapshot!(output, @r" 1308 ------- stderr ------- 1309 Restored to operation: 8f47435a3990 (2001-02-03 08:05:07) add workspace 'default' 1310 [EOF] 1311 "); 1312 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1313 newbookmark: qpvuntsm e8849ae1 (empty) (no description set) 1314 [EOF] 1315 "); 1316} 1317 1318#[test] 1319fn test_git_fetch_remove_fetch() { 1320 let test_env = TestEnvironment::default(); 1321 test_env.add_config("git.auto-local-bookmark = true"); 1322 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1323 let work_dir = test_env.work_dir("repo"); 1324 add_git_remote(&test_env, &work_dir, "origin"); 1325 1326 work_dir 1327 .run_jj(["bookmark", "create", "-r@", "origin"]) 1328 .success(); 1329 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1330 origin: qpvuntsm e8849ae1 (empty) (no description set) 1331 [EOF] 1332 "); 1333 1334 work_dir.run_jj(["git", "fetch"]).success(); 1335 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1336 origin (conflicted): 1337 + qpvuntsm e8849ae1 (empty) (no description set) 1338 + qmyrypzk ab8b299e message 1339 @origin (behind by 1 commits): qmyrypzk ab8b299e message 1340 [EOF] 1341 "); 1342 1343 work_dir 1344 .run_jj(["git", "remote", "remove", "origin"]) 1345 .success(); 1346 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1347 origin (conflicted): 1348 + qpvuntsm e8849ae1 (empty) (no description set) 1349 + qmyrypzk ab8b299e message 1350 [EOF] 1351 "); 1352 1353 work_dir 1354 .run_jj(["git", "remote", "add", "origin", "../origin"]) 1355 .success(); 1356 1357 // Check that origin@origin is properly recreated 1358 let output = work_dir.run_jj(["git", "fetch"]); 1359 insta::assert_snapshot!(output, @r" 1360 ------- stderr ------- 1361 bookmark: origin@origin [new] tracked 1362 [EOF] 1363 "); 1364 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1365 origin (conflicted): 1366 + qpvuntsm e8849ae1 (empty) (no description set) 1367 + qmyrypzk ab8b299e message 1368 @origin (behind by 1 commits): qmyrypzk ab8b299e message 1369 [EOF] 1370 "); 1371} 1372 1373#[test] 1374fn test_git_fetch_rename_fetch() { 1375 let test_env = TestEnvironment::default(); 1376 test_env.add_config("git.auto-local-bookmark = true"); 1377 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1378 let work_dir = test_env.work_dir("repo"); 1379 add_git_remote(&test_env, &work_dir, "origin"); 1380 1381 work_dir 1382 .run_jj(["bookmark", "create", "-r@", "origin"]) 1383 .success(); 1384 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1385 origin: qpvuntsm e8849ae1 (empty) (no description set) 1386 [EOF] 1387 "); 1388 1389 work_dir.run_jj(["git", "fetch"]).success(); 1390 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1391 origin (conflicted): 1392 + qpvuntsm e8849ae1 (empty) (no description set) 1393 + qmyrypzk ab8b299e message 1394 @origin (behind by 1 commits): qmyrypzk ab8b299e message 1395 [EOF] 1396 "); 1397 1398 work_dir 1399 .run_jj(["git", "remote", "rename", "origin", "upstream"]) 1400 .success(); 1401 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1402 origin (conflicted): 1403 + qpvuntsm e8849ae1 (empty) (no description set) 1404 + qmyrypzk ab8b299e message 1405 @upstream (behind by 1 commits): qmyrypzk ab8b299e message 1406 [EOF] 1407 "); 1408 1409 // Check that jj indicates that nothing has changed 1410 let output = work_dir.run_jj(["git", "fetch", "--remote", "upstream"]); 1411 insta::assert_snapshot!(output, @r" 1412 ------- stderr ------- 1413 Nothing changed. 1414 [EOF] 1415 "); 1416} 1417 1418#[test] 1419fn test_git_fetch_removed_bookmark() { 1420 let test_env = TestEnvironment::default(); 1421 test_env.add_config("git.auto-local-bookmark = true"); 1422 let source_dir = test_env.work_dir("source"); 1423 git::init(source_dir.root()); 1424 1425 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 1426 let output = test_env.run_jj_in(".", ["git", "clone", "source", "target"]); 1427 insta::assert_snapshot!(output, @r#" 1428 ------- stderr ------- 1429 Fetching into new repo in "$TEST_ENV/target" 1430 Nothing changed. 1431 [EOF] 1432 "#); 1433 let target_dir = test_env.work_dir("target"); 1434 1435 let source_log = create_colocated_repo_and_bookmarks_from_trunk1(&source_dir); 1436 insta::assert_snapshot!(source_log, @r#" 1437 ===== Source git repo contents ===== 1438 @ bc83465a3090 "b" b 1439 │ ○ d4d535f1d579 "a2" a2 1440 ├─╯ 1441 │ ○ c8303692b8e2 "a1" a1 1442 ├─╯ 1443 ○ 382881770501 "trunk1" trunk1 1444 ◆ 000000000000 "" 1445 [EOF] 1446 "#); 1447 1448 // Fetch all bookmarks 1449 let output = target_dir.run_jj(["git", "fetch"]); 1450 insta::assert_snapshot!(output, @r" 1451 ------- stderr ------- 1452 bookmark: a1@origin [new] tracked 1453 bookmark: a2@origin [new] tracked 1454 bookmark: b@origin [new] tracked 1455 bookmark: trunk1@origin [new] tracked 1456 [EOF] 1457 "); 1458 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1459 @ e8849ae12c70 "" 1460 │ ○ bc83465a3090 "b" b 1461 │ │ ○ d4d535f1d579 "a2" a2 1462 │ ├─╯ 1463 │ │ ○ c8303692b8e2 "a1" a1 1464 │ ├─╯ 1465 │ ○ 382881770501 "trunk1" trunk1 1466 ├─╯ 1467 ◆ 000000000000 "" 1468 [EOF] 1469 "#); 1470 1471 // Remove a2 bookmark in origin 1472 source_dir 1473 .run_jj(["bookmark", "forget", "--include-remotes", "a2"]) 1474 .success(); 1475 1476 // Fetch bookmark a1 from origin and check that a2 is still there 1477 let output = target_dir.run_jj(["git", "fetch", "--branch", "a1"]); 1478 insta::assert_snapshot!(output, @r" 1479 ------- stderr ------- 1480 Nothing changed. 1481 [EOF] 1482 "); 1483 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1484 @ e8849ae12c70 "" 1485 │ ○ bc83465a3090 "b" b 1486 │ │ ○ d4d535f1d579 "a2" a2 1487 │ ├─╯ 1488 │ │ ○ c8303692b8e2 "a1" a1 1489 │ ├─╯ 1490 │ ○ 382881770501 "trunk1" trunk1 1491 ├─╯ 1492 ◆ 000000000000 "" 1493 [EOF] 1494 "#); 1495 1496 // Fetch bookmarks a2 from origin, and check that it has been removed locally 1497 let output = target_dir.run_jj(["git", "fetch", "--branch", "a2"]); 1498 insta::assert_snapshot!(output, @r" 1499 ------- stderr ------- 1500 bookmark: a2@origin [deleted] untracked 1501 Abandoned 1 commits that are no longer reachable. 1502 [EOF] 1503 "); 1504 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1505 @ e8849ae12c70 "" 1506 │ ○ bc83465a3090 "b" b 1507 │ │ ○ c8303692b8e2 "a1" a1 1508 │ ├─╯ 1509 │ ○ 382881770501 "trunk1" trunk1 1510 ├─╯ 1511 ◆ 000000000000 "" 1512 [EOF] 1513 "#); 1514} 1515 1516#[test] 1517fn test_git_fetch_removed_parent_bookmark() { 1518 let test_env = TestEnvironment::default(); 1519 test_env.add_config("git.auto-local-bookmark = true"); 1520 let source_dir = test_env.work_dir("source"); 1521 git::init(source_dir.root()); 1522 1523 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 1524 let output = test_env.run_jj_in(".", ["git", "clone", "source", "target"]); 1525 insta::assert_snapshot!(output, @r#" 1526 ------- stderr ------- 1527 Fetching into new repo in "$TEST_ENV/target" 1528 Nothing changed. 1529 [EOF] 1530 "#); 1531 let target_dir = test_env.work_dir("target"); 1532 1533 let source_log = create_colocated_repo_and_bookmarks_from_trunk1(&source_dir); 1534 insta::assert_snapshot!(source_log, @r#" 1535 ===== Source git repo contents ===== 1536 @ bc83465a3090 "b" b 1537 │ ○ d4d535f1d579 "a2" a2 1538 ├─╯ 1539 │ ○ c8303692b8e2 "a1" a1 1540 ├─╯ 1541 ○ 382881770501 "trunk1" trunk1 1542 ◆ 000000000000 "" 1543 [EOF] 1544 "#); 1545 1546 // Fetch all bookmarks 1547 let output = target_dir.run_jj(["git", "fetch"]); 1548 insta::assert_snapshot!(output, @r" 1549 ------- stderr ------- 1550 bookmark: a1@origin [new] tracked 1551 bookmark: a2@origin [new] tracked 1552 bookmark: b@origin [new] tracked 1553 bookmark: trunk1@origin [new] tracked 1554 [EOF] 1555 "); 1556 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1557 @ e8849ae12c70 "" 1558 │ ○ bc83465a3090 "b" b 1559 │ │ ○ d4d535f1d579 "a2" a2 1560 │ ├─╯ 1561 │ │ ○ c8303692b8e2 "a1" a1 1562 │ ├─╯ 1563 │ ○ 382881770501 "trunk1" trunk1 1564 ├─╯ 1565 ◆ 000000000000 "" 1566 [EOF] 1567 "#); 1568 1569 // Remove all bookmarks in origin. 1570 source_dir 1571 .run_jj(["bookmark", "forget", "--include-remotes", "glob:*"]) 1572 .success(); 1573 1574 // Fetch bookmarks master, trunk1 and a1 from origin and check that only those 1575 // bookmarks have been removed and that others were not rebased because of 1576 // abandoned commits. 1577 let output = target_dir.run_jj([ 1578 "git", "fetch", "--branch", "master", "--branch", "trunk1", "--branch", "a1", 1579 ]); 1580 insta::assert_snapshot!(output, @r" 1581 ------- stderr ------- 1582 bookmark: a1@origin [deleted] untracked 1583 bookmark: trunk1@origin [deleted] untracked 1584 Abandoned 1 commits that are no longer reachable. 1585 Warning: No branch matching `master` found on any specified/configured remote 1586 [EOF] 1587 "); 1588 insta::assert_snapshot!(get_log_output(&target_dir), @r#" 1589 @ e8849ae12c70 "" 1590 │ ○ bc83465a3090 "b" b 1591 │ │ ○ d4d535f1d579 "a2" a2 1592 │ ├─╯ 1593 │ ○ 382881770501 "trunk1" 1594 ├─╯ 1595 ◆ 000000000000 "" 1596 [EOF] 1597 "#); 1598} 1599 1600#[test] 1601fn test_git_fetch_remote_only_bookmark() { 1602 let test_env = TestEnvironment::default(); 1603 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1604 let work_dir = test_env.work_dir("repo"); 1605 1606 // Create non-empty git repo to add as a remote 1607 let git_repo_path = test_env.env_root().join("git-repo"); 1608 let git_repo = git::init(git_repo_path); 1609 work_dir 1610 .run_jj(["git", "remote", "add", "origin", "../git-repo"]) 1611 .success(); 1612 1613 // Create a commit and a bookmark in the git repo 1614 let commit_result = git::add_commit( 1615 &git_repo, 1616 "refs/heads/feature1", 1617 "file", 1618 b"content", 1619 "message", 1620 &[], 1621 ); 1622 1623 // Fetch using git.auto_local_bookmark = true 1624 test_env.add_config("git.auto-local-bookmark = true"); 1625 work_dir 1626 .run_jj(["git", "fetch", "--remote=origin"]) 1627 .success(); 1628 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1629 feature1: qomsplrm ebeb70d8 message 1630 @origin: qomsplrm ebeb70d8 message 1631 [EOF] 1632 "); 1633 1634 git::write_commit( 1635 &git_repo, 1636 "refs/heads/feature2", 1637 commit_result.tree_id, 1638 "message", 1639 &[], 1640 ); 1641 1642 // Fetch using git.auto_local_bookmark = false 1643 test_env.add_config("git.auto-local-bookmark = false"); 1644 work_dir 1645 .run_jj(["git", "fetch", "--remote=origin"]) 1646 .success(); 1647 insta::assert_snapshot!(get_log_output(&work_dir), @r#" 1648 @ e8849ae12c70 "" 1649 │ ◆ ebeb70d8c5f9 "message" feature1 feature2@origin 1650 ├─╯ 1651 ◆ 000000000000 "" 1652 [EOF] 1653 "#); 1654 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1655 feature1: qomsplrm ebeb70d8 message 1656 @origin: qomsplrm ebeb70d8 message 1657 feature2@origin: qomsplrm ebeb70d8 message 1658 [EOF] 1659 "); 1660} 1661 1662#[test] 1663fn test_git_fetch_preserve_commits_across_repos() { 1664 let test_env = TestEnvironment::default(); 1665 test_env.add_config("git.auto-local-bookmark = true"); 1666 test_env.run_jj_in(".", ["git", "init", "repo"]).success(); 1667 let work_dir = test_env.work_dir("repo"); 1668 1669 let upstream_repo = add_git_remote(&test_env, &work_dir, "upstream"); 1670 1671 let fork_path = test_env.env_root().join("fork"); 1672 let fork_repo = clone_git_remote_into(&test_env, "upstream", "fork"); 1673 work_dir 1674 .run_jj(["git", "remote", "add", "fork", "../fork"]) 1675 .success(); 1676 1677 // add commit to fork remote in another branch 1678 add_commit_to_branch(&fork_repo, "feature"); 1679 1680 // fetch remote bookmarks 1681 work_dir 1682 .run_jj(["git", "fetch", "--remote=fork", "--remote=upstream"]) 1683 .success(); 1684 insta::assert_snapshot!(get_log_output(&work_dir), @r#" 1685 @ e8849ae12c70 "" 1686 │ ○ bcd7cd779791 "message" upstream 1687 ├─╯ 1688 │ ○ 16ec9ef2877a "message" feature 1689 ├─╯ 1690 ◆ 000000000000 "" 1691 [EOF] 1692 "#); 1693 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1694 feature: srwrtuky 16ec9ef2 message 1695 @fork: srwrtuky 16ec9ef2 message 1696 upstream: zkvzklqn bcd7cd77 message 1697 @fork: zkvzklqn bcd7cd77 message 1698 @upstream: zkvzklqn bcd7cd77 message 1699 [EOF] 1700 "); 1701 1702 // merge fork/feature into the upstream/upstream 1703 git::add_remote(upstream_repo.git_dir(), "fork", fork_path.to_str().unwrap()); 1704 git::fetch(upstream_repo.git_dir(), "fork"); 1705 1706 let base_id = upstream_repo 1707 .find_reference("refs/heads/upstream") 1708 .unwrap() 1709 .peel_to_commit() 1710 .unwrap() 1711 .id() 1712 .detach(); 1713 1714 let fork_id = upstream_repo 1715 .find_reference("refs/remotes/fork/feature") 1716 .unwrap() 1717 .peel_to_commit() 1718 .unwrap() 1719 .id() 1720 .detach(); 1721 1722 git::write_commit( 1723 &upstream_repo, 1724 "refs/heads/upstream", 1725 upstream_repo.empty_tree().id().detach(), 1726 "merge", 1727 &[base_id, fork_id], 1728 ); 1729 1730 // remove branch on the fork 1731 fork_repo 1732 .find_reference("refs/heads/feature") 1733 .unwrap() 1734 .delete() 1735 .unwrap(); 1736 1737 // fetch again on the jj repo, first looking at fork and then at upstream 1738 work_dir 1739 .run_jj(["git", "fetch", "--remote=fork", "--remote=upstream"]) 1740 .success(); 1741 insta::assert_snapshot!(get_log_output(&work_dir), @r#" 1742 @ e8849ae12c70 "" 1743 │ ○ f3e9250bd003 "merge" upstream* 1744 │ ├─╮ 1745 │ │ ○ 16ec9ef2877a "message" 1746 ├───╯ 1747 │ ○ bcd7cd779791 "message" upstream@fork 1748 ├─╯ 1749 ◆ 000000000000 "" 1750 [EOF] 1751 "#); 1752 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 1753 upstream: trrkvuqr f3e9250b merge 1754 @fork (behind by 2 commits): zkvzklqn bcd7cd77 message 1755 @upstream: trrkvuqr f3e9250b merge 1756 [EOF] 1757 "); 1758}