just playing with tangled
at diffedit3 1289 lines 49 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. 14use std::path::Path; 15 16use crate::common::TestEnvironment; 17 18/// Creates a remote Git repo containing a branch with the same name 19fn init_git_remote(test_env: &TestEnvironment, remote: &str) { 20 let git_repo_path = test_env.env_root().join(remote); 21 let git_repo = git2::Repository::init(git_repo_path).unwrap(); 22 let signature = 23 git2::Signature::new("Some One", "some.one@example.com", &git2::Time::new(0, 0)).unwrap(); 24 let mut tree_builder = git_repo.treebuilder(None).unwrap(); 25 let file_oid = git_repo.blob(remote.as_bytes()).unwrap(); 26 tree_builder 27 .insert("file", file_oid, git2::FileMode::Blob.into()) 28 .unwrap(); 29 let tree_oid = tree_builder.write().unwrap(); 30 let tree = git_repo.find_tree(tree_oid).unwrap(); 31 git_repo 32 .commit( 33 Some(&format!("refs/heads/{remote}")), 34 &signature, 35 &signature, 36 "message", 37 &tree, 38 &[], 39 ) 40 .unwrap(); 41} 42 43/// Add a remote containing a branch with the same name 44fn add_git_remote(test_env: &TestEnvironment, repo_path: &Path, remote: &str) { 45 init_git_remote(test_env, remote); 46 test_env.jj_cmd_ok( 47 repo_path, 48 &["git", "remote", "add", remote, &format!("../{remote}")], 49 ); 50} 51 52fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String { 53 test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes"]) 54} 55 56fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) { 57 let descr = format!("descr_for_{name}"); 58 if parents.is_empty() { 59 test_env.jj_cmd_ok(repo_path, &["new", "root()", "-m", &descr]); 60 } else { 61 let mut args = vec!["new", "-m", &descr]; 62 args.extend(parents); 63 test_env.jj_cmd_ok(repo_path, &args); 64 } 65 std::fs::write(repo_path.join(name), format!("{name}\n")).unwrap(); 66 test_env.jj_cmd_ok(repo_path, &["branch", "create", name]); 67} 68 69fn get_log_output(test_env: &TestEnvironment, workspace_root: &Path) -> String { 70 let template = r#"commit_id.short() ++ " " ++ description.first_line() ++ " " ++ branches"#; 71 test_env.jj_cmd_success(workspace_root, &["log", "-T", template, "-r", "all()"]) 72} 73 74#[test] 75fn test_git_fetch_with_default_config() { 76 let test_env = TestEnvironment::default(); 77 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 78 let repo_path = test_env.env_root().join("repo"); 79 add_git_remote(&test_env, &repo_path, "origin"); 80 81 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 82 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 83 origin@origin: oputwtnw ffecd2d6 message 84 "###); 85} 86 87#[test] 88fn test_git_fetch_default_remote() { 89 let test_env = TestEnvironment::default(); 90 test_env.add_config("git.auto-local-branch = true"); 91 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 92 let repo_path = test_env.env_root().join("repo"); 93 add_git_remote(&test_env, &repo_path, "origin"); 94 95 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 96 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 97 origin: oputwtnw ffecd2d6 message 98 @origin: oputwtnw ffecd2d6 message 99 "###); 100} 101 102#[test] 103fn test_git_fetch_single_remote() { 104 let test_env = TestEnvironment::default(); 105 test_env.add_config("git.auto-local-branch = true"); 106 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 107 let repo_path = test_env.env_root().join("repo"); 108 add_git_remote(&test_env, &repo_path, "rem1"); 109 110 let (_stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 111 insta::assert_snapshot!(stderr, @r###" 112 Hint: Fetching from the only existing remote: rem1 113 branch: rem1@rem1 [new] tracked 114 "###); 115 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 116 rem1: qxosxrvv 6a211027 message 117 @rem1: qxosxrvv 6a211027 message 118 "###); 119} 120 121#[test] 122fn test_git_fetch_single_remote_all_remotes_flag() { 123 let test_env = TestEnvironment::default(); 124 test_env.add_config("git.auto-local-branch = true"); 125 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 126 let repo_path = test_env.env_root().join("repo"); 127 add_git_remote(&test_env, &repo_path, "rem1"); 128 129 test_env 130 .jj_cmd(&repo_path, &["git", "fetch", "--all-remotes"]) 131 .assert() 132 .success(); 133 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 134 rem1: qxosxrvv 6a211027 message 135 @rem1: qxosxrvv 6a211027 message 136 "###); 137} 138 139#[test] 140fn test_git_fetch_single_remote_from_arg() { 141 let test_env = TestEnvironment::default(); 142 test_env.add_config("git.auto-local-branch = true"); 143 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 144 let repo_path = test_env.env_root().join("repo"); 145 add_git_remote(&test_env, &repo_path, "rem1"); 146 147 test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote", "rem1"]); 148 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 149 rem1: qxosxrvv 6a211027 message 150 @rem1: qxosxrvv 6a211027 message 151 "###); 152} 153 154#[test] 155fn test_git_fetch_single_remote_from_config() { 156 let test_env = TestEnvironment::default(); 157 test_env.add_config("git.auto-local-branch = true"); 158 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 159 let repo_path = test_env.env_root().join("repo"); 160 add_git_remote(&test_env, &repo_path, "rem1"); 161 test_env.add_config(r#"git.fetch = "rem1""#); 162 163 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 164 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 165 rem1: qxosxrvv 6a211027 message 166 @rem1: qxosxrvv 6a211027 message 167 "###); 168} 169 170#[test] 171fn test_git_fetch_multiple_remotes() { 172 let test_env = TestEnvironment::default(); 173 test_env.add_config("git.auto-local-branch = true"); 174 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 175 let repo_path = test_env.env_root().join("repo"); 176 add_git_remote(&test_env, &repo_path, "rem1"); 177 add_git_remote(&test_env, &repo_path, "rem2"); 178 179 test_env.jj_cmd_ok( 180 &repo_path, 181 &["git", "fetch", "--remote", "rem1", "--remote", "rem2"], 182 ); 183 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 184 rem1: qxosxrvv 6a211027 message 185 @rem1: qxosxrvv 6a211027 message 186 rem2: yszkquru 2497a8a0 message 187 @rem2: yszkquru 2497a8a0 message 188 "###); 189} 190 191#[test] 192fn test_git_fetch_all_remotes() { 193 let test_env = TestEnvironment::default(); 194 test_env.add_config("git.auto-local-branch = true"); 195 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 196 let repo_path = test_env.env_root().join("repo"); 197 add_git_remote(&test_env, &repo_path, "rem1"); 198 add_git_remote(&test_env, &repo_path, "rem2"); 199 200 test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--all-remotes"]); 201 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 202 rem1: qxosxrvv 6a211027 message 203 @rem1: qxosxrvv 6a211027 message 204 rem2: yszkquru 2497a8a0 message 205 @rem2: yszkquru 2497a8a0 message 206 "###); 207} 208 209#[test] 210fn test_git_fetch_multiple_remotes_from_config() { 211 let test_env = TestEnvironment::default(); 212 test_env.add_config("git.auto-local-branch = true"); 213 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 214 let repo_path = test_env.env_root().join("repo"); 215 add_git_remote(&test_env, &repo_path, "rem1"); 216 add_git_remote(&test_env, &repo_path, "rem2"); 217 test_env.add_config(r#"git.fetch = ["rem1", "rem2"]"#); 218 219 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 220 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 221 rem1: qxosxrvv 6a211027 message 222 @rem1: qxosxrvv 6a211027 message 223 rem2: yszkquru 2497a8a0 message 224 @rem2: yszkquru 2497a8a0 message 225 "###); 226} 227 228#[test] 229fn test_git_fetch_nonexistent_remote() { 230 let test_env = TestEnvironment::default(); 231 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 232 let repo_path = test_env.env_root().join("repo"); 233 add_git_remote(&test_env, &repo_path, "rem1"); 234 235 let stderr = &test_env.jj_cmd_failure( 236 &repo_path, 237 &["git", "fetch", "--remote", "rem1", "--remote", "rem2"], 238 ); 239 insta::assert_snapshot!(stderr, @r###" 240 branch: rem1@rem1 [new] untracked 241 Error: No git remote named 'rem2' 242 "###); 243 // No remote should have been fetched as part of the failing transaction 244 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); 245} 246 247#[test] 248fn test_git_fetch_nonexistent_remote_from_config() { 249 let test_env = TestEnvironment::default(); 250 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 251 let repo_path = test_env.env_root().join("repo"); 252 add_git_remote(&test_env, &repo_path, "rem1"); 253 test_env.add_config(r#"git.fetch = ["rem1", "rem2"]"#); 254 255 let stderr = &test_env.jj_cmd_failure(&repo_path, &["git", "fetch"]); 256 insta::assert_snapshot!(stderr, @r###" 257 branch: rem1@rem1 [new] untracked 258 Error: No git remote named 'rem2' 259 "###); 260 // No remote should have been fetched as part of the failing transaction 261 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); 262} 263 264#[test] 265fn test_git_fetch_from_remote_named_git() { 266 let test_env = TestEnvironment::default(); 267 test_env.add_config("git.auto-local-branch = true"); 268 let repo_path = test_env.env_root().join("repo"); 269 init_git_remote(&test_env, "git"); 270 let git_repo = git2::Repository::init(&repo_path).unwrap(); 271 git_repo.remote("git", "../git").unwrap(); 272 273 // Existing remote named 'git' shouldn't block the repo initialization. 274 test_env.jj_cmd_ok(&repo_path, &["init", "--git-repo=."]); 275 276 // Try fetching from the remote named 'git'. 277 let stderr = &test_env.jj_cmd_failure(&repo_path, &["git", "fetch", "--remote=git"]); 278 insta::assert_snapshot!(stderr, @r###" 279 Error: Failed to import refs from underlying Git repo 280 Caused by: Git remote named 'git' is reserved for local Git repository 281 Hint: Run `jj git remote rename` to give different name. 282 "###); 283 284 // Implicit import shouldn't fail because of the remote ref. 285 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "list", "--all-remotes"]); 286 insta::assert_snapshot!(stdout, @""); 287 insta::assert_snapshot!(stderr, @""); 288 289 // Explicit import is an error. 290 // (This could be warning if we add mechanism to report ignored refs.) 291 insta::assert_snapshot!(test_env.jj_cmd_failure(&repo_path, &["git", "import"]), @r###" 292 Error: Failed to import refs from underlying Git repo 293 Caused by: Git remote named 'git' is reserved for local Git repository 294 Hint: Run `jj git remote rename` to give different name. 295 "###); 296 297 // The remote can be renamed, and the ref can be imported. 298 test_env.jj_cmd_ok(&repo_path, &["git", "remote", "rename", "git", "bar"]); 299 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["branch", "list", "--all-remotes"]); 300 insta::assert_snapshot!(stdout, @r###" 301 git: mrylzrtu 76fc7466 message 302 @bar: mrylzrtu 76fc7466 message 303 @git: mrylzrtu 76fc7466 message 304 "###); 305 insta::assert_snapshot!(stderr, @r###" 306 Done importing changes from the underlying Git repo. 307 "###); 308} 309 310#[test] 311fn test_git_fetch_prune_before_updating_tips() { 312 let test_env = TestEnvironment::default(); 313 test_env.add_config("git.auto-local-branch = true"); 314 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 315 let repo_path = test_env.env_root().join("repo"); 316 add_git_remote(&test_env, &repo_path, "origin"); 317 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 318 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 319 origin: oputwtnw ffecd2d6 message 320 @origin: oputwtnw ffecd2d6 message 321 "###); 322 323 // Remove origin branch in git repo and create origin/subname 324 let git_repo = git2::Repository::open(test_env.env_root().join("origin")).unwrap(); 325 git_repo 326 .find_branch("origin", git2::BranchType::Local) 327 .unwrap() 328 .rename("origin/subname", false) 329 .unwrap(); 330 331 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 332 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 333 origin/subname: oputwtnw ffecd2d6 message 334 @origin: oputwtnw ffecd2d6 message 335 "###); 336} 337 338#[test] 339fn test_git_fetch_conflicting_branches() { 340 let test_env = TestEnvironment::default(); 341 test_env.add_config("git.auto-local-branch = true"); 342 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 343 let repo_path = test_env.env_root().join("repo"); 344 add_git_remote(&test_env, &repo_path, "rem1"); 345 346 // Create a rem1 branch locally 347 test_env.jj_cmd_ok(&repo_path, &["new", "root()"]); 348 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "rem1"]); 349 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 350 rem1: kkmpptxz fcdbbd73 (empty) (no description set) 351 "###); 352 353 test_env.jj_cmd_ok( 354 &repo_path, 355 &["git", "fetch", "--remote", "rem1", "--branch", "glob:*"], 356 ); 357 // This should result in a CONFLICTED branch 358 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 359 rem1 (conflicted): 360 + kkmpptxz fcdbbd73 (empty) (no description set) 361 + qxosxrvv 6a211027 message 362 @rem1 (behind by 1 commits): qxosxrvv 6a211027 message 363 "###); 364} 365 366#[test] 367fn test_git_fetch_conflicting_branches_colocated() { 368 let test_env = TestEnvironment::default(); 369 test_env.add_config("git.auto-local-branch = true"); 370 let repo_path = test_env.env_root().join("repo"); 371 let _git_repo = git2::Repository::init(&repo_path).unwrap(); 372 // create_colocated_repo_and_branches_from_trunk1(&test_env, &repo_path); 373 test_env.jj_cmd_ok(&repo_path, &["init", "--git-repo", "."]); 374 add_git_remote(&test_env, &repo_path, "rem1"); 375 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); 376 377 // Create a rem1 branch locally 378 test_env.jj_cmd_ok(&repo_path, &["new", "root()"]); 379 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "rem1"]); 380 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 381 rem1: zsuskuln f652c321 (empty) (no description set) 382 @git: zsuskuln f652c321 (empty) (no description set) 383 "###); 384 385 test_env.jj_cmd_ok( 386 &repo_path, 387 &["git", "fetch", "--remote", "rem1", "--branch", "rem1"], 388 ); 389 // This should result in a CONFLICTED branch 390 // See https://github.com/martinvonz/jj/pull/1146#discussion_r1112372340 for the bug this tests for. 391 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 392 rem1 (conflicted): 393 + zsuskuln f652c321 (empty) (no description set) 394 + qxosxrvv 6a211027 message 395 @git (behind by 1 commits): zsuskuln f652c321 (empty) (no description set) 396 @rem1 (behind by 1 commits): qxosxrvv 6a211027 message 397 "###); 398} 399 400// Helper functions to test obtaining multiple branches at once and changed 401// branches 402fn create_colocated_repo_and_branches_from_trunk1( 403 test_env: &TestEnvironment, 404 repo_path: &Path, 405) -> String { 406 // Create a colocated repo in `source` to populate it more easily 407 test_env.jj_cmd_ok(repo_path, &["init", "--git-repo", "."]); 408 create_commit(test_env, repo_path, "trunk1", &[]); 409 create_commit(test_env, repo_path, "a1", &["trunk1"]); 410 create_commit(test_env, repo_path, "a2", &["trunk1"]); 411 create_commit(test_env, repo_path, "b", &["trunk1"]); 412 format!( 413 " ===== Source git repo contents =====\n{}", 414 get_log_output(test_env, repo_path) 415 ) 416} 417 418fn create_trunk2_and_rebase_branches(test_env: &TestEnvironment, repo_path: &Path) -> String { 419 create_commit(test_env, repo_path, "trunk2", &["trunk1"]); 420 for br in ["a1", "a2", "b"] { 421 test_env.jj_cmd_ok(repo_path, &["rebase", "-b", br, "-d", "trunk2"]); 422 } 423 format!( 424 " ===== Source git repo contents =====\n{}", 425 get_log_output(test_env, repo_path) 426 ) 427} 428 429#[test] 430fn test_git_fetch_all() { 431 let test_env = TestEnvironment::default(); 432 test_env.add_config("git.auto-local-branch = true"); 433 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 434 let source_git_repo_path = test_env.env_root().join("source"); 435 let _git_repo = git2::Repository::init(source_git_repo_path.clone()).unwrap(); 436 437 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 438 let (stdout, stderr) = 439 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "target"]); 440 insta::assert_snapshot!(stdout, @""); 441 insta::assert_snapshot!(stderr, @r###" 442 Fetching into new repo in "$TEST_ENV/target" 443 Nothing changed. 444 "###); 445 let target_jj_repo_path = test_env.env_root().join("target"); 446 447 let source_log = 448 create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path); 449 insta::assert_snapshot!(source_log, @r###" 450 ===== Source git repo contents ===== 451 @ c7d4bdcbc215 descr_for_b b 452 │ ◉ decaa3966c83 descr_for_a2 a2 453 ├─╯ 454 │ ◉ 359a9a02457d descr_for_a1 a1 455 ├─╯ 456 ◉ ff36dc55760e descr_for_trunk1 trunk1 457 ◉ 000000000000 458 "###); 459 460 // Nothing in our repo before the fetch 461 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 462 @ 230dd059e1b0 463 ◉ 000000000000 464 "###); 465 insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @""); 466 let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); 467 insta::assert_snapshot!(stdout, @""); 468 insta::assert_snapshot!(stderr, @r###" 469 branch: a1@origin [new] tracked 470 branch: a2@origin [new] tracked 471 branch: b@origin [new] tracked 472 branch: trunk1@origin [new] tracked 473 "###); 474 insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" 475 a1: nknoxmzm 359a9a02 descr_for_a1 476 @origin: nknoxmzm 359a9a02 descr_for_a1 477 a2: qkvnknrk decaa396 descr_for_a2 478 @origin: qkvnknrk decaa396 descr_for_a2 479 b: vpupmnsl c7d4bdcb descr_for_b 480 @origin: vpupmnsl c7d4bdcb descr_for_b 481 trunk1: zowqyktl ff36dc55 descr_for_trunk1 482 @origin: zowqyktl ff36dc55 descr_for_trunk1 483 "###); 484 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 485 ◉ c7d4bdcbc215 descr_for_b b 486 │ ◉ decaa3966c83 descr_for_a2 a2 487 ├─╯ 488 │ ◉ 359a9a02457d descr_for_a1 a1 489 ├─╯ 490 ◉ ff36dc55760e descr_for_trunk1 trunk1 491 │ @ 230dd059e1b0 492 ├─╯ 493 ◉ 000000000000 494 "###); 495 496 // ==== Change both repos ==== 497 // First, change the target repo: 498 let source_log = create_trunk2_and_rebase_branches(&test_env, &source_git_repo_path); 499 insta::assert_snapshot!(source_log, @r###" 500 ===== Source git repo contents ===== 501 ◉ babc49226c14 descr_for_b b 502 │ ◉ 91e46b4b2653 descr_for_a2 a2 503 ├─╯ 504 │ ◉ 0424f6dfc1ff descr_for_a1 a1 505 ├─╯ 506 @ 8f1f14fbbf42 descr_for_trunk2 trunk2 507 ◉ ff36dc55760e descr_for_trunk1 trunk1 508 ◉ 000000000000 509 "###); 510 // Change a branch in the source repo as well, so that it becomes conflicted. 511 test_env.jj_cmd_ok( 512 &target_jj_repo_path, 513 &["describe", "b", "-m=new_descr_for_b_to_create_conflict"], 514 ); 515 516 // Our repo before and after fetch 517 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 518 ◉ 061eddbb43ab new_descr_for_b_to_create_conflict b* 519 │ ◉ decaa3966c83 descr_for_a2 a2 520 ├─╯ 521 │ ◉ 359a9a02457d descr_for_a1 a1 522 ├─╯ 523 ◉ ff36dc55760e descr_for_trunk1 trunk1 524 │ @ 230dd059e1b0 525 ├─╯ 526 ◉ 000000000000 527 "###); 528 insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" 529 a1: nknoxmzm 359a9a02 descr_for_a1 530 @origin: nknoxmzm 359a9a02 descr_for_a1 531 a2: qkvnknrk decaa396 descr_for_a2 532 @origin: qkvnknrk decaa396 descr_for_a2 533 b: vpupmnsl 061eddbb new_descr_for_b_to_create_conflict 534 @origin (ahead by 1 commits, behind by 1 commits): vpupmnsl hidden c7d4bdcb descr_for_b 535 trunk1: zowqyktl ff36dc55 descr_for_trunk1 536 @origin: zowqyktl ff36dc55 descr_for_trunk1 537 "###); 538 let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); 539 insta::assert_snapshot!(stdout, @""); 540 insta::assert_snapshot!(stderr, @r###" 541 branch: a1@origin [updated] tracked 542 branch: a2@origin [updated] tracked 543 branch: b@origin [updated] tracked 544 branch: trunk2@origin [new] tracked 545 Abandoned 2 commits that are no longer reachable. 546 "###); 547 insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" 548 a1: quxllqov 0424f6df descr_for_a1 549 @origin: quxllqov 0424f6df descr_for_a1 550 a2: osusxwst 91e46b4b descr_for_a2 551 @origin: osusxwst 91e46b4b descr_for_a2 552 b (conflicted): 553 - vpupmnsl hidden c7d4bdcb descr_for_b 554 + vpupmnsl 061eddbb new_descr_for_b_to_create_conflict 555 + vktnwlsu babc4922 descr_for_b 556 @origin (behind by 1 commits): vktnwlsu babc4922 descr_for_b 557 trunk1: zowqyktl ff36dc55 descr_for_trunk1 558 @origin: zowqyktl ff36dc55 descr_for_trunk1 559 trunk2: umznmzko 8f1f14fb descr_for_trunk2 560 @origin: umznmzko 8f1f14fb descr_for_trunk2 561 "###); 562 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 563 ◉ babc49226c14 descr_for_b b?? b@origin 564 │ ◉ 91e46b4b2653 descr_for_a2 a2 565 ├─╯ 566 │ ◉ 0424f6dfc1ff descr_for_a1 a1 567 ├─╯ 568 ◉ 8f1f14fbbf42 descr_for_trunk2 trunk2 569 │ ◉ 061eddbb43ab new_descr_for_b_to_create_conflict b?? 570 ├─╯ 571 ◉ ff36dc55760e descr_for_trunk1 trunk1 572 │ @ 230dd059e1b0 573 ├─╯ 574 ◉ 000000000000 575 "###); 576} 577 578#[test] 579fn test_git_fetch_some_of_many_branches() { 580 let test_env = TestEnvironment::default(); 581 test_env.add_config("git.auto-local-branch = true"); 582 test_env.add_config(r#"revset-aliases."immutable_heads()" = "none()""#); 583 let source_git_repo_path = test_env.env_root().join("source"); 584 let _git_repo = git2::Repository::init(source_git_repo_path.clone()).unwrap(); 585 586 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 587 let (stdout, stderr) = 588 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "target"]); 589 insta::assert_snapshot!(stdout, @""); 590 insta::assert_snapshot!(stderr, @r###" 591 Fetching into new repo in "$TEST_ENV/target" 592 Nothing changed. 593 "###); 594 let target_jj_repo_path = test_env.env_root().join("target"); 595 596 let source_log = 597 create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path); 598 insta::assert_snapshot!(source_log, @r###" 599 ===== Source git repo contents ===== 600 @ c7d4bdcbc215 descr_for_b b 601 │ ◉ decaa3966c83 descr_for_a2 a2 602 ├─╯ 603 │ ◉ 359a9a02457d descr_for_a1 a1 604 ├─╯ 605 ◉ ff36dc55760e descr_for_trunk1 trunk1 606 ◉ 000000000000 607 "###); 608 609 // Test an error message 610 let stderr = test_env.jj_cmd_failure( 611 &target_jj_repo_path, 612 &["git", "fetch", "--branch", "glob:^:a*"], 613 ); 614 insta::assert_snapshot!(stderr, @r###" 615 Error: Invalid branch pattern provided. Patterns may not contain the characters `:`, `^`, `?`, `[`, `]` 616 "###); 617 let stderr = test_env.jj_cmd_failure(&target_jj_repo_path, &["git", "fetch", "--branch", "a*"]); 618 insta::assert_snapshot!(stderr, @r###" 619 Error: Invalid branch pattern provided. Patterns may not contain the characters `:`, `^`, `?`, `[`, `]` 620 Hint: Prefix the pattern with `glob:` to expand `*` as a glob 621 "###); 622 623 // Nothing in our repo before the fetch 624 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 625 @ 230dd059e1b0 626 ◉ 000000000000 627 "###); 628 // Fetch one branch... 629 let (stdout, stderr) = 630 test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "b"]); 631 insta::assert_snapshot!(stdout, @""); 632 insta::assert_snapshot!(stderr, @r###" 633 branch: b@origin [new] tracked 634 "###); 635 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 636 ◉ c7d4bdcbc215 descr_for_b b 637 ◉ ff36dc55760e descr_for_trunk1 638 │ @ 230dd059e1b0 639 ├─╯ 640 ◉ 000000000000 641 "###); 642 // ...check what the intermediate state looks like... 643 insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" 644 b: vpupmnsl c7d4bdcb descr_for_b 645 @origin: vpupmnsl c7d4bdcb descr_for_b 646 "###); 647 // ...then fetch two others with a glob. 648 let (stdout, stderr) = test_env.jj_cmd_ok( 649 &target_jj_repo_path, 650 &["git", "fetch", "--branch", "glob:a*"], 651 ); 652 insta::assert_snapshot!(stdout, @""); 653 insta::assert_snapshot!(stderr, @r###" 654 branch: a1@origin [new] tracked 655 branch: a2@origin [new] tracked 656 "###); 657 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 658 ◉ decaa3966c83 descr_for_a2 a2 659 │ ◉ 359a9a02457d descr_for_a1 a1 660 ├─╯ 661 │ ◉ c7d4bdcbc215 descr_for_b b 662 ├─╯ 663 ◉ ff36dc55760e descr_for_trunk1 664 │ @ 230dd059e1b0 665 ├─╯ 666 ◉ 000000000000 667 "###); 668 // Fetching the same branch again 669 let (stdout, stderr) = 670 test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a1"]); 671 insta::assert_snapshot!(stdout, @""); 672 insta::assert_snapshot!(stderr, @r###" 673 Nothing changed. 674 "###); 675 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 676 ◉ decaa3966c83 descr_for_a2 a2 677 │ ◉ 359a9a02457d descr_for_a1 a1 678 ├─╯ 679 │ ◉ c7d4bdcbc215 descr_for_b b 680 ├─╯ 681 ◉ ff36dc55760e descr_for_trunk1 682 │ @ 230dd059e1b0 683 ├─╯ 684 ◉ 000000000000 685 "###); 686 687 // ==== Change both repos ==== 688 // First, change the target repo: 689 let source_log = create_trunk2_and_rebase_branches(&test_env, &source_git_repo_path); 690 insta::assert_snapshot!(source_log, @r###" 691 ===== Source git repo contents ===== 692 ◉ 01d115196c39 descr_for_b b 693 │ ◉ 31c7d94b1f29 descr_for_a2 a2 694 ├─╯ 695 │ ◉ 6df2d34cf0da descr_for_a1 a1 696 ├─╯ 697 @ 2bb3ebd2bba3 descr_for_trunk2 trunk2 698 ◉ ff36dc55760e descr_for_trunk1 trunk1 699 ◉ 000000000000 700 "###); 701 // Change a branch in the source repo as well, so that it becomes conflicted. 702 test_env.jj_cmd_ok( 703 &target_jj_repo_path, 704 &["describe", "b", "-m=new_descr_for_b_to_create_conflict"], 705 ); 706 707 // Our repo before and after fetch of two branches 708 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 709 ◉ 6ebd41dc4f13 new_descr_for_b_to_create_conflict b* 710 │ ◉ decaa3966c83 descr_for_a2 a2 711 ├─╯ 712 │ ◉ 359a9a02457d descr_for_a1 a1 713 ├─╯ 714 ◉ ff36dc55760e descr_for_trunk1 715 │ @ 230dd059e1b0 716 ├─╯ 717 ◉ 000000000000 718 "###); 719 let (stdout, stderr) = test_env.jj_cmd_ok( 720 &target_jj_repo_path, 721 &["git", "fetch", "--branch", "b", "--branch", "a1"], 722 ); 723 insta::assert_snapshot!(stdout, @""); 724 insta::assert_snapshot!(stderr, @r###" 725 branch: a1@origin [updated] tracked 726 branch: b@origin [updated] tracked 727 Abandoned 1 commits that are no longer reachable. 728 "###); 729 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 730 ◉ 01d115196c39 descr_for_b b?? b@origin 731 │ ◉ 6df2d34cf0da descr_for_a1 a1 732 ├─╯ 733 ◉ 2bb3ebd2bba3 descr_for_trunk2 734 │ ◉ 6ebd41dc4f13 new_descr_for_b_to_create_conflict b?? 735 ├─╯ 736 │ ◉ decaa3966c83 descr_for_a2 a2 737 ├─╯ 738 ◉ ff36dc55760e descr_for_trunk1 739 │ @ 230dd059e1b0 740 ├─╯ 741 ◉ 000000000000 742 "###); 743 744 // We left a2 where it was before, let's see how `jj branch list` sees this. 745 insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" 746 a1: ypowunwp 6df2d34c descr_for_a1 747 @origin: ypowunwp 6df2d34c descr_for_a1 748 a2: qkvnknrk decaa396 descr_for_a2 749 @origin: qkvnknrk decaa396 descr_for_a2 750 b (conflicted): 751 - vpupmnsl hidden c7d4bdcb descr_for_b 752 + vpupmnsl 6ebd41dc new_descr_for_b_to_create_conflict 753 + nxrpswuq 01d11519 descr_for_b 754 @origin (behind by 1 commits): nxrpswuq 01d11519 descr_for_b 755 "###); 756 // Now, let's fetch a2 and double-check that fetching a1 and b again doesn't do 757 // anything. 758 let (stdout, stderr) = test_env.jj_cmd_ok( 759 &target_jj_repo_path, 760 &["git", "fetch", "--branch", "b", "--branch", "glob:a*"], 761 ); 762 insta::assert_snapshot!(stdout, @""); 763 insta::assert_snapshot!(stderr, @r###" 764 branch: a2@origin [updated] tracked 765 Abandoned 1 commits that are no longer reachable. 766 "###); 767 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 768 ◉ 31c7d94b1f29 descr_for_a2 a2 769 │ ◉ 01d115196c39 descr_for_b b?? b@origin 770 ├─╯ 771 │ ◉ 6df2d34cf0da descr_for_a1 a1 772 ├─╯ 773 ◉ 2bb3ebd2bba3 descr_for_trunk2 774 │ ◉ 6ebd41dc4f13 new_descr_for_b_to_create_conflict b?? 775 ├─╯ 776 ◉ ff36dc55760e descr_for_trunk1 777 │ @ 230dd059e1b0 778 ├─╯ 779 ◉ 000000000000 780 "###); 781 insta::assert_snapshot!(get_branch_output(&test_env, &target_jj_repo_path), @r###" 782 a1: ypowunwp 6df2d34c descr_for_a1 783 @origin: ypowunwp 6df2d34c descr_for_a1 784 a2: qrmzolkr 31c7d94b descr_for_a2 785 @origin: qrmzolkr 31c7d94b descr_for_a2 786 b (conflicted): 787 - vpupmnsl hidden c7d4bdcb descr_for_b 788 + vpupmnsl 6ebd41dc new_descr_for_b_to_create_conflict 789 + nxrpswuq 01d11519 descr_for_b 790 @origin (behind by 1 commits): nxrpswuq 01d11519 descr_for_b 791 "###); 792} 793 794// See `test_undo_restore_commands.rs` for fetch-undo-push and fetch-undo-fetch 795// of the same branches for various kinds of undo. 796#[test] 797fn test_git_fetch_undo() { 798 let test_env = TestEnvironment::default(); 799 test_env.add_config("git.auto-local-branch = true"); 800 let source_git_repo_path = test_env.env_root().join("source"); 801 let _git_repo = git2::Repository::init(source_git_repo_path.clone()).unwrap(); 802 803 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 804 let (stdout, stderr) = 805 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "target"]); 806 insta::assert_snapshot!(stdout, @""); 807 insta::assert_snapshot!(stderr, @r###" 808 Fetching into new repo in "$TEST_ENV/target" 809 Nothing changed. 810 "###); 811 let target_jj_repo_path = test_env.env_root().join("target"); 812 813 let source_log = 814 create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path); 815 insta::assert_snapshot!(source_log, @r###" 816 ===== Source git repo contents ===== 817 @ c7d4bdcbc215 descr_for_b b 818 │ ◉ decaa3966c83 descr_for_a2 a2 819 ├─╯ 820 │ ◉ 359a9a02457d descr_for_a1 a1 821 ├─╯ 822 ◉ ff36dc55760e descr_for_trunk1 trunk1 823 ◉ 000000000000 824 "###); 825 826 // Fetch 2 branches 827 let (stdout, stderr) = test_env.jj_cmd_ok( 828 &target_jj_repo_path, 829 &["git", "fetch", "--branch", "b", "--branch", "a1"], 830 ); 831 insta::assert_snapshot!(stdout, @""); 832 insta::assert_snapshot!(stderr, @r###" 833 branch: a1@origin [new] tracked 834 branch: b@origin [new] tracked 835 "###); 836 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 837 ◉ c7d4bdcbc215 descr_for_b b 838 │ ◉ 359a9a02457d descr_for_a1 a1 839 ├─╯ 840 ◉ ff36dc55760e descr_for_trunk1 841 │ @ 230dd059e1b0 842 ├─╯ 843 ◉ 000000000000 844 "###); 845 let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["undo"]); 846 insta::assert_snapshot!(stdout, @""); 847 insta::assert_snapshot!(stderr, @""); 848 // The undo works as expected 849 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 850 @ 230dd059e1b0 851 ◉ 000000000000 852 "###); 853 // Now try to fetch just one branch 854 let (stdout, stderr) = 855 test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "b"]); 856 insta::assert_snapshot!(stdout, @""); 857 insta::assert_snapshot!(stderr, @r###" 858 branch: b@origin [new] tracked 859 "###); 860 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 861 ◉ c7d4bdcbc215 descr_for_b b 862 ◉ ff36dc55760e descr_for_trunk1 863 │ @ 230dd059e1b0 864 ├─╯ 865 ◉ 000000000000 866 "###); 867} 868 869// Compare to `test_git_import_undo` in test_git_import_export 870// TODO: Explain why these behaviors are useful 871#[test] 872fn test_fetch_undo_what() { 873 let test_env = TestEnvironment::default(); 874 test_env.add_config("git.auto-local-branch = true"); 875 let source_git_repo_path = test_env.env_root().join("source"); 876 let _git_repo = git2::Repository::init(source_git_repo_path.clone()).unwrap(); 877 878 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 879 let (stdout, stderr) = 880 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "target"]); 881 insta::assert_snapshot!(stdout, @""); 882 insta::assert_snapshot!(stderr, @r###" 883 Fetching into new repo in "$TEST_ENV/target" 884 Nothing changed. 885 "###); 886 let repo_path = test_env.env_root().join("target"); 887 888 let source_log = 889 create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path); 890 insta::assert_snapshot!(source_log, @r###" 891 ===== Source git repo contents ===== 892 @ c7d4bdcbc215 descr_for_b b 893 │ ◉ decaa3966c83 descr_for_a2 a2 894 ├─╯ 895 │ ◉ 359a9a02457d descr_for_a1 a1 896 ├─╯ 897 ◉ ff36dc55760e descr_for_trunk1 trunk1 898 ◉ 000000000000 899 "###); 900 901 // Initial state we will try to return to after `op restore`. There are no 902 // branches. 903 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @""); 904 let base_operation_id = test_env.current_operation_id(&repo_path); 905 906 // Fetch a branch 907 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--branch", "b"]); 908 insta::assert_snapshot!(stdout, @""); 909 insta::assert_snapshot!(stderr, @r###" 910 branch: b@origin [new] tracked 911 "###); 912 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 913 ◉ c7d4bdcbc215 descr_for_b b 914 ◉ ff36dc55760e descr_for_trunk1 915 │ @ 230dd059e1b0 916 ├─╯ 917 ◉ 000000000000 918 "###); 919 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 920 b: vpupmnsl c7d4bdcb descr_for_b 921 @origin: vpupmnsl c7d4bdcb descr_for_b 922 "###); 923 924 // We can undo the change in the repo without moving the remote-tracking branch 925 let (stdout, stderr) = test_env.jj_cmd_ok( 926 &repo_path, 927 &["op", "restore", "--what", "repo", &base_operation_id], 928 ); 929 insta::assert_snapshot!(stdout, @""); 930 insta::assert_snapshot!(stderr, @""); 931 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 932 b (deleted) 933 @origin: vpupmnsl hidden c7d4bdcb descr_for_b 934 (this branch will be *deleted permanently* on the remote on the next `jj git push`. Use `jj branch forget` to prevent this) 935 "###); 936 937 // Now, let's demo restoring just the remote-tracking branch. First, let's 938 // change our local repo state... 939 test_env.jj_cmd_ok(&repo_path, &["branch", "c", "newbranch"]); 940 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 941 b (deleted) 942 @origin: vpupmnsl hidden c7d4bdcb descr_for_b 943 (this branch will be *deleted permanently* on the remote on the next `jj git push`. Use `jj branch forget` to prevent this) 944 newbranch: qpvuntsm 230dd059 (empty) (no description set) 945 "###); 946 // Restoring just the remote-tracking state will not affect `newbranch`, but 947 // will eliminate `b@origin`. 948 let (stdout, stderr) = test_env.jj_cmd_ok( 949 &repo_path, 950 &[ 951 "op", 952 "restore", 953 "--what", 954 "remote-tracking", 955 &base_operation_id, 956 ], 957 ); 958 insta::assert_snapshot!(stdout, @""); 959 insta::assert_snapshot!(stderr, @""); 960 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 961 newbranch: qpvuntsm 230dd059 (empty) (no description set) 962 "###); 963} 964 965#[test] 966fn test_git_fetch_remove_fetch() { 967 let test_env = TestEnvironment::default(); 968 test_env.add_config("git.auto-local-branch = true"); 969 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 970 let repo_path = test_env.env_root().join("repo"); 971 add_git_remote(&test_env, &repo_path, "origin"); 972 973 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "origin"]); 974 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 975 origin: qpvuntsm 230dd059 (empty) (no description set) 976 "###); 977 978 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 979 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 980 origin (conflicted): 981 + qpvuntsm 230dd059 (empty) (no description set) 982 + oputwtnw ffecd2d6 message 983 @origin (behind by 1 commits): oputwtnw ffecd2d6 message 984 "###); 985 986 test_env.jj_cmd_ok(&repo_path, &["git", "remote", "remove", "origin"]); 987 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 988 origin (conflicted): 989 + qpvuntsm 230dd059 (empty) (no description set) 990 + oputwtnw ffecd2d6 message 991 "###); 992 993 test_env.jj_cmd_ok(&repo_path, &["git", "remote", "add", "origin", "../origin"]); 994 995 // Check that origin@origin is properly recreated 996 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 997 insta::assert_snapshot!(stdout, @""); 998 insta::assert_snapshot!(stderr, @r###" 999 branch: origin@origin [new] tracked 1000 "###); 1001 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 1002 origin (conflicted): 1003 + qpvuntsm 230dd059 (empty) (no description set) 1004 + oputwtnw ffecd2d6 message 1005 @origin (behind by 1 commits): oputwtnw ffecd2d6 message 1006 "###); 1007} 1008 1009#[test] 1010fn test_git_fetch_rename_fetch() { 1011 let test_env = TestEnvironment::default(); 1012 test_env.add_config("git.auto-local-branch = true"); 1013 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 1014 let repo_path = test_env.env_root().join("repo"); 1015 add_git_remote(&test_env, &repo_path, "origin"); 1016 1017 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "origin"]); 1018 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 1019 origin: qpvuntsm 230dd059 (empty) (no description set) 1020 "###); 1021 1022 test_env.jj_cmd_ok(&repo_path, &["git", "fetch"]); 1023 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 1024 origin (conflicted): 1025 + qpvuntsm 230dd059 (empty) (no description set) 1026 + oputwtnw ffecd2d6 message 1027 @origin (behind by 1 commits): oputwtnw ffecd2d6 message 1028 "###); 1029 1030 test_env.jj_cmd_ok( 1031 &repo_path, 1032 &["git", "remote", "rename", "origin", "upstream"], 1033 ); 1034 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 1035 origin (conflicted): 1036 + qpvuntsm 230dd059 (empty) (no description set) 1037 + oputwtnw ffecd2d6 message 1038 @upstream (behind by 1 commits): oputwtnw ffecd2d6 message 1039 "###); 1040 1041 // Check that jj indicates that nothing has changed 1042 let (stdout, stderr) = 1043 test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote", "upstream"]); 1044 insta::assert_snapshot!(stdout, @""); 1045 insta::assert_snapshot!(stderr, @r###" 1046 Nothing changed. 1047 "###); 1048} 1049 1050#[test] 1051fn test_git_fetch_removed_branch() { 1052 let test_env = TestEnvironment::default(); 1053 test_env.add_config("git.auto-local-branch = true"); 1054 let source_git_repo_path = test_env.env_root().join("source"); 1055 let _git_repo = git2::Repository::init(source_git_repo_path.clone()).unwrap(); 1056 1057 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 1058 let (stdout, stderr) = 1059 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "target"]); 1060 insta::assert_snapshot!(stdout, @""); 1061 insta::assert_snapshot!(stderr, @r###" 1062 Fetching into new repo in "$TEST_ENV/target" 1063 Nothing changed. 1064 "###); 1065 let target_jj_repo_path = test_env.env_root().join("target"); 1066 1067 let source_log = 1068 create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path); 1069 insta::assert_snapshot!(source_log, @r###" 1070 ===== Source git repo contents ===== 1071 @ c7d4bdcbc215 descr_for_b b 1072 │ ◉ decaa3966c83 descr_for_a2 a2 1073 ├─╯ 1074 │ ◉ 359a9a02457d descr_for_a1 a1 1075 ├─╯ 1076 ◉ ff36dc55760e descr_for_trunk1 trunk1 1077 ◉ 000000000000 1078 "###); 1079 1080 // Fetch all branches 1081 let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); 1082 insta::assert_snapshot!(stdout, @""); 1083 insta::assert_snapshot!(stderr, @r###" 1084 branch: a1@origin [new] tracked 1085 branch: a2@origin [new] tracked 1086 branch: b@origin [new] tracked 1087 branch: trunk1@origin [new] tracked 1088 "###); 1089 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 1090 ◉ c7d4bdcbc215 descr_for_b b 1091 │ ◉ decaa3966c83 descr_for_a2 a2 1092 ├─╯ 1093 │ ◉ 359a9a02457d descr_for_a1 a1 1094 ├─╯ 1095 ◉ ff36dc55760e descr_for_trunk1 trunk1 1096 │ @ 230dd059e1b0 1097 ├─╯ 1098 ◉ 000000000000 1099 "###); 1100 1101 // Remove a2 branch in origin 1102 test_env.jj_cmd_ok(&source_git_repo_path, &["branch", "forget", "a2"]); 1103 1104 // Fetch branch a1 from origin and check that a2 is still there 1105 let (stdout, stderr) = 1106 test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a1"]); 1107 insta::assert_snapshot!(stdout, @""); 1108 insta::assert_snapshot!(stderr, @r###" 1109 Nothing changed. 1110 "###); 1111 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 1112 ◉ c7d4bdcbc215 descr_for_b b 1113 │ ◉ decaa3966c83 descr_for_a2 a2 1114 ├─╯ 1115 │ ◉ 359a9a02457d descr_for_a1 a1 1116 ├─╯ 1117 ◉ ff36dc55760e descr_for_trunk1 trunk1 1118 │ @ 230dd059e1b0 1119 ├─╯ 1120 ◉ 000000000000 1121 "###); 1122 1123 // Fetch branches a2 from origin, and check that it has been removed locally 1124 let (stdout, stderr) = 1125 test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch", "--branch", "a2"]); 1126 insta::assert_snapshot!(stdout, @""); 1127 insta::assert_snapshot!(stderr, @r###" 1128 branch: a2@origin [deleted] untracked 1129 Abandoned 1 commits that are no longer reachable. 1130 "###); 1131 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 1132 ◉ c7d4bdcbc215 descr_for_b b 1133 │ ◉ 359a9a02457d descr_for_a1 a1 1134 ├─╯ 1135 ◉ ff36dc55760e descr_for_trunk1 trunk1 1136 │ @ 230dd059e1b0 1137 ├─╯ 1138 ◉ 000000000000 1139 "###); 1140} 1141 1142#[test] 1143fn test_git_fetch_removed_parent_branch() { 1144 let test_env = TestEnvironment::default(); 1145 test_env.add_config("git.auto-local-branch = true"); 1146 let source_git_repo_path = test_env.env_root().join("source"); 1147 let _git_repo = git2::Repository::init(source_git_repo_path.clone()).unwrap(); 1148 1149 // Clone an empty repo. The target repo is a normal `jj` repo, *not* colocated 1150 let (stdout, stderr) = 1151 test_env.jj_cmd_ok(test_env.env_root(), &["git", "clone", "source", "target"]); 1152 insta::assert_snapshot!(stdout, @""); 1153 insta::assert_snapshot!(stderr, @r###" 1154 Fetching into new repo in "$TEST_ENV/target" 1155 Nothing changed. 1156 "###); 1157 let target_jj_repo_path = test_env.env_root().join("target"); 1158 1159 let source_log = 1160 create_colocated_repo_and_branches_from_trunk1(&test_env, &source_git_repo_path); 1161 insta::assert_snapshot!(source_log, @r###" 1162 ===== Source git repo contents ===== 1163 @ c7d4bdcbc215 descr_for_b b 1164 │ ◉ decaa3966c83 descr_for_a2 a2 1165 ├─╯ 1166 │ ◉ 359a9a02457d descr_for_a1 a1 1167 ├─╯ 1168 ◉ ff36dc55760e descr_for_trunk1 trunk1 1169 ◉ 000000000000 1170 "###); 1171 1172 // Fetch all branches 1173 let (stdout, stderr) = test_env.jj_cmd_ok(&target_jj_repo_path, &["git", "fetch"]); 1174 insta::assert_snapshot!(stdout, @""); 1175 insta::assert_snapshot!(stderr, @r###" 1176 branch: a1@origin [new] tracked 1177 branch: a2@origin [new] tracked 1178 branch: b@origin [new] tracked 1179 branch: trunk1@origin [new] tracked 1180 "###); 1181 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 1182 ◉ c7d4bdcbc215 descr_for_b b 1183 │ ◉ decaa3966c83 descr_for_a2 a2 1184 ├─╯ 1185 │ ◉ 359a9a02457d descr_for_a1 a1 1186 ├─╯ 1187 ◉ ff36dc55760e descr_for_trunk1 trunk1 1188 │ @ 230dd059e1b0 1189 ├─╯ 1190 ◉ 000000000000 1191 "###); 1192 1193 // Remove all branches in origin. 1194 test_env.jj_cmd_ok(&source_git_repo_path, &["branch", "forget", "--glob", "*"]); 1195 1196 // Fetch branches master, trunk1 and a1 from origin and check that only those 1197 // branches have been removed and that others were not rebased because of 1198 // abandoned commits. 1199 let (stdout, stderr) = test_env.jj_cmd_ok( 1200 &target_jj_repo_path, 1201 &[ 1202 "git", "fetch", "--branch", "master", "--branch", "trunk1", "--branch", "a1", 1203 ], 1204 ); 1205 insta::assert_snapshot!(stdout, @""); 1206 insta::assert_snapshot!(stderr, @r###" 1207 branch: a1@origin [deleted] untracked 1208 branch: trunk1@origin [deleted] untracked 1209 Abandoned 1 commits that are no longer reachable. 1210 "###); 1211 insta::assert_snapshot!(get_log_output(&test_env, &target_jj_repo_path), @r###" 1212 ◉ c7d4bdcbc215 descr_for_b b 1213 │ ◉ decaa3966c83 descr_for_a2 a2 1214 ├─╯ 1215 ◉ ff36dc55760e descr_for_trunk1 1216 │ @ 230dd059e1b0 1217 ├─╯ 1218 ◉ 000000000000 1219 "###); 1220} 1221 1222#[test] 1223fn test_git_fetch_remote_only_branch() { 1224 let test_env = TestEnvironment::default(); 1225 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 1226 let repo_path = test_env.env_root().join("repo"); 1227 1228 // Create non-empty git repo to add as a remote 1229 let git_repo_path = test_env.env_root().join("git-repo"); 1230 let git_repo = git2::Repository::init(git_repo_path).unwrap(); 1231 let signature = 1232 git2::Signature::new("Some One", "some.one@example.com", &git2::Time::new(0, 0)).unwrap(); 1233 let mut tree_builder = git_repo.treebuilder(None).unwrap(); 1234 let file_oid = git_repo.blob(b"content").unwrap(); 1235 tree_builder 1236 .insert("file", file_oid, git2::FileMode::Blob.into()) 1237 .unwrap(); 1238 let tree_oid = tree_builder.write().unwrap(); 1239 let tree = git_repo.find_tree(tree_oid).unwrap(); 1240 test_env.jj_cmd_ok( 1241 &repo_path, 1242 &["git", "remote", "add", "origin", "../git-repo"], 1243 ); 1244 // Create a commit and a branch in the git repo 1245 git_repo 1246 .commit( 1247 Some("refs/heads/feature1"), 1248 &signature, 1249 &signature, 1250 "message", 1251 &tree, 1252 &[], 1253 ) 1254 .unwrap(); 1255 1256 // Fetch using git.auto_local_branch = true 1257 test_env.add_config("git.auto-local-branch = true"); 1258 test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]); 1259 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 1260 feature1: mzyxwzks 9f01a0e0 message 1261 @origin: mzyxwzks 9f01a0e0 message 1262 "###); 1263 1264 git_repo 1265 .commit( 1266 Some("refs/heads/feature2"), 1267 &signature, 1268 &signature, 1269 "message", 1270 &tree, 1271 &[], 1272 ) 1273 .unwrap(); 1274 1275 // Fetch using git.auto_local_branch = false 1276 test_env.add_config("git.auto-local-branch = false"); 1277 test_env.jj_cmd_ok(&repo_path, &["git", "fetch", "--remote=origin"]); 1278 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 1279 ◉ 9f01a0e04879 message feature1 feature2@origin 1280 │ @ 230dd059e1b0 1281 ├─╯ 1282 ◉ 000000000000 1283 "###); 1284 insta::assert_snapshot!(get_branch_output(&test_env, &repo_path), @r###" 1285 feature1: mzyxwzks 9f01a0e0 message 1286 @origin: mzyxwzks 9f01a0e0 message 1287 feature2@origin: mzyxwzks 9f01a0e0 message 1288 "###); 1289}