just playing with tangled
at main 912 lines 30 kB view raw
1// Copyright 2024 The Jujutsu Authors 2// 3// Licensed under the Apache License, Version 2.0 (the "License"); 4// you may not use this file except in compliance with the License. 5// You may obtain a copy of the License at 6// 7// https://www.apache.org/licenses/LICENSE-2.0 8// 9// Unless required by applicable law or agreed to in writing, software 10// distributed under the License is distributed on an "AS IS" BASIS, 11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12// See the License for the specific language governing permissions and 13// limitations under the License. 14 15use std::path::Path; 16use std::path::PathBuf; 17 18use indoc::formatdoc; 19use test_case::test_case; 20use testutils::git; 21 22use crate::common::to_toml_value; 23use crate::common::CommandOutput; 24use crate::common::TestEnvironment; 25use crate::common::TestWorkDir; 26 27fn init_git_repo(git_repo_path: &Path, bare: bool) -> gix::Repository { 28 let git_repo = if bare { 29 git::init_bare(git_repo_path) 30 } else { 31 git::init(git_repo_path) 32 }; 33 34 let git::CommitResult { commit_id, .. } = git::add_commit( 35 &git_repo, 36 "refs/heads/my-bookmark", 37 "some-file", 38 b"some content", 39 "My commit message", 40 &[], 41 ); 42 git::set_head_to_id(&git_repo, commit_id); 43 git_repo 44} 45 46#[must_use] 47fn get_bookmark_output(work_dir: &TestWorkDir) -> CommandOutput { 48 work_dir.run_jj(["bookmark", "list", "--all-remotes"]) 49} 50 51#[must_use] 52fn get_log_output(work_dir: &TestWorkDir) -> CommandOutput { 53 let template = r#" 54 separate(" ", 55 commit_id.short(), 56 bookmarks, 57 if(git_head, "git_head()"), 58 description, 59 )"#; 60 work_dir.run_jj(["log", "-T", template, "-r=all()"]) 61} 62 63fn read_git_target(work_dir: &TestWorkDir) -> String { 64 String::from_utf8(work_dir.read_file(".jj/repo/store/git_target").into()).unwrap() 65} 66 67#[test] 68fn test_git_init_internal() { 69 let test_env = TestEnvironment::default(); 70 let output = test_env.run_jj_in(".", ["git", "init", "repo"]); 71 insta::assert_snapshot!(output, @r#" 72 ------- stderr ------- 73 Initialized repo in "repo" 74 [EOF] 75 "#); 76 77 let work_dir = test_env.work_dir("repo"); 78 let jj_path = work_dir.root().join(".jj"); 79 let repo_path = jj_path.join("repo"); 80 let store_path = repo_path.join("store"); 81 assert!(work_dir.root().is_dir()); 82 assert!(jj_path.is_dir()); 83 assert!(jj_path.join("working_copy").is_dir()); 84 assert!(repo_path.is_dir()); 85 assert!(store_path.is_dir()); 86 assert!(store_path.join("git").is_dir()); 87 assert_eq!(read_git_target(&work_dir), "git"); 88} 89 90#[test] 91fn test_git_init_internal_ignore_working_copy() { 92 let test_env = TestEnvironment::default(); 93 let work_dir = test_env.work_dir("").create_dir("repo"); 94 work_dir.write_file("file1", ""); 95 96 let output = work_dir.run_jj(["git", "init", "--ignore-working-copy"]); 97 insta::assert_snapshot!(output, @r" 98 ------- stderr ------- 99 Error: --ignore-working-copy is not respected 100 [EOF] 101 [exit status: 2] 102 "); 103} 104 105#[test] 106fn test_git_init_internal_at_operation() { 107 let test_env = TestEnvironment::default(); 108 let work_dir = test_env.work_dir("").create_dir("repo"); 109 110 let output = work_dir.run_jj(["git", "init", "--at-op=@-"]); 111 insta::assert_snapshot!(output, @r" 112 ------- stderr ------- 113 Error: --at-op is not respected 114 [EOF] 115 [exit status: 2] 116 "); 117} 118 119#[test_case(false; "full")] 120#[test_case(true; "bare")] 121fn test_git_init_external(bare: bool) { 122 let test_env = TestEnvironment::default(); 123 let git_repo_path = test_env.env_root().join("git-repo"); 124 init_git_repo(&git_repo_path, bare); 125 126 let output = test_env.run_jj_in( 127 ".", 128 [ 129 "git", 130 "init", 131 "repo", 132 "--git-repo", 133 git_repo_path.to_str().unwrap(), 134 ], 135 ); 136 insta::allow_duplicates! { 137 insta::assert_snapshot!(output, @r#" 138 ------- stderr ------- 139 Done importing changes from the underlying Git repo. 140 Working copy (@) now at: sqpuoqvx ed6b5138 (empty) (no description set) 141 Parent commit (@-) : nntyzxmz e80a42cc my-bookmark | My commit message 142 Added 1 files, modified 0 files, removed 0 files 143 Initialized repo in "repo" 144 [EOF] 145 "#); 146 } 147 148 let work_dir = test_env.work_dir("repo"); 149 let jj_path = work_dir.root().join(".jj"); 150 let repo_path = jj_path.join("repo"); 151 let store_path = repo_path.join("store"); 152 assert!(work_dir.root().is_dir()); 153 assert!(jj_path.is_dir()); 154 assert!(jj_path.join("working_copy").is_dir()); 155 assert!(repo_path.is_dir()); 156 assert!(store_path.is_dir()); 157 let unix_git_target_file_contents = read_git_target(&work_dir).replace('\\', "/"); 158 if bare { 159 assert!(unix_git_target_file_contents.ends_with("/git-repo")); 160 } else { 161 assert!(unix_git_target_file_contents.ends_with("/git-repo/.git")); 162 } 163 164 // Check that the Git repo's HEAD got checked out 165 insta::allow_duplicates! { 166 insta::assert_snapshot!(get_log_output(&work_dir), @r" 167 @ ed6b513890ae 168 ○ e80a42cccd06 my-bookmark git_head() My commit message 169 ◆ 000000000000 170 [EOF] 171 "); 172 } 173} 174 175#[test_case(false; "full")] 176#[test_case(true; "bare")] 177fn test_git_init_external_import_trunk(bare: bool) { 178 let test_env = TestEnvironment::default(); 179 let git_repo_path = test_env.env_root().join("git-repo"); 180 let git_repo = init_git_repo(&git_repo_path, bare); 181 182 // Add remote bookmark "trunk" for remote "origin", and set it as "origin/HEAD" 183 let oid = git_repo 184 .find_reference("refs/heads/my-bookmark") 185 .unwrap() 186 .id(); 187 188 git_repo 189 .reference( 190 "refs/remotes/origin/trunk", 191 oid.detach(), 192 gix::refs::transaction::PreviousValue::MustNotExist, 193 "create remote ref", 194 ) 195 .unwrap(); 196 197 git::set_symbolic_reference( 198 &git_repo, 199 "refs/remotes/origin/HEAD", 200 "refs/remotes/origin/trunk", 201 ); 202 203 let output = test_env.run_jj_in( 204 ".", 205 [ 206 "git", 207 "init", 208 "repo", 209 "--git-repo", 210 git_repo_path.to_str().unwrap(), 211 ], 212 ); 213 insta::allow_duplicates! { 214 insta::assert_snapshot!(output, @r#" 215 ------- stderr ------- 216 Done importing changes from the underlying Git repo. 217 Setting the revset alias `trunk()` to `trunk@origin` 218 Working copy (@) now at: sqpuoqvx ed6b5138 (empty) (no description set) 219 Parent commit (@-) : nntyzxmz e80a42cc my-bookmark trunk@origin | My commit message 220 Added 1 files, modified 0 files, removed 0 files 221 Initialized repo in "repo" 222 [EOF] 223 "#); 224 } 225 226 // "trunk()" alias should be set to remote "origin"'s default bookmark "trunk" 227 let work_dir = test_env.work_dir("repo"); 228 let output = work_dir.run_jj(["config", "list", "--repo", "revset-aliases.\"trunk()\""]); 229 insta::allow_duplicates! { 230 insta::assert_snapshot!(output, @r#" 231 revset-aliases."trunk()" = "trunk@origin" 232 [EOF] 233 "#); 234 } 235} 236 237#[test] 238fn test_git_init_external_ignore_working_copy() { 239 let test_env = TestEnvironment::default(); 240 let git_repo_path = test_env.env_root().join("git-repo"); 241 init_git_repo(&git_repo_path, false); 242 let work_dir = test_env.work_dir("").create_dir("repo"); 243 work_dir.write_file("file1", ""); 244 245 // No snapshot should be taken 246 let output = work_dir.run_jj([ 247 "git", 248 "init", 249 "--ignore-working-copy", 250 "--git-repo", 251 git_repo_path.to_str().unwrap(), 252 ]); 253 insta::assert_snapshot!(output, @r" 254 ------- stderr ------- 255 Error: --ignore-working-copy is not respected 256 [EOF] 257 [exit status: 2] 258 "); 259} 260 261#[test] 262fn test_git_init_external_at_operation() { 263 let test_env = TestEnvironment::default(); 264 let git_repo_path = test_env.env_root().join("git-repo"); 265 init_git_repo(&git_repo_path, false); 266 let work_dir = test_env.work_dir("").create_dir("repo"); 267 268 let output = work_dir.run_jj([ 269 "git", 270 "init", 271 "--at-op=@-", 272 "--git-repo", 273 git_repo_path.to_str().unwrap(), 274 ]); 275 insta::assert_snapshot!(output, @r" 276 ------- stderr ------- 277 Error: --at-op is not respected 278 [EOF] 279 [exit status: 2] 280 "); 281} 282 283#[test] 284fn test_git_init_external_non_existent_directory() { 285 let test_env = TestEnvironment::default(); 286 let output = test_env.run_jj_in(".", ["git", "init", "repo", "--git-repo", "non-existent"]); 287 insta::assert_snapshot!(output.strip_stderr_last_line(), @r" 288 ------- stderr ------- 289 Error: Failed to access the repository 290 Caused by: 291 1: Cannot access $TEST_ENV/non-existent 292 [EOF] 293 [exit status: 1] 294 "); 295} 296 297#[test] 298fn test_git_init_external_non_existent_git_directory() { 299 let test_env = TestEnvironment::default(); 300 let work_dir = test_env.work_dir("repo"); 301 let output = test_env.run_jj_in(".", ["git", "init", "repo", "--git-repo", "repo"]); 302 insta::assert_snapshot!(output, @r#" 303 ------- stderr ------- 304 Error: Failed to access the repository 305 Caused by: 306 1: Failed to open git repository 307 2: "$TEST_ENV/repo" does not appear to be a git repository 308 3: Missing HEAD at '.git/HEAD' 309 [EOF] 310 [exit status: 1] 311 "#); 312 let jj_path = work_dir.root().join(".jj"); 313 assert!(!jj_path.exists()); 314} 315 316#[test] 317fn test_git_init_colocated_via_git_repo_path() { 318 let test_env = TestEnvironment::default(); 319 let work_dir = test_env.work_dir("repo"); 320 init_git_repo(work_dir.root(), false); 321 let output = work_dir.run_jj(["git", "init", "--git-repo", "."]); 322 insta::assert_snapshot!(output, @r#" 323 ------- stderr ------- 324 Done importing changes from the underlying Git repo. 325 Initialized repo in "." 326 [EOF] 327 "#); 328 329 let jj_path = work_dir.root().join(".jj"); 330 let repo_path = jj_path.join("repo"); 331 let store_path = repo_path.join("store"); 332 assert!(work_dir.root().is_dir()); 333 assert!(jj_path.is_dir()); 334 assert!(jj_path.join("working_copy").is_dir()); 335 assert!(repo_path.is_dir()); 336 assert!(store_path.is_dir()); 337 assert!(read_git_target(&work_dir) 338 .replace('\\', "/") 339 .ends_with("../../../.git")); 340 341 // Check that the Git repo's HEAD got checked out 342 insta::assert_snapshot!(get_log_output(&work_dir), @r" 343 @ f3fe58bc88cc 344 ○ e80a42cccd06 my-bookmark git_head() My commit message 345 ◆ 000000000000 346 [EOF] 347 "); 348 349 // Check that the Git repo's HEAD moves 350 work_dir.run_jj(["new"]).success(); 351 insta::assert_snapshot!(get_log_output(&work_dir), @r" 352 @ 0c77f9e21b55 353 ○ f3fe58bc88cc git_head() 354 ○ e80a42cccd06 my-bookmark My commit message 355 ◆ 000000000000 356 [EOF] 357 "); 358} 359 360#[test] 361fn test_git_init_colocated_via_git_repo_path_gitlink() { 362 let test_env = TestEnvironment::default(); 363 // <jj_work_dir>/.git -> <git_repo_path> 364 let git_repo_path = test_env.env_root().join("git-repo"); 365 let git_repo = init_git_repo(&git_repo_path, false); 366 let jj_work_dir = test_env.work_dir("").create_dir("repo"); 367 git::create_gitlink(jj_work_dir.root(), git_repo.path()); 368 369 assert!(jj_work_dir.root().join(".git").is_file()); 370 let output = jj_work_dir.run_jj(["git", "init", "--git-repo", "."]); 371 insta::assert_snapshot!(output, @r#" 372 ------- stderr ------- 373 Done importing changes from the underlying Git repo. 374 Initialized repo in "." 375 [EOF] 376 "#); 377 insta::assert_snapshot!(read_git_target(&jj_work_dir), @"../../../.git"); 378 379 // Check that the Git repo's HEAD got checked out 380 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 381 @ f3fe58bc88cc 382 ○ e80a42cccd06 my-bookmark git_head() My commit message 383 ◆ 000000000000 384 [EOF] 385 "); 386 387 // Check that the Git repo's HEAD moves 388 jj_work_dir.run_jj(["new"]).success(); 389 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 390 @ 0c77f9e21b55 391 ○ f3fe58bc88cc git_head() 392 ○ e80a42cccd06 my-bookmark My commit message 393 ◆ 000000000000 394 [EOF] 395 "); 396} 397 398#[cfg(unix)] 399#[test] 400fn test_git_init_colocated_via_git_repo_path_symlink_directory() { 401 let test_env = TestEnvironment::default(); 402 // <jj_work_dir>/.git -> <git_repo_path> 403 let git_repo_path = test_env.env_root().join("git-repo"); 404 init_git_repo(&git_repo_path, false); 405 let jj_work_dir = test_env.work_dir("").create_dir("repo"); 406 std::os::unix::fs::symlink(git_repo_path.join(".git"), jj_work_dir.root().join(".git")) 407 .unwrap(); 408 let output = jj_work_dir.run_jj(["git", "init", "--git-repo", "."]); 409 insta::assert_snapshot!(output, @r#" 410 ------- stderr ------- 411 Done importing changes from the underlying Git repo. 412 Initialized repo in "." 413 [EOF] 414 "#); 415 insta::assert_snapshot!(read_git_target(&jj_work_dir), @"../../../.git"); 416 417 // Check that the Git repo's HEAD got checked out 418 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 419 @ f3fe58bc88cc 420 ○ e80a42cccd06 my-bookmark git_head() My commit message 421 ◆ 000000000000 422 [EOF] 423 "); 424 425 // Check that the Git repo's HEAD moves 426 jj_work_dir.run_jj(["new"]).success(); 427 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 428 @ 0c77f9e21b55 429 ○ f3fe58bc88cc git_head() 430 ○ e80a42cccd06 my-bookmark My commit message 431 ◆ 000000000000 432 [EOF] 433 "); 434} 435 436#[cfg(unix)] 437#[test] 438fn test_git_init_colocated_via_git_repo_path_symlink_directory_without_bare_config() { 439 let test_env = TestEnvironment::default(); 440 // <jj_work_dir>/.git -> <git_repo_path> 441 let git_repo_path = test_env.env_root().join("git-repo.git"); 442 let jj_work_dir = test_env.work_dir("repo"); 443 // Set up git repo without core.bare set (as the "repo" tool would do.) 444 // The core.bare config is deduced from the directory name. 445 let git_repo = init_git_repo(jj_work_dir.root(), false); 446 git::remove_config_value(git_repo, "config", "bare"); 447 448 std::fs::rename(jj_work_dir.root().join(".git"), &git_repo_path).unwrap(); 449 std::os::unix::fs::symlink(&git_repo_path, jj_work_dir.root().join(".git")).unwrap(); 450 let output = jj_work_dir.run_jj(["git", "init", "--git-repo", "."]); 451 insta::assert_snapshot!(output, @r#" 452 ------- stderr ------- 453 Done importing changes from the underlying Git repo. 454 Initialized repo in "." 455 [EOF] 456 "#); 457 insta::assert_snapshot!(read_git_target(&jj_work_dir), @"../../../.git"); 458 459 // Check that the Git repo's HEAD got checked out 460 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 461 @ f3fe58bc88cc 462 ○ e80a42cccd06 my-bookmark git_head() My commit message 463 ◆ 000000000000 464 [EOF] 465 "); 466 467 // Check that the Git repo's HEAD moves 468 jj_work_dir.run_jj(["new"]).success(); 469 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 470 @ 0c77f9e21b55 471 ○ f3fe58bc88cc git_head() 472 ○ e80a42cccd06 my-bookmark My commit message 473 ◆ 000000000000 474 [EOF] 475 "); 476} 477 478#[cfg(unix)] 479#[test] 480fn test_git_init_colocated_via_git_repo_path_symlink_gitlink() { 481 let test_env = TestEnvironment::default(); 482 // <jj_work_dir>/.git -> <git_workdir_path>/.git -> <git_repo_path> 483 let git_repo_path = test_env.env_root().join("git-repo"); 484 let git_workdir_path = test_env.env_root().join("git-workdir"); 485 let git_repo = init_git_repo(&git_repo_path, false); 486 std::fs::create_dir(&git_workdir_path).unwrap(); 487 git::create_gitlink(&git_workdir_path, git_repo.path()); 488 assert!(git_workdir_path.join(".git").is_file()); 489 let jj_work_dir = test_env.work_dir("").create_dir("repo"); 490 std::os::unix::fs::symlink( 491 git_workdir_path.join(".git"), 492 jj_work_dir.root().join(".git"), 493 ) 494 .unwrap(); 495 let output = jj_work_dir.run_jj(["git", "init", "--git-repo", "."]); 496 insta::assert_snapshot!(output, @r#" 497 ------- stderr ------- 498 Done importing changes from the underlying Git repo. 499 Initialized repo in "." 500 [EOF] 501 "#); 502 insta::assert_snapshot!(read_git_target(&jj_work_dir), @"../../../.git"); 503 504 // Check that the Git repo's HEAD got checked out 505 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 506 @ f3fe58bc88cc 507 ○ e80a42cccd06 my-bookmark git_head() My commit message 508 ◆ 000000000000 509 [EOF] 510 "); 511 512 // Check that the Git repo's HEAD moves 513 jj_work_dir.run_jj(["new"]).success(); 514 insta::assert_snapshot!(get_log_output(&jj_work_dir), @r" 515 @ 0c77f9e21b55 516 ○ f3fe58bc88cc git_head() 517 ○ e80a42cccd06 my-bookmark My commit message 518 ◆ 000000000000 519 [EOF] 520 "); 521} 522 523#[test] 524fn test_git_init_colocated_via_git_repo_path_imported_refs() { 525 let test_env = TestEnvironment::default(); 526 test_env.add_config("git.auto-local-bookmark = true"); 527 528 // Set up remote refs 529 test_env.run_jj_in(".", ["git", "init", "remote"]).success(); 530 let remote_dir = test_env.work_dir("remote"); 531 remote_dir 532 .run_jj(["bookmark", "create", "-r@", "local-remote", "remote-only"]) 533 .success(); 534 remote_dir.run_jj(["new"]).success(); 535 remote_dir.run_jj(["git", "export"]).success(); 536 537 let remote_git_path = remote_dir 538 .root() 539 .join(PathBuf::from_iter([".jj", "repo", "store", "git"])); 540 let set_up_local_repo = |local_path: &Path| { 541 let git_repo = git::clone(local_path, remote_git_path.to_str().unwrap(), None); 542 let git_ref = git_repo 543 .find_reference("refs/remotes/origin/local-remote") 544 .unwrap(); 545 git_repo 546 .reference( 547 "refs/heads/local-remote", 548 git_ref.target().id().to_owned(), 549 gix::refs::transaction::PreviousValue::MustNotExist, 550 "move local-remote bookmark", 551 ) 552 .unwrap(); 553 }; 554 555 // With git.auto-local-bookmark = true 556 let local_dir = test_env.work_dir("local1"); 557 set_up_local_repo(local_dir.root()); 558 let output = local_dir.run_jj(["git", "init", "--git-repo=."]); 559 insta::assert_snapshot!(output, @r#" 560 ------- stderr ------- 561 Done importing changes from the underlying Git repo. 562 Initialized repo in "." 563 [EOF] 564 "#); 565 insta::assert_snapshot!(get_bookmark_output(&local_dir), @r" 566 local-remote: qpvuntsm e8849ae1 (empty) (no description set) 567 @git: qpvuntsm e8849ae1 (empty) (no description set) 568 @origin: qpvuntsm e8849ae1 (empty) (no description set) 569 remote-only: qpvuntsm e8849ae1 (empty) (no description set) 570 @git: qpvuntsm e8849ae1 (empty) (no description set) 571 @origin: qpvuntsm e8849ae1 (empty) (no description set) 572 [EOF] 573 "); 574 575 // With git.auto-local-bookmark = false 576 test_env.add_config("git.auto-local-bookmark = false"); 577 let local_dir = test_env.work_dir("local2"); 578 set_up_local_repo(local_dir.root()); 579 let output = local_dir.run_jj(["git", "init", "--git-repo=."]); 580 insta::assert_snapshot!(output, @r#" 581 ------- stderr ------- 582 Done importing changes from the underlying Git repo. 583 Hint: The following remote bookmarks aren't associated with the existing local bookmarks: 584 local-remote@origin 585 Hint: Run the following command to keep local bookmarks updated on future pulls: 586 jj bookmark track local-remote@origin 587 Initialized repo in "." 588 [EOF] 589 "#); 590 insta::assert_snapshot!(get_bookmark_output(&local_dir), @r" 591 local-remote: qpvuntsm e8849ae1 (empty) (no description set) 592 @git: qpvuntsm e8849ae1 (empty) (no description set) 593 local-remote@origin: qpvuntsm e8849ae1 (empty) (no description set) 594 remote-only@origin: qpvuntsm e8849ae1 (empty) (no description set) 595 [EOF] 596 "); 597} 598 599#[test] 600fn test_git_init_colocated_dirty_working_copy() { 601 let test_env = TestEnvironment::default(); 602 let work_dir = test_env.work_dir("repo"); 603 let git_repo = init_git_repo(work_dir.root(), false); 604 605 let mut index_manager = git::IndexManager::new(&git_repo); 606 607 index_manager.add_file("new-staged-file", b"new content"); 608 index_manager.add_file("some-file", b"new content"); 609 index_manager.sync_index(); 610 611 work_dir.write_file("unstaged-file", "new content"); 612 insta::assert_debug_snapshot!(git::status(&git_repo), @r#" 613 [ 614 GitStatus { 615 path: "new-staged-file", 616 status: Index( 617 Addition, 618 ), 619 }, 620 GitStatus { 621 path: "some-file", 622 status: Index( 623 Modification, 624 ), 625 }, 626 GitStatus { 627 path: "unstaged-file", 628 status: Worktree( 629 Added, 630 ), 631 }, 632 ] 633 "#); 634 635 let output = work_dir.run_jj(["git", "init", "--git-repo", "."]); 636 insta::assert_snapshot!(output, @r#" 637 ------- stderr ------- 638 Done importing changes from the underlying Git repo. 639 Initialized repo in "." 640 [EOF] 641 "#); 642 643 // Working-copy changes should have been snapshotted. 644 let output = work_dir.run_jj(["log", "-s", "--ignore-working-copy"]); 645 insta::assert_snapshot!(output, @r" 646 @ sqpuoqvx test.user@example.com 2001-02-03 08:05:07 6efc2a53 647 │ (no description set) 648 │ C {some-file => new-staged-file} 649 │ M some-file 650 │ C {some-file => unstaged-file} 651 ○ nntyzxmz someone@example.org 1970-01-01 11:00:00 my-bookmark git_head() e80a42cc 652 │ My commit message 653 │ A some-file 654 ◆ zzzzzzzz root() 00000000 655 [EOF] 656 "); 657 658 // Git index should be consistent with the working copy parent. With the 659 // current implementation, the index is unchanged. Since jj created new 660 // working copy commit, it's also okay to update the index reflecting the 661 // working copy commit or the working copy parent. 662 insta::assert_debug_snapshot!(git::status(&git_repo), @r#" 663 [ 664 GitStatus { 665 path: ".jj/.gitignore", 666 status: Worktree( 667 Ignored, 668 ), 669 }, 670 GitStatus { 671 path: ".jj/repo", 672 status: Worktree( 673 Ignored, 674 ), 675 }, 676 GitStatus { 677 path: ".jj/working_copy", 678 status: Worktree( 679 Ignored, 680 ), 681 }, 682 GitStatus { 683 path: "new-staged-file", 684 status: Index( 685 Addition, 686 ), 687 }, 688 GitStatus { 689 path: "some-file", 690 status: Index( 691 Modification, 692 ), 693 }, 694 GitStatus { 695 path: "unstaged-file", 696 status: Worktree( 697 IntentToAdd, 698 ), 699 }, 700 ] 701 "#); 702} 703 704#[test] 705fn test_git_init_colocated_ignore_working_copy() { 706 let test_env = TestEnvironment::default(); 707 let work_dir = test_env.work_dir("repo"); 708 init_git_repo(work_dir.root(), false); 709 work_dir.write_file("file1", ""); 710 711 let output = work_dir.run_jj(["git", "init", "--ignore-working-copy", "--colocate"]); 712 insta::assert_snapshot!(output, @r" 713 ------- stderr ------- 714 Error: --ignore-working-copy is not respected 715 [EOF] 716 [exit status: 2] 717 "); 718} 719 720#[test] 721fn test_git_init_colocated_at_operation() { 722 let test_env = TestEnvironment::default(); 723 let work_dir = test_env.work_dir("repo"); 724 init_git_repo(work_dir.root(), false); 725 726 let output = work_dir.run_jj(["git", "init", "--at-op=@-", "--colocate"]); 727 insta::assert_snapshot!(output, @r" 728 ------- stderr ------- 729 Error: --at-op is not respected 730 [EOF] 731 [exit status: 2] 732 "); 733} 734 735#[test] 736fn test_git_init_external_but_git_dir_exists() { 737 let test_env = TestEnvironment::default(); 738 let git_repo_path = test_env.env_root().join("git-repo"); 739 let work_dir = test_env.work_dir("repo"); 740 git::init(&git_repo_path); 741 init_git_repo(work_dir.root(), false); 742 let output = work_dir.run_jj(["git", "init", "--git-repo", git_repo_path.to_str().unwrap()]); 743 insta::assert_snapshot!(output, @r#" 744 ------- stderr ------- 745 Initialized repo in "." 746 [EOF] 747 "#); 748 749 // The local ".git" repository is unrelated, so no commits should be imported 750 insta::assert_snapshot!(get_log_output(&work_dir), @r" 751 @ e8849ae12c70 752 ◆ 000000000000 753 [EOF] 754 "); 755 756 // Check that Git HEAD is not set because this isn't a colocated repo 757 work_dir.run_jj(["new"]).success(); 758 insta::assert_snapshot!(get_log_output(&work_dir), @r" 759 @ 1c1c95df80e5 760 ○ e8849ae12c70 761 ◆ 000000000000 762 [EOF] 763 "); 764} 765 766#[test] 767fn test_git_init_colocated_via_flag_git_dir_exists() { 768 let test_env = TestEnvironment::default(); 769 let work_dir = test_env.work_dir("repo"); 770 init_git_repo(work_dir.root(), false); 771 772 let output = test_env.run_jj_in(".", ["git", "init", "--colocate", "repo"]); 773 insta::assert_snapshot!(output, @r#" 774 ------- stderr ------- 775 Done importing changes from the underlying Git repo. 776 Initialized repo in "repo" 777 [EOF] 778 "#); 779 780 // Check that the Git repo's HEAD got checked out 781 insta::assert_snapshot!(get_log_output(&work_dir), @r" 782 @ f3fe58bc88cc 783 ○ e80a42cccd06 my-bookmark git_head() My commit message 784 ◆ 000000000000 785 [EOF] 786 "); 787 788 // Check that the Git repo's HEAD moves 789 work_dir.run_jj(["new"]).success(); 790 insta::assert_snapshot!(get_log_output(&work_dir), @r" 791 @ 0c77f9e21b55 792 ○ f3fe58bc88cc git_head() 793 ○ e80a42cccd06 my-bookmark My commit message 794 ◆ 000000000000 795 [EOF] 796 "); 797} 798 799#[test] 800fn test_git_init_colocated_via_flag_git_dir_not_exists() { 801 let test_env = TestEnvironment::default(); 802 let work_dir = test_env.work_dir("repo"); 803 let output = test_env.run_jj_in(".", ["git", "init", "--colocate", "repo"]); 804 insta::assert_snapshot!(output, @r#" 805 ------- stderr ------- 806 Initialized repo in "repo" 807 [EOF] 808 "#); 809 // No HEAD ref is available yet 810 insta::assert_snapshot!(get_log_output(&work_dir), @r" 811 @ e8849ae12c70 812 ◆ 000000000000 813 [EOF] 814 "); 815 816 // Create the default bookmark (create both in case we change the default) 817 work_dir 818 .run_jj(["bookmark", "create", "-r@", "main", "master"]) 819 .success(); 820 821 // If .git/HEAD pointed to the default bookmark, new working-copy commit would 822 // be created on top. 823 insta::assert_snapshot!(get_log_output(&work_dir), @r" 824 @ e8849ae12c70 main master 825 ◆ 000000000000 826 [EOF] 827 "); 828} 829 830#[test] 831fn test_git_init_conditional_config() { 832 let test_env = TestEnvironment::default(); 833 let old_workspace_dir = test_env.work_dir("old"); 834 let new_workspace_dir = test_env.work_dir("new"); 835 836 let run_jj = |work_dir: &TestWorkDir, args: &[&str]| { 837 work_dir.run_jj_with(|cmd| { 838 cmd.args(args) 839 .env_remove("JJ_EMAIL") 840 .env_remove("JJ_OP_HOSTNAME") 841 .env_remove("JJ_OP_USERNAME") 842 }) 843 }; 844 let log_template = r#"separate(' ', author.email(), description.first_line()) ++ "\n""#; 845 let op_log_template = r#"separate(' ', user, description.first_line()) ++ "\n""#; 846 847 // Override user.email and operation.username conditionally 848 test_env.add_config(formatdoc! {" 849 user.email = 'base@example.org' 850 operation.hostname = 'base' 851 operation.username = 'base' 852 [[--scope]] 853 --when.repositories = [{new_workspace_root}] 854 user.email = 'new-repo@example.org' 855 operation.username = 'new-repo' 856 ", 857 new_workspace_root = to_toml_value(new_workspace_dir.root().to_str().unwrap()), 858 }); 859 860 // Override operation.hostname by repo config, which should be loaded into 861 // the command settings, but shouldn't be copied to the new repo. 862 run_jj(&test_env.work_dir(""), &["git", "init", "old"]).success(); 863 run_jj( 864 &old_workspace_dir, 865 &["config", "set", "--repo", "operation.hostname", "old-repo"], 866 ) 867 .success(); 868 run_jj(&old_workspace_dir, &["new"]).success(); 869 let output = run_jj(&old_workspace_dir, &["op", "log", "-T", op_log_template]); 870 insta::assert_snapshot!(output, @r" 871 @ base@old-repo new empty commit 872 ○ base@base add workspace 'default' 873 ○ @ 874 [EOF] 875 "); 876 877 // Create new repo at the old workspace directory. 878 let output = run_jj(&old_workspace_dir, &["git", "init", "../new"]); 879 insta::assert_snapshot!(output.normalize_backslash(), @r#" 880 ------- stderr ------- 881 Initialized repo in "../new" 882 [EOF] 883 "#); 884 run_jj(&new_workspace_dir, &["new"]).success(); 885 let output = run_jj(&new_workspace_dir, &["log", "-T", log_template]); 886 insta::assert_snapshot!(output, @r" 887 @ new-repo@example.org 888 ○ new-repo@example.org 889890 [EOF] 891 "); 892 let output = run_jj(&new_workspace_dir, &["op", "log", "-T", op_log_template]); 893 insta::assert_snapshot!(output, @r" 894 @ new-repo@base new empty commit 895 ○ new-repo@base add workspace 'default' 896 ○ @ 897 [EOF] 898 "); 899} 900 901#[test] 902fn test_git_init_bad_wc_path() { 903 let test_env = TestEnvironment::default(); 904 std::fs::write(test_env.env_root().join("existing-file"), b"").unwrap(); 905 let output = test_env.run_jj_in(".", ["git", "init", "existing-file"]); 906 insta::assert_snapshot!(output.strip_stderr_last_line(), @r" 907 ------- stderr ------- 908 Error: Failed to create workspace 909 [EOF] 910 [exit status: 1] 911 "); 912}