Skip to content

Commit aa2d3a6

Browse files
committed
Merge branch 'js/rebase-r-strategy' into jch
"git rebase --rebase-merges" learned to drive different merge strategies and pass strategy specific options to them. * js/rebase-r-strategy: t3427: accelerate this test by using fast-export and fast-import rebase -r: do not (re-)generate root commits with `--root` *and* `--onto` t3418: test `rebase -r` with merge strategies t/lib-rebase: prepare for testing `git rebase --rebase-merges` rebase -r: support merge strategies other than `recursive` t3427: fix another incorrect assumption t3427: accommodate for the `rebase --merge` backend having been replaced t3427: fix erroneous assumption t3427: condense the unnecessarily repetitive test cases into three t3427: move the `filter-branch` invocation into the `setup` case t3427: simplify the `setup` test case significantly t3427: add a clarifying comment rebase: fold git-rebase--common into the -p backend sequencer: the `am` and `rebase--interactive` scripts are gone .gitignore: there is no longer a built-in `git-rebase--interactive` t3400: stop referring to the scripted rebase Drop unused git-rebase--am.sh
2 parents 3cb8921 + 476998d commit aa2d3a6

9 files changed

+139
-117
lines changed

Documentation/git-rebase.txt

-2
Original file line numberDiff line numberDiff line change
@@ -543,8 +543,6 @@ In addition, the following pairs of options are incompatible:
543543
* --preserve-merges and --interactive
544544
* --preserve-merges and --signoff
545545
* --preserve-merges and --rebase-merges
546-
* --rebase-merges and --strategy
547-
* --rebase-merges and --strategy-option
548546

549547
BEHAVIORAL DIFFERENCES
550548
-----------------------

builtin/rebase.c

+5-11
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ struct rebase_options {
6262
const char *onto_name;
6363
const char *revisions;
6464
const char *switch_to;
65-
int root;
65+
int root, root_with_onto;
6666
struct object_id *squash_onto;
6767
struct commit *restrict_revision;
6868
int dont_finish_rebase;
@@ -374,6 +374,7 @@ static int run_rebase_interactive(struct rebase_options *opts,
374374
flags |= abbreviate_commands ? TODO_LIST_ABBREVIATE_CMDS : 0;
375375
flags |= opts->rebase_merges ? TODO_LIST_REBASE_MERGES : 0;
376376
flags |= opts->rebase_cousins > 0 ? TODO_LIST_REBASE_COUSINS : 0;
377+
flags |= opts->root_with_onto ? TODO_LIST_ROOT_WITH_ONTO : 0;
377378
flags |= command == ACTION_SHORTEN_OIDS ? TODO_LIST_SHORTEN_IDS : 0;
378379

379380
switch (command) {
@@ -1833,15 +1834,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
18331834
"'--reschedule-failed-exec'"));
18341835
}
18351836

1836-
if (options.rebase_merges) {
1837-
if (strategy_options.nr)
1838-
die(_("cannot combine '--rebase-merges' with "
1839-
"'--strategy-option'"));
1840-
if (options.strategy)
1841-
die(_("cannot combine '--rebase-merges' with "
1842-
"'--strategy'"));
1843-
}
1844-
18451837
if (!options.root) {
18461838
if (argc < 1) {
18471839
struct branch *branch;
@@ -1872,7 +1864,9 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
18721864
options.squash_onto = &squash_onto;
18731865
options.onto_name = squash_onto_name =
18741866
xstrdup(oid_to_hex(&squash_onto));
1875-
}
1867+
} else
1868+
options.root_with_onto = 1;
1869+
18761870
options.upstream_name = NULL;
18771871
options.upstream = NULL;
18781872
if (argc > 1)

sequencer.c

