2929#include "rebase-interactive.h"
3030
3131static char const * const builtin_rebase_usage [] = {
32- N_ ("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
33- "[<upstream> ] [<branch>]" ),
32+ N_ ("git rebase [-i] [options] [--exec <cmd>] "
33+ "[--onto <newbase> | --keep-base ] [<upstream> [< branch>] ]" ),
3434 N_ ("git rebase [-i] [options] [--exec <cmd>] [--onto <newbase>] "
3535 "--root [<branch>]" ),
3636 N_ ("git rebase --continue | --abort | --skip | --edit-todo" ),
@@ -1262,25 +1262,46 @@ static int is_linear_history(struct commit *from, struct commit *to)
12621262 return 1 ;
12631263}
12641264
1265- static int can_fast_forward (struct commit * onto , struct object_id * head_oid ,
1266- struct object_id * merge_base )
1265+ static int can_fast_forward (struct commit * onto , struct commit * upstream ,
1266+ struct commit * restrict_revision ,
1267+ struct object_id * head_oid , struct object_id * merge_base )
12671268{
12681269 struct commit * head = lookup_commit (the_repository , head_oid );
1269- struct commit_list * merge_bases ;
1270- int res ;
1270+ struct commit_list * merge_bases = NULL ;
1271+ int res = 0 ;
12711272
12721273 if (!head )
1273- return 0 ;
1274+ goto done ;
12741275
12751276 merge_bases = get_merge_bases (onto , head );
1276- if (merge_bases && !merge_bases -> next ) {
1277- oidcpy (merge_base , & merge_bases -> item -> object .oid );
1278- res = oideq (merge_base , & onto -> object .oid );
1279- } else {
1277+ if (!merge_bases || merge_bases -> next ) {
12801278 oidcpy (merge_base , & null_oid );
1281- res = 0 ;
1279+ goto done ;
12821280 }
1281+
1282+ oidcpy (merge_base , & merge_bases -> item -> object .oid );
1283+ if (!oideq (merge_base , & onto -> object .oid ))
1284+ goto done ;
1285+
1286+ if (restrict_revision && !oideq (& restrict_revision -> object .oid , merge_base ))
1287+ goto done ;
1288+
1289+ if (!upstream )
1290+ goto done ;
1291+
12831292 free_commit_list (merge_bases );
1293+ merge_bases = get_merge_bases (upstream , head );
1294+ if (!merge_bases || merge_bases -> next )
1295+ goto done ;
1296+
1297+ if (!oideq (& onto -> object .oid , & merge_bases -> item -> object .oid ))
1298+ goto done ;
1299+
1300+ res = 1 ;
1301+
1302+ done :
1303+ if (merge_bases )
1304+ free_commit_list (merge_bases );
12841305 return res && is_linear_history (onto , head );
12851306}
12861307
@@ -1378,6 +1399,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
13781399 struct rebase_options options = REBASE_OPTIONS_INIT ;
13791400 const char * branch_name ;
13801401 int ret , flags , total_argc , in_progress = 0 ;
1402+ int keep_base = 0 ;
13811403 int ok_to_skip_pre_rebase = 0 ;
13821404 struct strbuf msg = STRBUF_INIT ;
13831405 struct strbuf revisions = STRBUF_INIT ;
@@ -1395,6 +1417,8 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
13951417 OPT_STRING (0 , "onto" , & options .onto_name ,
13961418 N_ ("revision" ),
13971419 N_ ("rebase onto given branch instead of upstream" )),
1420+ OPT_BOOL (0 , "keep-base" , & keep_base ,
1421+ N_ ("use the merge-base of upstream and branch as the current base" )),
13981422 OPT_BOOL (0 , "no-verify" , & ok_to_skip_pre_rebase ,
13991423 N_ ("allow pre-rebase hook to run" )),
14001424 OPT_NEGBIT ('q' , "quiet" , & options .flags ,
@@ -1548,9 +1572,12 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
15481572 usage_with_options (builtin_rebase_usage ,
15491573 builtin_rebase_options );
15501574
1551- if (options .type == REBASE_PRESERVE_MERGES )
1552- warning (_ ("git rebase --preserve-merges is deprecated. "
1553- "Use --rebase-merges instead." ));
1575+ if (keep_base ) {
1576+ if (options .onto_name )
1577+ die (_ ("cannot combine '--keep-base' with '--onto'" ));
1578+ if (options .root )
1579+ die (_ ("cannot combine '--keep-base' with '--root'" ));
1580+ }
15541581
15551582 if (action != ACTION_NONE && !in_progress )
15561583 die (_ ("No rebase in progress?" ));
@@ -1876,12 +1903,22 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
18761903 }
18771904
18781905 /* Make sure the branch to rebase onto is valid. */
1879- if (!options .onto_name )
1906+ if (keep_base ) {
1907+ strbuf_reset (& buf );
1908+ strbuf_addstr (& buf , options .upstream_name );
1909+ strbuf_addstr (& buf , "..." );
1910+ options .onto_name = xstrdup (buf .buf );
1911+ } else if (!options .onto_name )
18801912 options .onto_name = options .upstream_name ;
18811913 if (strstr (options .onto_name , "..." )) {
1882- if (get_oid_mb (options .onto_name , & merge_base ) < 0 )
1883- die (_ ("'%s': need exactly one merge base" ),
1884- options .onto_name );
1914+ if (get_oid_mb (options .onto_name , & merge_base ) < 0 ) {
1915+ if (keep_base )
1916+ die (_ ("'%s': need exactly one merge base with branch" ),
1917+ options .upstream_name );
1918+ else
1919+ die (_ ("'%s': need exactly one merge base" ),
1920+ options .onto_name );
1921+ }
18851922 options .onto = lookup_commit_or_die (& merge_base ,
18861923 options .onto_name );
18871924 } else {
@@ -2016,13 +2053,13 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
20162053
20172054 /*
20182055 * Check if we are already based on onto with linear history,
2019- * but this should be done only when upstream and onto are the same
2020- * and if this is not an interactive rebase.
2056+ * in which case we could fast-forward without replacing the commits
2057+ * with new commits recreated by replaying their changes. This
2058+ * optimization must not be done if this is an interactive rebase.
20212059 */
2022- if (can_fast_forward (options .onto , & options .orig_head , & merge_base ) &&
2023- !is_interactive (& options ) && !options .restrict_revision &&
2024- options .upstream &&
2025- !oidcmp (& options .upstream -> object .oid , & options .onto -> object .oid )) {
2060+ if (can_fast_forward (options .onto , options .upstream , options .restrict_revision ,
2061+ & options .orig_head , & merge_base ) &&
2062+ !is_interactive (& options )) {
20262063 int flag ;
20272064
20282065 if (!(options .flags & REBASE_FORCE )) {
@@ -2116,7 +2153,7 @@ int cmd_rebase(int argc, const char **argv, const char *prefix)
21162153 strbuf_addf (& msg , "%s: checkout %s" ,
21172154 getenv (GIT_REFLOG_ACTION_ENVIRONMENT ), options .onto_name );
21182155 if (reset_head (& options .onto -> object .oid , "checkout" , NULL ,
2119- RESET_HEAD_DETACH | RESET_ORIG_HEAD |
2156+ RESET_HEAD_DETACH | RESET_ORIG_HEAD |
21202157 RESET_HEAD_RUN_POST_CHECKOUT_HOOK ,
21212158 NULL , msg .buf ))
21222159 die (_ ("Could not detach HEAD" ));
0 commit comments