just playing with tangled
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
19fn create_commit(test_env: &TestEnvironment, repo_path: &Path, name: &str, parents: &[&str]) {
20 if parents.is_empty() {
21 test_env.jj_cmd_ok(repo_path, &["new", "root()", "-m", name]);
22 } else {
23 let mut args = vec!["new", "-m", name];
24 args.extend(parents);
25 test_env.jj_cmd_ok(repo_path, &args);
26 }
27 std::fs::write(repo_path.join(name), format!("{name}\n")).unwrap();
28 test_env.jj_cmd_ok(repo_path, &["branch", "create", name]);
29}
30
31#[test]
32fn test_rebase_invalid() {
33 let test_env = TestEnvironment::default();
34 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
35 let repo_path = test_env.env_root().join("repo");
36
37 create_commit(&test_env, &repo_path, "a", &[]);
38 create_commit(&test_env, &repo_path, "b", &["a"]);
39
40 // Missing destination
41 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["rebase"]);
42 insta::assert_snapshot!(stderr, @r###"
43 error: the following required arguments were not provided:
44 <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
45
46 Usage: jj rebase <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
47
48 For more information, try '--help'.
49 "###);
50
51 // Both -r and -s
52 let stderr =
53 test_env.jj_cmd_cli_error(&repo_path, &["rebase", "-r", "a", "-s", "a", "-d", "b"]);
54 insta::assert_snapshot!(stderr, @r###"
55 error: the argument '--revisions <REVISIONS>' cannot be used with '--source <SOURCE>'
56
57 Usage: jj rebase --revisions <REVISIONS> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
58
59 For more information, try '--help'.
60 "###);
61
62 // Both -b and -s
63 let stderr =
64 test_env.jj_cmd_cli_error(&repo_path, &["rebase", "-b", "a", "-s", "a", "-d", "b"]);
65 insta::assert_snapshot!(stderr, @r###"
66 error: the argument '--branch <BRANCH>' cannot be used with '--source <SOURCE>'
67
68 Usage: jj rebase --branch <BRANCH> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
69
70 For more information, try '--help'.
71 "###);
72
73 // Both -r and --skip-empty
74 let stderr = test_env.jj_cmd_cli_error(
75 &repo_path,
76 &["rebase", "-r", "a", "-d", "b", "--skip-empty"],
77 );
78 insta::assert_snapshot!(stderr, @r###"
79 error: the argument '--revisions <REVISIONS>' cannot be used with '--skip-empty'
80
81 Usage: jj rebase --revisions <REVISIONS> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
82
83 For more information, try '--help'.
84 "###);
85
86 // Both -d and --after
87 let stderr = test_env.jj_cmd_cli_error(
88 &repo_path,
89 &["rebase", "-r", "a", "-d", "b", "--after", "b"],
90 );
91 insta::assert_snapshot!(stderr, @r###"
92 error: the argument '--destination <DESTINATION>' cannot be used with '--insert-after <INSERT_AFTER>'
93
94 Usage: jj rebase --revisions <REVISIONS> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
95
96 For more information, try '--help'.
97 "###);
98
99 // -s with --after
100 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["rebase", "-s", "a", "--after", "b"]);
101 insta::assert_snapshot!(stderr, @r###"
102 error: the argument '--source <SOURCE>' cannot be used with '--insert-after <INSERT_AFTER>'
103
104 Usage: jj rebase --source <SOURCE> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
105
106 For more information, try '--help'.
107 "###);
108
109 // -b with --after
110 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["rebase", "-b", "a", "--after", "b"]);
111 insta::assert_snapshot!(stderr, @r###"
112 error: the argument '--branch <BRANCH>' cannot be used with '--insert-after <INSERT_AFTER>'
113
114 Usage: jj rebase --branch <BRANCH> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
115
116 For more information, try '--help'.
117 "###);
118
119 // Both -d and --before
120 let stderr = test_env.jj_cmd_cli_error(
121 &repo_path,
122 &["rebase", "-r", "a", "-d", "b", "--before", "b"],
123 );
124 insta::assert_snapshot!(stderr, @r###"
125 error: the argument '--destination <DESTINATION>' cannot be used with '--insert-before <INSERT_BEFORE>'
126
127 Usage: jj rebase --revisions <REVISIONS> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
128
129 For more information, try '--help'.
130 "###);
131
132 // -s with --before
133 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["rebase", "-s", "a", "--before", "b"]);
134 insta::assert_snapshot!(stderr, @r###"
135 error: the argument '--source <SOURCE>' cannot be used with '--insert-before <INSERT_BEFORE>'
136
137 Usage: jj rebase --source <SOURCE> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
138
139 For more information, try '--help'.
140 "###);
141
142 // -b with --before
143 let stderr = test_env.jj_cmd_cli_error(&repo_path, &["rebase", "-b", "a", "--before", "b"]);
144 insta::assert_snapshot!(stderr, @r###"
145 error: the argument '--branch <BRANCH>' cannot be used with '--insert-before <INSERT_BEFORE>'
146
147 Usage: jj rebase --branch <BRANCH> <--destination <DESTINATION>|--insert-after <INSERT_AFTER>|--insert-before <INSERT_BEFORE>>
148
149 For more information, try '--help'.
150 "###);
151
152 // Rebase onto self with -r
153 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r", "a", "-d", "a"]);
154 insta::assert_snapshot!(stderr, @r###"
155 Error: Cannot rebase 2443ea76b0b1 onto itself
156 "###);
157
158 // Rebase root with -r
159 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r", "root()", "-d", "a"]);
160 insta::assert_snapshot!(stderr, @r###"
161 Error: The root commit 000000000000 is immutable
162 "###);
163
164 // Rebase onto descendant with -s
165 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s", "a", "-d", "b"]);
166 insta::assert_snapshot!(stderr, @r###"
167 Error: Cannot rebase 2443ea76b0b1 onto descendant 1394f625cbbd
168 "###);
169}
170
171#[test]
172fn test_rebase_branch() {
173 let test_env = TestEnvironment::default();
174 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
175 let repo_path = test_env.env_root().join("repo");
176
177 create_commit(&test_env, &repo_path, "a", &[]);
178 create_commit(&test_env, &repo_path, "b", &["a"]);
179 create_commit(&test_env, &repo_path, "c", &["b"]);
180 create_commit(&test_env, &repo_path, "d", &["b"]);
181 create_commit(&test_env, &repo_path, "e", &["a"]);
182 // Test the setup
183 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
184 @ e
185 │ ◉ d
186 │ │ ◉ c
187 │ ├─╯
188 │ ◉ b
189 ├─╯
190 ◉ a
191 ◉
192 "###);
193
194 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b", "c", "-d", "e"]);
195 insta::assert_snapshot!(stdout, @"");
196 insta::assert_snapshot!(stderr, @r###"
197 Rebased 3 commits
198 "###);
199 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
200 ◉ d
201 │ ◉ c
202 ├─╯
203 ◉ b
204 @ e
205 ◉ a
206 ◉
207 "###);
208
209 // Test rebasing multiple branches at once
210 test_env.jj_cmd_ok(&repo_path, &["undo"]);
211 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b=e", "-b=d", "-d=b"]);
212 insta::assert_snapshot!(stdout, @"");
213 insta::assert_snapshot!(stderr, @r###"
214 Skipped rebase of 1 commits that were already in place
215 Rebased 1 commits
216 Working copy now at: znkkpsqq 9ca2a154 e | e
217 Parent commit : zsuskuln 1394f625 b | b
218 Added 1 files, modified 0 files, removed 0 files
219 "###);
220 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
221 @ e
222 │ ◉ d
223 ├─╯
224 │ ◉ c
225 ├─╯
226 ◉ b
227 ◉ a
228 ◉
229 "###);
230
231 // Same test but with more than one revision per argument
232 test_env.jj_cmd_ok(&repo_path, &["undo"]);
233 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-b=e|d", "-d=b"]);
234 insta::assert_snapshot!(stderr, @r###"
235 Error: Revset "e|d" resolved to more than one revision
236 Hint: The revset "e|d" resolved to these revisions:
237 znkkpsqq e52756c8 e | e
238 vruxwmqv 514fa6b2 d | d
239 Hint: Prefix the expression with 'all:' to allow any number of revisions (i.e. 'all:e|d').
240 "###);
241 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b=all:e|d", "-d=b"]);
242 insta::assert_snapshot!(stdout, @"");
243 insta::assert_snapshot!(stderr, @r###"
244 Skipped rebase of 1 commits that were already in place
245 Rebased 1 commits
246 Working copy now at: znkkpsqq 817e3fb0 e | e
247 Parent commit : zsuskuln 1394f625 b | b
248 Added 1 files, modified 0 files, removed 0 files
249 "###);
250 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
251 @ e
252 │ ◉ d
253 ├─╯
254 │ ◉ c
255 ├─╯
256 ◉ b
257 ◉ a
258 ◉
259 "###);
260}
261
262#[test]
263fn test_rebase_branch_with_merge() {
264 let test_env = TestEnvironment::default();
265 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
266 let repo_path = test_env.env_root().join("repo");
267
268 create_commit(&test_env, &repo_path, "a", &[]);
269 create_commit(&test_env, &repo_path, "b", &["a"]);
270 create_commit(&test_env, &repo_path, "c", &[]);
271 create_commit(&test_env, &repo_path, "d", &["c"]);
272 create_commit(&test_env, &repo_path, "e", &["a", "d"]);
273 // Test the setup
274 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
275 @ e
276 ├─╮
277 │ ◉ d
278 │ ◉ c
279 │ │ ◉ b
280 ├───╯
281 ◉ │ a
282 ├─╯
283 ◉
284 "###);
285
286 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b", "d", "-d", "b"]);
287 insta::assert_snapshot!(stdout, @"");
288 insta::assert_snapshot!(stderr, @r###"
289 Rebased 3 commits
290 Working copy now at: znkkpsqq 5f8a3db2 e | e
291 Parent commit : rlvkpnrz 2443ea76 a | a
292 Parent commit : vruxwmqv 1677f795 d | d
293 Added 1 files, modified 0 files, removed 0 files
294 "###);
295 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
296 @ e
297 ├─╮
298 │ ◉ d
299 │ ◉ c
300 │ ◉ b
301 ├─╯
302 ◉ a
303 ◉
304 "###);
305
306 test_env.jj_cmd_ok(&repo_path, &["undo"]);
307 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-d", "b"]);
308 insta::assert_snapshot!(stdout, @"");
309 insta::assert_snapshot!(stderr, @r###"
310 Rebased 3 commits
311 Working copy now at: znkkpsqq a331ac11 e | e
312 Parent commit : rlvkpnrz 2443ea76 a | a
313 Parent commit : vruxwmqv 3d0f3644 d | d
314 Added 1 files, modified 0 files, removed 0 files
315 "###);
316 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
317 @ e
318 ├─╮
319 │ ◉ d
320 │ ◉ c
321 │ ◉ b
322 ├─╯
323 ◉ a
324 ◉
325 "###);
326}
327
328#[test]
329fn test_rebase_single_revision() {
330 let test_env = TestEnvironment::default();
331 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
332 let repo_path = test_env.env_root().join("repo");
333
334 create_commit(&test_env, &repo_path, "a", &[]);
335 create_commit(&test_env, &repo_path, "b", &["a"]);
336 create_commit(&test_env, &repo_path, "c", &["a"]);
337 create_commit(&test_env, &repo_path, "d", &["b", "c"]);
338 create_commit(&test_env, &repo_path, "e", &["d"]);
339 // Test the setup
340 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
341 @ e
342 ◉ d
343 ├─╮
344 │ ◉ c
345 ◉ │ b
346 ├─╯
347 ◉ a
348 ◉
349 "###);
350
351 // Descendants of the rebased commit "c" should be rebased onto parents. First
352 // we test with a non-merge commit.
353 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "-d", "b"]);
354 insta::assert_snapshot!(stdout, @"");
355 insta::assert_snapshot!(stderr, @r###"
356 Rebased 1 commits onto destination
357 Rebased 2 descendant commits
358 Working copy now at: znkkpsqq 2668ffbe e | e
359 Parent commit : vruxwmqv 7b370c85 d | d
360 Added 0 files, modified 0 files, removed 1 files
361 "###);
362 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
363 @ e
364 ◉ d
365 ├─╮
366 │ │ ◉ c
367 ├───╯
368 ◉ │ b
369 ├─╯
370 ◉ a
371 ◉
372 "###);
373 test_env.jj_cmd_ok(&repo_path, &["undo"]);
374
375 // Now, let's try moving the merge commit. After, both parents of "d" ("b" and
376 // "c") should become parents of "e".
377 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "d", "-d", "a"]);
378 insta::assert_snapshot!(stdout, @"");
379 insta::assert_snapshot!(stderr, @r###"
380 Rebased 1 commits onto destination
381 Rebased 1 descendant commits
382 Working copy now at: znkkpsqq ed210c15 e | e
383 Parent commit : zsuskuln 1394f625 b | b
384 Parent commit : royxmykx c0cb3a0b c | c
385 Added 0 files, modified 0 files, removed 1 files
386 "###);
387 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
388 @ e
389 ├─╮
390 │ ◉ c
391 ◉ │ b
392 ├─╯
393 │ ◉ d
394 ├─╯
395 ◉ a
396 ◉
397 "###);
398}
399
400#[test]
401fn test_rebase_single_revision_merge_parent() {
402 let test_env = TestEnvironment::default();
403 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
404 let repo_path = test_env.env_root().join("repo");
405
406 create_commit(&test_env, &repo_path, "a", &[]);
407 create_commit(&test_env, &repo_path, "b", &[]);
408 create_commit(&test_env, &repo_path, "c", &["b"]);
409 create_commit(&test_env, &repo_path, "d", &["a", "c"]);
410 // Test the setup
411 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
412 @ d
413 ├─╮
414 │ ◉ c
415 │ ◉ b
416 ◉ │ a
417 ├─╯
418 ◉
419 "###);
420
421 // Descendants of the rebased commit should be rebased onto parents, and if
422 // the descendant is a merge commit, it shouldn't forget its other parents.
423 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "-d", "a"]);
424 insta::assert_snapshot!(stdout, @"");
425 insta::assert_snapshot!(stderr, @r###"
426 Rebased 1 commits onto destination
427 Rebased 1 descendant commits
428 Working copy now at: vruxwmqv a37531e8 d | d
429 Parent commit : rlvkpnrz 2443ea76 a | a
430 Parent commit : zsuskuln d370aee1 b | b
431 Added 0 files, modified 0 files, removed 1 files
432 "###);
433 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
434 @ d
435 ├─╮
436 │ ◉ b
437 │ │ ◉ c
438 ├───╯
439 ◉ │ a
440 ├─╯
441 ◉
442 "###);
443}
444
445#[test]
446fn test_rebase_multiple_revisions() {
447 let test_env = TestEnvironment::default();
448 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
449 let repo_path = test_env.env_root().join("repo");
450
451 create_commit(&test_env, &repo_path, "a", &[]);
452 create_commit(&test_env, &repo_path, "b", &["a"]);
453 create_commit(&test_env, &repo_path, "c", &["b"]);
454 create_commit(&test_env, &repo_path, "d", &["a"]);
455 create_commit(&test_env, &repo_path, "e", &["d"]);
456 create_commit(&test_env, &repo_path, "f", &["c", "e"]);
457 create_commit(&test_env, &repo_path, "g", &["f"]);
458 create_commit(&test_env, &repo_path, "h", &["g"]);
459 create_commit(&test_env, &repo_path, "i", &["f"]);
460 // Test the setup
461 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
462 @ i
463 │ ◉ h
464 │ ◉ g
465 ├─╯
466 ◉ f
467 ├─╮
468 │ ◉ e
469 │ ◉ d
470 ◉ │ c
471 ◉ │ b
472 ├─╯
473 ◉ a
474 ◉
475 "###);
476
477 // Test with two non-related non-merge commits.
478 let (stdout, stderr) =
479 test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "-r", "e", "-d", "a"]);
480 insta::assert_snapshot!(stdout, @"");
481 insta::assert_snapshot!(stderr, @r###"
482 Rebased 2 commits onto destination
483 Rebased 4 descendant commits
484 Working copy now at: xznxytkn 016685dc i | i
485 Parent commit : kmkuslsw e04d3932 f | f
486 Added 0 files, modified 0 files, removed 2 files
487 "###);
488 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
489 @ i
490 │ ◉ h
491 │ ◉ g
492 ├─╯
493 ◉ f
494 ├─╮
495 │ ◉ d
496 ◉ │ b
497 ├─╯
498 │ ◉ e
499 ├─╯
500 │ ◉ c
501 ├─╯
502 ◉ a
503 ◉
504 "###);
505 test_env.jj_cmd_ok(&repo_path, &["undo"]);
506
507 // Test with two related non-merge commits. Since "b" is a parent of "c", when
508 // rebasing commits "b" and "c", their ancestry relationship should be
509 // preserved.
510 let (stdout, stderr) =
511 test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "b", "-r", "c", "-d", "e"]);
512 insta::assert_snapshot!(stdout, @"");
513 insta::assert_snapshot!(stderr, @r###"
514 Rebased 2 commits onto destination
515 Rebased 4 descendant commits
516 Working copy now at: xznxytkn 94538385 i | i
517 Parent commit : kmkuslsw dae8d293 f | f
518 Added 0 files, modified 0 files, removed 2 files
519 "###);
520 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
521 @ i
522 │ ◉ h
523 │ ◉ g
524 ├─╯
525 ◉ f
526 ├─╮
527 │ │ ◉ c
528 │ │ ◉ b
529 │ ├─╯
530 │ ◉ e
531 │ ◉ d
532 ├─╯
533 ◉ a
534 ◉
535 "###);
536 test_env.jj_cmd_ok(&repo_path, &["undo"]);
537
538 // Test with a subgraph containing a merge commit. Since the merge commit "f"
539 // was extracted, its descendants which are not part of the subgraph will
540 // inherit its descendants which are not in the subtree ("c" and "d").
541 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "e::g", "-d", "a"]);
542 insta::assert_snapshot!(stdout, @"");
543 insta::assert_snapshot!(stderr, @r###"
544 Rebased 3 commits onto destination
545 Rebased 2 descendant commits
546 Working copy now at: xznxytkn 1868ded4 i | i
547 Parent commit : royxmykx 7e4fbf4f c | c
548 Parent commit : vruxwmqv 4cc44fbf d | d
549 Added 0 files, modified 0 files, removed 2 files
550 "###);
551 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
552 @ i
553 ├─╮
554 │ │ ◉ h
555 ╭─┬─╯
556 │ ◉ d
557 ◉ │ c
558 ◉ │ b
559 ├─╯
560 │ ◉ g
561 │ ◉ f
562 │ ◉ e
563 ├─╯
564 ◉ a
565 ◉
566 "###);
567 test_env.jj_cmd_ok(&repo_path, &["undo"]);
568
569 // Test with commits in a disconnected subgraph. The subgraph has the
570 // relationship d->e->f->g->h, but only "d", "f" and "h" are in the set of
571 // rebased commits. "d" should be a new parent of "f", and "f" should be a
572 // new parent of "g".
573 let (stdout, stderr) = test_env.jj_cmd_ok(
574 &repo_path,
575 &["rebase", "-r", "d", "-r", "f", "-r", "h", "-d", "b"],
576 );
577 insta::assert_snapshot!(stdout, @"");
578 insta::assert_snapshot!(stderr, @r###"
579 Rebased 3 commits onto destination
580 Rebased 3 descendant commits
581 Working copy now at: xznxytkn 9cfd1635 i | i
582 Parent commit : royxmykx 7e4fbf4f c | c
583 Parent commit : znkkpsqq ecf9a1d5 e | e
584 Added 0 files, modified 0 files, removed 2 files
585 "###);
586 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
587 @ i
588 ├─╮
589 │ │ ◉ g
590 ╭─┬─╯
591 │ ◉ e
592 ◉ │ c
593 │ │ ◉ h
594 │ │ ◉ f
595 │ │ ◉ d
596 ├───╯
597 ◉ │ b
598 ├─╯
599 ◉ a
600 ◉
601 "###);
602 test_env.jj_cmd_ok(&repo_path, &["undo"]);
603
604 // Test rebasing a subgraph onto its descendants.
605 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "d::e", "-d", "i"]);
606 insta::assert_snapshot!(stdout, @"");
607 insta::assert_snapshot!(stderr, @r###"
608 Rebased 2 commits onto destination
609 Rebased 4 descendant commits
610 Working copy now at: xznxytkn 5d911e5c i | i
611 Parent commit : kmkuslsw d1bfda8c f | f
612 Added 0 files, modified 0 files, removed 2 files
613 "###);
614 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
615 ◉ h
616 ◉ g
617 │ ◉ e
618 │ ◉ d
619 │ @ i
620 ├─╯
621 ◉ f
622 ├─╮
623 ◉ │ c
624 ◉ │ b
625 ├─╯
626 ◉ a
627 ◉
628 "###);
629}
630
631#[test]
632fn test_rebase_revision_onto_descendant() {
633 let test_env = TestEnvironment::default();
634 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
635 let repo_path = test_env.env_root().join("repo");
636
637 create_commit(&test_env, &repo_path, "base", &[]);
638 create_commit(&test_env, &repo_path, "a", &["base"]);
639 create_commit(&test_env, &repo_path, "b", &["base"]);
640 create_commit(&test_env, &repo_path, "merge", &["b", "a"]);
641 // Test the setup
642 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
643 @ merge
644 ├─╮
645 │ ◉ a
646 ◉ │ b
647 ├─╯
648 ◉ base
649 ◉
650 "###);
651 let setup_opid = test_env.current_operation_id(&repo_path);
652
653 // Simpler example
654 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "base", "-d", "a"]);
655 insta::assert_snapshot!(stdout, @"");
656 insta::assert_snapshot!(stderr, @r###"
657 Rebased 1 commits onto destination
658 Rebased 3 descendant commits
659 Working copy now at: vruxwmqv bff4a4eb merge | merge
660 Parent commit : royxmykx c84e900d b | b
661 Parent commit : zsuskuln d57db87b a | a
662 Added 0 files, modified 0 files, removed 1 files
663 "###);
664 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
665 @ merge
666 ├─╮
667 ◉ │ b
668 │ │ ◉ base
669 │ ├─╯
670 │ ◉ a
671 ├─╯
672 ◉
673 "###);
674
675 // Now, let's rebase onto the descendant merge
676 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
677 insta::assert_snapshot!(stdout, @"");
678 insta::assert_snapshot!(stderr, @r###"
679 Working copy now at: vruxwmqv b05964d1 merge | merge
680 Parent commit : royxmykx cea87a87 b | b
681 Parent commit : zsuskuln 2c5b7858 a | a
682 Added 1 files, modified 0 files, removed 0 files
683 "###);
684 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "base", "-d", "merge"]);
685 insta::assert_snapshot!(stdout, @"");
686 insta::assert_snapshot!(stderr, @r###"
687 Rebased 1 commits onto destination
688 Rebased 3 descendant commits
689 Working copy now at: vruxwmqv 986b7a49 merge | merge
690 Parent commit : royxmykx c07c677c b | b
691 Parent commit : zsuskuln abc90087 a | a
692 Added 0 files, modified 0 files, removed 1 files
693 "###);
694 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
695 ◉ base
696 @ merge
697 ├─╮
698 │ ◉ a
699 ◉ │ b
700 ├─╯
701 ◉
702 "###);
703
704 // TODO(ilyagr): These will be good tests for `jj rebase --insert-after` and
705 // `--insert-before`, once those are implemented.
706}
707
708#[test]
709fn test_rebase_multiple_destinations() {
710 let test_env = TestEnvironment::default();
711 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
712 let repo_path = test_env.env_root().join("repo");
713
714 create_commit(&test_env, &repo_path, "a", &[]);
715 create_commit(&test_env, &repo_path, "b", &[]);
716 create_commit(&test_env, &repo_path, "c", &[]);
717 // Test the setup
718 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
719 @ c
720 │ ◉ b
721 ├─╯
722 │ ◉ a
723 ├─╯
724 ◉
725 "###);
726
727 let (stdout, stderr) =
728 test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "a", "-d", "b", "-d", "c"]);
729 insta::assert_snapshot!(stdout, @"");
730 insta::assert_snapshot!(stderr, @r###"
731 Rebased 1 commits onto destination
732 "###);
733 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
734 ◉ a
735 ├─╮
736 │ @ c
737 ◉ │ b
738 ├─╯
739 ◉
740 "###);
741
742 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r", "a", "-d", "b|c"]);
743 insta::assert_snapshot!(stderr, @r###"
744 Error: Revset "b|c" resolved to more than one revision
745 Hint: The revset "b|c" resolved to these revisions:
746 royxmykx fe2e8e8b c | c
747 zsuskuln d370aee1 b | b
748 Hint: Prefix the expression with 'all:' to allow any number of revisions (i.e. 'all:b|c').
749 "###);
750
751 // try with 'all:' and succeed
752 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "a", "-d", "all:b|c"]);
753 insta::assert_snapshot!(stdout, @"");
754 insta::assert_snapshot!(stderr, @r###"
755 Rebased 1 commits onto destination
756 "###);
757 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
758 ◉ a
759 ├─╮
760 │ ◉ b
761 @ │ c
762 ├─╯
763 ◉
764 "###);
765
766 // undo and do it again, but with 'ui.always-allow-large-revsets'
767 let (_, _) = test_env.jj_cmd_ok(&repo_path, &["undo"]);
768 let (_, _) = test_env.jj_cmd_ok(
769 &repo_path,
770 &[
771 "rebase",
772 "--config-toml=ui.always-allow-large-revsets=true",
773 "-r=a",
774 "-d=b|c",
775 ],
776 );
777 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
778 ◉ a
779 ├─╮
780 │ ◉ b
781 @ │ c
782 ├─╯
783 ◉
784 "###);
785
786 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r", "a", "-d", "b", "-d", "b"]);
787 insta::assert_snapshot!(stderr, @r###"
788 Error: More than one revset resolved to revision d370aee184ba
789 "###);
790
791 // Same error with 'all:' if there is overlap.
792 let stderr = test_env.jj_cmd_failure(
793 &repo_path,
794 &["rebase", "-r", "a", "-d", "all:b|c", "-d", "b"],
795 );
796 insta::assert_snapshot!(stderr, @r###"
797 Error: More than one revset resolved to revision d370aee184ba
798 "###);
799
800 let stderr = test_env.jj_cmd_failure(
801 &repo_path,
802 &["rebase", "-r", "a", "-d", "b", "-d", "root()"],
803 );
804 insta::assert_snapshot!(stderr, @r###"
805 Error: The Git backend does not support creating merge commits with the root commit as one of the parents.
806 "###);
807}
808
809#[test]
810fn test_rebase_with_descendants() {
811 let test_env = TestEnvironment::default();
812 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
813 let repo_path = test_env.env_root().join("repo");
814
815 create_commit(&test_env, &repo_path, "a", &[]);
816 create_commit(&test_env, &repo_path, "b", &[]);
817 create_commit(&test_env, &repo_path, "c", &["a", "b"]);
818 create_commit(&test_env, &repo_path, "d", &["c"]);
819 // Test the setup
820 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
821 @ d
822 ◉ c
823 ├─╮
824 │ ◉ b
825 ◉ │ a
826 ├─╯
827 ◉
828 "###);
829
830 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-s", "b", "-d", "a"]);
831 insta::assert_snapshot!(stdout, @"");
832 insta::assert_snapshot!(stderr, @r###"
833 Rebased 3 commits
834 Working copy now at: vruxwmqv 705832bd d | d
835 Parent commit : royxmykx 57c7246a c | c
836 "###);
837 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
838 @ d
839 ◉ c
840 ├─╮
841 │ ◉ b
842 ├─╯
843 ◉ a
844 ◉
845 "###);
846
847 // Rebase several subtrees at once.
848 test_env.jj_cmd_ok(&repo_path, &["undo"]);
849 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-s=c", "-s=d", "-d=a"]);
850 insta::assert_snapshot!(stdout, @"");
851 insta::assert_snapshot!(stderr, @r###"
852 Rebased 2 commits
853 Working copy now at: vruxwmqv 92c2bc9a d | d
854 Parent commit : rlvkpnrz 2443ea76 a | a
855 Added 0 files, modified 0 files, removed 2 files
856 "###);
857 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
858 @ d
859 │ ◉ c
860 ├─╯
861 ◉ a
862 │ ◉ b
863 ├─╯
864 ◉
865 "###);
866
867 test_env.jj_cmd_ok(&repo_path, &["undo"]);
868 // Reminder of the setup
869 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
870 @ d
871 ◉ c
872 ├─╮
873 │ ◉ b
874 ◉ │ a
875 ├─╯
876 ◉
877 "###);
878
879 // `d` was a descendant of `b`, and both are moved to be direct descendants of
880 // `a`. `c` remains a descendant of `b`.
881 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-s=b", "-s=d", "-d=a"]);
882 insta::assert_snapshot!(stdout, @"");
883 insta::assert_snapshot!(stderr, @r###"
884 Rebased 3 commits
885 Working copy now at: vruxwmqv f1e71cb7 d | d
886 Parent commit : rlvkpnrz 2443ea76 a | a
887 Added 0 files, modified 0 files, removed 2 files
888 "###);
889 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
890 ◉ c
891 ├─╮
892 │ ◉ b
893 ├─╯
894 │ @ d
895 ├─╯
896 ◉ a
897 ◉
898 "###);
899
900 // Same test as above, but with multiple commits per argument
901 test_env.jj_cmd_ok(&repo_path, &["undo"]);
902 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-s=b|d", "-d=a"]);
903 insta::assert_snapshot!(stderr, @r###"
904 Error: Revset "b|d" resolved to more than one revision
905 Hint: The revset "b|d" resolved to these revisions:
906 vruxwmqv df54a9fd d | d
907 zsuskuln d370aee1 b | b
908 Hint: Prefix the expression with 'all:' to allow any number of revisions (i.e. 'all:b|d').
909 "###);
910 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-s=all:b|d", "-d=a"]);
911 insta::assert_snapshot!(stdout, @"");
912 insta::assert_snapshot!(stderr, @r###"
913 Rebased 3 commits
914 Working copy now at: vruxwmqv d17539f7 d | d
915 Parent commit : rlvkpnrz 2443ea76 a | a
916 Added 0 files, modified 0 files, removed 2 files
917 "###);
918 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
919 ◉ c
920 ├─╮
921 │ ◉ b
922 ├─╯
923 │ @ d
924 ├─╯
925 ◉ a
926 ◉
927 "###);
928}
929
930#[test]
931fn test_rebase_error_revision_does_not_exist() {
932 let test_env = TestEnvironment::default();
933 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
934 let repo_path = test_env.env_root().join("repo");
935
936 test_env.jj_cmd_ok(&repo_path, &["describe", "-m", "one"]);
937 test_env.jj_cmd_ok(&repo_path, &["branch", "create", "b-one"]);
938 test_env.jj_cmd_ok(&repo_path, &["new", "-r", "@-", "-m", "two"]);
939
940 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-b", "b-one", "-d", "this"]);
941 insta::assert_snapshot!(stderr, @r###"
942 Error: Revision "this" doesn't exist
943 "###);
944
945 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-b", "this", "-d", "b-one"]);
946 insta::assert_snapshot!(stderr, @r###"
947 Error: Revision "this" doesn't exist
948 "###);
949}
950
951fn get_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
952 test_env.jj_cmd_success(repo_path, &["log", "-T", "branches"])
953}
954
955// This behavior illustrates https://github.com/martinvonz/jj/issues/2600
956#[test]
957fn test_rebase_with_child_and_descendant_bug_2600() {
958 let test_env = TestEnvironment::default();
959 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
960 let repo_path = test_env.env_root().join("repo");
961
962 create_commit(&test_env, &repo_path, "notroot", &[]);
963 create_commit(&test_env, &repo_path, "base", &["notroot"]);
964 create_commit(&test_env, &repo_path, "a", &["base"]);
965 create_commit(&test_env, &repo_path, "b", &["base", "a"]);
966 create_commit(&test_env, &repo_path, "c", &["b"]);
967 let setup_opid = test_env.current_operation_id(&repo_path);
968
969 // Test the setup
970 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
971 @ c
972 ◉ b
973 ├─╮
974 │ ◉ a
975 ├─╯
976 ◉ base
977 ◉ notroot
978 ◉
979 "###);
980
981 // ===================== rebase -s tests =================
982 let (stdout, stderr) =
983 test_env.jj_cmd_ok(&repo_path, &["rebase", "-s", "base", "-d", "notroot"]);
984 insta::assert_snapshot!(stdout, @"");
985 // This should be a no-op
986 insta::assert_snapshot!(stderr, @r###"
987 Skipped rebase of 1 commits that were already in place
988 "###);
989 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
990 @ c
991 ◉ b
992 ├─╮
993 │ ◉ a
994 ├─╯
995 ◉ base
996 ◉ notroot
997 ◉
998 "###);
999
1000 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1001 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-s", "a", "-d", "base"]);
1002 insta::assert_snapshot!(stdout, @"");
1003 // This should be a no-op
1004 insta::assert_snapshot!(stderr, @r###"
1005 Skipped rebase of 1 commits that were already in place
1006 "###);
1007 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1008 @ c
1009 ◉ b
1010 ├─╮
1011 │ ◉ a
1012 ├─╯
1013 ◉ base
1014 ◉ notroot
1015 ◉
1016 "###);
1017
1018 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1019 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-s", "a", "-d", "root()"]);
1020 insta::assert_snapshot!(stdout, @"");
1021 insta::assert_snapshot!(stderr, @r###"
1022 Rebased 3 commits
1023 Working copy now at: znkkpsqq cf8ecff5 c | c
1024 Parent commit : vruxwmqv 24e1a270 b | b
1025 "###);
1026 // Commit "a" should be rebased onto the root commit. Commit "b" should have
1027 // "base" and "a" as parents as before.
1028 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1029 @ c
1030 ◉ b
1031 ├─╮
1032 │ ◉ a
1033 ◉ │ base
1034 ◉ │ notroot
1035 ├─╯
1036 ◉
1037 "###);
1038
1039 // ===================== rebase -b tests =================
1040 // ====== Reminder of the setup =========
1041 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1042 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1043 @ c
1044 ◉ b
1045 ├─╮
1046 │ ◉ a
1047 ├─╯
1048 ◉ base
1049 ◉ notroot
1050 ◉
1051 "###);
1052
1053 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b", "c", "-d", "base"]);
1054 insta::assert_snapshot!(stdout, @"");
1055 // The commits in roots(base..c), i.e. commit "a" should be rebased onto "base",
1056 // which is a no-op
1057 insta::assert_snapshot!(stderr, @r###"
1058 Skipped rebase of 1 commits that were already in place
1059 "###);
1060 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1061 @ c
1062 ◉ b
1063 ├─╮
1064 │ ◉ a
1065 ├─╯
1066 ◉ base
1067 ◉ notroot
1068 ◉
1069 "###);
1070
1071 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1072 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b", "c", "-d", "a"]);
1073 insta::assert_snapshot!(stdout, @"");
1074 insta::assert_snapshot!(stderr, @r###"
1075 Rebased 2 commits
1076 Working copy now at: znkkpsqq 76914dcc c | c
1077 Parent commit : vruxwmqv f73f03c7 b | b
1078 "###);
1079 // The commits in roots(a..c), i.e. commit "b" should be rebased onto "a",
1080 // which means "b" loses its "base" parent
1081 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1082 @ c
1083 ◉ b
1084 ◉ a
1085 ◉ base
1086 ◉ notroot
1087 ◉
1088 "###);
1089
1090 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1091 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b", "a", "-d", "root()"]);
1092 insta::assert_snapshot!(stdout, @"");
1093 // This should be a no-op
1094 insta::assert_snapshot!(stderr, @r###"
1095 Skipped rebase of 1 commits that were already in place
1096 "###);
1097 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1098 @ c
1099 ◉ b
1100 ├─╮
1101 │ ◉ a
1102 ├─╯
1103 ◉ base
1104 ◉ notroot
1105 ◉
1106 "###);
1107
1108 // ===================== rebase -r tests =================
1109 // ====== Reminder of the setup =========
1110 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1111 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1112 @ c
1113 ◉ b
1114 ├─╮
1115 │ ◉ a
1116 ├─╯
1117 ◉ base
1118 ◉ notroot
1119 ◉
1120 "###);
1121
1122 let (stdout, stderr) =
1123 test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "base", "-d", "root()"]);
1124 insta::assert_snapshot!(stdout, @"");
1125 insta::assert_snapshot!(stderr, @r###"
1126 Rebased 1 commits onto destination
1127 Rebased 3 descendant commits
1128 Working copy now at: znkkpsqq 45371aaf c | c
1129 Parent commit : vruxwmqv c0a76bf4 b | b
1130 Added 0 files, modified 0 files, removed 1 files
1131 "###);
1132 // The user would expect unsimplified ancestry here.
1133 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1134 @ c
1135 ◉ b
1136 ├─╮
1137 │ ◉ a
1138 ├─╯
1139 ◉ notroot
1140 │ ◉ base
1141 ├─╯
1142 ◉
1143 "###);
1144
1145 // This tests the algorithm for rebasing onto descendants. The result should
1146 // have unsimplified ancestry.
1147 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1148 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "base", "-d", "b"]);
1149 insta::assert_snapshot!(stdout, @"");
1150 insta::assert_snapshot!(stderr, @r###"
1151 Rebased 1 commits onto destination
1152 Rebased 3 descendant commits
1153 Working copy now at: znkkpsqq e28fa972 c | c
1154 Parent commit : vruxwmqv 8d0eeb6a b | b
1155 Added 0 files, modified 0 files, removed 1 files
1156 "###);
1157 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1158 @ c
1159 │ ◉ base
1160 ├─╯
1161 ◉ b
1162 ├─╮
1163 │ ◉ a
1164 ├─╯
1165 ◉ notroot
1166 ◉
1167 "###);
1168
1169 // This tests the algorithm for rebasing onto descendants. The result should
1170 // have unsimplified ancestry.
1171 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1172 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "base", "-d", "a"]);
1173 insta::assert_snapshot!(stdout, @"");
1174 insta::assert_snapshot!(stderr, @r###"
1175 Rebased 1 commits onto destination
1176 Rebased 3 descendant commits
1177 Working copy now at: znkkpsqq a9da974c c | c
1178 Parent commit : vruxwmqv 0072139c b | b
1179 Added 0 files, modified 0 files, removed 1 files
1180 "###);
1181 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1182 @ c
1183 ◉ b
1184 ├─╮
1185 │ │ ◉ base
1186 │ ├─╯
1187 │ ◉ a
1188 ├─╯
1189 ◉ notroot
1190 ◉
1191 "###);
1192
1193 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1194 // ====== Reminder of the setup =========
1195 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1196 @ c
1197 ◉ b
1198 ├─╮
1199 │ ◉ a
1200 ├─╯
1201 ◉ base
1202 ◉ notroot
1203 ◉
1204 "###);
1205
1206 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "a", "-d", "root()"]);
1207 insta::assert_snapshot!(stdout, @"");
1208 insta::assert_snapshot!(stderr, @r###"
1209 Rebased 1 commits onto destination
1210 Rebased 2 descendant commits
1211 Working copy now at: znkkpsqq 7210b05e c | c
1212 Parent commit : vruxwmqv da3f7511 b | b
1213 Added 0 files, modified 0 files, removed 1 files
1214 "###);
1215 // In this case, it is unclear whether the user would always prefer unsimplified
1216 // ancestry (whether `b` should also be a direct child of the root commit).
1217 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1218 @ c
1219 ◉ b
1220 ◉ base
1221 ◉ notroot
1222 │ ◉ a
1223 ├─╯
1224 ◉
1225 "###);
1226
1227 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1228 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "b", "-d", "root()"]);
1229 insta::assert_snapshot!(stdout, @"");
1230 insta::assert_snapshot!(stderr, @r###"
1231 Rebased 1 commits onto destination
1232 Rebased 1 descendant commits
1233 Working copy now at: znkkpsqq f280545e c | c
1234 Parent commit : zsuskuln 0a7fb8f6 base | base
1235 Parent commit : royxmykx 86a06598 a | a
1236 Added 0 files, modified 0 files, removed 1 files
1237 "###);
1238 // The user would expect unsimplified ancestry here.
1239 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1240 @ c
1241 ├─╮
1242 │ ◉ a
1243 ├─╯
1244 ◉ base
1245 ◉ notroot
1246 │ ◉ b
1247 ├─╯
1248 ◉
1249 "###);
1250
1251 // This tests the algorithm for rebasing onto descendants. The result should
1252 // have unsimplified ancestry.
1253 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1254 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "b", "-d", "c"]);
1255 insta::assert_snapshot!(stdout, @"");
1256 insta::assert_snapshot!(stderr, @r###"
1257 Rebased 1 commits onto destination
1258 Rebased 1 descendant commits
1259 Working copy now at: znkkpsqq c0a7cd80 c | c
1260 Parent commit : zsuskuln 0a7fb8f6 base | base
1261 Parent commit : royxmykx 86a06598 a | a
1262 Added 0 files, modified 0 files, removed 1 files
1263 "###);
1264 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1265 ◉ b
1266 @ c
1267 ├─╮
1268 │ ◉ a
1269 ├─╯
1270 ◉ base
1271 ◉ notroot
1272 ◉
1273 "###);
1274
1275 // In this test, the commit with weird ancestry is not rebased (neither directly
1276 // nor indirectly).
1277 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1278 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "-d", "a"]);
1279 insta::assert_snapshot!(stdout, @"");
1280 insta::assert_snapshot!(stderr, @r###"
1281 Rebased 1 commits onto destination
1282 Working copy now at: znkkpsqq 7a3bc050 c | c
1283 Parent commit : royxmykx 86a06598 a | a
1284 Added 0 files, modified 0 files, removed 1 files
1285 "###);
1286 insta::assert_snapshot!(get_log_output(&test_env, &repo_path), @r###"
1287 @ c
1288 │ ◉ b
1289 ╭─┤
1290 ◉ │ a
1291 ├─╯
1292 ◉ base
1293 ◉ notroot
1294 ◉
1295 "###);
1296}
1297
1298#[test]
1299fn test_rebase_revisions_after() {
1300 let test_env = TestEnvironment::default();
1301 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
1302 let repo_path = test_env.env_root().join("repo");
1303
1304 create_commit(&test_env, &repo_path, "a", &[]);
1305 create_commit(&test_env, &repo_path, "b1", &["a"]);
1306 create_commit(&test_env, &repo_path, "b2", &["b1"]);
1307 create_commit(&test_env, &repo_path, "b3", &["a"]);
1308 create_commit(&test_env, &repo_path, "b4", &["b3"]);
1309 create_commit(&test_env, &repo_path, "c", &["b2", "b4"]);
1310 create_commit(&test_env, &repo_path, "d", &["c"]);
1311 create_commit(&test_env, &repo_path, "e", &["c"]);
1312 create_commit(&test_env, &repo_path, "f", &["e"]);
1313 // Test the setup
1314 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1315 @ f xznxytkn e4a00798
1316 ◉ e nkmrtpmo 858693f7
1317 │ ◉ d lylxulpl 7d0512e5
1318 ├─╯
1319 ◉ c kmkuslsw cd86b3e4
1320 ├─╮
1321 │ ◉ b4 znkkpsqq a52a83a4
1322 │ ◉ b3 vruxwmqv 523e6a8b
1323 ◉ │ b2 royxmykx 2b8e1148
1324 ◉ │ b1 zsuskuln 072d5ae1
1325 ├─╯
1326 ◉ a rlvkpnrz 2443ea76
1327 ◉ zzzzzzzz 00000000
1328 "###);
1329 let setup_opid = test_env.current_operation_id(&repo_path);
1330
1331 // Rebasing a commit after its parents should be a no-op.
1332 let (stdout, stderr) = test_env.jj_cmd_ok(
1333 &repo_path,
1334 &["rebase", "-r", "c", "--after", "b2", "--after", "b4"],
1335 );
1336 insta::assert_snapshot!(stdout, @"");
1337 insta::assert_snapshot!(stderr, @r###"
1338 Skipped rebase of 4 commits that were already in place
1339 Nothing changed.
1340 "###);
1341 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1342 @ f xznxytkn e4a00798
1343 ◉ e nkmrtpmo 858693f7
1344 │ ◉ d lylxulpl 7d0512e5
1345 ├─╯
1346 ◉ c kmkuslsw cd86b3e4
1347 ├─╮
1348 │ ◉ b4 znkkpsqq a52a83a4
1349 │ ◉ b3 vruxwmqv 523e6a8b
1350 ◉ │ b2 royxmykx 2b8e1148
1351 ◉ │ b1 zsuskuln 072d5ae1
1352 ├─╯
1353 ◉ a rlvkpnrz 2443ea76
1354 ◉ zzzzzzzz 00000000
1355 "###);
1356
1357 // Rebasing a commit after itself should be a no-op.
1358 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "--after", "c"]);
1359 insta::assert_snapshot!(stdout, @"");
1360 insta::assert_snapshot!(stderr, @r###"
1361 Skipped rebase of 4 commits that were already in place
1362 Nothing changed.
1363 "###);
1364 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1365 @ f xznxytkn e4a00798
1366 ◉ e nkmrtpmo 858693f7
1367 │ ◉ d lylxulpl 7d0512e5
1368 ├─╯
1369 ◉ c kmkuslsw cd86b3e4
1370 ├─╮
1371 │ ◉ b4 znkkpsqq a52a83a4
1372 │ ◉ b3 vruxwmqv 523e6a8b
1373 ◉ │ b2 royxmykx 2b8e1148
1374 ◉ │ b1 zsuskuln 072d5ae1
1375 ├─╯
1376 ◉ a rlvkpnrz 2443ea76
1377 ◉ zzzzzzzz 00000000
1378 "###);
1379
1380 // Rebase a commit after another commit. "c" has parents "b2" and "b4", so its
1381 // children "d" and "e" should be rebased onto "b2" and "b4" respectively.
1382 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "--after", "e"]);
1383 insta::assert_snapshot!(stdout, @"");
1384 insta::assert_snapshot!(stderr, @r###"
1385 Rebased 1 commits onto destination
1386 Rebased 3 descendant commits
1387 Working copy now at: xznxytkn e0e873c8 f | f
1388 Parent commit : kmkuslsw 754793f3 c | c
1389 "###);
1390 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1391 @ f xznxytkn e0e873c8
1392 ◉ c kmkuslsw 754793f3
1393 ◉ e nkmrtpmo e0d7fb63
1394 ├─╮
1395 │ │ ◉ d lylxulpl 5e9cb58d
1396 ╭─┬─╯
1397 │ ◉ b4 znkkpsqq a52a83a4
1398 │ ◉ b3 vruxwmqv 523e6a8b
1399 ◉ │ b2 royxmykx 2b8e1148
1400 ◉ │ b1 zsuskuln 072d5ae1
1401 ├─╯
1402 ◉ a rlvkpnrz 2443ea76
1403 ◉ zzzzzzzz 00000000
1404 "###);
1405 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1406
1407 // Rebase a commit after a leaf commit.
1408 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "e", "--after", "f"]);
1409 insta::assert_snapshot!(stdout, @"");
1410 insta::assert_snapshot!(stderr, @r###"
1411 Rebased 1 commits onto destination
1412 Rebased 1 descendant commits
1413 Working copy now at: xznxytkn 9804b742 f | f
1414 Parent commit : kmkuslsw cd86b3e4 c | c
1415 Added 0 files, modified 0 files, removed 1 files
1416 "###);
1417 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1418 ◉ e nkmrtpmo 76ac6464
1419 @ f xznxytkn 9804b742
1420 │ ◉ d lylxulpl 7d0512e5
1421 ├─╯
1422 ◉ c kmkuslsw cd86b3e4
1423 ├─╮
1424 │ ◉ b4 znkkpsqq a52a83a4
1425 │ ◉ b3 vruxwmqv 523e6a8b
1426 ◉ │ b2 royxmykx 2b8e1148
1427 ◉ │ b1 zsuskuln 072d5ae1
1428 ├─╯
1429 ◉ a rlvkpnrz 2443ea76
1430 ◉ zzzzzzzz 00000000
1431 "###);
1432 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1433
1434 // Rebase a commit after a commit in a branch of a merge commit.
1435 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--after", "b1"]);
1436 insta::assert_snapshot!(stdout, @"");
1437 insta::assert_snapshot!(stderr, @r###"
1438 Rebased 1 commits onto destination
1439 Rebased 4 descendant commits
1440 Working copy now at: xznxytkn 80c27408 f | f
1441 Parent commit : zsuskuln 072d5ae1 b1 | b1
1442 Added 0 files, modified 0 files, removed 5 files
1443 "###);
1444 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1445 ◉ e nkmrtpmo cee7a197
1446 │ ◉ d lylxulpl 1eb960ec
1447 ├─╯
1448 ◉ c kmkuslsw 305a7803
1449 ├─╮
1450 │ ◉ b4 znkkpsqq a52a83a4
1451 │ ◉ b3 vruxwmqv 523e6a8b
1452 ◉ │ b2 royxmykx 526481b4
1453 @ │ f xznxytkn 80c27408
1454 ◉ │ b1 zsuskuln 072d5ae1
1455 ├─╯
1456 ◉ a rlvkpnrz 2443ea76
1457 ◉ zzzzzzzz 00000000
1458 "###);
1459 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1460
1461 // Rebase a commit after the last commit in a branch of a merge commit.
1462 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--after", "b2"]);
1463 insta::assert_snapshot!(stdout, @"");
1464 insta::assert_snapshot!(stderr, @r###"
1465 Rebased 1 commits onto destination
1466 Rebased 3 descendant commits
1467 Working copy now at: xznxytkn ebbc24b1 f | f
1468 Parent commit : royxmykx 2b8e1148 b2 | b2
1469 Added 0 files, modified 0 files, removed 4 files
1470 "###);
1471 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1472 ◉ e nkmrtpmo 3162ac52
1473 │ ◉ d lylxulpl 6f7f3b2a
1474 ├─╯
1475 ◉ c kmkuslsw d33f69f1
1476 ├─╮
1477 │ @ f xznxytkn ebbc24b1
1478 │ ◉ b2 royxmykx 2b8e1148
1479 │ ◉ b1 zsuskuln 072d5ae1
1480 ◉ │ b4 znkkpsqq a52a83a4
1481 ◉ │ b3 vruxwmqv 523e6a8b
1482 ├─╯
1483 ◉ a rlvkpnrz 2443ea76
1484 ◉ zzzzzzzz 00000000
1485 "###);
1486 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1487
1488 // Rebase a commit after a commit with multiple children.
1489 // "c" has two children "d" and "e", so the rebased commit "f" will inherit the
1490 // two children.
1491 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--after", "c"]);
1492 insta::assert_snapshot!(stdout, @"");
1493 insta::assert_snapshot!(stderr, @r###"
1494 Rebased 1 commits onto destination
1495 Rebased 2 descendant commits
1496 Working copy now at: xznxytkn 8f8c91d3 f | f
1497 Parent commit : kmkuslsw cd86b3e4 c | c
1498 Added 0 files, modified 0 files, removed 1 files
1499 "###);
1500 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1501 ◉ e nkmrtpmo 03ade273
1502 │ ◉ d lylxulpl 8bccbeda
1503 ├─╯
1504 @ f xznxytkn 8f8c91d3
1505 ◉ c kmkuslsw cd86b3e4
1506 ├─╮
1507 │ ◉ b4 znkkpsqq a52a83a4
1508 │ ◉ b3 vruxwmqv 523e6a8b
1509 ◉ │ b2 royxmykx 2b8e1148
1510 ◉ │ b1 zsuskuln 072d5ae1
1511 ├─╯
1512 ◉ a rlvkpnrz 2443ea76
1513 ◉ zzzzzzzz 00000000
1514 "###);
1515 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1516
1517 // Rebase a commit after multiple commits.
1518 let (stdout, stderr) = test_env.jj_cmd_ok(
1519 &repo_path,
1520 &["rebase", "-r", "f", "--after", "e", "--after", "d"],
1521 );
1522 insta::assert_snapshot!(stdout, @"");
1523 insta::assert_snapshot!(stderr, @r###"
1524 Rebased 1 commits onto destination
1525 Working copy now at: xznxytkn 7784e5a0 f | f
1526 Parent commit : nkmrtpmo 858693f7 e | e
1527 Parent commit : lylxulpl 7d0512e5 d | d
1528 Added 1 files, modified 0 files, removed 0 files
1529 "###);
1530 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1531 @ f xznxytkn 7784e5a0
1532 ├─╮
1533 │ ◉ d lylxulpl 7d0512e5
1534 ◉ │ e nkmrtpmo 858693f7
1535 ├─╯
1536 ◉ c kmkuslsw cd86b3e4
1537 ├─╮
1538 │ ◉ b4 znkkpsqq a52a83a4
1539 │ ◉ b3 vruxwmqv 523e6a8b
1540 ◉ │ b2 royxmykx 2b8e1148
1541 ◉ │ b1 zsuskuln 072d5ae1
1542 ├─╯
1543 ◉ a rlvkpnrz 2443ea76
1544 ◉ zzzzzzzz 00000000
1545 "###);
1546 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1547
1548 // Rebase two unrelated commits.
1549 let (stdout, stderr) = test_env.jj_cmd_ok(
1550 &repo_path,
1551 &["rebase", "-r", "d", "-r", "e", "--after", "a"],
1552 );
1553 insta::assert_snapshot!(stdout, @"");
1554 insta::assert_snapshot!(stderr, @r###"
1555 Rebased 2 commits onto destination
1556 Rebased 6 descendant commits
1557 Working copy now at: xznxytkn 0b53613e f | f
1558 Parent commit : kmkuslsw 193687bb c | c
1559 Added 1 files, modified 0 files, removed 0 files
1560 "###);
1561 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1562 @ f xznxytkn 0b53613e
1563 ◉ c kmkuslsw 193687bb
1564 ├─╮
1565 │ ◉ b4 znkkpsqq e8d0f57b
1566 │ ◉ b3 vruxwmqv cb48344c
1567 │ ├─╮
1568 ◉ │ │ b2 royxmykx 535f779d
1569 ◉ │ │ b1 zsuskuln 693186c0
1570 ╰─┬─╮
1571 │ ◉ e nkmrtpmo 2bb4e0b6
1572 ◉ │ d lylxulpl 0b921a1c
1573 ├─╯
1574 ◉ a rlvkpnrz 2443ea76
1575 ◉ zzzzzzzz 00000000
1576 "###);
1577 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1578
1579 // Rebase a subgraph with merge commit and two parents, which should preserve
1580 // the merge.
1581 let (stdout, stderr) = test_env.jj_cmd_ok(
1582 &repo_path,
1583 &["rebase", "-r", "b2", "-r", "b4", "-r", "c", "--after", "f"],
1584 );
1585 insta::assert_snapshot!(stdout, @"");
1586 insta::assert_snapshot!(stderr, @r###"
1587 Rebased 3 commits onto destination
1588 Rebased 3 descendant commits
1589 Working copy now at: xznxytkn eaf1d6b8 f | f
1590 Parent commit : nkmrtpmo 0d7e4ce9 e | e
1591 Added 0 files, modified 0 files, removed 3 files
1592 "###);
1593 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1594 ◉ d lylxulpl 16060da9
1595 ├─╮
1596 │ │ ◉ c kmkuslsw ef5ead27
1597 │ │ ├─╮
1598 │ │ │ ◉ b4 znkkpsqq 9c884b94
1599 │ │ ◉ │ b2 royxmykx bdfea21d
1600 │ │ ├─╯
1601 │ │ @ f xznxytkn eaf1d6b8
1602 │ │ ◉ e nkmrtpmo 0d7e4ce9
1603 ╭─┬─╯
1604 │ ◉ b3 vruxwmqv 523e6a8b
1605 ◉ │ b1 zsuskuln 072d5ae1
1606 ├─╯
1607 ◉ a rlvkpnrz 2443ea76
1608 ◉ zzzzzzzz 00000000
1609 "###);
1610 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1611
1612 // Rebase a subgraph with four commits after one of the commits itself.
1613 let (stdout, stderr) =
1614 test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "b1::d", "--after", "c"]);
1615 insta::assert_snapshot!(stdout, @"");
1616 insta::assert_snapshot!(stderr, @r###"
1617 Rebased 4 commits onto destination
1618 Rebased 2 descendant commits
1619 Working copy now at: xznxytkn 084e0629 f | f
1620 Parent commit : nkmrtpmo 563d78c6 e | e
1621 Added 1 files, modified 0 files, removed 0 files
1622 "###);
1623 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1624 @ f xznxytkn 084e0629
1625 ◉ e nkmrtpmo 563d78c6
1626 ◉ d lylxulpl e67ba5c9
1627 ◉ c kmkuslsw 049aa109
1628 ◉ b2 royxmykx 7af3d6cd
1629 ◉ b1 zsuskuln cd84b343
1630 ├─╮
1631 │ ◉ b4 znkkpsqq a52a83a4
1632 │ ◉ b3 vruxwmqv 523e6a8b
1633 ├─╯
1634 ◉ a rlvkpnrz 2443ea76
1635 ◉ zzzzzzzz 00000000
1636 "###);
1637 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1638
1639 // Rebase a subgraph with disconnected commits. Since "b2" is an ancestor of
1640 // "e", "b2" should be a parent of "e" after the rebase.
1641 let (stdout, stderr) = test_env.jj_cmd_ok(
1642 &repo_path,
1643 &["rebase", "-r", "e", "-r", "b2", "--after", "d"],
1644 );
1645 insta::assert_snapshot!(stdout, @"");
1646 insta::assert_snapshot!(stderr, @r###"
1647 Rebased 2 commits onto destination
1648 Rebased 3 descendant commits
1649 Working copy now at: xznxytkn 4fb2bb60 f | f
1650 Parent commit : kmkuslsw cebde86a c | c
1651 Added 0 files, modified 0 files, removed 2 files
1652 "###);
1653 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1654 @ f xznxytkn 4fb2bb60
1655 │ ◉ e nkmrtpmo 1ea93588
1656 │ ◉ b2 royxmykx 064e3bcb
1657 │ ◉ d lylxulpl b46a9d31
1658 ├─╯
1659 ◉ c kmkuslsw cebde86a
1660 ├─╮
1661 │ ◉ b4 znkkpsqq a52a83a4
1662 │ ◉ b3 vruxwmqv 523e6a8b
1663 ◉ │ b1 zsuskuln 072d5ae1
1664 ├─╯
1665 ◉ a rlvkpnrz 2443ea76
1666 ◉ zzzzzzzz 00000000
1667 "###);
1668 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1669
1670 // Should error if a loop will be created.
1671 let stderr = test_env.jj_cmd_failure(
1672 &repo_path,
1673 &["rebase", "-r", "e", "--after", "a", "--after", "b2"],
1674 );
1675 insta::assert_snapshot!(stderr, @r###"
1676 Error: Refusing to create a loop: commit 2b8e1148290f would be both an ancestor and a descendant of the rebased commits
1677 "###);
1678}
1679
1680#[test]
1681fn test_rebase_revisions_before() {
1682 let test_env = TestEnvironment::default();
1683 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
1684 let repo_path = test_env.env_root().join("repo");
1685
1686 create_commit(&test_env, &repo_path, "a", &[]);
1687 create_commit(&test_env, &repo_path, "b1", &["a"]);
1688 create_commit(&test_env, &repo_path, "b2", &["b1"]);
1689 create_commit(&test_env, &repo_path, "b3", &["a"]);
1690 create_commit(&test_env, &repo_path, "b4", &["b3"]);
1691 create_commit(&test_env, &repo_path, "c", &["b2", "b4"]);
1692 create_commit(&test_env, &repo_path, "d", &["c"]);
1693 create_commit(&test_env, &repo_path, "e", &["c"]);
1694 create_commit(&test_env, &repo_path, "f", &["e"]);
1695 // Test the setup
1696 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1697 @ f xznxytkn e4a00798
1698 ◉ e nkmrtpmo 858693f7
1699 │ ◉ d lylxulpl 7d0512e5
1700 ├─╯
1701 ◉ c kmkuslsw cd86b3e4
1702 ├─╮
1703 │ ◉ b4 znkkpsqq a52a83a4
1704 │ ◉ b3 vruxwmqv 523e6a8b
1705 ◉ │ b2 royxmykx 2b8e1148
1706 ◉ │ b1 zsuskuln 072d5ae1
1707 ├─╯
1708 ◉ a rlvkpnrz 2443ea76
1709 ◉ zzzzzzzz 00000000
1710 "###);
1711 let setup_opid = test_env.current_operation_id(&repo_path);
1712
1713 // Rebasing a commit before its children should be a no-op.
1714 let (stdout, stderr) = test_env.jj_cmd_ok(
1715 &repo_path,
1716 &["rebase", "-r", "c", "--before", "d", "--before", "e"],
1717 );
1718 insta::assert_snapshot!(stdout, @"");
1719 insta::assert_snapshot!(stderr, @r###"
1720 Skipped rebase of 4 commits that were already in place
1721 Nothing changed.
1722 "###);
1723 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1724 @ f xznxytkn e4a00798
1725 ◉ e nkmrtpmo 858693f7
1726 │ ◉ d lylxulpl 7d0512e5
1727 ├─╯
1728 ◉ c kmkuslsw cd86b3e4
1729 ├─╮
1730 │ ◉ b4 znkkpsqq a52a83a4
1731 │ ◉ b3 vruxwmqv 523e6a8b
1732 ◉ │ b2 royxmykx 2b8e1148
1733 ◉ │ b1 zsuskuln 072d5ae1
1734 ├─╯
1735 ◉ a rlvkpnrz 2443ea76
1736 ◉ zzzzzzzz 00000000
1737 "###);
1738
1739 // Rebasing a commit before itself should be a no-op.
1740 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "--before", "c"]);
1741 insta::assert_snapshot!(stdout, @"");
1742 insta::assert_snapshot!(stderr, @r###"
1743 Skipped rebase of 4 commits that were already in place
1744 Nothing changed.
1745 "###);
1746 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1747 @ f xznxytkn e4a00798
1748 ◉ e nkmrtpmo 858693f7
1749 │ ◉ d lylxulpl 7d0512e5
1750 ├─╯
1751 ◉ c kmkuslsw cd86b3e4
1752 ├─╮
1753 │ ◉ b4 znkkpsqq a52a83a4
1754 │ ◉ b3 vruxwmqv 523e6a8b
1755 ◉ │ b2 royxmykx 2b8e1148
1756 ◉ │ b1 zsuskuln 072d5ae1
1757 ├─╯
1758 ◉ a rlvkpnrz 2443ea76
1759 ◉ zzzzzzzz 00000000
1760 "###);
1761
1762 // Rebasing a commit before the root commit should error.
1763 let stderr = test_env.jj_cmd_failure(&repo_path, &["rebase", "-r", "c", "--before", "root()"]);
1764 insta::assert_snapshot!(stderr, @r###"
1765 Error: The root commit 000000000000 is immutable
1766 "###);
1767
1768 // Rebase a commit before another commit. "c" has parents "b2" and "b4", so its
1769 // children "d" and "e" should be rebased onto "b2" and "b4" respectively.
1770 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "c", "--before", "a"]);
1771 insta::assert_snapshot!(stdout, @"");
1772 insta::assert_snapshot!(stderr, @r###"
1773 Rebased 1 commits onto destination
1774 Rebased 8 descendant commits
1775 Working copy now at: xznxytkn 24335685 f | f
1776 Parent commit : nkmrtpmo e9a28d4b e | e
1777 "###);
1778 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1779 @ f xznxytkn 24335685
1780 ◉ e nkmrtpmo e9a28d4b
1781 ├─╮
1782 │ │ ◉ d lylxulpl 6609e9c6
1783 ╭─┬─╯
1784 │ ◉ b4 znkkpsqq 4b39b18c
1785 │ ◉ b3 vruxwmqv 39f79dcc
1786 ◉ │ b2 royxmykx ffcf6038
1787 ◉ │ b1 zsuskuln 85e90af6
1788 ├─╯
1789 ◉ a rlvkpnrz 318ea816
1790 ◉ c kmkuslsw 5f99791e
1791 ◉ zzzzzzzz 00000000
1792 "###);
1793 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1794
1795 // Rebase a commit before its parent.
1796 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--before", "e"]);
1797 insta::assert_snapshot!(stdout, @"");
1798 insta::assert_snapshot!(stderr, @r###"
1799 Rebased 1 commits onto destination
1800 Rebased 1 descendant commits
1801 Working copy now at: xznxytkn 8e3b728a f | f
1802 Parent commit : kmkuslsw cd86b3e4 c | c
1803 Added 0 files, modified 0 files, removed 1 files
1804 "###);
1805 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1806 ◉ e nkmrtpmo 41706bd9
1807 @ f xznxytkn 8e3b728a
1808 │ ◉ d lylxulpl 7d0512e5
1809 ├─╯
1810 ◉ c kmkuslsw cd86b3e4
1811 ├─╮
1812 │ ◉ b4 znkkpsqq a52a83a4
1813 │ ◉ b3 vruxwmqv 523e6a8b
1814 ◉ │ b2 royxmykx 2b8e1148
1815 ◉ │ b1 zsuskuln 072d5ae1
1816 ├─╯
1817 ◉ a rlvkpnrz 2443ea76
1818 ◉ zzzzzzzz 00000000
1819 "###);
1820 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1821
1822 // Rebase a commit before a commit in a branch of a merge commit.
1823 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--before", "b2"]);
1824 insta::assert_snapshot!(stdout, @"");
1825 insta::assert_snapshot!(stderr, @r###"
1826 Rebased 1 commits onto destination
1827 Rebased 4 descendant commits
1828 Working copy now at: xznxytkn 2b4f48f8 f | f
1829 Parent commit : zsuskuln 072d5ae1 b1 | b1
1830 Added 0 files, modified 0 files, removed 5 files
1831 "###);
1832 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1833 ◉ e nkmrtpmo 7cad61fd
1834 │ ◉ d lylxulpl 526b6ab6
1835 ├─╯
1836 ◉ c kmkuslsw 445f6927
1837 ├─╮
1838 │ ◉ b4 znkkpsqq a52a83a4
1839 │ ◉ b3 vruxwmqv 523e6a8b
1840 ◉ │ b2 royxmykx 972bfeb7
1841 @ │ f xznxytkn 2b4f48f8
1842 ◉ │ b1 zsuskuln 072d5ae1
1843 ├─╯
1844 ◉ a rlvkpnrz 2443ea76
1845 ◉ zzzzzzzz 00000000
1846 "###);
1847 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1848
1849 // Rebase a commit before the first commit in a branch of a merge commit.
1850 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--before", "b1"]);
1851 insta::assert_snapshot!(stdout, @"");
1852 insta::assert_snapshot!(stderr, @r###"
1853 Rebased 1 commits onto destination
1854 Rebased 5 descendant commits
1855 Working copy now at: xznxytkn 488ebb95 f | f
1856 Parent commit : rlvkpnrz 2443ea76 a | a
1857 Added 0 files, modified 0 files, removed 6 files
1858 "###);
1859 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1860 ◉ e nkmrtpmo 9d5fa6a2
1861 │ ◉ d lylxulpl ca323694
1862 ├─╯
1863 ◉ c kmkuslsw 07426e1a
1864 ├─╮
1865 │ ◉ b4 znkkpsqq a52a83a4
1866 │ ◉ b3 vruxwmqv 523e6a8b
1867 ◉ │ b2 royxmykx 55376058
1868 ◉ │ b1 zsuskuln cd5b1d04
1869 @ │ f xznxytkn 488ebb95
1870 ├─╯
1871 ◉ a rlvkpnrz 2443ea76
1872 ◉ zzzzzzzz 00000000
1873 "###);
1874 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1875
1876 // Rebase a commit before a merge commit. "c" has two parents "b2" and "b4", so
1877 // the rebased commit "f" will have the two commits "b2" and "b4" as its
1878 // parents.
1879 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "f", "--before", "c"]);
1880 insta::assert_snapshot!(stdout, @"");
1881 insta::assert_snapshot!(stderr, @r###"
1882 Rebased 1 commits onto destination
1883 Rebased 3 descendant commits
1884 Working copy now at: xznxytkn aae1bc10 f | f
1885 Parent commit : royxmykx 2b8e1148 b2 | b2
1886 Parent commit : znkkpsqq a52a83a4 b4 | b4
1887 Added 0 files, modified 0 files, removed 2 files
1888 "###);
1889 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1890 ◉ e nkmrtpmo 0ea67093
1891 │ ◉ d lylxulpl c079568d
1892 ├─╯
1893 ◉ c kmkuslsw 6371742b
1894 @ f xznxytkn aae1bc10
1895 ├─╮
1896 │ ◉ b4 znkkpsqq a52a83a4
1897 │ ◉ b3 vruxwmqv 523e6a8b
1898 ◉ │ b2 royxmykx 2b8e1148
1899 ◉ │ b1 zsuskuln 072d5ae1
1900 ├─╯
1901 ◉ a rlvkpnrz 2443ea76
1902 ◉ zzzzzzzz 00000000
1903 "###);
1904 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1905
1906 // Rebase a commit before multiple commits.
1907 let (stdout, stderr) = test_env.jj_cmd_ok(
1908 &repo_path,
1909 &["rebase", "-r", "b1", "--before", "d", "--before", "e"],
1910 );
1911 insta::assert_snapshot!(stdout, @"");
1912 insta::assert_snapshot!(stderr, @r###"
1913 Rebased 1 commits onto destination
1914 Rebased 5 descendant commits
1915 Working copy now at: xznxytkn 8268ec4d f | f
1916 Parent commit : nkmrtpmo fd26fbd4 e | e
1917 "###);
1918 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1919 @ f xznxytkn 8268ec4d
1920 ◉ e nkmrtpmo fd26fbd4
1921 │ ◉ d lylxulpl 21da64b4
1922 ├─╯
1923 ◉ b1 zsuskuln 83e9b8ac
1924 ◉ c kmkuslsw a89354fc
1925 ├─╮
1926 │ ◉ b4 znkkpsqq a52a83a4
1927 │ ◉ b3 vruxwmqv 523e6a8b
1928 ◉ │ b2 royxmykx b7f03180
1929 ├─╯
1930 ◉ a rlvkpnrz 2443ea76
1931 ◉ zzzzzzzz 00000000
1932 "###);
1933 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1934
1935 // Rebase a commit before two commits in separate branches to create a merge
1936 // commit.
1937 let (stdout, stderr) = test_env.jj_cmd_ok(
1938 &repo_path,
1939 &["rebase", "-r", "f", "--before", "b2", "--before", "b4"],
1940 );
1941 insta::assert_snapshot!(stdout, @"");
1942 insta::assert_snapshot!(stderr, @r###"
1943 Rebased 1 commits onto destination
1944 Rebased 5 descendant commits
1945 Working copy now at: xznxytkn 7ba8014f f | f
1946 Parent commit : zsuskuln 072d5ae1 b1 | b1
1947 Parent commit : vruxwmqv 523e6a8b b3 | b3
1948 Added 0 files, modified 0 files, removed 4 files
1949 "###);
1950 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1951 ◉ e nkmrtpmo 9436134a
1952 │ ◉ d lylxulpl 534be1ee
1953 ├─╯
1954 ◉ c kmkuslsw bc3ed9f8
1955 ├─╮
1956 │ ◉ b4 znkkpsqq 3e59611b
1957 ◉ │ b2 royxmykx 148d7e50
1958 ├─╯
1959 @ f xznxytkn 7ba8014f
1960 ├─╮
1961 │ ◉ b3 vruxwmqv 523e6a8b
1962 ◉ │ b1 zsuskuln 072d5ae1
1963 ├─╯
1964 ◉ a rlvkpnrz 2443ea76
1965 ◉ zzzzzzzz 00000000
1966 "###);
1967 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
1968
1969 // Rebase two unrelated commits "b2" and "b4" before a single commit "a". This
1970 // creates a merge commit "a" with the two parents "b2" and "b4".
1971 let (stdout, stderr) = test_env.jj_cmd_ok(
1972 &repo_path,
1973 &["rebase", "-r", "b2", "-r", "b4", "--before", "a"],
1974 );
1975 insta::assert_snapshot!(stdout, @"");
1976 insta::assert_snapshot!(stderr, @r###"
1977 Rebased 2 commits onto destination
1978 Rebased 7 descendant commits
1979 Working copy now at: xznxytkn fabd8dd7 f | f
1980 Parent commit : nkmrtpmo b5933877 e | e
1981 "###);
1982 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
1983 @ f xznxytkn fabd8dd7
1984 ◉ e nkmrtpmo b5933877
1985 │ ◉ d lylxulpl 6b91dd66
1986 ├─╯
1987 ◉ c kmkuslsw d873acf7
1988 ├─╮
1989 │ ◉ b3 vruxwmqv 1fd332d8
1990 ◉ │ b1 zsuskuln 8e39430f
1991 ├─╯
1992 ◉ a rlvkpnrz 414580f5
1993 ├─╮
1994 │ ◉ b4 znkkpsqq ae3d5bdb
1995 ◉ │ b2 royxmykx a225236e
1996 ├─╯
1997 ◉ zzzzzzzz 00000000
1998 "###);
1999 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
2000
2001 // Rebase a subgraph with a merge commit and two parents.
2002 let (stdout, stderr) = test_env.jj_cmd_ok(
2003 &repo_path,
2004 &["rebase", "-r", "b2", "-r", "b4", "-r", "c", "--before", "e"],
2005 );
2006 insta::assert_snapshot!(stdout, @"");
2007 insta::assert_snapshot!(stderr, @r###"
2008 Rebased 3 commits onto destination
2009 Rebased 3 descendant commits
2010 Working copy now at: xznxytkn cbe2be58 f | f
2011 Parent commit : nkmrtpmo e31053d1 e | e
2012 "###);
2013 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2014 @ f xznxytkn cbe2be58
2015 ◉ e nkmrtpmo e31053d1
2016 ◉ c kmkuslsw 23155860
2017 ├─╮
2018 │ ◉ b4 znkkpsqq e50520ad
2019 │ ├─╮
2020 ◉ │ │ b2 royxmykx 54f03b06
2021 ╰─┬─╮
2022 ◉ │ │ d lylxulpl 0c74206e
2023 ╰─┬─╮
2024 │ ◉ b3 vruxwmqv 523e6a8b
2025 ◉ │ b1 zsuskuln 072d5ae1
2026 ├─╯
2027 ◉ a rlvkpnrz 2443ea76
2028 ◉ zzzzzzzz 00000000
2029 "###);
2030 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
2031
2032 // Rebase a subgraph with disconnected commits. Since "b1" is an ancestor of
2033 // "e", "b1" should be a parent of "e" after the rebase.
2034 let (stdout, stderr) = test_env.jj_cmd_ok(
2035 &repo_path,
2036 &["rebase", "-r", "b1", "-r", "e", "--before", "a"],
2037 );
2038 insta::assert_snapshot!(stdout, @"");
2039 insta::assert_snapshot!(stderr, @r###"
2040 Rebased 2 commits onto destination
2041 Rebased 7 descendant commits
2042 Working copy now at: xznxytkn 1c48b514 f | f
2043 Parent commit : kmkuslsw c0fd979a c | c
2044 "###);
2045 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2046 @ f xznxytkn 1c48b514
2047 │ ◉ d lylxulpl 4dbbc808
2048 ├─╯
2049 ◉ c kmkuslsw c0fd979a
2050 ├─╮
2051 │ ◉ b4 znkkpsqq 4d5c61f4
2052 │ ◉ b3 vruxwmqv d5699c24
2053 ◉ │ b2 royxmykx e23ab998
2054 ├─╯
2055 ◉ a rlvkpnrz 076f0094
2056 ◉ e nkmrtpmo 20d1f131
2057 ◉ b1 zsuskuln 11db739a
2058 ◉ zzzzzzzz 00000000
2059 "###);
2060 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
2061
2062 // Should error if a loop will be created.
2063 let stderr = test_env.jj_cmd_failure(
2064 &repo_path,
2065 &["rebase", "-r", "e", "--before", "b2", "--before", "c"],
2066 );
2067 insta::assert_snapshot!(stderr, @r###"
2068 Error: Refusing to create a loop: commit 2b8e1148290f would be both an ancestor and a descendant of the rebased commits
2069 "###);
2070}
2071
2072#[test]
2073fn test_rebase_revisions_after_before() {
2074 let test_env = TestEnvironment::default();
2075 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
2076 let repo_path = test_env.env_root().join("repo");
2077
2078 create_commit(&test_env, &repo_path, "a", &[]);
2079 create_commit(&test_env, &repo_path, "b1", &["a"]);
2080 create_commit(&test_env, &repo_path, "b2", &["a"]);
2081 create_commit(&test_env, &repo_path, "c", &["b1", "b2"]);
2082 create_commit(&test_env, &repo_path, "d", &["c"]);
2083 create_commit(&test_env, &repo_path, "e", &["c"]);
2084 create_commit(&test_env, &repo_path, "f", &["e"]);
2085 // Test the setup
2086 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2087 @ f lylxulpl 88f778c5
2088 ◉ e kmkuslsw 48dd9e3f
2089 │ ◉ d znkkpsqq 92438fc9
2090 ├─╯
2091 ◉ c vruxwmqv c41e416e
2092 ├─╮
2093 │ ◉ b2 royxmykx 903ab0d6
2094 ◉ │ b1 zsuskuln 072d5ae1
2095 ├─╯
2096 ◉ a rlvkpnrz 2443ea76
2097 ◉ zzzzzzzz 00000000
2098 "###);
2099 let setup_opid = test_env.current_operation_id(&repo_path);
2100
2101 // Rebase a commit after another commit and before that commit's child to
2102 // insert directly between the two commits.
2103 let (stdout, stderr) = test_env.jj_cmd_ok(
2104 &repo_path,
2105 &["rebase", "-r", "d", "--after", "e", "--before", "f"],
2106 );
2107 insta::assert_snapshot!(stdout, @"");
2108 insta::assert_snapshot!(stderr, @r###"
2109 Rebased 1 commits onto destination
2110 Rebased 1 descendant commits
2111 Working copy now at: lylxulpl fe3d8c30 f | f
2112 Parent commit : znkkpsqq cca70ee1 d | d
2113 Added 1 files, modified 0 files, removed 0 files
2114 "###);
2115 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2116 @ f lylxulpl fe3d8c30
2117 ◉ d znkkpsqq cca70ee1
2118 ◉ e kmkuslsw 48dd9e3f
2119 ◉ c vruxwmqv c41e416e
2120 ├─╮
2121 │ ◉ b2 royxmykx 903ab0d6
2122 ◉ │ b1 zsuskuln 072d5ae1
2123 ├─╯
2124 ◉ a rlvkpnrz 2443ea76
2125 ◉ zzzzzzzz 00000000
2126 "###);
2127 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
2128
2129 // Rebase a commit after another commit and before that commit's descendant to
2130 // create a new merge commit.
2131 let (stdout, stderr) = test_env.jj_cmd_ok(
2132 &repo_path,
2133 &["rebase", "-r", "d", "--after", "a", "--before", "f"],
2134 );
2135 insta::assert_snapshot!(stdout, @"");
2136 insta::assert_snapshot!(stderr, @r###"
2137 Rebased 1 commits onto destination
2138 Rebased 1 descendant commits
2139 Working copy now at: lylxulpl 22f0323c f | f
2140 Parent commit : kmkuslsw 48dd9e3f e | e
2141 Parent commit : znkkpsqq 61388bb6 d | d
2142 Added 1 files, modified 0 files, removed 0 files
2143 "###);
2144 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2145 @ f lylxulpl 22f0323c
2146 ├─╮
2147 │ ◉ d znkkpsqq 61388bb6
2148 ◉ │ e kmkuslsw 48dd9e3f
2149 ◉ │ c vruxwmqv c41e416e
2150 ├───╮
2151 │ │ ◉ b2 royxmykx 903ab0d6
2152 │ ├─╯
2153 ◉ │ b1 zsuskuln 072d5ae1
2154 ├─╯
2155 ◉ a rlvkpnrz 2443ea76
2156 ◉ zzzzzzzz 00000000
2157 "###);
2158 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
2159
2160 // "c" has parents "b1" and "b2", so when it is rebased, its children "d" and
2161 // "e" should have "b1" and "b2" as parents as well. "c" is then inserted in
2162 // between "d" and "e", making "e" a merge commit with 3 parents "b1", "b2",
2163 // and "c".
2164 let (stdout, stderr) = test_env.jj_cmd_ok(
2165 &repo_path,
2166 &["rebase", "-r", "c", "--after", "d", "--before", "e"],
2167 );
2168 insta::assert_snapshot!(stdout, @"");
2169 insta::assert_snapshot!(stderr, @r###"
2170 Rebased 1 commits onto destination
2171 Rebased 3 descendant commits
2172 Working copy now at: lylxulpl e37682c5 f | f
2173 Parent commit : kmkuslsw 9bbc9e53 e | e
2174 Added 1 files, modified 0 files, removed 0 files
2175 "###);
2176 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2177 @ f lylxulpl e37682c5
2178 ◉ e kmkuslsw 9bbc9e53
2179 ├─┬─╮
2180 │ │ ◉ c vruxwmqv e11c7c95
2181 │ │ ◉ d znkkpsqq 37869bd5
2182 ╭─┬─╯
2183 │ ◉ b2 royxmykx 903ab0d6
2184 ◉ │ b1 zsuskuln 072d5ae1
2185 ├─╯
2186 ◉ a rlvkpnrz 2443ea76
2187 ◉ zzzzzzzz 00000000
2188 "###);
2189 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
2190
2191 // Rebase multiple commits and preserve their ancestry. Apart from the heads of
2192 // the target commits ("d" and "e"), "f" also has commits "b1" and "b2" as
2193 // parents since its parents "d" and "e" were in the target set and were
2194 // replaced by their closest ancestors outside the target set.
2195 let (stdout, stderr) = test_env.jj_cmd_ok(
2196 &repo_path,
2197 &[
2198 "rebase", "-r", "c", "-r", "d", "-r", "e", "--after", "a", "--before", "f",
2199 ],
2200 );
2201 insta::assert_snapshot!(stdout, @"");
2202 insta::assert_snapshot!(stderr, @r###"
2203 Rebased 3 commits onto destination
2204 Rebased 1 descendant commits
2205 Working copy now at: lylxulpl 868f6c61 f | f
2206 Parent commit : zsuskuln 072d5ae1 b1 | b1
2207 Parent commit : royxmykx 903ab0d6 b2 | b2
2208 Parent commit : znkkpsqq ae6181e6 d | d
2209 Parent commit : kmkuslsw a55a6779 e | e
2210 Added 1 files, modified 0 files, removed 0 files
2211 "###);
2212 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2213 @ f lylxulpl 868f6c61
2214 ├─┬─┬─╮
2215 │ │ │ ◉ e kmkuslsw a55a6779
2216 │ │ ◉ │ d znkkpsqq ae6181e6
2217 │ │ ├─╯
2218 │ │ ◉ c vruxwmqv 22540859
2219 │ ◉ │ b2 royxmykx 903ab0d6
2220 │ ├─╯
2221 ◉ │ b1 zsuskuln 072d5ae1
2222 ├─╯
2223 ◉ a rlvkpnrz 2443ea76
2224 ◉ zzzzzzzz 00000000
2225 "###);
2226 test_env.jj_cmd_ok(&repo_path, &["op", "restore", &setup_opid]);
2227
2228 // Should error if a loop will be created.
2229 let stderr = test_env.jj_cmd_failure(
2230 &repo_path,
2231 &["rebase", "-r", "e", "--after", "c", "--before", "a"],
2232 );
2233 insta::assert_snapshot!(stderr, @r###"
2234 Error: Refusing to create a loop: commit c41e416ee4cf would be both an ancestor and a descendant of the rebased commits
2235 "###);
2236}
2237
2238#[test]
2239fn test_rebase_skip_empty() {
2240 let test_env = TestEnvironment::default();
2241 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
2242 let repo_path = test_env.env_root().join("repo");
2243
2244 create_commit(&test_env, &repo_path, "a", &[]);
2245 create_commit(&test_env, &repo_path, "b", &["a"]);
2246 test_env.jj_cmd_ok(&repo_path, &["new", "a", "-m", "will become empty"]);
2247 test_env.jj_cmd_ok(&repo_path, &["restore", "--from=b"]);
2248 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "already empty"]);
2249 test_env.jj_cmd_ok(&repo_path, &["new", "-m", "also already empty"]);
2250
2251 // Test the setup
2252 insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["log", "-T", "description"]), @r###"
2253 @ also already empty
2254 ◉ already empty
2255 ◉ will become empty
2256 │ ◉ b
2257 ├─╯
2258 ◉ a
2259 ◉
2260 "###);
2261
2262 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-d=b", "--skip-empty"]);
2263 insta::assert_snapshot!(stdout, @"");
2264 insta::assert_snapshot!(stderr, @r###"
2265 Rebased 3 commits
2266 Working copy now at: yostqsxw 6b74c840 (empty) also already empty
2267 Parent commit : vruxwmqv 48a31526 (empty) already empty
2268 "###);
2269
2270 // The parent commit became empty and was dropped, but the already empty commits
2271 // were kept
2272 insta::assert_snapshot!(test_env.jj_cmd_success(&repo_path, &["log", "-T", "description"]), @r###"
2273 @ also already empty
2274 ◉ already empty
2275 ◉ b
2276 ◉ a
2277 ◉
2278 "###);
2279}
2280
2281#[test]
2282fn test_rebase_skip_if_on_destination() {
2283 let test_env = TestEnvironment::default();
2284 test_env.jj_cmd_ok(test_env.env_root(), &["init", "repo", "--git"]);
2285 let repo_path = test_env.env_root().join("repo");
2286
2287 create_commit(&test_env, &repo_path, "a", &[]);
2288 create_commit(&test_env, &repo_path, "b1", &["a"]);
2289 create_commit(&test_env, &repo_path, "b2", &["a"]);
2290 create_commit(&test_env, &repo_path, "c", &["b1", "b2"]);
2291 create_commit(&test_env, &repo_path, "d", &["c"]);
2292 create_commit(&test_env, &repo_path, "e", &["c"]);
2293 create_commit(&test_env, &repo_path, "f", &["e"]);
2294 // Test the setup
2295 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2296 @ f lylxulpl 88f778c5
2297 ◉ e kmkuslsw 48dd9e3f
2298 │ ◉ d znkkpsqq 92438fc9
2299 ├─╯
2300 ◉ c vruxwmqv c41e416e
2301 ├─╮
2302 │ ◉ b2 royxmykx 903ab0d6
2303 ◉ │ b1 zsuskuln 072d5ae1
2304 ├─╯
2305 ◉ a rlvkpnrz 2443ea76
2306 ◉ zzzzzzzz 00000000
2307 "###);
2308
2309 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-b", "d", "-d", "a"]);
2310 insta::assert_snapshot!(stdout, @"");
2311 // Skip rebase with -b
2312 insta::assert_snapshot!(stderr, @r###"
2313 Skipped rebase of 2 commits that were already in place
2314 "###);
2315 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2316 @ f lylxulpl 88f778c5
2317 ◉ e kmkuslsw 48dd9e3f
2318 │ ◉ d znkkpsqq 92438fc9
2319 ├─╯
2320 ◉ c vruxwmqv c41e416e
2321 ├─╮
2322 │ ◉ b2 royxmykx 903ab0d6
2323 ◉ │ b1 zsuskuln 072d5ae1
2324 ├─╯
2325 ◉ a rlvkpnrz 2443ea76
2326 ◉ zzzzzzzz 00000000
2327 "###);
2328
2329 let (stdout, stderr) =
2330 test_env.jj_cmd_ok(&repo_path, &["rebase", "-s", "c", "-d", "b1", "-d", "b2"]);
2331 insta::assert_snapshot!(stdout, @"");
2332 // Skip rebase with -s
2333 insta::assert_snapshot!(stderr, @r###"
2334 Skipped rebase of 1 commits that were already in place
2335 "###);
2336 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2337 @ f lylxulpl 88f778c5
2338 ◉ e kmkuslsw 48dd9e3f
2339 │ ◉ d znkkpsqq 92438fc9
2340 ├─╯
2341 ◉ c vruxwmqv c41e416e
2342 ├─╮
2343 │ ◉ b2 royxmykx 903ab0d6
2344 ◉ │ b1 zsuskuln 072d5ae1
2345 ├─╯
2346 ◉ a rlvkpnrz 2443ea76
2347 ◉ zzzzzzzz 00000000
2348 "###);
2349
2350 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "d", "-d", "c"]);
2351 insta::assert_snapshot!(stdout, @"");
2352 // Skip rebase with -r since commit has no children
2353 insta::assert_snapshot!(stderr, @r###"
2354 Skipped rebase of 1 commits that were already in place
2355 Nothing changed.
2356 "###);
2357 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2358 @ f lylxulpl 88f778c5
2359 ◉ e kmkuslsw 48dd9e3f
2360 │ ◉ d znkkpsqq 92438fc9
2361 ├─╯
2362 ◉ c vruxwmqv c41e416e
2363 ├─╮
2364 │ ◉ b2 royxmykx 903ab0d6
2365 ◉ │ b1 zsuskuln 072d5ae1
2366 ├─╯
2367 ◉ a rlvkpnrz 2443ea76
2368 ◉ zzzzzzzz 00000000
2369 "###);
2370
2371 let (stdout, stderr) = test_env.jj_cmd_ok(&repo_path, &["rebase", "-r", "e", "-d", "c"]);
2372 insta::assert_snapshot!(stdout, @"");
2373 // Skip rebase of commit, but rebases children onto destination with -r
2374 insta::assert_snapshot!(stderr, @r###"
2375 Skipped rebase of 1 commits that were already in place
2376 Rebased 1 descendant commits
2377 Working copy now at: lylxulpl 77cb229f f | f
2378 Parent commit : vruxwmqv c41e416e c | c
2379 Added 0 files, modified 0 files, removed 1 files
2380 "###);
2381 insta::assert_snapshot!(get_long_log_output(&test_env, &repo_path), @r###"
2382 @ f lylxulpl 77cb229f
2383 │ ◉ e kmkuslsw 48dd9e3f
2384 ├─╯
2385 │ ◉ d znkkpsqq 92438fc9
2386 ├─╯
2387 ◉ c vruxwmqv c41e416e
2388 ├─╮
2389 │ ◉ b2 royxmykx 903ab0d6
2390 ◉ │ b1 zsuskuln 072d5ae1
2391 ├─╯
2392 ◉ a rlvkpnrz 2443ea76
2393 ◉ zzzzzzzz 00000000
2394 "###);
2395}
2396
2397fn get_long_log_output(test_env: &TestEnvironment, repo_path: &Path) -> String {
2398 let template = r#"description.first_line() ++ " " ++ change_id.shortest(8) ++ " " ++ commit_id.shortest(8)"#;
2399 test_env.jj_cmd_success(repo_path, &["log", "-T", template])
2400}