just playing with tangled
1// Copyright 2024 The Jujutsu Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// https://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15use crate::common::CommandOutput;
16use crate::common::TestEnvironment;
17use crate::common::TestWorkDir;
18
19#[test]
20fn test_absorb_simple() {
21 let test_env = TestEnvironment::default();
22 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
23 let work_dir = test_env.work_dir("repo");
24
25 work_dir.run_jj(["describe", "-m0"]).success();
26 work_dir.write_file("file1", "");
27
28 work_dir.run_jj(["new", "-m1"]).success();
29 work_dir.write_file("file1", "1a\n1b\n");
30
31 work_dir.run_jj(["new", "-m2"]).success();
32 work_dir.write_file("file1", "1a\n1b\n2a\n2b\n");
33
34 // Empty commit
35 work_dir.run_jj(["new"]).success();
36 let output = work_dir.run_jj(["absorb"]);
37 insta::assert_snapshot!(output, @r"
38 ------- stderr -------
39 Nothing changed.
40 [EOF]
41 ");
42
43 // Insert first and last lines
44 work_dir.write_file("file1", "1X\n1a\n1b\n2a\n2b\n2Z\n");
45 let output = work_dir.run_jj(["absorb"]);
46 insta::assert_snapshot!(output, @r"
47 ------- stderr -------
48 Absorbed changes into 2 revisions:
49 zsuskuln 3027ca7a 2
50 kkmpptxz d0f1e8dd 1
51 Working copy (@) now at: yqosqzyt 277bed24 (empty) (no description set)
52 Parent commit (@-) : zsuskuln 3027ca7a 2
53 [EOF]
54 ");
55
56 // Modify middle line in hunk
57 work_dir.write_file("file1", "1X\n1A\n1b\n2a\n2b\n2Z\n");
58 let output = work_dir.run_jj(["absorb"]);
59 insta::assert_snapshot!(output, @r"
60 ------- stderr -------
61 Absorbed changes into 1 revisions:
62 kkmpptxz d366d92c 1
63 Rebased 1 descendant commits.
64 Working copy (@) now at: vruxwmqv 32eb72fe (empty) (no description set)
65 Parent commit (@-) : zsuskuln 5bf0bc06 2
66 [EOF]
67 ");
68
69 // Remove middle line from hunk
70 work_dir.write_file("file1", "1X\n1A\n1b\n2a\n2Z\n");
71 let output = work_dir.run_jj(["absorb"]);
72 insta::assert_snapshot!(output, @r"
73 ------- stderr -------
74 Absorbed changes into 1 revisions:
75 zsuskuln 6e2c4777 2
76 Working copy (@) now at: yostqsxw 4a48490c (empty) (no description set)
77 Parent commit (@-) : zsuskuln 6e2c4777 2
78 [EOF]
79 ");
80
81 // Insert ambiguous line in between
82 work_dir.write_file("file1", "1X\n1A\n1b\nY\n2a\n2Z\n");
83 let output = work_dir.run_jj(["absorb"]);
84 insta::assert_snapshot!(output, @r"
85 ------- stderr -------
86 Nothing changed.
87 [EOF]
88 ");
89
90 insta::assert_snapshot!(get_diffs(&work_dir, "mutable()"), @r"
91 @ yostqsxw 80965bcc (no description set)
92 │ diff --git a/file1 b/file1
93 │ index 8653ca354d..88eb438902 100644
94 │ --- a/file1
95 │ +++ b/file1
96 │ @@ -1,5 +1,6 @@
97 │ 1X
98 │ 1A
99 │ 1b
100 │ +Y
101 │ 2a
102 │ 2Z
103 ○ zsuskuln 6e2c4777 2
104 │ diff --git a/file1 b/file1
105 │ index ed237b5112..8653ca354d 100644
106 │ --- a/file1
107 │ +++ b/file1
108 │ @@ -1,3 +1,5 @@
109 │ 1X
110 │ 1A
111 │ 1b
112 │ +2a
113 │ +2Z
114 ○ kkmpptxz d366d92c 1
115 │ diff --git a/file1 b/file1
116 │ index e69de29bb2..ed237b5112 100644
117 │ --- a/file1
118 │ +++ b/file1
119 │ @@ -0,0 +1,3 @@
120 │ +1X
121 │ +1A
122 │ +1b
123 ○ qpvuntsm 1a4edb91 0
124 │ diff --git a/file1 b/file1
125 ~ new file mode 100644
126 index 0000000000..e69de29bb2
127 [EOF]
128 ");
129 insta::assert_snapshot!(get_evolog(&work_dir, "description(1)"), @r"
130 ○ kkmpptxz d366d92c 1
131 ├─╮
132 │ ○ yqosqzyt hidden c506fbc7 (no description set)
133 │ ○ yqosqzyt hidden 277bed24 (empty) (no description set)
134 ○ kkmpptxz hidden d0f1e8dd 1
135 ├─╮
136 │ ○ mzvwutvl hidden 8935ee61 (no description set)
137 │ ○ mzvwutvl hidden 2bc3d2ce (empty) (no description set)
138 ○ kkmpptxz hidden ee76d790 1
139 ○ kkmpptxz hidden 677e62d5 (empty) 1
140 [EOF]
141 ");
142 insta::assert_snapshot!(get_evolog(&work_dir, "description(2)"), @r"
143 ○ zsuskuln 6e2c4777 2
144 ├─╮
145 │ ○ vruxwmqv hidden 7b1da5cd (no description set)
146 │ ○ vruxwmqv hidden 32eb72fe (empty) (no description set)
147 ○ zsuskuln hidden 5bf0bc06 2
148 ○ zsuskuln hidden 3027ca7a 2
149 ├─╮
150 │ ○ mzvwutvl hidden 8935ee61 (no description set)
151 │ ○ mzvwutvl hidden 2bc3d2ce (empty) (no description set)
152 ○ zsuskuln hidden cca09b4d 2
153 ○ zsuskuln hidden 7b092471 (empty) 2
154 [EOF]
155 ");
156}
157
158#[test]
159fn test_absorb_replace_single_line_hunk() {
160 let test_env = TestEnvironment::default();
161 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
162 let work_dir = test_env.work_dir("repo");
163
164 work_dir.run_jj(["describe", "-m1"]).success();
165 work_dir.write_file("file1", "1a\n");
166
167 work_dir.run_jj(["new", "-m2"]).success();
168 work_dir.write_file("file1", "2a\n1a\n2b\n");
169
170 // Replace single-line hunk, which produces a conflict right now. If our
171 // merge logic were based on interleaved delta, the hunk would be applied
172 // cleanly.
173 work_dir.run_jj(["new"]).success();
174 work_dir.write_file("file1", "2a\n1A\n2b\n");
175 let output = work_dir.run_jj(["absorb"]);
176 insta::assert_snapshot!(output, @r"
177 ------- stderr -------
178 Absorbed changes into 1 revisions:
179 qpvuntsm 7e885236 (conflict) 1
180 Rebased 1 descendant commits.
181 Working copy (@) now at: mzvwutvl e9c3b95b (empty) (no description set)
182 Parent commit (@-) : kkmpptxz 7c36845c 2
183 New conflicts appeared in 1 commits:
184 qpvuntsm 7e885236 (conflict) 1
185 Hint: To resolve the conflicts, start by updating to it:
186 jj new qpvuntsm
187 Then use `jj resolve`, or edit the conflict markers in the file directly.
188 Once the conflicts are resolved, you may want to inspect the result with `jj diff`.
189 Then run `jj squash` to move the resolution into the conflicted commit.
190 [EOF]
191 ");
192
193 insta::assert_snapshot!(get_diffs(&work_dir, "mutable()"), @r"
194 @ mzvwutvl e9c3b95b (empty) (no description set)
195 ○ kkmpptxz 7c36845c 2
196 │ diff --git a/file1 b/file1
197 │ index 0000000000..2f87e8e465 100644
198 │ --- a/file1
199 │ +++ b/file1
200 │ @@ -1,10 +1,3 @@
201 │ -<<<<<<< Conflict 1 of 1
202 │ -%%%%%%% Changes from base to side #1
203 │ --2a
204 │ - 1a
205 │ --2b
206 │ -+++++++ Contents of side #2
207 │ 2a
208 │ 1A
209 │ 2b
210 │ ->>>>>>> Conflict 1 of 1 ends
211 × qpvuntsm 7e885236 (conflict) 1
212 │ diff --git a/file1 b/file1
213 ~ new file mode 100644
214 index 0000000000..0000000000
215 --- /dev/null
216 +++ b/file1
217 @@ -0,0 +1,10 @@
218 +<<<<<<< Conflict 1 of 1
219 +%%%%%%% Changes from base to side #1
220 +-2a
221 + 1a
222 +-2b
223 ++++++++ Contents of side #2
224 +2a
225 +1A
226 +2b
227 +>>>>>>> Conflict 1 of 1 ends
228 [EOF]
229 ");
230}
231
232#[test]
233fn test_absorb_merge() {
234 let test_env = TestEnvironment::default();
235 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
236 let work_dir = test_env.work_dir("repo");
237
238 work_dir.run_jj(["describe", "-m0"]).success();
239 work_dir.write_file("file1", "0a\n");
240
241 work_dir.run_jj(["new", "-m1"]).success();
242 work_dir.write_file("file1", "1a\n1b\n0a\n");
243
244 work_dir.run_jj(["new", "-m2", "description(0)"]).success();
245 work_dir.write_file("file1", "0a\n2a\n2b\n");
246
247 let output = work_dir.run_jj(["new", "-m3", "description(1)", "description(2)"]);
248 insta::assert_snapshot!(output, @r"
249 ------- stderr -------
250 Working copy (@) now at: mzvwutvl 08898161 (empty) 3
251 Parent commit (@-) : kkmpptxz 7e9df299 1
252 Parent commit (@-) : zsuskuln baf056cf 2
253 Added 0 files, modified 1 files, removed 0 files
254 [EOF]
255 ");
256
257 // Modify first and last lines, absorb from merge
258 work_dir.write_file("file1", "1A\n1b\n0a\n2a\n2B\n");
259 let output = work_dir.run_jj(["absorb"]);
260 insta::assert_snapshot!(output, @r"
261 ------- stderr -------
262 Absorbed changes into 2 revisions:
263 zsuskuln 71d1ee56 2
264 kkmpptxz 4d379399 1
265 Rebased 1 descendant commits.
266 Working copy (@) now at: mzvwutvl 9db19b54 (empty) 3
267 Parent commit (@-) : kkmpptxz 4d379399 1
268 Parent commit (@-) : zsuskuln 71d1ee56 2
269 [EOF]
270 ");
271
272 // Add hunk to merge revision
273 work_dir.write_file("file2", "3a\n");
274
275 // Absorb into merge
276 work_dir.run_jj(["new"]).success();
277 work_dir.write_file("file2", "3A\n");
278 let output = work_dir.run_jj(["absorb"]);
279 insta::assert_snapshot!(output, @r"
280 ------- stderr -------
281 Absorbed changes into 1 revisions:
282 mzvwutvl e93c0210 3
283 Working copy (@) now at: vruxwmqv 1b10dfa4 (empty) (no description set)
284 Parent commit (@-) : mzvwutvl e93c0210 3
285 [EOF]
286 ");
287
288 insta::assert_snapshot!(get_diffs(&work_dir, "mutable()"), @r"
289 @ vruxwmqv 1b10dfa4 (empty) (no description set)
290 ○ mzvwutvl e93c0210 3
291 ├─╮ diff --git a/file2 b/file2
292 │ │ new file mode 100644
293 │ │ index 0000000000..44442d2d7b
294 │ │ --- /dev/null
295 │ │ +++ b/file2
296 │ │ @@ -0,0 +1,1 @@
297 │ │ +3A
298 │ ○ zsuskuln 71d1ee56 2
299 │ │ diff --git a/file1 b/file1
300 │ │ index eb6e8821f1..4907935b9f 100644
301 │ │ --- a/file1
302 │ │ +++ b/file1
303 │ │ @@ -1,1 +1,3 @@
304 │ │ 0a
305 │ │ +2a
306 │ │ +2B
307 ○ │ kkmpptxz 4d379399 1
308 ├─╯ diff --git a/file1 b/file1
309 │ index eb6e8821f1..902dd8ef13 100644
310 │ --- a/file1
311 │ +++ b/file1
312 │ @@ -1,1 +1,3 @@
313 │ +1A
314 │ +1b
315 │ 0a
316 ○ qpvuntsm 3777b700 0
317 │ diff --git a/file1 b/file1
318 ~ new file mode 100644
319 index 0000000000..eb6e8821f1
320 --- /dev/null
321 +++ b/file1
322 @@ -0,0 +1,1 @@
323 +0a
324 [EOF]
325 ");
326}
327
328#[test]
329fn test_absorb_discardable_merge_with_descendant() {
330 let test_env = TestEnvironment::default();
331 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
332 let work_dir = test_env.work_dir("repo");
333
334 work_dir.run_jj(["describe", "-m0"]).success();
335 work_dir.write_file("file1", "0a\n");
336
337 work_dir.run_jj(["new", "-m1"]).success();
338 work_dir.write_file("file1", "1a\n1b\n0a\n");
339
340 work_dir.run_jj(["new", "-m2", "description(0)"]).success();
341 work_dir.write_file("file1", "0a\n2a\n2b\n");
342
343 let output = work_dir.run_jj(["new", "description(1)", "description(2)"]);
344 insta::assert_snapshot!(output, @r"
345 ------- stderr -------
346 Working copy (@) now at: mzvwutvl f59b2364 (empty) (no description set)
347 Parent commit (@-) : kkmpptxz 7e9df299 1
348 Parent commit (@-) : zsuskuln baf056cf 2
349 Added 0 files, modified 1 files, removed 0 files
350 [EOF]
351 ");
352
353 // Modify first and last lines in the merge commit
354 work_dir.write_file("file1", "1A\n1b\n0a\n2a\n2B\n");
355 // Add new commit on top
356 work_dir.run_jj(["new", "-m3"]).success();
357 work_dir.write_file("file2", "3a\n");
358 // Then absorb the merge commit
359 let output = work_dir.run_jj(["absorb", "--from=@-"]);
360 insta::assert_snapshot!(output, @r"
361 ------- stderr -------
362 Absorbed changes into 2 revisions:
363 zsuskuln 02668cf6 2
364 kkmpptxz fcabe394 1
365 Rebased 1 descendant commits.
366 Working copy (@) now at: royxmykx f04f1247 3
367 Parent commit (@-) : kkmpptxz fcabe394 1
368 Parent commit (@-) : zsuskuln 02668cf6 2
369 [EOF]
370 ");
371
372 insta::assert_snapshot!(get_diffs(&work_dir, "mutable()"), @r"
373 @ royxmykx f04f1247 3
374 ├─╮ diff --git a/file2 b/file2
375 │ │ new file mode 100644
376 │ │ index 0000000000..31cd755d20
377 │ │ --- /dev/null
378 │ │ +++ b/file2
379 │ │ @@ -0,0 +1,1 @@
380 │ │ +3a
381 │ ○ zsuskuln 02668cf6 2
382 │ │ diff --git a/file1 b/file1
383 │ │ index eb6e8821f1..4907935b9f 100644
384 │ │ --- a/file1
385 │ │ +++ b/file1
386 │ │ @@ -1,1 +1,3 @@
387 │ │ 0a
388 │ │ +2a
389 │ │ +2B
390 ○ │ kkmpptxz fcabe394 1
391 ├─╯ diff --git a/file1 b/file1
392 │ index eb6e8821f1..902dd8ef13 100644
393 │ --- a/file1
394 │ +++ b/file1
395 │ @@ -1,1 +1,3 @@
396 │ +1A
397 │ +1b
398 │ 0a
399 ○ qpvuntsm 3777b700 0
400 │ diff --git a/file1 b/file1
401 ~ new file mode 100644
402 index 0000000000..eb6e8821f1
403 --- /dev/null
404 +++ b/file1
405 @@ -0,0 +1,1 @@
406 +0a
407 [EOF]
408 ");
409}
410
411#[test]
412fn test_absorb_conflict() {
413 let test_env = TestEnvironment::default();
414 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
415 let work_dir = test_env.work_dir("repo");
416
417 work_dir.run_jj(["describe", "-m1"]).success();
418 work_dir.write_file("file1", "1a\n1b\n");
419
420 work_dir.run_jj(["new", "root()"]).success();
421 work_dir.write_file("file1", "2a\n2b\n");
422 let output = work_dir.run_jj(["rebase", "-r@", "-ddescription(1)"]);
423 insta::assert_snapshot!(output, @r"
424 ------- stderr -------
425 Rebased 1 commits onto destination
426 Working copy (@) now at: kkmpptxz 74405a07 (conflict) (no description set)
427 Parent commit (@-) : qpvuntsm 3619e4e5 1
428 Added 0 files, modified 1 files, removed 0 files
429 Warning: There are unresolved conflicts at these paths:
430 file1 2-sided conflict
431 New conflicts appeared in 1 commits:
432 kkmpptxz 74405a07 (conflict) (no description set)
433 Hint: To resolve the conflicts, start by updating to it:
434 jj new kkmpptxz
435 Then use `jj resolve`, or edit the conflict markers in the file directly.
436 Once the conflicts are resolved, you may want to inspect the result with `jj diff`.
437 Then run `jj squash` to move the resolution into the conflicted commit.
438 [EOF]
439 ");
440
441 let conflict_content = work_dir.read_file("file1");
442 insta::assert_snapshot!(conflict_content, @r"
443 <<<<<<< Conflict 1 of 1
444 %%%%%%% Changes from base to side #1
445 +1a
446 +1b
447 +++++++ Contents of side #2
448 2a
449 2b
450 >>>>>>> Conflict 1 of 1 ends
451 ");
452
453 // Cannot absorb from conflict
454 let output = work_dir.run_jj(["absorb"]);
455 insta::assert_snapshot!(output, @r"
456 ------- stderr -------
457 Warning: Skipping file1: Is a conflict
458 Nothing changed.
459 [EOF]
460 ");
461
462 // Cannot absorb from resolved conflict
463 work_dir.run_jj(["new"]).success();
464 work_dir.write_file("file1", "1A\n1b\n2a\n2B\n");
465 let output = work_dir.run_jj(["absorb"]);
466 insta::assert_snapshot!(output, @r"
467 ------- stderr -------
468 Warning: Skipping file1: Is a conflict
469 Nothing changed.
470 [EOF]
471 ");
472}
473
474#[test]
475fn test_absorb_deleted_file() {
476 let test_env = TestEnvironment::default();
477 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
478 let work_dir = test_env.work_dir("repo");
479
480 work_dir.run_jj(["describe", "-m1"]).success();
481 work_dir.write_file("file1", "1a\n");
482
483 work_dir.run_jj(["new"]).success();
484 work_dir.remove_file("file1");
485
486 let output = work_dir.run_jj(["absorb"]);
487 insta::assert_snapshot!(output, @r"
488 ------- stderr -------
489 Warning: Skipping file1: Deleted file
490 Nothing changed.
491 [EOF]
492 ");
493}
494
495#[test]
496fn test_absorb_file_mode() {
497 let test_env = TestEnvironment::default();
498 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
499 let work_dir = test_env.work_dir("repo");
500
501 work_dir.run_jj(["describe", "-m1"]).success();
502 work_dir.write_file("file1", "1a\n");
503 work_dir.run_jj(["file", "chmod", "x", "file1"]).success();
504
505 // Modify content and mode
506 work_dir.run_jj(["new"]).success();
507 work_dir.write_file("file1", "1A\n");
508 work_dir.run_jj(["file", "chmod", "n", "file1"]).success();
509
510 // Mode change shouldn't be absorbed
511 let output = work_dir.run_jj(["absorb"]);
512 insta::assert_snapshot!(output, @r"
513 ------- stderr -------
514 Absorbed changes into 1 revisions:
515 qpvuntsm 991365da 1
516 Rebased 1 descendant commits.
517 Working copy (@) now at: zsuskuln 77de368e (no description set)
518 Parent commit (@-) : qpvuntsm 991365da 1
519 Remaining changes:
520 M file1
521 [EOF]
522 ");
523
524 insta::assert_snapshot!(get_diffs(&work_dir, "mutable()"), @r"
525 @ zsuskuln 77de368e (no description set)
526 │ diff --git a/file1 b/file1
527 │ old mode 100755
528 │ new mode 100644
529 ○ qpvuntsm 991365da 1
530 │ diff --git a/file1 b/file1
531 ~ new file mode 100755
532 index 0000000000..268de3f3ec
533 --- /dev/null
534 +++ b/file1
535 @@ -0,0 +1,1 @@
536 +1A
537 [EOF]
538 ");
539}
540
541#[test]
542fn test_absorb_from_into() {
543 let test_env = TestEnvironment::default();
544 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
545 let work_dir = test_env.work_dir("repo");
546
547 work_dir.run_jj(["new", "-m1"]).success();
548 work_dir.write_file("file1", "1a\n1b\n1c\n");
549
550 work_dir.run_jj(["new", "-m2"]).success();
551 work_dir.write_file("file1", "1a\n2a\n1b\n1c\n2b\n");
552
553 // Line "X" and "Z" have unambiguous adjacent line within the destinations
554 // range. Line "Y" doesn't have such line.
555 work_dir.run_jj(["new"]).success();
556 work_dir.write_file("file1", "1a\nX\n2a\n1b\nY\n1c\n2b\nZ\n");
557 let output = work_dir.run_jj(["absorb", "--into=@-"]);
558 insta::assert_snapshot!(output, @r"
559 ------- stderr -------
560 Absorbed changes into 1 revisions:
561 kkmpptxz 91df4543 2
562 Rebased 1 descendant commits.
563 Working copy (@) now at: zsuskuln d5424357 (no description set)
564 Parent commit (@-) : kkmpptxz 91df4543 2
565 Remaining changes:
566 M file1
567 [EOF]
568 ");
569
570 insta::assert_snapshot!(get_diffs(&work_dir, "@-::"), @r"
571 @ zsuskuln d5424357 (no description set)
572 │ diff --git a/file1 b/file1
573 │ index faf62af049..c2d0b12547 100644
574 │ --- a/file1
575 │ +++ b/file1
576 │ @@ -2,6 +2,7 @@
577 │ X
578 │ 2a
579 │ 1b
580 │ +Y
581 │ 1c
582 │ 2b
583 │ Z
584 ○ kkmpptxz 91df4543 2
585 │ diff --git a/file1 b/file1
586 ~ index 352e9b3794..faf62af049 100644
587 --- a/file1
588 +++ b/file1
589 @@ -1,3 +1,7 @@
590 1a
591 +X
592 +2a
593 1b
594 1c
595 +2b
596 +Z
597 [EOF]
598 ");
599
600 // Absorb all lines from the working-copy parent. An empty commit won't be
601 // discarded because "absorb" isn't a command to squash commit descriptions.
602 let output = work_dir.run_jj(["absorb", "--from=@-"]);
603 insta::assert_snapshot!(output, @r"
604 ------- stderr -------
605 Absorbed changes into 1 revisions:
606 rlvkpnrz 3a5fd02e 1
607 Rebased 2 descendant commits.
608 Working copy (@) now at: zsuskuln 53ce490b (no description set)
609 Parent commit (@-) : kkmpptxz c94cd773 (empty) 2
610 [EOF]
611 ");
612
613 insta::assert_snapshot!(get_diffs(&work_dir, "mutable()"), @r"
614 @ zsuskuln 53ce490b (no description set)
615 │ diff --git a/file1 b/file1
616 │ index faf62af049..c2d0b12547 100644
617 │ --- a/file1
618 │ +++ b/file1
619 │ @@ -2,6 +2,7 @@
620 │ X
621 │ 2a
622 │ 1b
623 │ +Y
624 │ 1c
625 │ 2b
626 │ Z
627 ○ kkmpptxz c94cd773 (empty) 2
628 ○ rlvkpnrz 3a5fd02e 1
629 │ diff --git a/file1 b/file1
630 │ new file mode 100644
631 │ index 0000000000..faf62af049
632 │ --- /dev/null
633 │ +++ b/file1
634 │ @@ -0,0 +1,7 @@
635 │ +1a
636 │ +X
637 │ +2a
638 │ +1b
639 │ +1c
640 │ +2b
641 │ +Z
642 ○ qpvuntsm 230dd059 (empty) (no description set)
643 │
644 ~
645 [EOF]
646 ");
647}
648
649#[test]
650fn test_absorb_paths() {
651 let test_env = TestEnvironment::default();
652 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
653 let work_dir = test_env.work_dir("repo");
654
655 work_dir.run_jj(["describe", "-m1"]).success();
656 work_dir.write_file("file1", "1a\n");
657 work_dir.write_file("file2", "1a\n");
658
659 // Modify both files
660 work_dir.run_jj(["new"]).success();
661 work_dir.write_file("file1", "1A\n");
662 work_dir.write_file("file2", "1A\n");
663
664 let output = work_dir.run_jj(["absorb", "unknown"]);
665 insta::assert_snapshot!(output, @r"
666 ------- stderr -------
667 Nothing changed.
668 [EOF]
669 ");
670
671 let output = work_dir.run_jj(["absorb", "file1"]);
672 insta::assert_snapshot!(output, @r"
673 ------- stderr -------
674 Absorbed changes into 1 revisions:
675 qpvuntsm ae044adb 1
676 Rebased 1 descendant commits.
677 Working copy (@) now at: kkmpptxz c6f31836 (no description set)
678 Parent commit (@-) : qpvuntsm ae044adb 1
679 Remaining changes:
680 M file2
681 [EOF]
682 ");
683
684 insta::assert_snapshot!(get_diffs(&work_dir, "mutable()"), @r"
685 @ kkmpptxz c6f31836 (no description set)
686 │ diff --git a/file2 b/file2
687 │ index a8994dc188..268de3f3ec 100644
688 │ --- a/file2
689 │ +++ b/file2
690 │ @@ -1,1 +1,1 @@
691 │ -1a
692 │ +1A
693 ○ qpvuntsm ae044adb 1
694 │ diff --git a/file1 b/file1
695 ~ new file mode 100644
696 index 0000000000..268de3f3ec
697 --- /dev/null
698 +++ b/file1
699 @@ -0,0 +1,1 @@
700 +1A
701 diff --git a/file2 b/file2
702 new file mode 100644
703 index 0000000000..a8994dc188
704 --- /dev/null
705 +++ b/file2
706 @@ -0,0 +1,1 @@
707 +1a
708 [EOF]
709 ");
710}
711
712#[test]
713fn test_absorb_immutable() {
714 let test_env = TestEnvironment::default();
715 test_env.run_jj_in(".", ["git", "init", "repo"]).success();
716 let work_dir = test_env.work_dir("repo");
717 test_env.add_config("revset-aliases.'immutable_heads()' = 'present(main)'");
718
719 work_dir.run_jj(["describe", "-m1"]).success();
720 work_dir.write_file("file1", "1a\n1b\n");
721
722 work_dir.run_jj(["new", "-m2"]).success();
723 work_dir
724 .run_jj(["bookmark", "set", "-r@-", "main"])
725 .success();
726 work_dir.write_file("file1", "1a\n1b\n2a\n2b\n");
727
728 work_dir.run_jj(["new"]).success();
729 work_dir.write_file("file1", "1A\n1b\n2a\n2B\n");
730
731 // Immutable revisions are excluded by default
732 let output = work_dir.run_jj(["absorb"]);
733 insta::assert_snapshot!(output, @r"
734 ------- stderr -------
735 Absorbed changes into 1 revisions:
736 kkmpptxz d80e3c2a 2
737 Rebased 1 descendant commits.
738 Working copy (@) now at: mzvwutvl 3021153d (no description set)
739 Parent commit (@-) : kkmpptxz d80e3c2a 2
740 Remaining changes:
741 M file1
742 [EOF]
743 ");
744
745 // Immutable revisions shouldn't be rewritten
746 let output = work_dir.run_jj(["absorb", "--into=all()"]);
747 insta::assert_snapshot!(output, @r#"
748 ------- stderr -------
749 Error: Commit 3619e4e52fce is immutable
750 Hint: Could not modify commit: qpvuntsm 3619e4e5 main | 1
751 Hint: Immutable commits are used to protect shared history.
752 Hint: For more information, see:
753 - https://jj-vcs.github.io/jj/latest/config/#set-of-immutable-commits
754 - `jj help -k config`, "Set of immutable commits"
755 Hint: This operation would rewrite 1 immutable commits.
756 [EOF]
757 [exit status: 1]
758 "#);
759
760 insta::assert_snapshot!(get_diffs(&work_dir, ".."), @r"
761 @ mzvwutvl 3021153d (no description set)
762 │ diff --git a/file1 b/file1
763 │ index 75e4047831..428796ca20 100644
764 │ --- a/file1
765 │ +++ b/file1
766 │ @@ -1,4 +1,4 @@
767 │ -1a
768 │ +1A
769 │ 1b
770 │ 2a
771 │ 2B
772 ○ kkmpptxz d80e3c2a 2
773 │ diff --git a/file1 b/file1
774 │ index 8c5268f893..75e4047831 100644
775 │ --- a/file1
776 │ +++ b/file1
777 │ @@ -1,2 +1,4 @@
778 │ 1a
779 │ 1b
780 │ +2a
781 │ +2B
782 ◆ qpvuntsm 3619e4e5 1
783 │ diff --git a/file1 b/file1
784 ~ new file mode 100644
785 index 0000000000..8c5268f893
786 --- /dev/null
787 +++ b/file1
788 @@ -0,0 +1,2 @@
789 +1a
790 +1b
791 [EOF]
792 ");
793}
794
795#[must_use]
796fn get_diffs(work_dir: &TestWorkDir, revision: &str) -> CommandOutput {
797 let template = r#"format_commit_summary_with_refs(self, "") ++ "\n""#;
798 work_dir.run_jj(["log", "-r", revision, "-T", template, "--git"])
799}
800
801#[must_use]
802fn get_evolog(work_dir: &TestWorkDir, revision: &str) -> CommandOutput {
803 let template = r#"format_commit_summary_with_refs(self, "") ++ "\n""#;
804 work_dir.run_jj(["evolog", "-r", revision, "-T", template])
805}