Skip to content

Commit c0efb4c

Browse files
Denton-Lgitster
authored andcommitted
rebase: fast-forward --onto in more cases
Before, when we had the following graph, A---B---C (master) \ D (side) running 'git rebase --onto master... master side' would result in D being always rebased, no matter what. However, the desired behavior is that rebase should notice that this is fast-forwardable and do that instead. Add detection to `can_fast_forward` so that this case can be detected and a fast-forward will be performed. First of all, rewrite the function to use gotos which simplifies the logic. Next, since the options.upstream && !oidcmp(&options.upstream->object.oid, &options.onto->object.oid) conditions were removed in `cmd_rebase`, we reintroduce a substitute in `can_fast_forward`. In particular, checking the merge bases of `upstream` and `head` fixes a failing case in t3416. The abbreviated graph for t3416 is as follows: F---G topic / A---B---C---D---E master and the failing command was git rebase --onto master...topic F topic Before, Git would see that there was one merge base (C), and the merge and onto were the same so it would incorrectly return 1, indicating that we could fast-forward. This would cause the rebased graph to be 'ABCFG' when we were expecting 'ABCG'. With the additional logic, we detect that upstream and head's merge base is F. Since onto isn't F, it means we're not rebasing the full set of commits from master..topic. Since we're excluding some commits, a fast-forward cannot be performed and so we correctly return 0. Add '-f' to test cases that failed as a result of this change because they were not expecting a fast-forward so that a rebase is forced. Helped-by: Phillip Wood <[email protected]> Signed-off-by: Denton Liu <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 2b318aa commit c0efb4c

File tree

4 files changed

+23
-12
lines changed

4 files changed

+23
-12
lines changed

builtin/rebase.c

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1260,8 +1260,8 @@ static int is_linear_history(struct commit *from, struct commit *to)
12601260
return 1;
12611261
}
12621262

1263-
static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
1264-
struct object_id *merge_base)
1263+
static int can_fast_forward(struct commit *onto, struct commit *upstream,
1264+
struct object_id *head_oid, struct object_id *merge_base)
12651265
{
12661266
struct commit *head = lookup_commit(the_repository, head_oid);
12671267
struct commit_list *merge_bases = NULL;
@@ -1280,6 +1280,17 @@ static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
12801280
if (!oideq(merge_base, &onto->object.oid))
12811281
goto done;
12821282

1283+
if (!upstream)
1284+
goto done;
1285+
1286+
free_commit_list(merge_bases);
1287+
merge_bases = get_merge_bases(upstream, head);
1288+
if (!merge_bases || merge_bases->next)
1289+
goto done;
1290+
1291+
if (!oideq(&onto->object.oid, &merge_bases->item->object.oid))
1292+
goto done;
1293+
12831294
res = 1;
12841295

12851296
done:
@@ -2027,13 +2038,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
20272038

20282039
/*
20292040
* Check if we are already based on onto with linear history,
2030-
* but this should be done only when upstream and onto are the same
2031-
* and if this is not an interactive rebase.
2041+
* in which case we could fast-forward without replacing the commits
2042+
* with new commits recreated by replaying their changes. This
2043+
* optimization must not be done if this is an interactive rebase.
20322044
*/
2033-
if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
2034-
!is_interactive(&options) && !options.restrict_revision &&
2035-
options.upstream &&
2036-
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
2045+
if (can_fast_forward(options.onto, options.upstream, &options.orig_head,
2046+
&merge_base) &&
2047+
!is_interactive(&options) && !options.restrict_revision) {
20372048
int flag;
20382049

20392050
if (!(options.flags & REBASE_FORCE)) {

t/t3400-rebase.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,7 @@ test_expect_success 'rebase --am and --show-current-patch' '
295295
echo two >>init.t &&
296296
git commit -a -m two &&
297297
git tag two &&
298-
test_must_fail git rebase --onto init HEAD^ &&
298+
test_must_fail git rebase -f --onto init HEAD^ &&
299299
GIT_TRACE=1 git rebase --show-current-patch >/dev/null 2>stderr &&
300300
grep "show.*$(git rev-parse two)" stderr
301301
)

t/t3404-rebase-interactive.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1058,7 +1058,7 @@ test_expect_success C_LOCALE_OUTPUT 'rebase --edit-todo does not work on non-int
10581058
git reset --hard &&
10591059
git checkout conflict-branch &&
10601060
set_fake_editor &&
1061-
test_must_fail git rebase --onto HEAD~2 HEAD~ &&
1061+
test_must_fail git rebase -f --onto HEAD~2 HEAD~ &&
10621062
test_must_fail git rebase --edit-todo &&
10631063
git rebase --abort
10641064
'

t/t3432-rebase-fast-forward.sh

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -106,9 +106,9 @@ test_expect_success 'add work same to upstream' '
106106
changes='our and their changes'
107107
test_rebase_same_head success noop same success noop-force diff --onto B B
108108
test_rebase_same_head success noop same success noop-force diff --onto B... B
109-
test_rebase_same_head failure work same success work diff --onto master... master
109+
test_rebase_same_head success noop same success work diff --onto master... master
110110
test_rebase_same_head failure work same success work diff --fork-point --onto B B
111111
test_rebase_same_head failure work same success work diff --fork-point --onto B... B
112-
test_rebase_same_head failure work same success work diff --fork-point --onto master... master
112+
test_rebase_same_head success noop same success work diff --fork-point --onto master... master
113113

114114
test_done

0 commit comments

Comments
 (0)