just playing with tangled
at diffedit3 549 lines 21 kB view raw
1// Copyright 2020 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, PathBuf}; 16 17use test_case::test_case; 18 19use crate::common::{strip_last_line, TestEnvironment}; 20 21fn init_git_repo(git_repo_path: &Path, bare: bool) -> git2::Repository { 22 init_git_repo_with_opts(git_repo_path, git2::RepositoryInitOptions::new().bare(bare)) 23} 24 25fn init_git_repo_with_opts( 26 git_repo_path: &Path, 27 opts: &git2::RepositoryInitOptions, 28) -> git2::Repository { 29 let git_repo = git2::Repository::init_opts(git_repo_path, opts).unwrap(); 30 let git_blob_oid = git_repo.blob(b"some content").unwrap(); 31 let mut git_tree_builder = git_repo.treebuilder(None).unwrap(); 32 git_tree_builder 33 .insert("some-file", git_blob_oid, 0o100644) 34 .unwrap(); 35 let git_tree_id = git_tree_builder.write().unwrap(); 36 drop(git_tree_builder); 37 let git_tree = git_repo.find_tree(git_tree_id).unwrap(); 38 let git_signature = git2::Signature::new( 39 "Git User", 40 "git.user@example.com", 41 &git2::Time::new(123, 60), 42 ) 43 .unwrap(); 44 git_repo 45 .commit( 46 Some("refs/heads/my-branch"), 47 &git_signature, 48 &git_signature, 49 "My commit message", 50 &git_tree, 51 &[], 52 ) 53 .unwrap(); 54 drop(git_tree); 55 git_repo.set_head("refs/heads/my-branch").unwrap(); 56 git_repo 57} 58 59fn get_branch_output(test_env: &TestEnvironment, repo_path: &Path) -> String { 60 test_env.jj_cmd_success(repo_path, &["branch", "list", "--all-remotes"]) 61} 62 63fn read_git_target(workspace_root: &Path) -> String { 64 let mut path = workspace_root.to_path_buf(); 65 path.extend([".jj", "repo", "store", "git_target"]); 66 std::fs::read_to_string(path).unwrap() 67} 68 69#[test] 70fn test_init_git_internal() { 71 let test_env = TestEnvironment::default(); 72 let (stdout, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 73 insta::assert_snapshot!(stdout, @""); 74 insta::assert_snapshot!(stderr, @r###" 75 Warning: `--git` and `--git-repo` are deprecated. 76 Use `jj git init` instead 77 Initialized repo in "repo" 78 "###); 79 80 let workspace_root = test_env.env_root().join("repo"); 81 let jj_path = workspace_root.join(".jj"); 82 let repo_path = jj_path.join("repo"); 83 let store_path = repo_path.join("store"); 84 assert!(workspace_root.is_dir()); 85 assert!(jj_path.is_dir()); 86 assert!(jj_path.join("working_copy").is_dir()); 87 assert!(repo_path.is_dir()); 88 assert!(store_path.is_dir()); 89 assert!(store_path.join("git").is_dir()); 90 assert_eq!(read_git_target(&workspace_root), "git"); 91} 92 93#[test_case(false; "full")] 94#[test_case(true; "bare")] 95fn test_init_git_external(bare: bool) { 96 let test_env = TestEnvironment::default(); 97 let git_repo_path = test_env.env_root().join("git-repo"); 98 init_git_repo(&git_repo_path, bare); 99 100 let (stdout, stderr) = test_env.jj_cmd_ok( 101 test_env.env_root(), 102 &[ 103 "init", 104 "repo", 105 "--git-repo", 106 git_repo_path.to_str().unwrap(), 107 ], 108 ); 109 insta::allow_duplicates! { 110 insta::assert_snapshot!(stdout, @""); 111 insta::assert_snapshot!(stderr, @r###" 112 Done importing changes from the underlying Git repo. 113 Working copy now at: sqpuoqvx f6950fc1 (empty) (no description set) 114 Parent commit : mwrttmos 8d698d4a my-branch | My commit message 115 Added 1 files, modified 0 files, removed 0 files 116 Warning: `--git` and `--git-repo` are deprecated. 117 Use `jj git init` instead 118 Initialized repo in "repo" 119 "###); 120 } 121 122 let workspace_root = test_env.env_root().join("repo"); 123 let jj_path = workspace_root.join(".jj"); 124 let repo_path = jj_path.join("repo"); 125 let store_path = repo_path.join("store"); 126 assert!(workspace_root.is_dir()); 127 assert!(jj_path.is_dir()); 128 assert!(jj_path.join("working_copy").is_dir()); 129 assert!(repo_path.is_dir()); 130 assert!(store_path.is_dir()); 131 let unix_git_target_file_contents = read_git_target(&workspace_root).replace('\\', "/"); 132 if bare { 133 assert!(unix_git_target_file_contents.ends_with("/git-repo")); 134 } else { 135 assert!(unix_git_target_file_contents.ends_with("/git-repo/.git")); 136 } 137 138 // Check that the Git repo's HEAD got checked out 139 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-"]); 140 insta::allow_duplicates! { 141 insta::assert_snapshot!(stdout, @r###" 142 ◉ mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a 143 │ My commit message 144 ~ 145 "###); 146 } 147} 148 149#[test] 150fn test_init_git_external_non_existent_directory() { 151 let test_env = TestEnvironment::default(); 152 let stderr = test_env.jj_cmd_failure( 153 test_env.env_root(), 154 &["init", "repo", "--git-repo", "non-existent"], 155 ); 156 insta::assert_snapshot!(strip_last_line(&stderr), @r###" 157 Error: Failed to access the repository 158 Caused by: 159 1: Cannot access $TEST_ENV/non-existent 160 "###); 161} 162 163#[test] 164fn test_init_git_external_non_existent_git_directory() { 165 let test_env = TestEnvironment::default(); 166 let workspace_root = test_env.env_root().join("repo"); 167 let stderr = 168 test_env.jj_cmd_failure(test_env.env_root(), &["init", "repo", "--git-repo", "repo"]); 169 170 insta::assert_snapshot!(&stderr, @r###" 171 Error: Failed to access the repository 172 Caused by: 173 1: Failed to open git repository 174 2: "$TEST_ENV/repo" does not appear to be a git repository 175 3: Missing HEAD at '.git/HEAD' 176 "###); 177 let jj_path = workspace_root.join(".jj"); 178 assert!(!jj_path.exists()); 179} 180 181#[test] 182fn test_init_git_colocated() { 183 let test_env = TestEnvironment::default(); 184 let workspace_root = test_env.env_root().join("repo"); 185 init_git_repo(&workspace_root, false); 186 let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["init", "--git-repo", "."]); 187 insta::assert_snapshot!(stdout, @""); 188 insta::assert_snapshot!(stderr, @r###" 189 Done importing changes from the underlying Git repo. 190 Warning: `--git` and `--git-repo` are deprecated. 191 Use `jj git init` instead 192 Initialized repo in "." 193 "###); 194 195 let jj_path = workspace_root.join(".jj"); 196 let repo_path = jj_path.join("repo"); 197 let store_path = repo_path.join("store"); 198 assert!(workspace_root.is_dir()); 199 assert!(jj_path.is_dir()); 200 assert!(jj_path.join("working_copy").is_dir()); 201 assert!(repo_path.is_dir()); 202 assert!(store_path.is_dir()); 203 assert!(read_git_target(&workspace_root) 204 .replace('\\', "/") 205 .ends_with("../../../.git")); 206 207 // Check that the Git repo's HEAD got checked out 208 let stdout = test_env.jj_cmd_success(&repo_path, &["log", "-r", "@-"]); 209 insta::assert_snapshot!(stdout, @r###" 210 ◉ mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a 211 │ My commit message 212 ~ 213 "###); 214 215 // Check that the Git repo's HEAD moves 216 test_env.jj_cmd_ok(&workspace_root, &["new"]); 217 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 218 insta::assert_snapshot!(stdout, @r###" 219 ◉ sqpuoqvx test.user@example.com 2001-02-03 08:05:07 HEAD@git f61b77cd 220 │ (no description set) 221 ~ 222 "###); 223} 224 225#[test] 226fn test_init_git_colocated_gitlink() { 227 let test_env = TestEnvironment::default(); 228 // <workspace_root>/.git -> <git_repo_path> 229 let git_repo_path = test_env.env_root().join("git-repo"); 230 let workspace_root = test_env.env_root().join("repo"); 231 init_git_repo_with_opts( 232 &git_repo_path, 233 git2::RepositoryInitOptions::new().workdir_path(&workspace_root), 234 ); 235 assert!(workspace_root.join(".git").is_file()); 236 let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["init", "--git-repo", "."]); 237 insta::assert_snapshot!(stdout, @""); 238 insta::assert_snapshot!(stderr, @r###" 239 Done importing changes from the underlying Git repo. 240 Warning: `--git` and `--git-repo` are deprecated. 241 Use `jj git init` instead 242 Initialized repo in "." 243 "###); 244 insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git"); 245 246 // Check that the Git repo's HEAD got checked out 247 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 248 insta::assert_snapshot!(stdout, @r###" 249 ◉ mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a 250 │ My commit message 251 ~ 252 "###); 253 254 // Check that the Git repo's HEAD moves 255 test_env.jj_cmd_ok(&workspace_root, &["new"]); 256 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 257 insta::assert_snapshot!(stdout, @r###" 258 ◉ sqpuoqvx test.user@example.com 2001-02-03 08:05:07 HEAD@git f61b77cd 259 │ (no description set) 260 ~ 261 "###); 262} 263 264#[cfg(unix)] 265#[test] 266fn test_init_git_colocated_symlink_directory() { 267 let test_env = TestEnvironment::default(); 268 // <workspace_root>/.git -> <git_repo_path> 269 let git_repo_path = test_env.env_root().join("git-repo"); 270 let workspace_root = test_env.env_root().join("repo"); 271 init_git_repo(&git_repo_path, false); 272 std::fs::create_dir(&workspace_root).unwrap(); 273 std::os::unix::fs::symlink(git_repo_path.join(".git"), workspace_root.join(".git")).unwrap(); 274 let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["init", "--git-repo", "."]); 275 insta::assert_snapshot!(stdout, @""); 276 insta::assert_snapshot!(stderr, @r###" 277 Done importing changes from the underlying Git repo. 278 Warning: `--git` and `--git-repo` are deprecated. 279 Use `jj git init` instead 280 Initialized repo in "." 281 "###); 282 insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git"); 283 284 // Check that the Git repo's HEAD got checked out 285 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 286 insta::assert_snapshot!(stdout, @r###" 287 ◉ mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a 288 │ My commit message 289 ~ 290 "###); 291 292 // Check that the Git repo's HEAD moves 293 test_env.jj_cmd_ok(&workspace_root, &["new"]); 294 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 295 insta::assert_snapshot!(stdout, @r###" 296 ◉ sqpuoqvx test.user@example.com 2001-02-03 08:05:07 HEAD@git f61b77cd 297 │ (no description set) 298 ~ 299 "###); 300} 301 302#[cfg(unix)] 303#[test] 304fn test_init_git_colocated_symlink_directory_without_bare_config() { 305 let test_env = TestEnvironment::default(); 306 // <workspace_root>/.git -> <git_repo_path> 307 let git_repo_path = test_env.env_root().join("git-repo.git"); 308 let workspace_root = test_env.env_root().join("repo"); 309 // Set up git repo without core.bare set (as the "repo" tool would do.) 310 // The core.bare config is deduced from the directory name. 311 let git_repo = init_git_repo(&workspace_root, false); 312 git_repo.config().unwrap().remove("core.bare").unwrap(); 313 std::fs::rename(workspace_root.join(".git"), &git_repo_path).unwrap(); 314 std::os::unix::fs::symlink(&git_repo_path, workspace_root.join(".git")).unwrap(); 315 let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["init", "--git-repo", "."]); 316 insta::assert_snapshot!(stdout, @""); 317 insta::assert_snapshot!(stderr, @r###" 318 Done importing changes from the underlying Git repo. 319 Warning: `--git` and `--git-repo` are deprecated. 320 Use `jj git init` instead 321 Initialized repo in "." 322 "###); 323 insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git"); 324 325 // Check that the Git repo's HEAD got checked out 326 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 327 insta::assert_snapshot!(stdout, @r###" 328 ◉ mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a 329 │ My commit message 330 ~ 331 "###); 332 333 // Check that the Git repo's HEAD moves 334 test_env.jj_cmd_ok(&workspace_root, &["new"]); 335 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 336 insta::assert_snapshot!(stdout, @r###" 337 ◉ sqpuoqvx test.user@example.com 2001-02-03 08:05:07 HEAD@git f61b77cd 338 │ (no description set) 339 ~ 340 "###); 341} 342 343#[cfg(unix)] 344#[test] 345fn test_init_git_colocated_symlink_gitlink() { 346 let test_env = TestEnvironment::default(); 347 // <workspace_root>/.git -> <git_workdir_path>/.git -> <git_repo_path> 348 let git_repo_path = test_env.env_root().join("git-repo"); 349 let git_workdir_path = test_env.env_root().join("git-workdir"); 350 let workspace_root = test_env.env_root().join("repo"); 351 init_git_repo_with_opts( 352 &git_repo_path, 353 git2::RepositoryInitOptions::new().workdir_path(&git_workdir_path), 354 ); 355 assert!(git_workdir_path.join(".git").is_file()); 356 std::fs::create_dir(&workspace_root).unwrap(); 357 std::os::unix::fs::symlink(git_workdir_path.join(".git"), workspace_root.join(".git")).unwrap(); 358 let (stdout, stderr) = test_env.jj_cmd_ok(&workspace_root, &["init", "--git-repo", "."]); 359 insta::assert_snapshot!(stdout, @""); 360 insta::assert_snapshot!(stderr, @r###" 361 Done importing changes from the underlying Git repo. 362 Warning: `--git` and `--git-repo` are deprecated. 363 Use `jj git init` instead 364 Initialized repo in "." 365 "###); 366 insta::assert_snapshot!(read_git_target(&workspace_root), @"../../../.git"); 367 368 // Check that the Git repo's HEAD got checked out 369 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 370 insta::assert_snapshot!(stdout, @r###" 371 ◉ mwrttmos git.user@example.com 1970-01-01 11:02:03 my-branch HEAD@git 8d698d4a 372 │ My commit message 373 ~ 374 "###); 375 376 // Check that the Git repo's HEAD moves 377 test_env.jj_cmd_ok(&workspace_root, &["new"]); 378 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 379 insta::assert_snapshot!(stdout, @r###" 380 ◉ sqpuoqvx test.user@example.com 2001-02-03 08:05:07 HEAD@git f61b77cd 381 │ (no description set) 382 ~ 383 "###); 384} 385 386#[test] 387fn test_init_git_colocated_imported_refs() { 388 let test_env = TestEnvironment::default(); 389 test_env.add_config("git.auto-local-branch = true"); 390 391 // Set up remote refs 392 test_env.jj_cmd_ok(test_env.env_root(), &["init", "remote", "--git"]); 393 let remote_path = test_env.env_root().join("remote"); 394 test_env.jj_cmd_ok( 395 &remote_path, 396 &["branch", "create", "local-remote", "remote-only"], 397 ); 398 test_env.jj_cmd_ok(&remote_path, &["new"]); 399 test_env.jj_cmd_ok(&remote_path, &["git", "export"]); 400 401 let remote_git_path = remote_path.join(PathBuf::from_iter([".jj", "repo", "store", "git"])); 402 let set_up_local_repo = |local_path: &Path| { 403 let git_repo = 404 git2::Repository::clone(remote_git_path.to_str().unwrap(), local_path).unwrap(); 405 let git_ref = git_repo 406 .find_reference("refs/remotes/origin/local-remote") 407 .unwrap(); 408 git_repo 409 .reference( 410 "refs/heads/local-remote", 411 git_ref.target().unwrap(), 412 false, 413 "", 414 ) 415 .unwrap(); 416 }; 417 418 // With git.auto-local-branch = true 419 let local_path = test_env.env_root().join("local1"); 420 set_up_local_repo(&local_path); 421 let (_stdout, stderr) = test_env.jj_cmd_ok(&local_path, &["init", "--git-repo=."]); 422 insta::assert_snapshot!(stderr, @r###" 423 Done importing changes from the underlying Git repo. 424 Warning: `--git` and `--git-repo` are deprecated. 425 Use `jj git init` instead 426 Initialized repo in "." 427 "###); 428 insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###" 429 local-remote: vvkvtnvv 230dd059 (empty) (no description set) 430 @git: vvkvtnvv 230dd059 (empty) (no description set) 431 @origin: vvkvtnvv 230dd059 (empty) (no description set) 432 remote-only: vvkvtnvv 230dd059 (empty) (no description set) 433 @git: vvkvtnvv 230dd059 (empty) (no description set) 434 @origin: vvkvtnvv 230dd059 (empty) (no description set) 435 "###); 436 437 // With git.auto-local-branch = false 438 test_env.add_config("git.auto-local-branch = false"); 439 let local_path = test_env.env_root().join("local2"); 440 set_up_local_repo(&local_path); 441 let (_stdout, stderr) = test_env.jj_cmd_ok(&local_path, &["init", "--git-repo=."]); 442 insta::assert_snapshot!(stderr, @r###" 443 Done importing changes from the underlying Git repo. 444 Hint: The following remote branches aren't associated with the existing local branches: 445 local-remote@origin 446 Hint: Run `jj branch track local-remote@origin` to keep local branches updated on future pulls. 447 Warning: `--git` and `--git-repo` are deprecated. 448 Use `jj git init` instead 449 Initialized repo in "." 450 "###); 451 insta::assert_snapshot!(get_branch_output(&test_env, &local_path), @r###" 452 local-remote: vvkvtnvv 230dd059 (empty) (no description set) 453 @git: vvkvtnvv 230dd059 (empty) (no description set) 454 local-remote@origin: vvkvtnvv 230dd059 (empty) (no description set) 455 remote-only@origin: vvkvtnvv 230dd059 (empty) (no description set) 456 "###); 457} 458 459#[test] 460fn test_init_git_external_but_git_dir_exists() { 461 let test_env = TestEnvironment::default(); 462 let git_repo_path = test_env.env_root().join("git-repo"); 463 let workspace_root = test_env.env_root().join("repo"); 464 git2::Repository::init(&git_repo_path).unwrap(); 465 init_git_repo(&workspace_root, false); 466 let (stdout, stderr) = test_env.jj_cmd_ok( 467 &workspace_root, 468 &["init", "--git-repo", git_repo_path.to_str().unwrap()], 469 ); 470 insta::assert_snapshot!(stdout, @""); 471 insta::assert_snapshot!(stderr, @r###" 472 Warning: `--git` and `--git-repo` are deprecated. 473 Use `jj git init` instead 474 Initialized repo in "." 475 "###); 476 477 // The local ".git" repository is unrelated, so no commits should be imported 478 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 479 insta::assert_snapshot!(stdout, @r###" 480 ◉ zzzzzzzz root() 00000000 481 "###); 482 483 // Check that Git HEAD is not set because this isn't a colocated repo 484 test_env.jj_cmd_ok(&workspace_root, &["new"]); 485 let stdout = test_env.jj_cmd_success(&workspace_root, &["log", "-r", "@-"]); 486 insta::assert_snapshot!(stdout, @r###" 487 ◉ qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059 488 │ (empty) (no description set) 489 ~ 490 "###); 491} 492 493#[test] 494fn test_init_git_internal_must_be_colocated() { 495 let test_env = TestEnvironment::default(); 496 let workspace_root = test_env.env_root().join("repo"); 497 init_git_repo(&workspace_root, false); 498 499 let stderr = test_env.jj_cmd_failure(&workspace_root, &["init", "--git"]); 500 insta::assert_snapshot!(stderr, @r###" 501 Error: Did not create a jj repo because there is an existing Git repo in this directory. 502 Hint: To create a repo backed by the existing Git repo, run `jj git init --colocate` instead. 503 "###); 504} 505 506#[test] 507fn test_init_git_bad_wc_path() { 508 let test_env = TestEnvironment::default(); 509 std::fs::write(test_env.env_root().join("existing-file"), b"").unwrap(); 510 let stderr = test_env.jj_cmd_failure(test_env.env_root(), &["init", "--git", "existing-file"]); 511 assert!(stderr.contains("Failed to create workspace")); 512} 513 514#[test] 515fn test_init_local_disallowed() { 516 let test_env = TestEnvironment::default(); 517 let stdout = test_env.jj_cmd_failure(test_env.env_root(), &["init", "repo"]); 518 insta::assert_snapshot!(stdout, @r###" 519 Error: The native backend is disallowed by default. 520 Hint: Did you mean to call `jj git init`? 521 Set `ui.allow-init-native` to allow initializing a repo with the native backend. 522 "###); 523} 524 525#[test] 526fn test_init_local() { 527 let test_env = TestEnvironment::default(); 528 test_env.add_config(r#"ui.allow-init-native = true"#); 529 let (stdout, stderr) = test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo"]); 530 insta::assert_snapshot!(stdout, @""); 531 insta::assert_snapshot!(stderr, @r###" 532 Initialized repo in "repo" 533 "###); 534 535 let workspace_root = test_env.env_root().join("repo"); 536 let jj_path = workspace_root.join(".jj"); 537 let repo_path = jj_path.join("repo"); 538 let store_path = repo_path.join("store"); 539 assert!(workspace_root.is_dir()); 540 assert!(jj_path.is_dir()); 541 assert!(jj_path.join("working_copy").is_dir()); 542 assert!(repo_path.is_dir()); 543 assert!(store_path.is_dir()); 544 assert!(store_path.join("commits").is_dir()); 545 assert!(store_path.join("trees").is_dir()); 546 assert!(store_path.join("files").is_dir()); 547 assert!(store_path.join("symlinks").is_dir()); 548 assert!(store_path.join("conflicts").is_dir()); 549}