just playing with tangled
at diffedit3 499 lines 19 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 crate::common::{escaped_fake_diff_editor_path, TestEnvironment}; 16 17#[test] 18fn test_diffedit() { 19 let mut test_env = TestEnvironment::default(); 20 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 21 let repo_path = test_env.env_root().join("repo"); 22 23 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 24 test_env.jj_cmd_ok(&repo_path, &["new"]); 25 std::fs::write(repo_path.join("file2"), "a\n").unwrap(); 26 std::fs::write(repo_path.join("file3"), "a\n").unwrap(); 27 test_env.jj_cmd_ok(&repo_path, &["new"]); 28 std::fs::remove_file(repo_path.join("file1")).unwrap(); 29 std::fs::write(repo_path.join("file2"), "b\n").unwrap(); 30 31 let edit_script = test_env.set_up_fake_diff_editor(); 32 33 // Test the setup; nothing happens if we make no changes 34 std::fs::write( 35 &edit_script, 36 "files-before file1 file2\0files-after JJ-INSTRUCTIONS file2", 37 ) 38 .unwrap(); 39 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit"]); 40 insta::assert_snapshot!(stdout, @""); 41 insta::assert_snapshot!(stderr, @r###" 42 Nothing changed. 43 "###); 44 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 45 insta::assert_snapshot!(stdout, @r###" 46 D file1 47 M file2 48 "###); 49 50 // Try again with ui.diff-instructions=false 51 std::fs::write(&edit_script, "files-before file1 file2\0files-after file2").unwrap(); 52 let (stdout, stderr) = test_env.jj_cmd_ok( 53 &repo_path, 54 &["diffedit", "--config-toml=ui.diff-instructions=false"], 55 ); 56 insta::assert_snapshot!(stdout, @""); 57 insta::assert_snapshot!(stderr, @r###" 58 Nothing changed. 59 "###); 60 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 61 insta::assert_snapshot!(stdout, @r###" 62 D file1 63 M file2 64 "###); 65 66 // Try again with --tool=<name> 67 std::fs::write( 68 &edit_script, 69 "files-before file1 file2\0files-after JJ-INSTRUCTIONS file2", 70 ) 71 .unwrap(); 72 let (stdout, stderr) = test_env.jj_cmd_ok( 73 &repo_path, 74 &[ 75 "diffedit", 76 "--config-toml=ui.diff-editor='false'", 77 "--tool=fake-diff-editor", 78 ], 79 ); 80 insta::assert_snapshot!(stdout, @""); 81 insta::assert_snapshot!(stderr, @r###" 82 Nothing changed. 83 "###); 84 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 85 insta::assert_snapshot!(stdout, @r###" 86 D file1 87 M file2 88 "###); 89 90 // Nothing happens if the diff-editor exits with an error 91 std::fs::write(&edit_script, "rm file2\0fail").unwrap(); 92 insta::assert_snapshot!(&test_env.jj_cmd_failure(&repo_path, &["diffedit"]), @r###" 93 Error: Failed to edit diff 94 Caused by: Tool exited with a non-zero code (run with --debug to see the exact invocation). Exit code: 1. 95 "###); 96 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 97 insta::assert_snapshot!(stdout, @r###" 98 D file1 99 M file2 100 "###); 101 102 // Can edit changes to individual files 103 std::fs::write(&edit_script, "reset file2").unwrap(); 104 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit"]); 105 insta::assert_snapshot!(stdout, @""); 106 insta::assert_snapshot!(stderr, @r###" 107 Created kkmpptxz cc387f43 (no description set) 108 Working copy now at: kkmpptxz cc387f43 (no description set) 109 Parent commit : rlvkpnrz 613028a4 (no description set) 110 Added 0 files, modified 1 files, removed 0 files 111 "###); 112 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 113 insta::assert_snapshot!(stdout, @r###" 114 D file1 115 "###); 116 117 // Changes to a commit are propagated to descendants 118 test_env.jj_cmd_ok(&repo_path, &["undo"]); 119 std::fs::write(&edit_script, "write file3\nmodified\n").unwrap(); 120 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "-r", "@-"]); 121 insta::assert_snapshot!(stdout, @""); 122 insta::assert_snapshot!(stderr, @r###" 123 Created rlvkpnrz d842b979 (no description set) 124 Rebased 1 descendant commits 125 Working copy now at: kkmpptxz bc2b2dd6 (no description set) 126 Parent commit : rlvkpnrz d842b979 (no description set) 127 Added 0 files, modified 1 files, removed 0 files 128 "###); 129 let contents = String::from_utf8(std::fs::read(repo_path.join("file3")).unwrap()).unwrap(); 130 insta::assert_snapshot!(contents, @r###" 131 modified 132 "###); 133 134 // Test diffedit --from @-- 135 test_env.jj_cmd_ok(&repo_path, &["undo"]); 136 std::fs::write( 137 &edit_script, 138 "files-before file1\0files-after JJ-INSTRUCTIONS file2 file3\0reset file2", 139 ) 140 .unwrap(); 141 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "--from", "@--"]); 142 insta::assert_snapshot!(stdout, @""); 143 insta::assert_snapshot!(stderr, @r###" 144 Created kkmpptxz d78a207f (no description set) 145 Working copy now at: kkmpptxz d78a207f (no description set) 146 Parent commit : rlvkpnrz 613028a4 (no description set) 147 Added 0 files, modified 0 files, removed 1 files 148 "###); 149 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 150 insta::assert_snapshot!(stdout, @r###" 151 D file1 152 D file2 153 "###); 154} 155 156#[test] 157fn test_diffedit_new_file() { 158 let mut test_env = TestEnvironment::default(); 159 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 160 let repo_path = test_env.env_root().join("repo"); 161 162 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 163 test_env.jj_cmd_ok(&repo_path, &["new"]); 164 std::fs::remove_file(repo_path.join("file1")).unwrap(); 165 std::fs::write(repo_path.join("file2"), "b\n").unwrap(); 166 167 let edit_script = test_env.set_up_fake_diff_editor(); 168 169 // Test the setup; nothing happens if we make no changes 170 std::fs::write( 171 &edit_script, 172 "files-before file1\0files-after JJ-INSTRUCTIONS file2", 173 ) 174 .unwrap(); 175 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit"]); 176 insta::assert_snapshot!(stdout, @""); 177 insta::assert_snapshot!(stderr, @r###" 178 Nothing changed. 179 "###); 180 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 181 insta::assert_snapshot!(stdout, @r###" 182 D file1 183 A file2 184 "###); 185 186 // Creating `file1` on the right side is noticed by `jj diffedit` 187 std::fs::write(&edit_script, "write file1\nmodified\n").unwrap(); 188 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit"]); 189 insta::assert_snapshot!(stdout, @""); 190 insta::assert_snapshot!(stderr, @r###" 191 Created rlvkpnrz 7b849299 (no description set) 192 Working copy now at: rlvkpnrz 7b849299 (no description set) 193 Parent commit : qpvuntsm 414e1614 (no description set) 194 Added 1 files, modified 0 files, removed 0 files 195 "###); 196 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 197 insta::assert_snapshot!(stdout, @r###" 198 M file1 199 A file2 200 "###); 201 202 // Creating a file that wasn't on either side is ignored by diffedit. 203 // TODO(ilyagr) We should decide whether we like this behavior. 204 // 205 // On one hand, it is unexpected and potentially a minor BUG. On the other 206 // hand, this prevents `jj` from loading any backup files the merge tool 207 // generates. 208 test_env.jj_cmd_ok(&repo_path, &["undo"]); 209 std::fs::write(&edit_script, "write new_file\nnew file\n").unwrap(); 210 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit"]); 211 insta::assert_snapshot!(stdout, @""); 212 insta::assert_snapshot!(stderr, @r###" 213 Nothing changed. 214 "###); 215 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 216 insta::assert_snapshot!(stdout, @r###" 217 D file1 218 A file2 219 "###); 220} 221 222#[test] 223fn test_diffedit_3pane() { 224 let mut test_env = TestEnvironment::default(); 225 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 226 let repo_path = test_env.env_root().join("repo"); 227 228 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 229 test_env.jj_cmd_ok(&repo_path, &["new"]); 230 std::fs::write(repo_path.join("file2"), "a\n").unwrap(); 231 std::fs::write(repo_path.join("file3"), "a\n").unwrap(); 232 test_env.jj_cmd_ok(&repo_path, &["new"]); 233 std::fs::remove_file(repo_path.join("file1")).unwrap(); 234 std::fs::write(repo_path.join("file2"), "b\n").unwrap(); 235 236 // 2 configs for a 3-pane setup. In the first, "$right" is passed to what the 237 // fake diff editor considers the "after" state. 238 let config_with_right_as_after = format!( 239 r#"ui.diff-editor=["{}", "$left", "$right", "--ignore=$output"]"#, 240 escaped_fake_diff_editor_path() 241 ); 242 let config_with_output_as_after = format!( 243 r#"ui.diff-editor=["{}", "$left", "$output", "--ignore=$right"]"#, 244 escaped_fake_diff_editor_path() 245 ); 246 let edit_script = test_env.env_root().join("diff_edit_script"); 247 std::fs::write(&edit_script, "").unwrap(); 248 test_env.add_env_var("DIFF_EDIT_SCRIPT", edit_script.to_str().unwrap()); 249 250 // Nothing happens if we make no changes 251 std::fs::write( 252 &edit_script, 253 "files-before file1 file2\0files-after JJ-INSTRUCTIONS file2", 254 ) 255 .unwrap(); 256 let (stdout, stderr) = test_env.jj_cmd_ok( 257 &repo_path, 258 &["diffedit", "--config-toml", &config_with_output_as_after], 259 ); 260 insta::assert_snapshot!(stdout, @""); 261 insta::assert_snapshot!(stderr, @r###" 262 Nothing changed. 263 "###); 264 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 265 insta::assert_snapshot!(stdout, @r###" 266 D file1 267 M file2 268 "###); 269 // Nothing happens if we make no changes, `config_with_right_as_after` version 270 let (stdout, stderr) = test_env.jj_cmd_ok( 271 &repo_path, 272 &["diffedit", "--config-toml", &config_with_right_as_after], 273 ); 274 insta::assert_snapshot!(stdout, @""); 275 insta::assert_snapshot!(stderr, @r###" 276 Nothing changed. 277 "###); 278 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 279 insta::assert_snapshot!(stdout, @r###" 280 D file1 281 M file2 282 "###); 283 284 // Can edit changes to individual files 285 std::fs::write(&edit_script, "reset file2").unwrap(); 286 let (stdout, stderr) = test_env.jj_cmd_ok( 287 &repo_path, 288 &["diffedit", "--config-toml", &config_with_output_as_after], 289 ); 290 insta::assert_snapshot!(stdout, @""); 291 insta::assert_snapshot!(stderr, @r###" 292 Created kkmpptxz 1930da4a (no description set) 293 Working copy now at: kkmpptxz 1930da4a (no description set) 294 Parent commit : rlvkpnrz 613028a4 (no description set) 295 Added 0 files, modified 1 files, removed 0 files 296 "###); 297 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 298 insta::assert_snapshot!(stdout, @r###" 299 D file1 300 "###); 301 302 // Can write something new to `file1` 303 test_env.jj_cmd_ok(&repo_path, &["undo"]); 304 std::fs::write(&edit_script, "write file1\nnew content").unwrap(); 305 let (stdout, stderr) = test_env.jj_cmd_ok( 306 &repo_path, 307 &["diffedit", "--config-toml", &config_with_output_as_after], 308 ); 309 insta::assert_snapshot!(stdout, @""); 310 insta::assert_snapshot!(stderr, @r###" 311 Created kkmpptxz ff2907b6 (no description set) 312 Working copy now at: kkmpptxz ff2907b6 (no description set) 313 Parent commit : rlvkpnrz 613028a4 (no description set) 314 Added 1 files, modified 0 files, removed 0 files 315 "###); 316 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 317 insta::assert_snapshot!(stdout, @r###" 318 M file1 319 M file2 320 "###); 321 322 // But nothing happens if we modify the right side 323 test_env.jj_cmd_ok(&repo_path, &["undo"]); 324 std::fs::write(&edit_script, "write file1\nnew content").unwrap(); 325 let (stdout, stderr) = test_env.jj_cmd_ok( 326 &repo_path, 327 &["diffedit", "--config-toml", &config_with_right_as_after], 328 ); 329 insta::assert_snapshot!(stdout, @""); 330 insta::assert_snapshot!(stderr, @r###" 331 Nothing changed. 332 "###); 333 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 334 insta::assert_snapshot!(stdout, @r###" 335 D file1 336 M file2 337 "###); 338 339 // TODO: test with edit_script of "reset file2". This fails on right side 340 // since the file is readonly. 341} 342 343#[test] 344fn test_diffedit_merge() { 345 let mut test_env = TestEnvironment::default(); 346 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 347 let repo_path = test_env.env_root().join("repo"); 348 349 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 350 std::fs::write(repo_path.join("file2"), "a\n").unwrap(); 351 test_env.jj_cmd_ok(&repo_path, &["new"]); 352 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]); 353 std::fs::write(repo_path.join("file1"), "b\n").unwrap(); 354 std::fs::write(repo_path.join("file2"), "b\n").unwrap(); 355 test_env.jj_cmd_ok(&repo_path, &["new", "@-"]); 356 test_env.jj_cmd_ok(&repo_path, &["new"]); 357 std::fs::write(repo_path.join("file1"), "c\n").unwrap(); 358 std::fs::write(repo_path.join("file2"), "c\n").unwrap(); 359 test_env.jj_cmd_ok(&repo_path, &["new", "@", "b", "-m", "merge"]); 360 // Resolve the conflict in file1, but leave the conflict in file2 361 std::fs::write(repo_path.join("file1"), "d\n").unwrap(); 362 std::fs::write(repo_path.join("file3"), "d\n").unwrap(); 363 test_env.jj_cmd_ok(&repo_path, &["new"]); 364 // Test the setup 365 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-r", "@-", "-s"]); 366 insta::assert_snapshot!(stdout, @r###" 367 M file1 368 A file3 369 "###); 370 371 let edit_script = test_env.set_up_fake_diff_editor(); 372 373 // Remove file1. The conflict remains in the working copy on top of the merge. 374 std::fs::write( 375 edit_script, 376 "files-before file1\0files-after JJ-INSTRUCTIONS file1 file3\0rm file1", 377 ) 378 .unwrap(); 379 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "-r", "@-"]); 380 insta::assert_snapshot!(stdout, @""); 381 insta::assert_snapshot!(stderr, @r###" 382 Created royxmykx b9539d6e (conflict) merge 383 Rebased 1 descendant commits 384 Working copy now at: yqosqzyt 0a24ed24 (conflict) (empty) (no description set) 385 Parent commit : royxmykx b9539d6e (conflict) merge 386 Added 0 files, modified 0 files, removed 1 files 387 There are unresolved conflicts at these paths: 388 file2 2-sided conflict 389 "###); 390 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s", "-r", "@-"]); 391 insta::assert_snapshot!(stdout, @r###" 392 D file1 393 A file3 394 "###); 395 assert!(!repo_path.join("file1").exists()); 396 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]); 397 insta::assert_snapshot!(stdout, @r###" 398 <<<<<<< Conflict 1 of 1 399 %%%%%%% Changes from base to side #1 400 -a 401 +c 402 +++++++ Contents of side #2 403 b 404 >>>>>>> 405 "###); 406} 407 408#[test] 409fn test_diffedit_old_restore_interactive_tests() { 410 let mut test_env = TestEnvironment::default(); 411 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 412 let repo_path = test_env.env_root().join("repo"); 413 414 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 415 std::fs::write(repo_path.join("file2"), "a\n").unwrap(); 416 test_env.jj_cmd_ok(&repo_path, &["new"]); 417 std::fs::remove_file(repo_path.join("file1")).unwrap(); 418 std::fs::write(repo_path.join("file2"), "b\n").unwrap(); 419 std::fs::write(repo_path.join("file3"), "b\n").unwrap(); 420 421 let edit_script = test_env.set_up_fake_diff_editor(); 422 423 // Nothing happens if we make no changes 424 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "--from", "@-"]); 425 insta::assert_snapshot!(stdout, @""); 426 insta::assert_snapshot!(stderr, @r###" 427 Nothing changed. 428 "###); 429 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 430 insta::assert_snapshot!(stdout, @r###" 431 D file1 432 M file2 433 A file3 434 "###); 435 436 // Nothing happens if the diff-editor exits with an error 437 std::fs::write(&edit_script, "rm file2\0fail").unwrap(); 438 insta::assert_snapshot!(&test_env.jj_cmd_failure(&repo_path, &["diffedit", "--from", "@-"]), @r###" 439 Error: Failed to edit diff 440 Caused by: Tool exited with a non-zero code (run with --debug to see the exact invocation). Exit code: 1. 441 "###); 442 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 443 insta::assert_snapshot!(stdout, @r###" 444 D file1 445 M file2 446 A file3 447 "###); 448 449 // Can restore changes to individual files 450 std::fs::write(&edit_script, "reset file2\0reset file3").unwrap(); 451 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "--from", "@-"]); 452 insta::assert_snapshot!(stdout, @""); 453 insta::assert_snapshot!(stderr, @r###" 454 Created rlvkpnrz abdbf627 (no description set) 455 Working copy now at: rlvkpnrz abdbf627 (no description set) 456 Parent commit : qpvuntsm 2375fa16 (no description set) 457 Added 0 files, modified 1 files, removed 1 files 458 "###); 459 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "-s"]); 460 insta::assert_snapshot!(stdout, @r###" 461 D file1 462 "###); 463 464 // Can make unrelated edits 465 test_env.jj_cmd_ok(&repo_path, &["undo"]); 466 std::fs::write(&edit_script, "write file3\nunrelated\n").unwrap(); 467 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["diffedit", "--from", "@-"]); 468 insta::assert_snapshot!(stdout, @""); 469 insta::assert_snapshot!(stderr, @r###" 470 Created rlvkpnrz e31f7f33 (no description set) 471 Working copy now at: rlvkpnrz e31f7f33 (no description set) 472 Parent commit : qpvuntsm 2375fa16 (no description set) 473 Added 0 files, modified 1 files, removed 0 files 474 "###); 475 let stdout = test_env.jj_cmd_success(&repo_path, &["diff", "--git"]); 476 insta::assert_snapshot!(stdout, @r###" 477 diff --git a/file1 b/file1 478 deleted file mode 100644 479 index 7898192261..0000000000 480 --- a/file1 481 +++ /dev/null 482 @@ -1,1 +1,0 @@ 483 -a 484 diff --git a/file2 b/file2 485 index 7898192261...6178079822 100644 486 --- a/file2 487 +++ b/file2 488 @@ -1,1 +1,1 @@ 489 -a 490 +b 491 diff --git a/file3 b/file3 492 new file mode 100644 493 index 0000000000..c21c9352f7 494 --- /dev/null 495 +++ b/file3 496 @@ -1,0 +1,1 @@ 497 +unrelated 498 "###); 499}