Skip to content

Commit 9a48a61

Browse files
prertikgitster
authored andcommitted
builtin rebase: try to fast forward when possible
In this commit, we add support to fast forward. Note: we will need the merge base later, therefore the call to can_fast_forward() really needs to be the first one when testing whether we can skip the rebase entirely (otherwise, it would make more sense to skip the possibly expensive operation if, say, running an interactive rebase). Signed-off-by: Pratik Karki <[email protected]> Signed-off-by: Johannes Schindelin <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent e0333e5 commit 9a48a61

File tree

1 file changed

+72
-0
lines changed

1 file changed

+72
-0
lines changed

builtin/rebase.c

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
#include "commit.h"
2121
#include "diff.h"
2222
#include "wt-status.h"
23+
#include "revision.h"
2324

2425
static char const * const builtin_rebase_usage[] = {
2526
N_("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
@@ -89,6 +90,12 @@ struct rebase_options {
8990
struct strbuf git_am_opt;
9091
};
9192

93+
static int is_interactive(struct rebase_options *opts)
94+
{
95+
return opts->type == REBASE_INTERACTIVE ||
96+
opts->type == REBASE_PRESERVE_MERGES;
97+
}
98+
9299
/* Returns the filename prefixed by the state_dir */
93100
static const char *state_dir_path(const char *filename, struct rebase_options *opts)
94101
{
@@ -334,6 +341,46 @@ static int rebase_config(const char *var, const char *value, void *data)
334341
return git_default_config(var, value, data);
335342
}
336343

344+
/*
345+
* Determines whether the commits in from..to are linear, i.e. contain
346+
* no merge commits. This function *expects* `from` to be an ancestor of
347+
* `to`.
348+
*/
349+
static int is_linear_history(struct commit *from, struct commit *to)
350+
{
351+
while (to && to != from) {
352+
parse_commit(to);
353+
if (!to->parents)
354+
return 1;
355+
if (to->parents->next)
356+
return 0;
357+
to = to->parents->item;
358+
}
359+
return 1;
360+
}
361+
362+
static int can_fast_forward(struct commit *onto, struct object_id *head_oid,
363+
struct object_id *merge_base)
364+
{
365+
struct commit *head = lookup_commit(the_repository, head_oid);
366+
struct commit_list *merge_bases;
367+
int res;
368+
369+
if (!head)
370+
return 0;
371+
372+
merge_bases = get_merge_bases(onto, head);
373+
if (merge_bases && !merge_bases->next) {
374+
oidcpy(merge_base, &merge_bases->item->object.oid);
375+
res = !oidcmp(merge_base, &onto->object.oid);
376+
} else {
377+
oidcpy(merge_base, &null_oid);
378+
res = 0;
379+
}
380+
free_commit_list(merge_bases);
381+
return res && is_linear_history(onto, head);
382+
}
383+
337384
int cmd_rebase(int argc, const char **argv, const char *prefix)
338385
{
339386
struct rebase_options options = {
@@ -489,6 +536,31 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
489536
goto cleanup;
490537
}
491538

539+
/*
540+
* Now we are rebasing commits upstream..orig_head (or with --root,
541+
* everything leading up to orig_head) on top of onto.
542+
*/
543+
544+
/*
545+
* Check if we are already based on onto with linear history,
546+
* but this should be done only when upstream and onto are the same
547+
* and if this is not an interactive rebase.
548+
*/
549+
if (can_fast_forward(options.onto, &options.orig_head, &merge_base) &&
550+
!is_interactive(&options) && !options.restrict_revision &&
551+
!oidcmp(&options.upstream->object.oid, &options.onto->object.oid)) {
552+
int flag;
553+
554+
if (!(options.flags & REBASE_NO_QUIET))
555+
; /* be quiet */
556+
else if (!strcmp(branch_name, "HEAD") &&
557+
resolve_ref_unsafe("HEAD", 0, NULL, &flag))
558+
puts(_("HEAD is up to date, rebase forced."));
559+
else
560+
printf(_("Current branch %s is up to date, rebase "
561+
"forced.\n"), branch_name);
562+
}
563+
492564
/* If a hook exists, give it a chance to interrupt*/
493565
if (!ok_to_skip_pre_rebase &&
494566
run_hook_le(NULL, "pre-rebase", options.upstream_arg,

0 commit comments

Comments
 (0)