rebase: use correct base for --keep-base when a branch is given
--keep-base rebases onto the merge base of the given upstream and the current HEAD regardless of whether a branch is given. This is contrary to the documentation and to the option's intended purpose. Instead, rebase onto the merge base of the given upstream and the given branch. Signed-off-by: Alex Henrie <alexhenrie24@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Alex Henrie committed
Apr 20, 2022 at 22:42 UTC
9e5ebe9668a0f152757182b80bae9b66aad11952
3 files changed
+60
-29
Documentation/git-rebase.txt
+3
-2
@@ -215,9 +215,10 @@ leave out at most one of A and B, in which case it defaults to HEAD.
215
216
--keep-base::
217
Set the starting point at which to create the new commits to the
218
- merge base of <upstream> <branch>. Running
218
+ merge base of <upstream> and <branch>. Running
219
'git rebase --keep-base <upstream> <branch>' is equivalent to
220
- running 'git rebase --onto <upstream>... <upstream>'.
220
+ running
221
+ 'git rebase --onto <upstream>...<branch> <upstream> <branch>'.
222
+
223
This option is useful in the case where one is developing a feature on
224
top of an upstream branch. While the feature is being worked on, the
builtin/rebase.c
+28
-27
@@ -1583,33 +1583,6 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
1583
options.upstream_arg = "--root";
1584
}
1585
1586
- /* Make sure the branch to rebase onto is valid. */
1587
- if (keep_base) {
1588
- strbuf_reset(&buf);
1589
- strbuf_addstr(&buf, options.upstream_name);
1590
- strbuf_addstr(&buf, "...");
1591
- options.onto_name = xstrdup(buf.buf);
1592
- } else if (!options.onto_name)
1593
- options.onto_name = options.upstream_name;
1594
- if (strstr(options.onto_name, "...")) {
1595
- if (get_oid_mb(options.onto_name, &merge_base) < 0) {
1596
- if (keep_base)
1597
- die(_("'%s': need exactly one merge base with branch"),
1598
- options.upstream_name);
1599
- else
1600
- die(_("'%s': need exactly one merge base"),
1601
- options.onto_name);
1602
- }
1603
- options.onto = lookup_commit_or_die(&merge_base,
1604
- options.onto_name);
1605
- } else {
1606
- options.onto =
1607
- lookup_commit_reference_by_name(options.onto_name);
1608
- if (!options.onto)
1609
- die(_("Does not point to a valid commit '%s'"),
1610
- options.onto_name);
1611
- }
1612
-
1586
/*
1587
* If the branch to rebase is given, that is the branch we will rebase
1588
* branch_name -- branch/commit being rebased, or
@@ -1659,6 +1632,34 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
1632
} else
1633
BUG("unexpected number of arguments left to parse");
1634
1635
+ /* Make sure the branch to rebase onto is valid. */
1636
+ if (keep_base) {
1637
+ strbuf_reset(&buf);
1638
+ strbuf_addstr(&buf, options.upstream_name);
1639
+ strbuf_addstr(&buf, "...");
1640
+ strbuf_addstr(&buf, branch_name);
1641
+ options.onto_name = xstrdup(buf.buf);
1642
+ } else if (!options.onto_name)
1643
+ options.onto_name = options.upstream_name;
1644
+ if (strstr(options.onto_name, "...")) {
1645
+ if (get_oid_mb(options.onto_name, &merge_base) < 0) {
1646
+ if (keep_base)
1647
+ die(_("'%s': need exactly one merge base with branch"),
1648
+ options.upstream_name);
1649
+ else
1650
+ die(_("'%s': need exactly one merge base"),
1651
+ options.onto_name);
1652
+ }
1653
+ options.onto = lookup_commit_or_die(&merge_base,
1654
+ options.onto_name);
1655
+ } else {
1656
+ options.onto =
1657
+ lookup_commit_reference_by_name(options.onto_name);
1658
+ if (!options.onto)
1659
+ die(_("Does not point to a valid commit '%s'"),
1660
+ options.onto_name);
1661
+ }
1662
+
1663
if (options.fork_point > 0) {
1664
struct commit *head =
1665
lookup_commit_reference(the_repository,
t/t3416-rebase-onto-threedots.sh
+29
@@ -129,6 +129,20 @@ test_expect_success 'rebase --keep-base main from topic' '
129
test_cmp expect actual
130
'
131
132
+test_expect_success 'rebase --keep-base main topic from main' '
133
+ git checkout main &&
134
+ git branch -f topic G &&
135
+
136
+ git rebase --keep-base main topic &&
137
+ git rev-parse C >base.expect &&
138
+ git merge-base main HEAD >base.actual &&
139
+ test_cmp base.expect base.actual &&
140
+
141
+ git rev-parse HEAD~2 >actual &&
142
+ git rev-parse C^0 >expect &&
143
+ test_cmp expect actual
144
+'
145
+
146
test_expect_success 'rebase --keep-base main from side' '
147
git reset --hard &&
148
git checkout side &&
@@ -153,6 +167,21 @@ test_expect_success 'rebase -i --keep-base main from topic' '
167
test_cmp expect actual
168
'
169
170
+test_expect_success 'rebase -i --keep-base main topic from main' '
171
+ git checkout main &&
172
+ git branch -f topic G &&
173
+
174
+ set_fake_editor &&
175
+ EXPECT_COUNT=2 git rebase -i --keep-base main topic &&
176
+ git rev-parse C >base.expect &&
177
+ git merge-base main HEAD >base.actual &&
178
+ test_cmp base.expect base.actual &&
179
+
180
+ git rev-parse HEAD~2 >actual &&
181
+ git rev-parse C^0 >expect &&
182
+ test_cmp expect actual
183
+'
184
+
185
test_expect_success 'rebase -i --keep-base main from side' '
186
git reset --hard &&
187
git checkout side &&