just playing with tangled
at ig/vimdiffwarn 1639 lines 62 kB view raw
1// Copyright 2022 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::fmt::Write as _; 16use std::path::Path; 17 18use testutils::git; 19 20use crate::common::CommandOutput; 21use crate::common::TestEnvironment; 22use crate::common::TestWorkDir; 23 24#[test] 25fn test_git_colocated() { 26 let test_env = TestEnvironment::default(); 27 let work_dir = test_env.work_dir("repo"); 28 let git_repo = git::init(work_dir.root()); 29 30 // Create an initial commit in Git 31 let tree_id = git::add_commit( 32 &git_repo, 33 "refs/heads/master", 34 "file", 35 b"contents", 36 "initial", 37 &[], 38 ) 39 .tree_id; 40 git::checkout_tree_index(&git_repo, tree_id); 41 assert_eq!(work_dir.read_file("file"), b"contents"); 42 insta::assert_snapshot!( 43 git_repo.head_id().unwrap().to_string(), 44 @"97358f54806c7cd005ed5ade68a779595efbae7e" 45 ); 46 47 // Import the repo 48 work_dir 49 .run_jj(["git", "init", "--git-repo", "."]) 50 .success(); 51 insta::assert_snapshot!(get_log_output(&work_dir), @r" 52 @ c3a12656d5027825bc69f40e11dc0bb381d7c277 53 ○ 97358f54806c7cd005ed5ade68a779595efbae7e master git_head() initial 54 ◆ 0000000000000000000000000000000000000000 55 [EOF] 56 "); 57 insta::assert_snapshot!( 58 git_repo.head_id().unwrap().to_string(), 59 @"97358f54806c7cd005ed5ade68a779595efbae7e" 60 ); 61 62 // Modify the working copy. The working-copy commit should changed, but the Git 63 // HEAD commit should not 64 work_dir.write_file("file", "modified"); 65 insta::assert_snapshot!(get_log_output(&work_dir), @r" 66 @ 59642000f061cf23eb37ab6eecce428afe7824da 67 ○ 97358f54806c7cd005ed5ade68a779595efbae7e master git_head() initial 68 ◆ 0000000000000000000000000000000000000000 69 [EOF] 70 "); 71 insta::assert_snapshot!( 72 git_repo.head_id().unwrap().to_string(), 73 @"97358f54806c7cd005ed5ade68a779595efbae7e" 74 ); 75 76 // Create a new change from jj and check that it's reflected in Git 77 work_dir.run_jj(["new"]).success(); 78 insta::assert_snapshot!(get_log_output(&work_dir), @r" 79 @ fd4e130e43068d76ec6eb2c6df01653ea12eccb4 80 ○ 59642000f061cf23eb37ab6eecce428afe7824da git_head() 81 ○ 97358f54806c7cd005ed5ade68a779595efbae7e master initial 82 ◆ 0000000000000000000000000000000000000000 83 [EOF] 84 "); 85 assert!(git_repo.head().unwrap().is_detached()); 86 insta::assert_snapshot!( 87 git_repo.head_id().unwrap().to_string(), 88 @"59642000f061cf23eb37ab6eecce428afe7824da" 89 ); 90} 91 92#[test] 93fn test_git_colocated_intent_to_add() { 94 let test_env = TestEnvironment::default(); 95 test_env 96 .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 97 .success(); 98 let work_dir = test_env.work_dir("repo"); 99 100 // A file added directly on top of the root commit should be marked as 101 // intent-to-add 102 work_dir.write_file("file1.txt", "contents"); 103 work_dir.run_jj(["status"]).success(); 104 insta::assert_snapshot!(get_index_state(work_dir.root()), @"Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 file1.txt"); 105 106 // Another new file should be marked as intent-to-add 107 work_dir.run_jj(["new"]).success(); 108 work_dir.write_file("file2.txt", "contents"); 109 work_dir.run_jj(["status"]).success(); 110 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 111 Unconflicted Mode(FILE) 0839b2e9412b ctime=0:0 mtime=0:0 size=0 flags=0 file1.txt 112 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 file2.txt 113 "); 114 115 // After creating a new commit, it should not longer be marked as intent-to-add 116 work_dir.run_jj(["new"]).success(); 117 work_dir.write_file("file2.txt", "contents"); 118 work_dir.run_jj(["status"]).success(); 119 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 120 Unconflicted Mode(FILE) 0839b2e9412b ctime=0:0 mtime=0:0 size=0 flags=0 file1.txt 121 Unconflicted Mode(FILE) 0839b2e9412b ctime=0:0 mtime=0:0 size=0 flags=0 file2.txt 122 "); 123 124 // If we edit an existing commit, new files are marked as intent-to-add 125 work_dir.run_jj(["edit", "@-"]).success(); 126 work_dir.run_jj(["status"]).success(); 127 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 128 Unconflicted Mode(FILE) 0839b2e9412b ctime=0:0 mtime=0:0 size=0 flags=0 file1.txt 129 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 file2.txt 130 "); 131 132 // If we remove the added file, it's removed from the index 133 work_dir.remove_file("file2.txt"); 134 work_dir.run_jj(["status"]).success(); 135 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 136 Unconflicted Mode(FILE) 0839b2e9412b ctime=0:0 mtime=0:0 size=0 flags=0 file1.txt 137 "); 138} 139 140#[test] 141fn test_git_colocated_unborn_bookmark() { 142 let test_env = TestEnvironment::default(); 143 let work_dir = test_env.work_dir("repo"); 144 let git_repo = git::init(work_dir.root()); 145 146 // add a file to an (in memory) index 147 let add_file_to_index = |name: &str, data: &str| { 148 let mut index_manager = git::IndexManager::new(&git_repo); 149 index_manager.add_file(name, data.as_bytes()); 150 index_manager.sync_index(); 151 }; 152 153 // checkout index (i.e., drop the in-memory changes) 154 let checkout_index = || { 155 let mut index = git_repo.open_index().unwrap(); 156 let objects = git_repo.objects.clone(); 157 gix::worktree::state::checkout( 158 &mut index, 159 git_repo.work_dir().unwrap(), 160 objects, 161 &gix::progress::Discard, 162 &gix::progress::Discard, 163 &gix::interrupt::IS_INTERRUPTED, 164 gix::worktree::state::checkout::Options::default(), 165 ) 166 .unwrap(); 167 }; 168 169 // Initially, HEAD isn't set. 170 work_dir 171 .run_jj(["git", "init", "--git-repo", "."]) 172 .success(); 173 assert!(git_repo.head().unwrap().is_unborn()); 174 assert_eq!( 175 git_repo.head_name().unwrap().unwrap().as_bstr(), 176 b"refs/heads/master" 177 ); 178 insta::assert_snapshot!(get_log_output(&work_dir), @r" 179 @ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 180 ◆ 0000000000000000000000000000000000000000 181 [EOF] 182 "); 183 184 // Stage some change, and check out root. This shouldn't clobber the HEAD. 185 add_file_to_index("file0", ""); 186 let output = work_dir.run_jj(["new", "root()"]); 187 insta::assert_snapshot!(output, @r" 188 ------- stderr ------- 189 Working copy (@) now at: kkmpptxz fcdbbd73 (empty) (no description set) 190 Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set) 191 Added 0 files, modified 0 files, removed 1 files 192 [EOF] 193 "); 194 assert!(git_repo.head().unwrap().is_unborn()); 195 assert_eq!( 196 git_repo.head_name().unwrap().unwrap().as_bstr(), 197 b"refs/heads/master" 198 ); 199 insta::assert_snapshot!(get_log_output(&work_dir), @r" 200 @ fcdbbd731496cae17161cd6be9b6cf1f759655a8 201 │ ○ 993600f1189571af5bbeb492cf657dc7d0fde48a 202 ├─╯ 203 ◆ 0000000000000000000000000000000000000000 204 [EOF] 205 "); 206 // Staged change shouldn't persist. 207 checkout_index(); 208 insta::assert_snapshot!(work_dir.run_jj(["status"]), @r" 209 The working copy has no changes. 210 Working copy (@) : kkmpptxz fcdbbd73 (empty) (no description set) 211 Parent commit (@-): zzzzzzzz 00000000 (empty) (no description set) 212 [EOF] 213 "); 214 215 // Stage some change, and create new HEAD. This shouldn't move the default 216 // bookmark. 217 add_file_to_index("file1", ""); 218 let output = work_dir.run_jj(["new"]); 219 insta::assert_snapshot!(output, @r" 220 ------- stderr ------- 221 Working copy (@) now at: royxmykx 0e146103 (empty) (no description set) 222 Parent commit (@-) : kkmpptxz e3e01407 (no description set) 223 [EOF] 224 "); 225 assert!(git_repo.head().unwrap().is_detached()); 226 insta::assert_snapshot!( 227 git_repo.head_id().unwrap().to_string(), 228 @"e3e01407bd3539722ae4ffff077700d97c60cb11" 229 ); 230 insta::assert_snapshot!(get_log_output(&work_dir), @r" 231 @ 0e14610343ef50775f5c44db5aeef19aee45d9ad 232 ○ e3e01407bd3539722ae4ffff077700d97c60cb11 git_head() 233 │ ○ 993600f1189571af5bbeb492cf657dc7d0fde48a 234 ├─╯ 235 ◆ 0000000000000000000000000000000000000000 236 [EOF] 237 "); 238 // Staged change shouldn't persist. 239 checkout_index(); 240 insta::assert_snapshot!(work_dir.run_jj(["status"]), @r" 241 The working copy has no changes. 242 Working copy (@) : royxmykx 0e146103 (empty) (no description set) 243 Parent commit (@-): kkmpptxz e3e01407 (no description set) 244 [EOF] 245 "); 246 247 // Assign the default bookmark. The bookmark is no longer "unborn". 248 work_dir 249 .run_jj(["bookmark", "create", "-r@-", "master"]) 250 .success(); 251 252 // Stage some change, and check out root again. This should unset the HEAD. 253 // https://github.com/jj-vcs/jj/issues/1495 254 add_file_to_index("file2", ""); 255 let output = work_dir.run_jj(["new", "root()"]); 256 insta::assert_snapshot!(output, @r" 257 ------- stderr ------- 258 Working copy (@) now at: znkkpsqq 10dd328b (empty) (no description set) 259 Parent commit (@-) : zzzzzzzz 00000000 (empty) (no description set) 260 Added 0 files, modified 0 files, removed 2 files 261 [EOF] 262 "); 263 assert!(git_repo.head().unwrap().is_unborn()); 264 insta::assert_snapshot!(get_log_output(&work_dir), @r" 265 @ 10dd328bb906e15890e55047740eab2812a3b2f7 266 │ ○ ef75c0b0dcc9b080e00226908c21316acaa84dc6 267 │ ○ e3e01407bd3539722ae4ffff077700d97c60cb11 master 268 ├─╯ 269 │ ○ 993600f1189571af5bbeb492cf657dc7d0fde48a 270 ├─╯ 271 ◆ 0000000000000000000000000000000000000000 272 [EOF] 273 "); 274 // Staged change shouldn't persist. 275 checkout_index(); 276 insta::assert_snapshot!(work_dir.run_jj(["status"]), @r" 277 The working copy has no changes. 278 Working copy (@) : znkkpsqq 10dd328b (empty) (no description set) 279 Parent commit (@-): zzzzzzzz 00000000 (empty) (no description set) 280 [EOF] 281 "); 282 283 // New snapshot and commit can be created after the HEAD got unset. 284 work_dir.write_file("file3", ""); 285 let output = work_dir.run_jj(["new"]); 286 insta::assert_snapshot!(output, @r" 287 ------- stderr ------- 288 Working copy (@) now at: wqnwkozp 101e2723 (empty) (no description set) 289 Parent commit (@-) : znkkpsqq fc8af934 (no description set) 290 [EOF] 291 "); 292 insta::assert_snapshot!(get_log_output(&work_dir), @r" 293 @ 101e272377a9daff75358f10dbd078df922fe68c 294 ○ fc8af9345b0830dcb14716e04cd2af26e2d19f63 git_head() 295 │ ○ ef75c0b0dcc9b080e00226908c21316acaa84dc6 296 │ ○ e3e01407bd3539722ae4ffff077700d97c60cb11 master 297 ├─╯ 298 │ ○ 993600f1189571af5bbeb492cf657dc7d0fde48a 299 ├─╯ 300 ◆ 0000000000000000000000000000000000000000 301 [EOF] 302 "); 303} 304 305#[test] 306fn test_git_colocated_export_bookmarks_on_snapshot() { 307 // Checks that we export bookmarks that were changed only because the working 308 // copy was snapshotted 309 310 let test_env = TestEnvironment::default(); 311 let work_dir = test_env.work_dir("repo"); 312 let git_repo = git::init(work_dir.root()); 313 work_dir 314 .run_jj(["git", "init", "--git-repo", "."]) 315 .success(); 316 317 // Create bookmark pointing to the initial commit 318 work_dir.write_file("file", "initial"); 319 work_dir 320 .run_jj(["bookmark", "create", "-r@", "foo"]) 321 .success(); 322 insta::assert_snapshot!(get_log_output(&work_dir), @r" 323 @ b15ef4cdd277d2c63cce6d67c1916f53a36141f7 foo 324 ◆ 0000000000000000000000000000000000000000 325 [EOF] 326 "); 327 328 // The bookmark gets updated when we modify the working copy, and it should get 329 // exported to Git without requiring any other changes 330 work_dir.write_file("file", "modified"); 331 insta::assert_snapshot!(get_log_output(&work_dir), @r" 332 @ 4d2c49a8f8e2f1ba61f48ba79e5f4a5faa6512cf foo 333 ◆ 0000000000000000000000000000000000000000 334 [EOF] 335 "); 336 insta::assert_snapshot!(git_repo 337 .find_reference("refs/heads/foo") 338 .unwrap() 339 .id() 340 .to_string(), @"4d2c49a8f8e2f1ba61f48ba79e5f4a5faa6512cf"); 341} 342 343#[test] 344fn test_git_colocated_rebase_on_import() { 345 let test_env = TestEnvironment::default(); 346 let work_dir = test_env.work_dir("repo"); 347 let git_repo = git::init(work_dir.root()); 348 work_dir 349 .run_jj(["git", "init", "--git-repo", "."]) 350 .success(); 351 352 // Make some changes in jj and check that they're reflected in git 353 work_dir.write_file("file", "contents"); 354 work_dir.run_jj(["commit", "-m", "add a file"]).success(); 355 work_dir.write_file("file", "modified"); 356 work_dir 357 .run_jj(["bookmark", "create", "-r@", "master"]) 358 .success(); 359 work_dir.run_jj(["commit", "-m", "modify a file"]).success(); 360 // TODO: We shouldn't need this command here to trigger an import of the 361 // refs/heads/master we just exported 362 work_dir.run_jj(["st"]).success(); 363 364 // Move `master` backwards, which should result in commit2 getting hidden, 365 // and the working-copy commit rebased. 366 let parent_commit = git_repo 367 .find_reference("refs/heads/master") 368 .unwrap() 369 .peel_to_commit() 370 .unwrap() 371 .parent_ids() 372 .next() 373 .unwrap() 374 .detach(); 375 git_repo 376 .reference( 377 "refs/heads/master", 378 parent_commit, 379 gix::refs::transaction::PreviousValue::Any, 380 "update ref", 381 ) 382 .unwrap(); 383 insta::assert_snapshot!(get_log_output(&work_dir), @r" 384 @ 15b1d70c5e33b5d2b18383292b85324d5153ffed 385 ○ 47fe984daf66f7bf3ebf31b9cb3513c995afb857 master git_head() add a file 386 ◆ 0000000000000000000000000000000000000000 387 [EOF] 388 ------- stderr ------- 389 Abandoned 1 commits that are no longer reachable. 390 Rebased 1 descendant commits off of commits rewritten from git 391 Working copy (@) now at: zsuskuln 15b1d70c (empty) (no description set) 392 Parent commit (@-) : qpvuntsm 47fe984d master | add a file 393 Added 0 files, modified 1 files, removed 0 files 394 Done importing changes from the underlying Git repo. 395 [EOF] 396 "); 397} 398 399#[test] 400fn test_git_colocated_bookmarks() { 401 let test_env = TestEnvironment::default(); 402 let work_dir = test_env.work_dir("repo"); 403 let git_repo = git::init(work_dir.root()); 404 work_dir 405 .run_jj(["git", "init", "--git-repo", "."]) 406 .success(); 407 work_dir.run_jj(["new", "-m", "foo"]).success(); 408 work_dir.run_jj(["new", "@-", "-m", "bar"]).success(); 409 insta::assert_snapshot!(get_log_output(&work_dir), @r" 410 @ 3560559274ab431feea00b7b7e0b9250ecce951f bar 411 │ ○ 1e6f0b403ed2ff9713b5d6b1dc601e4804250cda foo 412 ├─╯ 413 ○ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 git_head() 414 ◆ 0000000000000000000000000000000000000000 415 [EOF] 416 "); 417 418 // Create a bookmark in jj. It should be exported to Git even though it points 419 // to the working- copy commit. 420 work_dir 421 .run_jj(["bookmark", "create", "-r@", "master"]) 422 .success(); 423 insta::assert_snapshot!( 424 git_repo.find_reference("refs/heads/master").unwrap().target().id().to_string(), 425 @"3560559274ab431feea00b7b7e0b9250ecce951f" 426 ); 427 assert!(git_repo.head().unwrap().is_detached()); 428 insta::assert_snapshot!( 429 git_repo.head_id().unwrap().to_string(), 430 @"230dd059e1b059aefc0da06a2e5a7dbf22362f22" 431 ); 432 433 // Update the bookmark in Git 434 let target_id = work_dir 435 .run_jj(["log", "--no-graph", "-T=commit_id", "-r=description(foo)"]) 436 .success() 437 .stdout 438 .into_raw(); 439 git_repo 440 .reference( 441 "refs/heads/master", 442 gix::ObjectId::from_hex(target_id.as_bytes()).unwrap(), 443 gix::refs::transaction::PreviousValue::Any, 444 "test", 445 ) 446 .unwrap(); 447 insta::assert_snapshot!(get_log_output(&work_dir), @r" 448 @ 096dc80da67094fbaa6683e2a205dddffa31f9a8 449 │ ○ 1e6f0b403ed2ff9713b5d6b1dc601e4804250cda master foo 450 ├─╯ 451 ○ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 git_head() 452 ◆ 0000000000000000000000000000000000000000 453 [EOF] 454 ------- stderr ------- 455 Abandoned 1 commits that are no longer reachable. 456 Working copy (@) now at: yqosqzyt 096dc80d (empty) (no description set) 457 Parent commit (@-) : qpvuntsm 230dd059 (empty) (no description set) 458 Done importing changes from the underlying Git repo. 459 [EOF] 460 "); 461} 462 463#[test] 464fn test_git_colocated_bookmark_forget() { 465 let test_env = TestEnvironment::default(); 466 let work_dir = test_env.work_dir("repo"); 467 git::init(work_dir.root()); 468 work_dir 469 .run_jj(["git", "init", "--git-repo", "."]) 470 .success(); 471 work_dir.run_jj(["new"]).success(); 472 work_dir 473 .run_jj(["bookmark", "create", "-r@", "foo"]) 474 .success(); 475 insta::assert_snapshot!(get_log_output(&work_dir), @r" 476 @ 65b6b74e08973b88d38404430f119c8c79465250 foo 477 ○ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 git_head() 478 ◆ 0000000000000000000000000000000000000000 479 [EOF] 480 "); 481 insta::assert_snapshot!(get_bookmark_output(&work_dir), @r" 482 foo: rlvkpnrz 65b6b74e (empty) (no description set) 483 @git: rlvkpnrz 65b6b74e (empty) (no description set) 484 [EOF] 485 "); 486 487 let output = work_dir.run_jj(["bookmark", "forget", "--include-remotes", "foo"]); 488 insta::assert_snapshot!(output, @r" 489 ------- stderr ------- 490 Forgot 1 local bookmarks. 491 Forgot 1 remote bookmarks. 492 [EOF] 493 "); 494 // A forgotten bookmark is deleted in the git repo. For a detailed demo 495 // explaining this, see `test_bookmark_forget_export` in 496 // `test_bookmark_command.rs`. 497 insta::assert_snapshot!(get_bookmark_output(&work_dir), @""); 498} 499 500#[test] 501fn test_git_colocated_bookmark_at_root() { 502 let test_env = TestEnvironment::default(); 503 test_env 504 .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 505 .success(); 506 let work_dir = test_env.work_dir("repo"); 507 508 let output = work_dir.run_jj(["bookmark", "create", "foo", "-r=root()"]); 509 insta::assert_snapshot!(output, @r" 510 ------- stderr ------- 511 Created 1 bookmarks pointing to zzzzzzzz 00000000 foo | (empty) (no description set) 512 Warning: Failed to export some bookmarks: 513 foo@git: Ref cannot point to the root commit in Git 514 [EOF] 515 "); 516 517 let output = work_dir.run_jj(["bookmark", "move", "foo", "--to=@"]); 518 insta::assert_snapshot!(output, @r" 519 ------- stderr ------- 520 Moved 1 bookmarks to qpvuntsm 230dd059 foo | (empty) (no description set) 521 [EOF] 522 "); 523 524 let output = work_dir.run_jj([ 525 "bookmark", 526 "move", 527 "foo", 528 "--allow-backwards", 529 "--to=root()", 530 ]); 531 insta::assert_snapshot!(output, @r" 532 ------- stderr ------- 533 Moved 1 bookmarks to zzzzzzzz 00000000 foo* | (empty) (no description set) 534 Warning: Failed to export some bookmarks: 535 foo@git: Ref cannot point to the root commit in Git 536 [EOF] 537 "); 538} 539 540#[test] 541fn test_git_colocated_conflicting_git_refs() { 542 let test_env = TestEnvironment::default(); 543 let work_dir = test_env.work_dir("repo"); 544 git::init(work_dir.root()); 545 work_dir 546 .run_jj(["git", "init", "--git-repo", "."]) 547 .success(); 548 work_dir 549 .run_jj(["bookmark", "create", "-r@", "main"]) 550 .success(); 551 let output = work_dir.run_jj(["bookmark", "create", "-r@", "main/sub"]); 552 insta::with_settings!({filters => vec![("Failed to set: .*", "Failed to set: ...")]}, { 553 insta::assert_snapshot!(output, @r#" 554 ------- stderr ------- 555 Created 1 bookmarks pointing to qpvuntsm 230dd059 main main/sub | (empty) (no description set) 556 Warning: Failed to export some bookmarks: 557 main/sub@git: Failed to set: ... 558 Hint: Git doesn't allow a branch name that looks like a parent directory of 559 another (e.g. `foo` and `foo/bar`). Try to rename the bookmarks that failed to 560 export or their "parent" bookmarks. 561 [EOF] 562 "#); 563 }); 564} 565 566#[test] 567fn test_git_colocated_checkout_non_empty_working_copy() { 568 let test_env = TestEnvironment::default(); 569 let work_dir = test_env.work_dir("repo"); 570 let git_repo = git::init(work_dir.root()); 571 work_dir 572 .run_jj(["git", "init", "--git-repo", "."]) 573 .success(); 574 575 // Create an initial commit in Git 576 // We use this to set HEAD to master 577 let tree_id = git::add_commit( 578 &git_repo, 579 "refs/heads/master", 580 "file", 581 b"contents", 582 "initial", 583 &[], 584 ) 585 .tree_id; 586 git::checkout_tree_index(&git_repo, tree_id); 587 assert_eq!(work_dir.read_file("file"), b"contents"); 588 insta::assert_snapshot!( 589 git_repo.head_id().unwrap().to_string(), 590 @"97358f54806c7cd005ed5ade68a779595efbae7e" 591 ); 592 593 work_dir.write_file("two", "y"); 594 595 work_dir.run_jj(["describe", "-m", "two"]).success(); 596 work_dir.run_jj(["new", "@-"]).success(); 597 let output = work_dir.run_jj(["describe", "-m", "new"]); 598 insta::assert_snapshot!(output, @r" 599 ------- stderr ------- 600 Working copy (@) now at: kkmpptxz acea3383 (empty) new 601 Parent commit (@-) : slsumksp 97358f54 master | initial 602 [EOF] 603 "); 604 605 assert_eq!( 606 git_repo.head_name().unwrap().unwrap().as_bstr(), 607 b"refs/heads/master" 608 ); 609 610 insta::assert_snapshot!(get_log_output(&work_dir), @r" 611 @ acea3383e7a9e4e0df035ee0e83d04cac44a3a14 new 612 │ ○ a99003c2d01be21a82b33c0946c30c596e900287 two 613 ├─╯ 614 ○ 97358f54806c7cd005ed5ade68a779595efbae7e master git_head() initial 615 ◆ 0000000000000000000000000000000000000000 616 [EOF] 617 "); 618} 619 620#[test] 621fn test_git_colocated_fetch_deleted_or_moved_bookmark() { 622 let test_env = TestEnvironment::default(); 623 test_env.add_config("git.auto-local-bookmark = true"); 624 let origin_dir = test_env.work_dir("origin"); 625 git::init(origin_dir.root()); 626 origin_dir.run_jj(["git", "init", "--git-repo=."]).success(); 627 origin_dir.run_jj(["describe", "-m=A"]).success(); 628 origin_dir 629 .run_jj(["bookmark", "create", "-r@", "A"]) 630 .success(); 631 origin_dir.run_jj(["new", "-m=B_to_delete"]).success(); 632 origin_dir 633 .run_jj(["bookmark", "create", "-r@", "B_to_delete"]) 634 .success(); 635 origin_dir.run_jj(["new", "-m=original C", "@-"]).success(); 636 origin_dir 637 .run_jj(["bookmark", "create", "-r@", "C_to_move"]) 638 .success(); 639 640 let clone_dir = test_env.work_dir("clone"); 641 git::clone(clone_dir.root(), origin_dir.root().to_str().unwrap(), None); 642 clone_dir.run_jj(["git", "init", "--git-repo=."]).success(); 643 clone_dir.run_jj(["new", "A"]).success(); 644 insta::assert_snapshot!(get_log_output(&clone_dir), @r" 645 @ 9c2de797c3c299a40173c5af724329012b77cbdd 646 │ ○ 4a191a9013d3f3398ccf5e172792a61439dbcf3a C_to_move original C 647 ├─╯ 648 │ ○ c49ec4fb50844d0e693f1609da970b11878772ee B_to_delete B_to_delete 649 ├─╯ 650 ◆ a7e4cec4256b7995129b9d1e1bda7e1df6e60678 A git_head() A 651 ◆ 0000000000000000000000000000000000000000 652 [EOF] 653 "); 654 655 origin_dir 656 .run_jj(["bookmark", "delete", "B_to_delete"]) 657 .success(); 658 // Move bookmark C sideways 659 origin_dir 660 .run_jj(["describe", "C_to_move", "-m", "moved C"]) 661 .success(); 662 let output = clone_dir.run_jj(["git", "fetch"]); 663 insta::assert_snapshot!(output, @r" 664 ------- stderr ------- 665 bookmark: B_to_delete@origin [deleted] untracked 666 bookmark: C_to_move@origin [updated] tracked 667 Abandoned 2 commits that are no longer reachable. 668 [EOF] 669 "); 670 // "original C" and "B_to_delete" are abandoned, as the corresponding bookmarks 671 // were deleted or moved on the remote (#864) 672 insta::assert_snapshot!(get_log_output(&clone_dir), @r" 673 @ 9c2de797c3c299a40173c5af724329012b77cbdd 674 │ ○ 4f3d13296f978cbc351c46a43b4619c91b888475 C_to_move moved C 675 ├─╯ 676 ◆ a7e4cec4256b7995129b9d1e1bda7e1df6e60678 A git_head() A 677 ◆ 0000000000000000000000000000000000000000 678 [EOF] 679 "); 680} 681 682#[test] 683fn test_git_colocated_rebase_dirty_working_copy() { 684 let test_env = TestEnvironment::default(); 685 let work_dir = test_env.work_dir("repo"); 686 let git_repo = git::init(work_dir.root()); 687 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 688 689 work_dir.write_file("file", "base"); 690 work_dir.run_jj(["new"]).success(); 691 work_dir.write_file("file", "old"); 692 work_dir 693 .run_jj(["bookmark", "create", "-r@", "feature"]) 694 .success(); 695 696 // Make the working-copy dirty, delete the checked out bookmark. 697 work_dir.write_file("file", "new"); 698 git_repo 699 .find_reference("refs/heads/feature") 700 .unwrap() 701 .delete() 702 .unwrap(); 703 704 // Because the working copy is dirty, the new working-copy commit will be 705 // diverged. Therefore, the feature bookmark has change-delete conflict. 706 let output = work_dir.run_jj(["status"]); 707 insta::assert_snapshot!(output, @r" 708 Working copy changes: 709 M file 710 Working copy (@) : rlvkpnrz 6bad94b1 feature?? | (no description set) 711 Parent commit (@-): qpvuntsm 3230d522 (no description set) 712 Warning: These bookmarks have conflicts: 713 feature 714 Hint: Use `jj bookmark list` to see details. Use `jj bookmark set <name> -r <rev>` to resolve. 715 [EOF] 716 ------- stderr ------- 717 Warning: Failed to export some bookmarks: 718 feature@git: Modified ref had been deleted in Git 719 Done importing changes from the underlying Git repo. 720 [EOF] 721 "); 722 insta::assert_snapshot!(get_log_output(&work_dir), @r" 723 @ 6bad94b10401f5fafc8a91064661224650d10d1b feature?? 724 ○ 3230d52258f6de7e9afbd10da8d64503cc7cdca5 git_head() 725 ◆ 0000000000000000000000000000000000000000 726 [EOF] 727 "); 728 729 // The working-copy content shouldn't be lost. 730 insta::assert_snapshot!(work_dir.read_file("file"), @"new"); 731} 732 733#[test] 734fn test_git_colocated_external_checkout() { 735 let test_env = TestEnvironment::default(); 736 let work_dir = test_env.work_dir("repo"); 737 let git_repo = git::init(work_dir.root()); 738 let git_check_out_ref = |name| { 739 let target = git_repo 740 .find_reference(name) 741 .unwrap() 742 .into_fully_peeled_id() 743 .unwrap() 744 .detach(); 745 git::set_head_to_id(&git_repo, target); 746 }; 747 748 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 749 work_dir.run_jj(["ci", "-m=A"]).success(); 750 work_dir 751 .run_jj(["bookmark", "create", "-r@-", "master"]) 752 .success(); 753 work_dir.run_jj(["new", "-m=B", "root()"]).success(); 754 work_dir.run_jj(["new"]).success(); 755 756 // Checked out anonymous bookmark 757 insta::assert_snapshot!(get_log_output(&work_dir), @r" 758 @ f8a23336e41840ed1757ef323402a770427dc89a 759 ○ eccedddfa5152d99fc8ddd1081b375387a8a382a git_head() B 760 │ ○ a7e4cec4256b7995129b9d1e1bda7e1df6e60678 master A 761 ├─╯ 762 ◆ 0000000000000000000000000000000000000000 763 [EOF] 764 "); 765 766 // Check out another bookmark by external command 767 git_check_out_ref("refs/heads/master"); 768 769 // The old working-copy commit gets abandoned, but the whole bookmark should not 770 // be abandoned. (#1042) 771 insta::assert_snapshot!(get_log_output(&work_dir), @r" 772 @ 8bb9e8d42a37c2a4e8dcfad97fce0b8f49bc7afa 773 ○ a7e4cec4256b7995129b9d1e1bda7e1df6e60678 master git_head() A 774 │ ○ eccedddfa5152d99fc8ddd1081b375387a8a382a B 775 ├─╯ 776 ◆ 0000000000000000000000000000000000000000 777 [EOF] 778 ------- stderr ------- 779 Reset the working copy parent to the new Git HEAD. 780 [EOF] 781 "); 782 783 // Edit non-head commit 784 work_dir.run_jj(["new", "description(B)"]).success(); 785 work_dir.run_jj(["new", "-m=C", "--no-edit"]).success(); 786 insta::assert_snapshot!(get_log_output(&work_dir), @r" 787 ○ 99a813753d6db988d8fc436b0d6b30a54d6b2707 C 788 @ 81e086b7f9b1dd7fde252e28bdcf4ba4abd86ce5 789 ○ eccedddfa5152d99fc8ddd1081b375387a8a382a git_head() B 790 │ ○ a7e4cec4256b7995129b9d1e1bda7e1df6e60678 master A 791 ├─╯ 792 ◆ 0000000000000000000000000000000000000000 793 [EOF] 794 "); 795 796 // Check out another bookmark by external command 797 git_check_out_ref("refs/heads/master"); 798 799 // The old working-copy commit shouldn't be abandoned. (#3747) 800 insta::assert_snapshot!(get_log_output(&work_dir), @r" 801 @ ca2a4e32f08688c6fb795c4c034a0a7e09c0d804 802 ○ a7e4cec4256b7995129b9d1e1bda7e1df6e60678 master git_head() A 803 │ ○ 99a813753d6db988d8fc436b0d6b30a54d6b2707 C 804 │ ○ 81e086b7f9b1dd7fde252e28bdcf4ba4abd86ce5 805 │ ○ eccedddfa5152d99fc8ddd1081b375387a8a382a B 806 ├─╯ 807 ◆ 0000000000000000000000000000000000000000 808 [EOF] 809 ------- stderr ------- 810 Reset the working copy parent to the new Git HEAD. 811 [EOF] 812 "); 813} 814 815#[test] 816#[cfg_attr(windows, ignore = "uses POSIX sh")] 817fn test_git_colocated_concurrent_checkout() { 818 let test_env = TestEnvironment::default(); 819 test_env 820 .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 821 .success(); 822 let work_dir = test_env.work_dir("repo"); 823 824 work_dir.run_jj(["new", "-mcommit1"]).success(); 825 work_dir.write_file("file1", ""); 826 work_dir.run_jj(["new", "-mcommit2"]).success(); 827 work_dir.write_file("file2", ""); 828 work_dir.run_jj(["new", "-mcommit3"]).success(); 829 830 // Run "jj commit" and "git checkout" concurrently 831 let output = work_dir.run_jj([ 832 "commit", 833 "--config=ui.editor=['sh', '-c', 'git checkout -q HEAD^']", 834 ]); 835 insta::assert_snapshot!(output, @r#" 836 ------- stderr ------- 837 Warning: Failed to update Git HEAD ref 838 Caused by: The reference "HEAD" should have content 58a6206c70b53dfc30dc2f8c9e3713034cfc323e, actual content was 363a08cf5e683485227336e24a006e0deac341bc 839 Working copy (@) now at: mzvwutvl 6b3bc9c8 (empty) (no description set) 840 Parent commit (@-) : zsuskuln 7d358222 (empty) commit3 841 [EOF] 842 "#); 843 844 // git_head() isn't updated because the export failed 845 insta::assert_snapshot!(work_dir.run_jj(["log", "--summary", "--ignore-working-copy"]), @r" 846 @ mzvwutvl test.user@example.com 2001-02-03 08:05:11 6b3bc9c8 847 │ (empty) (no description set) 848 ○ zsuskuln test.user@example.com 2001-02-03 08:05:11 7d358222 849 │ (empty) commit3 850 ○ kkmpptxz test.user@example.com 2001-02-03 08:05:10 git_head() 58a6206c 851 │ commit2 852 │ A file2 853 ○ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 363a08cf 854 │ commit1 855 │ A file1 856 ○ qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059 857 │ (empty) (no description set) 858 ◆ zzzzzzzz root() 00000000 859 [EOF] 860 "); 861 862 // The current Git HEAD is imported on the next jj invocation 863 insta::assert_snapshot!(work_dir.run_jj(["log", "--summary"]), @r" 864 @ yqosqzyt test.user@example.com 2001-02-03 08:05:13 690bd924 865 │ (empty) (no description set) 866 │ ○ zsuskuln test.user@example.com 2001-02-03 08:05:11 7d358222 867 │ │ (empty) commit3 868 │ ○ kkmpptxz test.user@example.com 2001-02-03 08:05:10 58a6206c 869 ├─╯ commit2 870 │ A file2 871 ○ rlvkpnrz test.user@example.com 2001-02-03 08:05:09 git_head() 363a08cf 872 │ commit1 873 │ A file1 874 ○ qpvuntsm test.user@example.com 2001-02-03 08:05:07 230dd059 875 │ (empty) (no description set) 876 ◆ zzzzzzzz root() 00000000 877 [EOF] 878 ------- stderr ------- 879 Reset the working copy parent to the new Git HEAD. 880 [EOF] 881 "); 882} 883 884#[test] 885fn test_git_colocated_squash_undo() { 886 let test_env = TestEnvironment::default(); 887 let work_dir = test_env.work_dir("repo"); 888 git::init(work_dir.root()); 889 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 890 work_dir.run_jj(["ci", "-m=A"]).success(); 891 // Test the setup 892 insta::assert_snapshot!(get_log_output_divergence(&work_dir), @r" 893 @ rlvkpnrzqnoo 9670380ac379 894 ○ qpvuntsmwlqt a7e4cec4256b A git_head() 895 ◆ zzzzzzzzzzzz 000000000000 896 [EOF] 897 "); 898 899 work_dir.run_jj(["squash"]).success(); 900 insta::assert_snapshot!(get_log_output_divergence(&work_dir), @r" 901 @ zsuskulnrvyr 6ee662324e5a 902 ○ qpvuntsmwlqt 13ab6b96d82e A git_head() 903 ◆ zzzzzzzzzzzz 000000000000 904 [EOF] 905 "); 906 work_dir.run_jj(["undo"]).success(); 907 // TODO: There should be no divergence here; 2f376ea1478c should be hidden 908 // (#922) 909 insta::assert_snapshot!(get_log_output_divergence(&work_dir), @r" 910 @ rlvkpnrzqnoo 9670380ac379 911 ○ qpvuntsmwlqt a7e4cec4256b A git_head() 912 ◆ zzzzzzzzzzzz 000000000000 913 [EOF] 914 "); 915} 916 917#[test] 918fn test_git_colocated_undo_head_move() { 919 let test_env = TestEnvironment::default(); 920 let work_dir = test_env.work_dir("repo"); 921 let git_repo = git::init(work_dir.root()); 922 work_dir.run_jj(["git", "init", "--git-repo=."]).success(); 923 924 // Create new HEAD 925 work_dir.run_jj(["new"]).success(); 926 assert!(git_repo.head().unwrap().is_detached()); 927 insta::assert_snapshot!( 928 git_repo.head_id().unwrap().to_string(), 929 @"230dd059e1b059aefc0da06a2e5a7dbf22362f22"); 930 insta::assert_snapshot!(get_log_output(&work_dir), @r" 931 @ 65b6b74e08973b88d38404430f119c8c79465250 932 ○ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 git_head() 933 ◆ 0000000000000000000000000000000000000000 934 [EOF] 935 "); 936 937 // HEAD should be unset 938 work_dir.run_jj(["undo"]).success(); 939 assert!(git_repo.head().unwrap().is_unborn()); 940 insta::assert_snapshot!(get_log_output(&work_dir), @r" 941 @ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 942 ◆ 0000000000000000000000000000000000000000 943 [EOF] 944 "); 945 946 // Create commit on non-root commit 947 work_dir.run_jj(["new"]).success(); 948 work_dir.run_jj(["new"]).success(); 949 insta::assert_snapshot!(get_log_output(&work_dir), @r" 950 @ 69b19f73cf584f162f078fb0d91c55ca39d10bc7 951 ○ eb08b363bb5ef8ee549314260488980d7bbe8f63 git_head() 952 ○ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 953 ◆ 0000000000000000000000000000000000000000 954 [EOF] 955 "); 956 assert!(git_repo.head().unwrap().is_detached()); 957 insta::assert_snapshot!( 958 git_repo.head_id().unwrap().to_string(), 959 @"eb08b363bb5ef8ee549314260488980d7bbe8f63"); 960 961 // HEAD should be moved back 962 let output = work_dir.run_jj(["undo"]); 963 insta::assert_snapshot!(output, @r" 964 ------- stderr ------- 965 Undid operation: b50ec983d1c1 (2001-02-03 08:05:13) new empty commit 966 Working copy (@) now at: royxmykx eb08b363 (empty) (no description set) 967 Parent commit (@-) : qpvuntsm 230dd059 (empty) (no description set) 968 [EOF] 969 "); 970 assert!(git_repo.head().unwrap().is_detached()); 971 insta::assert_snapshot!( 972 git_repo.head_id().unwrap().to_string(), 973 @"230dd059e1b059aefc0da06a2e5a7dbf22362f22"); 974 insta::assert_snapshot!(get_log_output(&work_dir), @r" 975 @ eb08b363bb5ef8ee549314260488980d7bbe8f63 976 ○ 230dd059e1b059aefc0da06a2e5a7dbf22362f22 git_head() 977 ◆ 0000000000000000000000000000000000000000 978 [EOF] 979 "); 980} 981 982#[test] 983fn test_git_colocated_update_index_preserves_timestamps() { 984 let test_env = TestEnvironment::default(); 985 test_env 986 .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 987 .success(); 988 let work_dir = test_env.work_dir("repo"); 989 990 // Create a commit with some files 991 work_dir.write_file("file1.txt", "will be unchanged\n"); 992 work_dir.write_file("file2.txt", "will be modified\n"); 993 work_dir.write_file("file3.txt", "will be deleted\n"); 994 work_dir 995 .run_jj(["bookmark", "create", "-r@", "commit1"]) 996 .success(); 997 work_dir.run_jj(["new"]).success(); 998 999 // Create a commit with some changes to the files 1000 work_dir.write_file("file2.txt", "modified\n"); 1001 work_dir.remove_file("file3.txt"); 1002 work_dir.write_file("file4.txt", "added\n"); 1003 work_dir 1004 .run_jj(["bookmark", "create", "-r@", "commit2"]) 1005 .success(); 1006 work_dir.run_jj(["new"]).success(); 1007 1008 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1009 @ 051508d190ffd04fe2d79367ad8e9c3713ac2375 1010 ○ 563dbc583c0d82eb10c40d3f3276183ea28a0fa7 commit2 git_head() 1011 ○ 3c270b473dd871b20d196316eb038f078f80c219 commit1 1012 ◆ 0000000000000000000000000000000000000000 1013 [EOF] 1014 "); 1015 1016 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1017 Unconflicted Mode(FILE) ed48318d9bf4 ctime=0:0 mtime=0:0 size=0 flags=0 file1.txt 1018 Unconflicted Mode(FILE) 2e0996000b7e ctime=0:0 mtime=0:0 size=0 flags=0 file2.txt 1019 Unconflicted Mode(FILE) d5f7fc3f74f7 ctime=0:0 mtime=0:0 size=0 flags=0 file4.txt 1020 "); 1021 1022 // Update index with stats for all files. We may want to do this automatically 1023 // in the future after we update the index in `git::reset_head` (#3786), but for 1024 // now, we at least want to preserve existing stat information when possible. 1025 update_git_index(work_dir.root()); 1026 1027 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1028 Unconflicted Mode(FILE) ed48318d9bf4 ctime=[nonzero] mtime=[nonzero] size=18 flags=0 file1.txt 1029 Unconflicted Mode(FILE) 2e0996000b7e ctime=[nonzero] mtime=[nonzero] size=9 flags=0 file2.txt 1030 Unconflicted Mode(FILE) d5f7fc3f74f7 ctime=[nonzero] mtime=[nonzero] size=6 flags=0 file4.txt 1031 "); 1032 1033 // Edit parent commit, causing the changes to be removed from the index without 1034 // touching the working copy 1035 work_dir.run_jj(["edit", "commit2"]).success(); 1036 1037 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1038 @ 563dbc583c0d82eb10c40d3f3276183ea28a0fa7 commit2 1039 ○ 3c270b473dd871b20d196316eb038f078f80c219 commit1 git_head() 1040 ◆ 0000000000000000000000000000000000000000 1041 [EOF] 1042 "); 1043 1044 // Index should contain stat for unchanged file still. 1045 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1046 Unconflicted Mode(FILE) ed48318d9bf4 ctime=[nonzero] mtime=[nonzero] size=18 flags=0 file1.txt 1047 Unconflicted Mode(FILE) 28d2718c947b ctime=0:0 mtime=0:0 size=0 flags=0 file2.txt 1048 Unconflicted Mode(FILE) 528557ab3a42 ctime=0:0 mtime=0:0 size=0 flags=0 file3.txt 1049 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 file4.txt 1050 "); 1051 1052 // Create sibling commit, causing working copy to match index 1053 work_dir.run_jj(["new", "commit1"]).success(); 1054 1055 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1056 @ ccb1b1807383dba5ff4d335fd9fb92aa540f4632 1057 │ ○ 563dbc583c0d82eb10c40d3f3276183ea28a0fa7 commit2 1058 ├─╯ 1059 ○ 3c270b473dd871b20d196316eb038f078f80c219 commit1 git_head() 1060 ◆ 0000000000000000000000000000000000000000 1061 [EOF] 1062 "); 1063 1064 // Index should contain stat for unchanged file still. 1065 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1066 Unconflicted Mode(FILE) ed48318d9bf4 ctime=[nonzero] mtime=[nonzero] size=18 flags=0 file1.txt 1067 Unconflicted Mode(FILE) 28d2718c947b ctime=0:0 mtime=0:0 size=0 flags=0 file2.txt 1068 Unconflicted Mode(FILE) 528557ab3a42 ctime=0:0 mtime=0:0 size=0 flags=0 file3.txt 1069 "); 1070} 1071 1072#[test] 1073fn test_git_colocated_update_index_merge_conflict() { 1074 let test_env = TestEnvironment::default(); 1075 test_env 1076 .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 1077 .success(); 1078 let work_dir = test_env.work_dir("repo"); 1079 1080 // Set up conflict files 1081 work_dir.write_file("conflict.txt", "base\n"); 1082 work_dir.write_file("base.txt", "base\n"); 1083 work_dir 1084 .run_jj(["bookmark", "create", "-r@", "base"]) 1085 .success(); 1086 1087 work_dir.run_jj(["new", "base"]).success(); 1088 work_dir.write_file("conflict.txt", "left\n"); 1089 work_dir.write_file("left.txt", "left\n"); 1090 work_dir 1091 .run_jj(["bookmark", "create", "-r@", "left"]) 1092 .success(); 1093 1094 work_dir.run_jj(["new", "base"]).success(); 1095 work_dir.write_file("conflict.txt", "right\n"); 1096 work_dir.write_file("right.txt", "right\n"); 1097 work_dir 1098 .run_jj(["bookmark", "create", "-r@", "right"]) 1099 .success(); 1100 1101 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1102 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 base.txt 1103 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1104 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 right.txt 1105 "); 1106 1107 // Update index with stat for base.txt 1108 update_git_index(work_dir.root()); 1109 1110 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1111 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1112 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1113 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 right.txt 1114 "); 1115 1116 // Create merge conflict 1117 work_dir.run_jj(["new", "left", "right"]).success(); 1118 1119 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1120 @ aea7acd77752c3f74914de1fe327075a579bf7c6 1121 ├─╮ 1122 │ ○ df62ad35fc873e89ade730fa9a407cd5cfa5e6ba right 1123 ○ │ 68cc2177623364e4f0719d6ec8da1d6ea8d6087e left git_head() 1124 ├─╯ 1125 ○ 14b3ff6c73a234ab2a26fc559512e0f056a46bd9 base 1126 ◆ 0000000000000000000000000000000000000000 1127 [EOF] 1128 "); 1129 1130 // Conflict should be added in index with correct blob IDs. The stat for 1131 // base.txt should not change. 1132 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1133 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1134 Base Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=1000 conflict.txt 1135 Ours Mode(FILE) 45cf141ba67d ctime=0:0 mtime=0:0 size=0 flags=2000 conflict.txt 1136 Theirs Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=3000 conflict.txt 1137 Unconflicted Mode(FILE) 45cf141ba67d ctime=0:0 mtime=0:0 size=0 flags=0 left.txt 1138 Unconflicted Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=0 right.txt 1139 "); 1140 1141 work_dir.run_jj(["new"]).success(); 1142 1143 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1144 @ cae33b49a8a514996983caaf171c5edbf0d70e78 1145 × aea7acd77752c3f74914de1fe327075a579bf7c6 git_head() 1146 ├─╮ 1147 │ ○ df62ad35fc873e89ade730fa9a407cd5cfa5e6ba right 1148 ○ │ 68cc2177623364e4f0719d6ec8da1d6ea8d6087e left 1149 ├─╯ 1150 ○ 14b3ff6c73a234ab2a26fc559512e0f056a46bd9 base 1151 ◆ 0000000000000000000000000000000000000000 1152 [EOF] 1153 "); 1154 1155 // Index should be the same after `jj new`. 1156 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1157 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1158 Base Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=1000 conflict.txt 1159 Ours Mode(FILE) 45cf141ba67d ctime=0:0 mtime=0:0 size=0 flags=2000 conflict.txt 1160 Theirs Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=3000 conflict.txt 1161 Unconflicted Mode(FILE) 45cf141ba67d ctime=0:0 mtime=0:0 size=0 flags=0 left.txt 1162 Unconflicted Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=0 right.txt 1163 "); 1164} 1165 1166#[test] 1167fn test_git_colocated_update_index_rebase_conflict() { 1168 let test_env = TestEnvironment::default(); 1169 test_env 1170 .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 1171 .success(); 1172 let work_dir = test_env.work_dir("repo"); 1173 1174 // Set up conflict files 1175 work_dir.write_file("conflict.txt", "base\n"); 1176 work_dir.write_file("base.txt", "base\n"); 1177 work_dir 1178 .run_jj(["bookmark", "create", "-r@", "base"]) 1179 .success(); 1180 1181 work_dir.run_jj(["new", "base"]).success(); 1182 work_dir.write_file("conflict.txt", "left\n"); 1183 work_dir.write_file("left.txt", "left\n"); 1184 work_dir 1185 .run_jj(["bookmark", "create", "-r@", "left"]) 1186 .success(); 1187 1188 work_dir.run_jj(["new", "base"]).success(); 1189 work_dir.write_file("conflict.txt", "right\n"); 1190 work_dir.write_file("right.txt", "right\n"); 1191 work_dir 1192 .run_jj(["bookmark", "create", "-r@", "right"]) 1193 .success(); 1194 1195 work_dir.run_jj(["edit", "left"]).success(); 1196 1197 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1198 @ 68cc2177623364e4f0719d6ec8da1d6ea8d6087e left 1199 │ ○ df62ad35fc873e89ade730fa9a407cd5cfa5e6ba right 1200 ├─╯ 1201 ○ 14b3ff6c73a234ab2a26fc559512e0f056a46bd9 base git_head() 1202 ◆ 0000000000000000000000000000000000000000 1203 [EOF] 1204 "); 1205 1206 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1207 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 base.txt 1208 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1209 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 left.txt 1210 "); 1211 1212 // Update index with stat for base.txt 1213 update_git_index(work_dir.root()); 1214 1215 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1216 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1217 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1218 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 left.txt 1219 "); 1220 1221 // Create rebase conflict 1222 work_dir 1223 .run_jj(["rebase", "-r", "left", "-d", "right"]) 1224 .success(); 1225 1226 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1227 @ 233cb41e128e74aa2fcbf01c85d69b33a118faa8 left 1228 ○ df62ad35fc873e89ade730fa9a407cd5cfa5e6ba right git_head() 1229 ○ 14b3ff6c73a234ab2a26fc559512e0f056a46bd9 base 1230 ◆ 0000000000000000000000000000000000000000 1231 [EOF] 1232 "); 1233 1234 // Index should contain files from parent commit, so there should be no conflict 1235 // in conflict.txt yet. The stat for base.txt should not change. 1236 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1237 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1238 Unconflicted Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1239 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 left.txt 1240 Unconflicted Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=0 right.txt 1241 "); 1242 1243 work_dir.run_jj(["new"]).success(); 1244 1245 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1246 @ 6d84b9021f9e07b69770687071c4e8e71113e688 1247 × 233cb41e128e74aa2fcbf01c85d69b33a118faa8 left git_head() 1248 ○ df62ad35fc873e89ade730fa9a407cd5cfa5e6ba right 1249 ○ 14b3ff6c73a234ab2a26fc559512e0f056a46bd9 base 1250 ◆ 0000000000000000000000000000000000000000 1251 [EOF] 1252 "); 1253 1254 // Now the working copy commit's parent is conflicted, so the index should have 1255 // a conflict with correct blob IDs. 1256 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1257 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1258 Base Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=1000 conflict.txt 1259 Ours Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=2000 conflict.txt 1260 Theirs Mode(FILE) 45cf141ba67d ctime=0:0 mtime=0:0 size=0 flags=3000 conflict.txt 1261 Unconflicted Mode(FILE) 45cf141ba67d ctime=0:0 mtime=0:0 size=0 flags=0 left.txt 1262 Unconflicted Mode(FILE) c376d892e8b1 ctime=0:0 mtime=0:0 size=0 flags=0 right.txt 1263 "); 1264} 1265 1266#[test] 1267fn test_git_colocated_update_index_3_sided_conflict() { 1268 let test_env = TestEnvironment::default(); 1269 test_env 1270 .run_jj_in(".", ["git", "init", "--colocate", "repo"]) 1271 .success(); 1272 let work_dir = test_env.work_dir("repo"); 1273 1274 // Set up conflict files 1275 work_dir.write_file("conflict.txt", "base\n"); 1276 work_dir.write_file("base.txt", "base\n"); 1277 work_dir 1278 .run_jj(["bookmark", "create", "-r@", "base"]) 1279 .success(); 1280 1281 work_dir.run_jj(["new", "base"]).success(); 1282 work_dir.write_file("conflict.txt", "side-1\n"); 1283 work_dir.write_file("side-1.txt", "side-1\n"); 1284 work_dir 1285 .run_jj(["bookmark", "create", "-r@", "side-1"]) 1286 .success(); 1287 1288 work_dir.run_jj(["new", "base"]).success(); 1289 work_dir.write_file("conflict.txt", "side-2\n"); 1290 work_dir.write_file("side-2.txt", "side-2\n"); 1291 work_dir 1292 .run_jj(["bookmark", "create", "-r@", "side-2"]) 1293 .success(); 1294 1295 work_dir.run_jj(["new", "base"]).success(); 1296 work_dir.write_file("conflict.txt", "side-3\n"); 1297 work_dir.write_file("side-3.txt", "side-3\n"); 1298 work_dir 1299 .run_jj(["bookmark", "create", "-r@", "side-3"]) 1300 .success(); 1301 1302 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1303 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 base.txt 1304 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1305 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 side-3.txt 1306 "); 1307 1308 // Update index with stat for base.txt 1309 update_git_index(work_dir.root()); 1310 1311 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1312 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1313 Unconflicted Mode(FILE) df967b96a579 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1314 Unconflicted Mode(FILE) e69de29bb2d1 ctime=0:0 mtime=0:0 size=0 flags=20004000 side-3.txt 1315 "); 1316 1317 // Create 3-sided merge conflict 1318 work_dir 1319 .run_jj(["new", "side-1", "side-2", "side-3"]) 1320 .success(); 1321 1322 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1323 @ faee07ad76218d193f2784f4988daa2ac46db30c 1324 ├─┬─╮ 1325 │ │ ○ 86e722ea6a9da2551f1e05bc9aa914acd1cb2304 side-3 1326 │ ○ │ b8b9ca2d8178c4ba727a61e2258603f30ac7c6d3 side-2 1327 │ ├─╯ 1328 ○ │ a4b3ce25ef4857172e7777567afd497a917a0486 side-1 git_head() 1329 ├─╯ 1330 ○ 14b3ff6c73a234ab2a26fc559512e0f056a46bd9 base 1331 ◆ 0000000000000000000000000000000000000000 1332 [EOF] 1333 "); 1334 1335 // We can't add conflicts with more than 2 sides to the index, so we add a dummy 1336 // conflict instead. The stat for base.txt should not change. 1337 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1338 Ours Mode(FILE) eb8299123d2a ctime=0:0 mtime=0:0 size=0 flags=2000 .jj-do-not-resolve-this-conflict 1339 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1340 Unconflicted Mode(FILE) dd8f930010b3 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1341 Unconflicted Mode(FILE) dd8f930010b3 ctime=0:0 mtime=0:0 size=0 flags=0 side-1.txt 1342 Unconflicted Mode(FILE) 7b44e11df720 ctime=0:0 mtime=0:0 size=0 flags=0 side-2.txt 1343 Unconflicted Mode(FILE) 42f37a71bf20 ctime=0:0 mtime=0:0 size=0 flags=0 side-3.txt 1344 "); 1345 1346 work_dir.run_jj(["new"]).success(); 1347 1348 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1349 @ b0e5644063c2a12fb265e5f65cd88c6a2e1cf865 1350 × faee07ad76218d193f2784f4988daa2ac46db30c git_head() 1351 ├─┬─╮ 1352 │ │ ○ 86e722ea6a9da2551f1e05bc9aa914acd1cb2304 side-3 1353 │ ○ │ b8b9ca2d8178c4ba727a61e2258603f30ac7c6d3 side-2 1354 │ ├─╯ 1355 ○ │ a4b3ce25ef4857172e7777567afd497a917a0486 side-1 1356 ├─╯ 1357 ○ 14b3ff6c73a234ab2a26fc559512e0f056a46bd9 base 1358 ◆ 0000000000000000000000000000000000000000 1359 [EOF] 1360 "); 1361 1362 // Index should be the same after `jj new`. 1363 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1364 Ours Mode(FILE) eb8299123d2a ctime=0:0 mtime=0:0 size=0 flags=2000 .jj-do-not-resolve-this-conflict 1365 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1366 Unconflicted Mode(FILE) dd8f930010b3 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1367 Unconflicted Mode(FILE) dd8f930010b3 ctime=0:0 mtime=0:0 size=0 flags=0 side-1.txt 1368 Unconflicted Mode(FILE) 7b44e11df720 ctime=0:0 mtime=0:0 size=0 flags=0 side-2.txt 1369 Unconflicted Mode(FILE) 42f37a71bf20 ctime=0:0 mtime=0:0 size=0 flags=0 side-3.txt 1370 "); 1371 1372 // If we add a file named ".jj-do-not-resolve-this-conflict", it should take 1373 // precedence over the dummy conflict. 1374 work_dir.write_file(".jj-do-not-resolve-this-conflict", "file\n"); 1375 work_dir.run_jj(["new"]).success(); 1376 insta::assert_snapshot!(get_index_state(work_dir.root()), @r" 1377 Unconflicted Mode(FILE) f73f3093ff86 ctime=0:0 mtime=0:0 size=0 flags=0 .jj-do-not-resolve-this-conflict 1378 Unconflicted Mode(FILE) df967b96a579 ctime=[nonzero] mtime=[nonzero] size=5 flags=0 base.txt 1379 Unconflicted Mode(FILE) dd8f930010b3 ctime=0:0 mtime=0:0 size=0 flags=0 conflict.txt 1380 Unconflicted Mode(FILE) dd8f930010b3 ctime=0:0 mtime=0:0 size=0 flags=0 side-1.txt 1381 Unconflicted Mode(FILE) 7b44e11df720 ctime=0:0 mtime=0:0 size=0 flags=0 side-2.txt 1382 Unconflicted Mode(FILE) 42f37a71bf20 ctime=0:0 mtime=0:0 size=0 flags=0 side-3.txt 1383 "); 1384} 1385 1386#[must_use] 1387fn get_log_output_divergence(work_dir: &TestWorkDir) -> CommandOutput { 1388 let template = r#" 1389 separate(" ", 1390 change_id.short(), 1391 commit_id.short(), 1392 description.first_line(), 1393 bookmarks, 1394 if(git_head, "git_head()"), 1395 if(divergent, "!divergence!"), 1396 ) 1397 "#; 1398 work_dir.run_jj(["log", "-T", template]) 1399} 1400 1401#[must_use] 1402fn get_log_output(work_dir: &TestWorkDir) -> CommandOutput { 1403 let template = r#" 1404 separate(" ", 1405 commit_id, 1406 bookmarks, 1407 if(git_head, "git_head()"), 1408 description, 1409 ) 1410 "#; 1411 work_dir.run_jj(["log", "-T", template, "-r=all()"]) 1412} 1413 1414fn update_git_index(repo_path: &Path) { 1415 let mut iter = git::open(repo_path) 1416 .status(gix::progress::Discard) 1417 .unwrap() 1418 .into_index_worktree_iter(None) 1419 .unwrap(); 1420 1421 // need to explicitly iterate over the changes to recreate the index 1422 1423 for item in iter.by_ref() { 1424 item.unwrap(); 1425 } 1426 1427 iter.outcome_mut() 1428 .unwrap() 1429 .write_changes() 1430 .unwrap() 1431 .unwrap(); 1432} 1433 1434fn get_index_state(repo_path: &Path) -> String { 1435 let git_repo = gix::open(repo_path).expect("git repo should exist"); 1436 let mut buffer = String::new(); 1437 // We can't use the real time from disk, since it would change each time the 1438 // tests are run. Instead, we just show whether it's zero or nonzero. 1439 let format_time = |time: gix::index::entry::stat::Time| { 1440 if time.secs == 0 && time.nsecs == 0 { 1441 "0:0" 1442 } else { 1443 "[nonzero]" 1444 } 1445 }; 1446 let index = git_repo.index_or_empty().unwrap(); 1447 for entry in index.entries() { 1448 writeln!( 1449 &mut buffer, 1450 "{:12} {:?} {} ctime={} mtime={} size={} flags={:x} {}", 1451 format!("{:?}", entry.stage()), 1452 entry.mode, 1453 entry.id.to_hex_with_len(12), 1454 format_time(entry.stat.ctime), 1455 format_time(entry.stat.mtime), 1456 entry.stat.size, 1457 entry.flags.bits(), 1458 entry.path_in(index.path_backing()), 1459 ) 1460 .unwrap(); 1461 } 1462 buffer 1463} 1464 1465#[test] 1466fn test_git_colocated_unreachable_commits() { 1467 let test_env = TestEnvironment::default(); 1468 let work_dir = test_env.work_dir("repo"); 1469 let git_repo = git::init(work_dir.root()); 1470 1471 // Create an initial commit in Git 1472 let commit1 = git::add_commit( 1473 &git_repo, 1474 "refs/heads/master", 1475 "some-file", 1476 b"some content", 1477 "initial", 1478 &[], 1479 ) 1480 .commit_id; 1481 insta::assert_snapshot!( 1482 git_repo.head_id().unwrap().to_string(), 1483 @"cd740e230992f334de13a0bd0b35709b3f7a89af" 1484 ); 1485 1486 // Add a second commit in Git 1487 let commit2 = git::add_commit( 1488 &git_repo, 1489 "refs/heads/dummy", 1490 "next-file", 1491 b"more content", 1492 "next", 1493 &[commit1], 1494 ) 1495 .commit_id; 1496 git_repo 1497 .find_reference("refs/heads/dummy") 1498 .unwrap() 1499 .delete() 1500 .unwrap(); 1501 insta::assert_snapshot!( 1502 git_repo.head_id().unwrap().to_string(), 1503 @"cd740e230992f334de13a0bd0b35709b3f7a89af" 1504 ); 1505 1506 // Import the repo while there is no path to the second commit 1507 work_dir 1508 .run_jj(["git", "init", "--git-repo", "."]) 1509 .success(); 1510 insta::assert_snapshot!(get_log_output(&work_dir), @r" 1511 @ 9ff88424a06a94d04738847733e68e510b906345 1512 ○ cd740e230992f334de13a0bd0b35709b3f7a89af master git_head() initial 1513 ◆ 0000000000000000000000000000000000000000 1514 [EOF] 1515 "); 1516 insta::assert_snapshot!( 1517 git_repo.head_id().unwrap().to_string(), 1518 @"cd740e230992f334de13a0bd0b35709b3f7a89af" 1519 ); 1520 1521 // Check that trying to look up the second commit fails gracefully 1522 let output = work_dir.run_jj(["show", &commit2.to_string()]); 1523 insta::assert_snapshot!(output, @r" 1524 ------- stderr ------- 1525 Error: Revision `b23bb53bdce25f0e03ff9e484eadb77626256041` doesn't exist 1526 [EOF] 1527 [exit status: 1] 1528 "); 1529} 1530 1531#[test] 1532fn test_git_colocated_operation_cleanup() { 1533 let test_env = TestEnvironment::default(); 1534 let output = test_env.run_jj_in(".", ["git", "init", "--colocate", "repo"]); 1535 insta::assert_snapshot!(output, @r#" 1536 ------- stderr ------- 1537 Initialized repo in "repo" 1538 [EOF] 1539 "#); 1540 1541 let work_dir = test_env.work_dir("repo"); 1542 1543 work_dir.write_file("file", "1"); 1544 work_dir.run_jj(["describe", "-m1"]).success(); 1545 work_dir.run_jj(["new"]).success(); 1546 1547 work_dir.write_file("file", "2"); 1548 work_dir.run_jj(["describe", "-m2"]).success(); 1549 work_dir 1550 .run_jj(["bookmark", "create", "-r@", "main"]) 1551 .success(); 1552 work_dir.run_jj(["new", "root()+"]).success(); 1553 1554 work_dir.write_file("file", "3"); 1555 work_dir.run_jj(["describe", "-m3"]).success(); 1556 work_dir 1557 .run_jj(["bookmark", "create", "-r@", "feature"]) 1558 .success(); 1559 work_dir.run_jj(["new"]).success(); 1560 1561 insta::assert_snapshot!(get_log_output(&work_dir), @r#" 1562 @ e3feb4fda7b5e1d458a460ce76cb840b8f3cae34 1563 ○ e810c2ff6f3287a27e5d08aa3f429e284d99fea0 feature git_head() 3 1564 │ ○ 52fef888179abf5819a0a0d4f7907fcc025cb2a1 main 2 1565 ├─╯ 1566 ○ 61c11921948922575504d7b9f2df236543d0cec9 1 1567 ◆ 0000000000000000000000000000000000000000 1568 [EOF] 1569 "#); 1570 1571 // Start a rebase in Git and expect a merge conflict. 1572 let output = std::process::Command::new("git") 1573 .current_dir(work_dir.root()) 1574 .args(["rebase", "main"]) 1575 .output() 1576 .unwrap(); 1577 assert!(!output.status.success()); 1578 1579 // Check that we’re in the middle of a conflicted rebase. 1580 assert!(std::fs::exists(work_dir.root().join(".git").join("rebase-merge")).unwrap()); 1581 let output = std::process::Command::new("git") 1582 .current_dir(work_dir.root()) 1583 .args(["status", "--porcelain=v1"]) 1584 .output() 1585 .unwrap(); 1586 assert!(output.status.success()); 1587 insta::assert_snapshot!(String::from_utf8(output.stdout).unwrap(), @r#" 1588 UU file 1589 "#); 1590 insta::assert_snapshot!(get_log_output(&work_dir), @r#" 1591 @ fbb4e341d1e7e1d3b87377c075bd8a407305ba3a 1592 ○ 52fef888179abf5819a0a0d4f7907fcc025cb2a1 main git_head() 2 1593 │ ○ e810c2ff6f3287a27e5d08aa3f429e284d99fea0 feature 3 1594 ├─╯ 1595 ○ 61c11921948922575504d7b9f2df236543d0cec9 1 1596 ◆ 0000000000000000000000000000000000000000 1597 [EOF] 1598 ------- stderr ------- 1599 Reset the working copy parent to the new Git HEAD. 1600 [EOF] 1601 "#); 1602 1603 // Reset the Git HEAD with Jujutsu. 1604 let output = work_dir.run_jj(["new", "main"]); 1605 insta::assert_snapshot!(output, @r" 1606 ------- stderr ------- 1607 Working copy (@) now at: kmkuslsw 92667528 (empty) (no description set) 1608 Parent commit (@-) : kkmpptxz 52fef888 main | 2 1609 Added 0 files, modified 1 files, removed 0 files 1610 [EOF] 1611 "); 1612 insta::assert_snapshot!(get_log_output(&work_dir), @r#" 1613 @ 926675286938f585d83b3646a95df96206968e8c 1614 │ ○ fbb4e341d1e7e1d3b87377c075bd8a407305ba3a 1615 ├─╯ 1616 ○ 52fef888179abf5819a0a0d4f7907fcc025cb2a1 main git_head() 2 1617 │ ○ e810c2ff6f3287a27e5d08aa3f429e284d99fea0 feature 3 1618 ├─╯ 1619 ○ 61c11921948922575504d7b9f2df236543d0cec9 1 1620 ◆ 0000000000000000000000000000000000000000 1621 [EOF] 1622 "#); 1623 1624 // Check that the operation was correctly aborted. 1625 assert!(!std::fs::exists(work_dir.root().join(".git").join("rebase-merge")).unwrap()); 1626 let output = std::process::Command::new("git") 1627 .current_dir(work_dir.root()) 1628 .args(["status", "--porcelain=v1"]) 1629 .output() 1630 .unwrap(); 1631 assert!(output.status.success()); 1632 insta::assert_snapshot!(String::from_utf8(output.stdout).unwrap(), @""); 1633} 1634 1635#[must_use] 1636fn get_bookmark_output(work_dir: &TestWorkDir) -> CommandOutput { 1637 // --quiet to suppress deleted bookmarks hint 1638 work_dir.run_jj(["bookmark", "list", "--all-remotes", "--quiet"]) 1639}