+15-3
Original file line numberDiff line numberDiff line change
@@ -3364,6 +3364,9 @@ static int do_merge(struct repository *r,
33643364
struct commit *head_commit, *merge_commit, *i;
33653365
struct commit_list *bases, *j, *reversed = NULL;
33663366
struct commit_list *to_merge = NULL, **tail = &to_merge;
3367+
const char *strategy = !opts->xopts_nr &&
3368+
(!opts->strategy || !strcmp(opts->strategy, "recursive")) ?
3369+
NULL : opts->strategy;
33673370
struct merge_options o;
33683371
int merge_arg_len, oneline_offset, can_fast_forward, ret, k;
33693372
static struct lock_file lock;
@@ -3516,7 +3519,7 @@ static int do_merge(struct repository *r,
35163519
goto leave_merge;
35173520
}
35183521

3519-
if (to_merge->next) {
3522+
if (strategy || to_merge->next) {
35203523
/* Octopus merge */
35213524
struct child_process cmd = CHILD_PROCESS_INIT;
35223525

@@ -3530,7 +3533,14 @@ static int do_merge(struct repository *r,
35303533
cmd.git_cmd = 1;
35313534
argv_array_push(&cmd.args, "merge");
35323535
argv_array_push(&cmd.args, "-s");
3533-
argv_array_push(&cmd.args, "octopus");
3536+
if (!strategy)
3537+
argv_array_push(&cmd.args, "octopus");
3538+
else {
3539+
argv_array_push(&cmd.args, strategy);
3540+
for (k = 0; k < opts->xopts_nr; k++)
3541+
argv_array_pushf(&cmd.args,
3542+
"-X%s", opts->xopts[k]);
3543+
}
35343544
argv_array_push(&cmd.args, "--no-edit");
35353545
argv_array_push(&cmd.args, "--no-ff");
35363546
argv_array_push(&cmd.args, "--no-log");
@@ -4554,6 +4564,7 @@ static int make_script_with_merges(struct pretty_print_context *pp,
45544564
{
45554565
int keep_empty = flags & TODO_LIST_KEEP_EMPTY;
45564566
int rebase_cousins = flags & TODO_LIST_REBASE_COUSINS;
4567+
int root_with_onto = flags & TODO_LIST_ROOT_WITH_ONTO;
45574568
struct strbuf buf = STRBUF_INIT, oneline = STRBUF_INIT;
45584569
struct strbuf label = STRBUF_INIT;
45594570
struct commit_list *commits = NULL, **tail = &commits, *iter;
@@ -4720,7 +4731,8 @@ static int make_script_with_merges(struct pretty_print_context *pp,
47204731

47214732
if (!commit)
47224733
strbuf_addf(out, "%s %s\n", cmd_reset,
4723-
rebase_cousins ? "onto" : "[new root]");
4734+
rebase_cousins || root_with_onto ?
4735+
"onto" : "[new root]");
47244736
else {
47254737
const char *to = NULL;
47264738

sequencer.h

+6
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ int sequencer_remove_state(struct replay_opts *opts);
143143
*/
144144
#define TODO_LIST_REBASE_COUSINS (1U << 4)
145145
#define TODO_LIST_APPEND_TODO_HELP (1U << 5)
146+
/*
147+
* When generating a script that rebases merges with `--root` *and* with
148+
* `--onto`, we do not want to re-generate the root commits.
149+
*/
150+
#define TODO_LIST_ROOT_WITH_ONTO (1U << 6)
151+
146152

147153
int sequencer_make_script(struct repository *r, struct strbuf *out, int argc,
148154
const char **argv, unsigned flags);

t/lib-rebase.sh

+5-4
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,10 @@ set_fake_editor () {
4444
rm -f "$1"
4545
echo 'rebase -i script before editing:'
4646
cat "$1".tmp
47-
action=pick
47+
action=\&
4848
for line in $FAKE_LINES; do
4949
case $line in
50-
pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d)
50+
pick|p|squash|s|fixup|f|edit|e|reword|r|drop|d|label|l|reset|r|merge|m)
5151
action="$line";;
5252
exec_*|x_*|break|b)
5353
echo "$line" | sed 's/_/ /g' >> "$1";;
@@ -58,11 +58,12 @@ set_fake_editor () {
5858
bad)
5959
action="badcmd";;
6060
fakesha)
61+
test \& != "$action" || action=pick
6162
echo "$action XXXXXXX False commit" >> "$1"
6263
action=pick;;
6364
*)
64-
sed -n "${line}s/^pick/$action/p" < "$1".tmp >> "$1"
65-
action=pick;;
65+
sed -n "${line}s/^[a-z][a-z]*/$action/p" < "$1".tmp >> "$1"
66+
action=\&;;
6667
esac
6768
done
6869
echo 'rebase -i script after editing:'

t/t3418-rebase-continue.sh

+14
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,20 @@ test_expect_success REBASE_P 'rebase passes merge strategy options correctly' '
120120
git rebase --continue
121121
'
122122

123+
test_expect_success 'rebase -r passes merge strategy options correctly' '
124+
rm -fr .git/rebase-* &&
125+
git reset --hard commit-new-file-F3-on-topic-branch &&
126+
test_commit merge-theirs &&
127+
git reset --hard HEAD^ &&
128+
test_commit some-other-commit &&
129+
test_tick &&
130+
git merge --no-ff merge-theirs &&
131+
FAKE_LINES="1 3 edit 4 5 7 8 9" git rebase -i -f -r -m \
132+
-s recursive --strategy-option=theirs HEAD~2 &&
133+
test_commit force-change-ours &&
134+
git rebase --continue
135+
'
136+
123137
test_expect_success '--skip after failed fixup cleans commit message' '
124138
test_when_finished "test_might_fail git rebase --abort" &&
125139
git checkout -b with-conflicting-fixup &&

t/t3422-rebase-incompatible-options.sh

-10
Original file line numberDiff line numberDiff line change
@@ -76,14 +76,4 @@ test_expect_success REBASE_P \
7676
test_must_fail git rebase --preserve-merges --rebase-merges A
7777
'
7878

79-
test_expect_success '--rebase-merges incompatible with --strategy' '
80-
git checkout B^0 &&
81-
test_must_fail git rebase --rebase-merges -s resolve A
82-
'
83-
84-
test_expect_success '--rebase-merges incompatible with --strategy-option' '
85-
git checkout B^0 &&
86-
test_must_fail git rebase --rebase-merges -Xignore-space-change A
87-
'
88-
8979
test_done

t/t3427-rebase-subtree.sh

+73-87
Original file line numberDiff line numberDiff line change
@@ -11,113 +11,99 @@ commit_message() {
1111
git log --pretty=format:%s -1 "$1"
1212
}
1313

14+
# There are a few bugs in the rebase with regards to the subtree strategy, and
15+
# this test script tries to document them. First, the following commit history
16+
# is generated (the onelines are shown, time flows from left to right):
17+
#
18+
# master1 - master2 - master3
19+
# \
20+
# README ---------------------- Add subproject master - master4 - files_subtree/master5
21+
#
22+
# Where the merge moves the files master[123].t into the subdirectory
23+
# files_subtree/ and master4 as well as files_subtree/master5 add files to that
24+
# directory directly.
25+
#
26+
# Then, in subsequent test cases, `git filter-branch` is used to distill just
27+
# the commits that touch files_subtree/. To give it a final pre-rebase touch,
28+
# an empty commit is added on top. The pre-rebase commit history looks like
29+
# this:
30+
#
31+
# Add subproject master - master4 - files_subtree/master5 - Empty commit
32+
#
33+
# where the root commit adds three files: master1.t, master2.t and master3.t.
34+
#
35+
# This commit history is then rebased onto `master3` with the
36+
# `-Xsubtree=files_subtree` option in three different ways:
37+
#
38+
# 1. using `--preserve-merges`
39+
# 2. using `--preserve-merges` and --keep-empty
40+
# 3. without specifying a rebase backend
41+
1442
test_expect_success 'setup' '
1543
test_commit README &&
16-
mkdir files &&
17-
(
18-
cd files &&
19-
git init &&
20-
test_commit master1 &&
21-
test_commit master2 &&
22-
test_commit master3
23-
) &&
24-
git fetch files master &&
25-
git branch files-master FETCH_HEAD &&
26-
git read-tree --prefix=files_subtree files-master &&
27-
git checkout -- files_subtree &&
28-
tree=$(git write-tree) &&
29-
head=$(git rev-parse HEAD) &&
30-
rev=$(git rev-parse --verify files-master^0) &&
31-
commit=$(git commit-tree -p $head -p $rev -m "Add subproject master" $tree) &&
32-
git update-ref HEAD $commit &&
33-
(
34-
cd files_subtree &&
35-
test_commit master4
36-
) &&
37-
test_commit files_subtree/master5
38-
'
3944
40-
# FAILURE: Does not preserve master4.
41-
test_expect_failure REBASE_P \
42-
'Rebase -Xsubtree --preserve-merges --onto commit 4' '
43-
reset_rebase &&
44-
git checkout -b rebase-preserve-merges-4 master &&
45-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
46-
git commit -m "Empty commit" --allow-empty &&
47-
git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master &&
48-
verbose test "$(commit_message HEAD~)" = "files_subtree/master4"
45+
git init files &&
46+
test_commit -C files master1 &&
47+
test_commit -C files master2 &&
48+
test_commit -C files master3 &&
49+
50+
: perform subtree merge into files_subtree/ &&
51+
git fetch files refs/heads/master:refs/heads/files-master &&
52+
git merge -s ours --no-commit --allow-unrelated-histories \
53+
files-master &&
54+
git read-tree --prefix=files_subtree -u files-master &&
55+
git commit -m "Add subproject master" &&
56+
57+
: add two extra commits to rebase &&
58+
test_commit -C files_subtree master4 &&
59+
test_commit files_subtree/master5 &&
60+
61+
git checkout -b to-rebase &&
62+
git fast-export --no-data HEAD -- files_subtree/ |
63+
sed -e "s%\([0-9a-f]\{40\} \)files_subtree/%\1%" |
64+
git fast-import --force --quiet &&
65+
git reset --hard &&
66+
git commit -m "Empty commit" --allow-empty
4967
'
5068

51-
# FAILURE: Does not preserve master5.
52-
test_expect_failure REBASE_P \
53-
'Rebase -Xsubtree --preserve-merges --onto commit 5' '
69+
# FAILURE: Does not preserve master4.
70+
test_expect_failure REBASE_P 'Rebase -Xsubtree --preserve-merges --onto commit' '
5471
reset_rebase &&
55-
git checkout -b rebase-preserve-merges-5 master &&
56-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
57-
git commit -m "Empty commit" --allow-empty &&
72+
git checkout -b rebase-preserve-merges to-rebase &&
5873
git rebase -Xsubtree=files_subtree --preserve-merges --onto files-master master &&
74+
verbose test "$(commit_message HEAD~)" = "master4" &&
5975
verbose test "$(commit_message HEAD)" = "files_subtree/master5"
6076
'
6177

6278
# FAILURE: Does not preserve master4.
63-
test_expect_failure REBASE_P \
64-
'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 4' '
65-
reset_rebase &&
66-
git checkout -b rebase-keep-empty-4 master &&
67-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
68-
git commit -m "Empty commit" --allow-empty &&
69-
git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
70-
verbose test "$(commit_message HEAD~2)" = "files_subtree/master4"
71-
'
72-
73-
# FAILURE: Does not preserve master5.
74-
test_expect_failure REBASE_P \
75-
'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit 5' '
76-
reset_rebase &&
77-
git checkout -b rebase-keep-empty-5 master &&
78-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
79-
git commit -m "Empty commit" --allow-empty &&
80-
git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
81-
verbose test "$(commit_message HEAD~)" = "files_subtree/master5"
82-
'
83-
84-
# FAILURE: Does not preserve Empty.
85-
test_expect_failure REBASE_P \
86-
'Rebase -Xsubtree --keep-empty --preserve-merges --onto empty commit' '
79+
test_expect_failure REBASE_P 'Rebase -Xsubtree --keep-empty --preserve-merges --onto commit' '
8780
reset_rebase &&
88-
git checkout -b rebase-keep-empty-empty master &&
89-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
90-
git commit -m "Empty commit" --allow-empty &&
81+
git checkout -b rebase-keep-empty to-rebase &&
9182
git rebase -Xsubtree=files_subtree --keep-empty --preserve-merges --onto files-master master &&
83+
verbose test "$(commit_message HEAD~2)" = "master4" &&
84+
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
9285
verbose test "$(commit_message HEAD)" = "Empty commit"
9386
'
9487

95-
# FAILURE: fatal: Could not parse object
96-
test_expect_failure 'Rebase -Xsubtree --onto commit 4' '
88+
test_expect_success 'Rebase -Xsubtree --keep-empty --onto commit' '
9789
reset_rebase &&
98-
git checkout -b rebase-onto-4 master &&
99-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
100-
git commit -m "Empty commit" --allow-empty &&
101-
git rebase -Xsubtree=files_subtree --onto files-master master &&
102-
verbose test "$(commit_message HEAD~2)" = "files_subtree/master4"
90+
git checkout -b rebase-onto to-rebase &&
91+
test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --onto files-master master &&
92+
: first pick results in no changes &&
93+
git rebase --continue &&
94+
verbose test "$(commit_message HEAD~2)" = "master4" &&
95+
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
96+
verbose test "$(commit_message HEAD)" = "Empty commit"
10397
'
10498

105-
# FAILURE: fatal: Could not parse object
106-
test_expect_failure 'Rebase -Xsubtree --onto commit 5' '
107-
reset_rebase &&
108-
git checkout -b rebase-onto-5 master &&
109-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
110-
git commit -m "Empty commit" --allow-empty &&
111-
git rebase -Xsubtree=files_subtree --onto files-master master &&
112-
verbose test "$(commit_message HEAD~)" = "files_subtree/master5"
113-
'
114-
# FAILURE: fatal: Could not parse object
115-
test_expect_failure 'Rebase -Xsubtree --onto empty commit' '
99+
test_expect_success 'Rebase -Xsubtree --keep-empty --rebase-merges --onto commit' '
116100
reset_rebase &&
117-
git checkout -b rebase-onto-empty master &&
118-
git filter-branch --prune-empty -f --subdirectory-filter files_subtree &&
119-
git commit -m "Empty commit" --allow-empty &&
120-
git rebase -Xsubtree=files_subtree --onto files-master master &&
101+
git checkout -b rebase-merges-onto to-rebase &&
102+
test_must_fail git rebase -Xsubtree=files_subtree --keep-empty --rebase-merges --onto files-master --root &&
103+
: first pick results in no changes &&
104+
git rebase --continue &&
105+
verbose test "$(commit_message HEAD~2)" = "master4" &&
106+
verbose test "$(commit_message HEAD~)" = "files_subtree/master5" &&
121107
verbose test "$(commit_message HEAD)" = "Empty commit"
122108
'
123109

t/t3430-rebase-merges.sh

+21
Original file line numberDiff line numberDiff line change
@@ -441,4 +441,25 @@ test_expect_success '--continue after resolving conflicts after a merge' '
441441
test_path_is_missing .git/MERGE_HEAD
442442
'
443443

444+
test_expect_success '--rebase-merges with strategies' '
445+
git checkout -b with-a-strategy F &&
446+
test_tick &&
447+
git merge -m "Merge conflicting-G" conflicting-G &&
448+
449+
: first, test with a merge strategy option &&
450+
git rebase -ir -Xtheirs G &&
451+
echo conflicting-G >expect &&
452+
test_cmp expect G.t &&
453+
454+
: now, try with a merge strategy other than recursive &&
455+
git reset --hard @{1} &&
456+
write_script git-merge-override <<-\EOF &&
457+
echo overridden$1 >>G.t
458+
git add G.t
459+
EOF
460+
PATH="$PWD:$PATH" git rebase -ir -s override -Xxopt G &&
461+
test_write_lines G overridden--xopt >expect &&
462+
test_cmp expect G.t
463+
'
464+
444465
test_done

0 commit comments

Comments
 (0)