just playing with tangled
at diffedit3 440 lines 18 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::path::Path; 16 17use crate::common::TestEnvironment; 18 19#[test] 20fn test_move() { 21 let test_env = TestEnvironment::default(); 22 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 23 let repo_path = test_env.env_root().join("repo"); 24 25 // Create history like this: 26 // F 27 // | 28 // E C 29 // | | 30 // D B 31 // |/ 32 // A 33 // 34 // When moving changes between e.g. C and F, we should not get unrelated changes 35 // from B and D. 36 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]); 37 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 38 std::fs::write(repo_path.join("file2"), "a\n").unwrap(); 39 std::fs::write(repo_path.join("file3"), "a\n").unwrap(); 40 test_env.jj_cmd_ok(&repo_path, &["new"]); 41 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]); 42 std::fs::write(repo_path.join("file3"), "b\n").unwrap(); 43 test_env.jj_cmd_ok(&repo_path, &["new"]); 44 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]); 45 std::fs::write(repo_path.join("file1"), "c\n").unwrap(); 46 test_env.jj_cmd_ok(&repo_path, &["edit", "a"]); 47 test_env.jj_cmd_ok(&repo_path, &["new"]); 48 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]); 49 std::fs::write(repo_path.join("file3"), "d\n").unwrap(); 50 test_env.jj_cmd_ok(&repo_path, &["new"]); 51 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "e"]); 52 std::fs::write(repo_path.join("file2"), "e\n").unwrap(); 53 test_env.jj_cmd_ok(&repo_path, &["new"]); 54 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "f"]); 55 std::fs::write(repo_path.join("file2"), "f\n").unwrap(); 56 // Test the setup 57 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 58 @ 0d7353584003 f 59 ◉ e9515f21068c e 60 ◉ bdd835cae844 d 61 │ ◉ caa4d0b23201 c 62 │ ◉ 55171e33db26 b 63 ├─╯ 64 ◉ 3db0a2f5b535 a 65 ◉ 000000000000 66 "###); 67 68 // Errors out without arguments 69 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["move"]); 70 insta::assert_snapshot!(stderr, @r###" 71 error: the following required arguments were not provided: 72 <--from <FROM>|--to <TO>> 73 74 Usage: jj move <--from <FROM>|--to <TO>> [PATHS]... 75 76 For more information, try '--help'. 77 "###); 78 // Errors out if source and destination are the same 79 let stderr = test_env.jj_cmd_failure(&repo_path, &["move", "--to", "@"]); 80 insta::assert_snapshot!(stderr, @r###" 81 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 82 Warning: `jj move` will be removed in a future version, and this will be a hard error 83 Error: Source and destination cannot be the same. 84 "###); 85 86 // Can move from sibling, which results in the source being abandoned 87 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["move", "--from", "c"]); 88 insta::assert_snapshot!(stdout, @""); 89 insta::assert_snapshot!(stderr, @r###" 90 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 91 Warning: `jj move` will be removed in a future version, and this will be a hard error 92 Working copy now at: kmkuslsw 1c03e3d3 f | (no description set) 93 Parent commit : znkkpsqq e9515f21 e | (no description set) 94 Added 0 files, modified 1 files, removed 0 files 95 "###); 96 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 97 @ 1c03e3d3c63f f 98 ◉ e9515f21068c e 99 ◉ bdd835cae844 d 100 │ ◉ 55171e33db26 b c 101 ├─╯ 102 ◉ 3db0a2f5b535 a 103 ◉ 000000000000 104 "###); 105 // The change from the source has been applied 106 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1"]); 107 insta::assert_snapshot!(stdout, @r###" 108 c 109 "###); 110 // File `file2`, which was not changed in source, is unchanged 111 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]); 112 insta::assert_snapshot!(stdout, @r###" 113 f 114 "###); 115 116 // Can move from ancestor 117 test_env.jj_cmd_ok(&repo_path, &["undo"]); 118 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["move", "--from", "@--"]); 119 insta::assert_snapshot!(stdout, @""); 120 insta::assert_snapshot!(stderr, @r###" 121 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 122 Warning: `jj move` will be removed in a future version, and this will be a hard error 123 Working copy now at: kmkuslsw c8d83075 f | (no description set) 124 Parent commit : znkkpsqq 2c50bfc5 e | (no description set) 125 "###); 126 // The change has been removed from the source (the change pointed to by 'd' 127 // became empty and was abandoned) 128 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 129 @ c8d83075e8c2 f 130 ◉ 2c50bfc59c68 e 131 │ ◉ caa4d0b23201 c 132 │ ◉ 55171e33db26 b 133 ├─╯ 134 ◉ 3db0a2f5b535 a d 135 ◉ 000000000000 136 "###); 137 // The change from the source has been applied (the file contents were already 138 // "f", as is typically the case when moving changes from an ancestor) 139 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]); 140 insta::assert_snapshot!(stdout, @r###" 141 f 142 "###); 143 144 // Can move from descendant 145 test_env.jj_cmd_ok(&repo_path, &["undo"]); 146 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["move", "--from", "e", "--to", "d"]); 147 insta::assert_snapshot!(stdout, @""); 148 insta::assert_snapshot!(stderr, @r###" 149 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 150 Warning: `jj move` will be removed in a future version, and this will be a hard error 151 Rebased 1 descendant commits 152 Working copy now at: kmkuslsw 2b723b1d f | (no description set) 153 Parent commit : vruxwmqv 4293930d d e | (no description set) 154 "###); 155 // The change has been removed from the source (the change pointed to by 'e' 156 // became empty and was abandoned) 157 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 158 @ 2b723b1d6033 f 159 ◉ 4293930d6333 d e 160 │ ◉ caa4d0b23201 c 161 │ ◉ 55171e33db26 b 162 ├─╯ 163 ◉ 3db0a2f5b535 a 164 ◉ 000000000000 165 "###); 166 // The change from the source has been applied 167 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2", "-r", "d"]); 168 insta::assert_snapshot!(stdout, @r###" 169 e 170 "###); 171} 172 173#[test] 174fn test_move_partial() { 175 let mut test_env = TestEnvironment::default(); 176 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 177 let repo_path = test_env.env_root().join("repo"); 178 179 // Create history like this: 180 // C 181 // | 182 // D B 183 // |/ 184 // A 185 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "a"]); 186 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 187 std::fs::write(repo_path.join("file2"), "a\n").unwrap(); 188 std::fs::write(repo_path.join("file3"), "a\n").unwrap(); 189 test_env.jj_cmd_ok(&repo_path, &["new"]); 190 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b"]); 191 std::fs::write(repo_path.join("file3"), "b\n").unwrap(); 192 test_env.jj_cmd_ok(&repo_path, &["new"]); 193 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "c"]); 194 std::fs::write(repo_path.join("file1"), "c\n").unwrap(); 195 std::fs::write(repo_path.join("file2"), "c\n").unwrap(); 196 test_env.jj_cmd_ok(&repo_path, &["edit", "a"]); 197 test_env.jj_cmd_ok(&repo_path, &["new"]); 198 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "d"]); 199 std::fs::write(repo_path.join("file3"), "d\n").unwrap(); 200 // Test the setup 201 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 202 @ bdd835cae844 d 203 │ ◉ 5028db694b6b c 204 │ ◉ 55171e33db26 b 205 ├─╯ 206 ◉ 3db0a2f5b535 a 207 ◉ 000000000000 208 "###); 209 210 let edit_script = test_env.set_up_fake_diff_editor(); 211 212 // If we don't make any changes in the diff-editor, the whole change is moved 213 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["move", "-i", "--from", "c"]); 214 insta::assert_snapshot!(stdout, @""); 215 insta::assert_snapshot!(stderr, @r###" 216 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 217 Warning: `jj move` will be removed in a future version, and this will be a hard error 218 Working copy now at: vruxwmqv 71b69e43 d | (no description set) 219 Parent commit : qpvuntsm 3db0a2f5 a | (no description set) 220 Added 0 files, modified 2 files, removed 0 files 221 "###); 222 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 223 @ 71b69e433fbc d 224 │ ◉ 55171e33db26 b c 225 ├─╯ 226 ◉ 3db0a2f5b535 a 227 ◉ 000000000000 228 "###); 229 // The changes from the source has been applied 230 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1"]); 231 insta::assert_snapshot!(stdout, @r###" 232 c 233 "###); 234 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]); 235 insta::assert_snapshot!(stdout, @r###" 236 c 237 "###); 238 // File `file3`, which was not changed in source, is unchanged 239 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file3"]); 240 insta::assert_snapshot!(stdout, @r###" 241 d 242 "###); 243 244 // Can move only part of the change in interactive mode 245 test_env.jj_cmd_ok(&repo_path, &["undo"]); 246 std::fs::write(&edit_script, "reset file2").unwrap(); 247 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["move", "-i", "--from", "c"]); 248 insta::assert_snapshot!(stdout, @""); 249 insta::assert_snapshot!(stderr, @r###" 250 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 251 Warning: `jj move` will be removed in a future version, and this will be a hard error 252 Working copy now at: vruxwmqv 63f1a6e9 d | (no description set) 253 Parent commit : qpvuntsm 3db0a2f5 a | (no description set) 254 Added 0 files, modified 1 files, removed 0 files 255 "###); 256 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 257 @ 63f1a6e96edb d 258 │ ◉ d027c6e3e6bc c 259 │ ◉ 55171e33db26 b 260 ├─╯ 261 ◉ 3db0a2f5b535 a 262 ◉ 000000000000 263 "###); 264 // The selected change from the source has been applied 265 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1"]); 266 insta::assert_snapshot!(stdout, @r###" 267 c 268 "###); 269 // The unselected change from the source has not been applied 270 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]); 271 insta::assert_snapshot!(stdout, @r###" 272 a 273 "###); 274 // File `file3`, which was changed in source's parent, is unchanged 275 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file3"]); 276 insta::assert_snapshot!(stdout, @r###" 277 d 278 "###); 279 280 // Can move only part of the change from a sibling in non-interactive mode 281 test_env.jj_cmd_ok(&repo_path, &["undo"]); 282 // Clear the script so we know it won't be used 283 std::fs::write(&edit_script, "").unwrap(); 284 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["move", "--from", "c", "file1"]); 285 insta::assert_snapshot!(stdout, @""); 286 insta::assert_snapshot!(stderr, @r###" 287 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 288 Warning: `jj move` will be removed in a future version, and this will be a hard error 289 Working copy now at: vruxwmqv 17c2e663 d | (no description set) 290 Parent commit : qpvuntsm 3db0a2f5 a | (no description set) 291 Added 0 files, modified 1 files, removed 0 files 292 "###); 293 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 294 @ 17c2e6632cc5 d 295 │ ◉ 6a3ae047a03e c 296 │ ◉ 55171e33db26 b 297 ├─╯ 298 ◉ 3db0a2f5b535 a 299 ◉ 000000000000 300 "###); 301 // The selected change from the source has been applied 302 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1"]); 303 insta::assert_snapshot!(stdout, @r###" 304 c 305 "###); 306 // The unselected change from the source has not been applied 307 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2"]); 308 insta::assert_snapshot!(stdout, @r###" 309 a 310 "###); 311 // File `file3`, which was changed in source's parent, is unchanged 312 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file3"]); 313 insta::assert_snapshot!(stdout, @r###" 314 d 315 "###); 316 317 // Can move only part of the change from a descendant in non-interactive mode 318 test_env.jj_cmd_ok(&repo_path, &["undo"]); 319 // Clear the script so we know it won't be used 320 std::fs::write(&edit_script, "").unwrap(); 321 let (stdout, stderr) = 322 test_env.jj_cmd_ok(&repo_path, &["move", "--from", "c", "--to", "b", "file1"]); 323 insta::assert_snapshot!(stdout, @""); 324 insta::assert_snapshot!(stderr, @r###" 325 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 326 Warning: `jj move` will be removed in a future version, and this will be a hard error 327 Rebased 1 descendant commits 328 "###); 329 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###" 330 ◉ 21253406d416 c 331 ◉ e1cf08aae711 b 332 │ @ bdd835cae844 d 333 ├─╯ 334 ◉ 3db0a2f5b535 a 335 ◉ 000000000000 336 "###); 337 // The selected change from the source has been applied 338 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file1", "-r", "b"]); 339 insta::assert_snapshot!(stdout, @r###" 340 c 341 "###); 342 // The unselected change from the source has not been applied 343 let stdout = test_env.jj_cmd_success(&repo_path, &["print", "file2", "-r", "b"]); 344 insta::assert_snapshot!(stdout, @r###" 345 a 346 "###); 347 348 // If we specify only a non-existent file, then nothing changes. 349 test_env.jj_cmd_ok(&repo_path, &["undo"]); 350 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["move", "--from", "c", "nonexistent"]); 351 insta::assert_snapshot!(stdout, @""); 352 insta::assert_snapshot!(stderr, @r###" 353 Warning: `jj move` is deprecated; use `jj squash` instead, which is equivalent 354 Warning: `jj move` will be removed in a future version, and this will be a hard error 355 Nothing changed. 356 "###); 357} 358 359fn get_log_output(test_env: &TestEnvironment, cwd: &Path) -> String { 360 let template = r#"commit_id.short() ++ " " ++ branches"#; 361 test_env.jj_cmd_success(cwd, &["log", "-T", template]) 362} 363 364#[test] 365fn test_move_description() { 366 let mut test_env = TestEnvironment::default(); 367 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]); 368 let repo_path = test_env.env_root().join("repo"); 369 370 let edit_script = test_env.set_up_fake_editor(); 371 std::fs::write(&edit_script, r#"fail"#).unwrap(); 372 373 // If both descriptions are empty, the resulting description is empty 374 std::fs::write(repo_path.join("file1"), "a\n").unwrap(); 375 std::fs::write(repo_path.join("file2"), "a\n").unwrap(); 376 test_env.jj_cmd_ok(&repo_path, &["new"]); 377 std::fs::write(repo_path.join("file1"), "b\n").unwrap(); 378 std::fs::write(repo_path.join("file2"), "b\n").unwrap(); 379 test_env.jj_cmd_ok(&repo_path, &["move", "--to", "@-"]); 380 insta::assert_snapshot!(get_description(&test_env, &repo_path, "@-"), @""); 381 382 // If the destination's description is empty and the source's description is 383 // non-empty, the resulting description is from the source 384 test_env.jj_cmd_ok(&repo_path, &["undo"]); 385 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "source"]); 386 test_env.jj_cmd_ok(&repo_path, &["move", "--to", "@-"]); 387 insta::assert_snapshot!(get_description(&test_env, &repo_path, "@-"), @r###" 388 source 389 "###); 390 391 // If the destination's description is non-empty and the source's description is 392 // empty, the resulting description is from the destination 393 test_env.jj_cmd_ok(&repo_path, &["op", "restore", "@--"]); 394 test_env.jj_cmd_ok(&repo_path, &["describe", "@-", "-m", "destination"]); 395 test_env.jj_cmd_ok(&repo_path, &["move", "--to", "@-"]); 396 insta::assert_snapshot!(get_description(&test_env, &repo_path, "@-"), @r###" 397 destination 398 "###); 399 400 // If both descriptions were non-empty, we get asked for a combined description 401 test_env.jj_cmd_ok(&repo_path, &["undo"]); 402 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "source"]); 403 std::fs::write(&edit_script, "dump editor0").unwrap(); 404 test_env.jj_cmd_ok(&repo_path, &["move", "--to", "@-"]); 405 insta::assert_snapshot!(get_description(&test_env, &repo_path, "@-"), @r###" 406 destination 407 408 source 409 "###); 410 insta::assert_snapshot!( 411 std::fs::read_to_string(test_env.env_root().join("editor0")).unwrap(), @r###" 412 JJ: Enter a description for the combined commit. 413 JJ: Description from the destination commit: 414 destination 415 416 JJ: Description from source commit: 417 source 418 419 JJ: Lines starting with "JJ: " (like this one) will be removed. 420 "###); 421 422 // If the source's *content* doesn't become empty, then the source remains and 423 // both descriptions are unchanged 424 test_env.jj_cmd_ok(&repo_path, &["undo"]); 425 std::fs::write(repo_path.join("file2"), "b\n").unwrap(); 426 test_env.jj_cmd_ok(&repo_path, &["move", "--to", "@-", "file1"]); 427 insta::assert_snapshot!(get_description(&test_env, &repo_path, "@-"), @r###" 428 destination 429 "###); 430 insta::assert_snapshot!(get_description(&test_env, &repo_path, "@"), @r###" 431 source 432 "###); 433} 434 435fn get_description(test_env: &TestEnvironment, repo_path: &Path, rev: &str) -> String { 436 test_env.jj_cmd_success( 437 repo_path, 438 &["log", "--no-graph", "-T", "description", "-r", rev], 439 ) 440}