just playing with tangled
at gvimdiff 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 0bd37cef (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 @ 0bd37cef2051 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 0bd37cef (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 @ 5f169ecc57b8 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 @ 62eda98b5eb4 353 ○ 5f169ecc57b8 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 @ 5f169ecc57b8 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 @ 62eda98b5eb4 391 ○ 5f169ecc57b8 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 @ 5f169ecc57b8 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 @ 62eda98b5eb4 429 ○ 5f169ecc57b8 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 @ 5f169ecc57b8 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 @ 62eda98b5eb4 471 ○ 5f169ecc57b8 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 @ 5f169ecc57b8 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 @ 62eda98b5eb4 516 ○ 5f169ecc57b8 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: vvkvtnvv 230dd059 (empty) (no description set) 567 @git: vvkvtnvv 230dd059 (empty) (no description set) 568 @origin: vvkvtnvv 230dd059 (empty) (no description set) 569 remote-only: vvkvtnvv 230dd059 (empty) (no description set) 570 @git: vvkvtnvv 230dd059 (empty) (no description set) 571 @origin: vvkvtnvv 230dd059 (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 `jj bookmark track local-remote@origin` to keep local bookmarks updated on future pulls. 586 Initialized repo in "." 587 [EOF] 588 "#); 589 insta::assert_snapshot!(get_bookmark_output(&local_dir), @r" 590 local-remote: vvkvtnvv 230dd059 (empty) (no description set) 591 @git: vvkvtnvv 230dd059 (empty) (no description set) 592 local-remote@origin: vvkvtnvv 230dd059 (empty) (no description set) 593 remote-only@origin: vvkvtnvv 230dd059 (empty) (no description set) 594 [EOF] 595 "); 596} 597 598#[test] 599fn test_git_init_colocated_dirty_working_copy() { 600 let test_env = TestEnvironment::default(); 601 let work_dir = test_env.work_dir("repo"); 602 let git_repo = init_git_repo(work_dir.root(), false); 603 604 let mut index_manager = git::IndexManager::new(&git_repo); 605 606 index_manager.add_file("new-staged-file", b"new content"); 607 index_manager.add_file("some-file", b"new content"); 608 index_manager.sync_index(); 609 610 work_dir.write_file("unstaged-file", "new content"); 611 insta::assert_debug_snapshot!(git::status(&git_repo), @r#" 612 [ 613 GitStatus { 614 path: "new-staged-file", 615 status: Index( 616 Addition, 617 ), 618 }, 619 GitStatus { 620 path: "some-file", 621 status: Index( 622 Modification, 623 ), 624 }, 625 GitStatus { 626 path: "unstaged-file", 627 status: Worktree( 628 Added, 629 ), 630 }, 631 ] 632 "#); 633 634 let output = work_dir.run_jj(["git", "init", "--git-repo", "."]); 635 insta::assert_snapshot!(output, @r#" 636 ------- stderr ------- 637 Done importing changes from the underlying Git repo. 638 Initialized repo in "." 639 [EOF] 640 "#); 641 642 // Working-copy changes should have been snapshotted. 643 let output = work_dir.run_jj(["log", "-s", "--ignore-working-copy"]); 644 insta::assert_snapshot!(output, @r" 645 @ sqpuoqvx test.user@example.com 2001-02-03 08:05:07 36dbd9a1 646 │ (no description set) 647 │ C {some-file => new-staged-file} 648 │ M some-file 649 │ C {some-file => unstaged-file} 650 ○ nntyzxmz someone@example.org 1970-01-01 11:00:00 my-bookmark git_head() e80a42cc 651 │ My commit message 652 │ A some-file 653 ◆ zzzzzzzz root() 00000000 654 [EOF] 655 "); 656 657 // Git index should be consistent with the working copy parent. With the 658 // current implementation, the index is unchanged. Since jj created new 659 // working copy commit, it's also okay to update the index reflecting the 660 // working copy commit or the working copy parent. 661 insta::assert_debug_snapshot!(git::status(&git_repo), @r#" 662 [ 663 GitStatus { 664 path: ".jj/.gitignore", 665 status: Worktree( 666 Ignored, 667 ), 668 }, 669 GitStatus { 670 path: ".jj/repo", 671 status: Worktree( 672 Ignored, 673 ), 674 }, 675 GitStatus { 676 path: ".jj/working_copy", 677 status: Worktree( 678 Ignored, 679 ), 680 }, 681 GitStatus { 682 path: "new-staged-file", 683 status: Index( 684 Addition, 685 ), 686 }, 687 GitStatus { 688 path: "some-file", 689 status: Index( 690 Modification, 691 ), 692 }, 693 GitStatus { 694 path: "unstaged-file", 695 status: Worktree( 696 IntentToAdd, 697 ), 698 }, 699 ] 700 "#); 701} 702 703#[test] 704fn test_git_init_colocated_ignore_working_copy() { 705 let test_env = TestEnvironment::default(); 706 let work_dir = test_env.work_dir("repo"); 707 init_git_repo(work_dir.root(), false); 708 work_dir.write_file("file1", ""); 709 710 let output = work_dir.run_jj(["git", "init", "--ignore-working-copy", "--colocate"]); 711 insta::assert_snapshot!(output, @r" 712 ------- stderr ------- 713 Error: --ignore-working-copy is not respected 714 [EOF] 715 [exit status: 2] 716 "); 717} 718 719#[test] 720fn test_git_init_colocated_at_operation() { 721 let test_env = TestEnvironment::default(); 722 let work_dir = test_env.work_dir("repo"); 723 init_git_repo(work_dir.root(), false); 724 725 let output = work_dir.run_jj(["git", "init", "--at-op=@-", "--colocate"]); 726 insta::assert_snapshot!(output, @r" 727 ------- stderr ------- 728 Error: --at-op is not respected 729 [EOF] 730 [exit status: 2] 731 "); 732} 733 734#[test] 735fn test_git_init_external_but_git_dir_exists() { 736 let test_env = TestEnvironment::default(); 737 let git_repo_path = test_env.env_root().join("git-repo"); 738 let work_dir = test_env.work_dir("repo"); 739 git::init(&git_repo_path); 740 init_git_repo(work_dir.root(), false); 741 let output = work_dir.run_jj(["git", "init", "--git-repo", git_repo_path.to_str().unwrap()]); 742 insta::assert_snapshot!(output, @r#" 743 ------- stderr ------- 744 Initialized repo in "." 745 [EOF] 746 "#); 747 748 // The local ".git" repository is unrelated, so no commits should be imported 749 insta::assert_snapshot!(get_log_output(&work_dir), @r" 750 @ 230dd059e1b0 751 ◆ 000000000000 752 [EOF] 753 "); 754 755 // Check that Git HEAD is not set because this isn't a colocated repo 756 work_dir.run_jj(["new"]).success(); 757 insta::assert_snapshot!(get_log_output(&work_dir), @r" 758 @ 4db490c88528 759 ○ 230dd059e1b0 760 ◆ 000000000000 761 [EOF] 762 "); 763} 764 765#[test] 766fn test_git_init_colocated_via_flag_git_dir_exists() { 767 let test_env = TestEnvironment::default(); 768 let work_dir = test_env.work_dir("repo"); 769 init_git_repo(work_dir.root(), false); 770 771 let output = test_env.run_jj_in(".", ["git", "init", "--colocate", "repo"]); 772 insta::assert_snapshot!(output, @r#" 773 ------- stderr ------- 774 Done importing changes from the underlying Git repo. 775 Initialized repo in "repo" 776 [EOF] 777 "#); 778 779 // Check that the Git repo's HEAD got checked out 780 insta::assert_snapshot!(get_log_output(&work_dir), @r" 781 @ 5f169ecc57b8 782 ○ e80a42cccd06 my-bookmark git_head() My commit message 783 ◆ 000000000000 784 [EOF] 785 "); 786 787 // Check that the Git repo's HEAD moves 788 work_dir.run_jj(["new"]).success(); 789 insta::assert_snapshot!(get_log_output(&work_dir), @r" 790 @ 62eda98b5eb4 791 ○ 5f169ecc57b8 git_head() 792 ○ e80a42cccd06 my-bookmark My commit message 793 ◆ 000000000000 794 [EOF] 795 "); 796} 797 798#[test] 799fn test_git_init_colocated_via_flag_git_dir_not_exists() { 800 let test_env = TestEnvironment::default(); 801 let work_dir = test_env.work_dir("repo"); 802 let output = test_env.run_jj_in(".", ["git", "init", "--colocate", "repo"]); 803 insta::assert_snapshot!(output, @r#" 804 ------- stderr ------- 805 Initialized repo in "repo" 806 [EOF] 807 "#); 808 // No HEAD ref is available yet 809 insta::assert_snapshot!(get_log_output(&work_dir), @r" 810 @ 230dd059e1b0 811 ◆ 000000000000 812 [EOF] 813 "); 814 815 // Create the default bookmark (create both in case we change the default) 816 work_dir 817 .run_jj(["bookmark", "create", "-r@", "main", "master"]) 818 .success(); 819 820 // If .git/HEAD pointed to the default bookmark, new working-copy commit would 821 // be created on top. 822 insta::assert_snapshot!(get_log_output(&work_dir), @r" 823 @ 230dd059e1b0 main master 824 ◆ 000000000000 825 [EOF] 826 "); 827} 828 829#[test] 830fn test_git_init_conditional_config() { 831 let test_env = TestEnvironment::default(); 832 let old_workspace_dir = test_env.work_dir("old"); 833 let new_workspace_dir = test_env.work_dir("new"); 834 835 let run_jj = |work_dir: &TestWorkDir, args: &[&str]| { 836 work_dir.run_jj_with(|cmd| { 837 cmd.args(args) 838 .env_remove("JJ_EMAIL") 839 .env_remove("JJ_OP_HOSTNAME") 840 .env_remove("JJ_OP_USERNAME") 841 }) 842 }; 843 let log_template = r#"separate(' ', author.email(), description.first_line()) ++ "\n""#; 844 let op_log_template = r#"separate(' ', user, description.first_line()) ++ "\n""#; 845 846 // Override user.email and operation.username conditionally 847 test_env.add_config(formatdoc! {" 848 user.email = 'base@example.org' 849 operation.hostname = 'base' 850 operation.username = 'base' 851 [[--scope]] 852 --when.repositories = [{new_workspace_root}] 853 user.email = 'new-repo@example.org' 854 operation.username = 'new-repo' 855 ", 856 new_workspace_root = to_toml_value(new_workspace_dir.root().to_str().unwrap()), 857 }); 858 859 // Override operation.hostname by repo config, which should be loaded into 860 // the command settings, but shouldn't be copied to the new repo. 861 run_jj(&test_env.work_dir(""), &["git", "init", "old"]).success(); 862 run_jj( 863 &old_workspace_dir, 864 &["config", "set", "--repo", "operation.hostname", "old-repo"], 865 ) 866 .success(); 867 run_jj(&old_workspace_dir, &["new"]).success(); 868 let output = run_jj(&old_workspace_dir, &["op", "log", "-T", op_log_template]); 869 insta::assert_snapshot!(output, @r" 870 @ base@old-repo new empty commit 871 ○ base@base add workspace 'default' 872 ○ @ 873 [EOF] 874 "); 875 876 // Create new repo at the old workspace directory. 877 let output = run_jj(&old_workspace_dir, &["git", "init", "../new"]); 878 insta::assert_snapshot!(output.normalize_backslash(), @r#" 879 ------- stderr ------- 880 Initialized repo in "../new" 881 [EOF] 882 "#); 883 run_jj(&new_workspace_dir, &["new"]).success(); 884 let output = run_jj(&new_workspace_dir, &["log", "-T", log_template]); 885 insta::assert_snapshot!(output, @r" 886 @ new-repo@example.org 887 ○ new-repo@example.org 888889 [EOF] 890 "); 891 let output = run_jj(&new_workspace_dir, &["op", "log", "-T", op_log_template]); 892 insta::assert_snapshot!(output, @r" 893 @ new-repo@base new empty commit 894 ○ new-repo@base add workspace 'default' 895 ○ @ 896 [EOF] 897 "); 898} 899 900#[test] 901fn test_git_init_bad_wc_path() { 902 let test_env = TestEnvironment::default(); 903 std::fs::write(test_env.env_root().join("existing-file"), b"").unwrap(); 904 let output = test_env.run_jj_in(".", ["git", "init", "existing-file"]); 905 insta::assert_snapshot!(output.strip_stderr_last_line(), @r" 906 ------- stderr ------- 907 Error: Failed to create workspace 908 [EOF] 909 [exit status: 1] 910 "); 911}