Git fork

Merge branch 'en/rebase-incompatible-opts'

"git rebase" often ignored incompatible options instead of
complaining, which has been corrected.

* en/rebase-incompatible-opts:
rebase: provide better error message for apply options vs. merge config
rebase: put rebase_options initialization in single place
rebase: fix formatting of rebase --reapply-cherry-picks option in docs
rebase: clarify the OPT_CMDMODE incompatibilities
rebase: add coverage of other incompatible options
rebase: fix incompatiblity checks for --[no-]reapply-cherry-picks
rebase: fix docs about incompatibilities with --root
rebase: remove --allow-empty-message from incompatible opts
rebase: flag --apply and --merge as incompatible
rebase: mark --update-refs as requiring the merge backend

+163 -64
+39 -38
Documentation/git-rebase.txt
··· 208 208 209 209 git rebase --abort 210 210 211 + MODE OPTIONS 212 + ------------ 213 + 214 + The options in this section cannot be used with any other option, 215 + including not with each other: 216 + 217 + --continue:: 218 + Restart the rebasing process after having resolved a merge conflict. 219 + 220 + --skip:: 221 + Restart the rebasing process by skipping the current patch. 222 + 223 + --abort:: 224 + Abort the rebase operation and reset HEAD to the original 225 + branch. If `<branch>` was provided when the rebase operation was 226 + started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD` 227 + will be reset to where it was when the rebase operation was 228 + started. 229 + 230 + --quit:: 231 + Abort the rebase operation but `HEAD` is not reset back to the 232 + original branch. The index and working tree are also left 233 + unchanged as a result. If a temporary stash entry was created 234 + using `--autostash`, it will be saved to the stash list. 235 + 236 + --edit-todo:: 237 + Edit the todo list during an interactive rebase. 238 + 239 + --show-current-patch:: 240 + Show the current patch in an interactive rebase or when rebase 241 + is stopped because of conflicts. This is the equivalent of 242 + `git show REBASE_HEAD`. 243 + 211 244 OPTIONS 212 245 ------- 213 246 --onto <newbase>:: ··· 249 282 <branch>:: 250 283 Working branch; defaults to `HEAD`. 251 284 252 - --continue:: 253 - Restart the rebasing process after having resolved a merge conflict. 254 - 255 - --abort:: 256 - Abort the rebase operation and reset HEAD to the original 257 - branch. If `<branch>` was provided when the rebase operation was 258 - started, then `HEAD` will be reset to `<branch>`. Otherwise `HEAD` 259 - will be reset to where it was when the rebase operation was 260 - started. 261 - 262 - --quit:: 263 - Abort the rebase operation but `HEAD` is not reset back to the 264 - original branch. The index and working tree are also left 265 - unchanged as a result. If a temporary stash entry was created 266 - using `--autostash`, it will be saved to the stash list. 267 - 268 285 --apply:: 269 286 Use applying strategies to rebase (calling `git-am` 270 287 internally). This option may become a no-op in the future ··· 321 338 upstream changes, the behavior towards them is controlled by 322 339 the `--empty` flag.) 323 340 + 324 - 325 341 In the absence of `--keep-base` (or if `--no-reapply-cherry-picks` is 326 342 given), these commits will be automatically dropped. Because this 327 343 necessitates reading all upstream commits, this can be expensive in ··· 330 346 dropped commit (unless `--quiet` is given). Advice will also be issued 331 347 unless `advice.skippedCherryPicks` is set to false (see 332 348 linkgit:git-config[1]). 333 - 334 349 + 335 350 `--reapply-cherry-picks` allows rebase to forgo reading all upstream 336 351 commits, potentially improving performance. ··· 344 359 message do not cause rebasing to halt. 345 360 + 346 361 See also INCOMPATIBLE OPTIONS below. 347 - 348 - --skip:: 349 - Restart the rebasing process by skipping the current patch. 350 - 351 - --edit-todo:: 352 - Edit the todo list during an interactive rebase. 353 - 354 - --show-current-patch:: 355 - Show the current patch in an interactive rebase or when rebase 356 - is stopped because of conflicts. This is the equivalent of 357 - `git show REBASE_HEAD`. 358 362 359 363 -m:: 360 364 --merge:: ··· 574 578 --root:: 575 579 Rebase all commits reachable from `<branch>`, instead of 576 580 limiting them with an `<upstream>`. This allows you to rebase 577 - the root commit(s) on a branch. When used with `--onto`, it 578 - will skip changes already contained in `<newbase>` (instead of 579 - `<upstream>`) whereas without `--onto` it will operate on every 580 - change. 581 + the root commit(s) on a branch. 581 582 + 582 583 See also INCOMPATIBLE OPTIONS below. 583 584 ··· 630 631 + 631 632 If the configuration variable `rebase.updateRefs` is set, then this option 632 633 can be used to override and disable this setting. 634 + + 635 + See also INCOMPATIBLE OPTIONS below. 633 636 634 637 INCOMPATIBLE OPTIONS 635 638 -------------------- ··· 645 648 * --merge 646 649 * --strategy 647 650 * --strategy-option 648 - * --allow-empty-message 649 - * --[no-]autosquash 651 + * --autosquash 650 652 * --rebase-merges 651 653 * --interactive 652 654 * --exec 653 655 * --no-keep-empty 654 656 * --empty= 655 - * --reapply-cherry-picks 656 - * --edit-todo 657 + * --[no-]reapply-cherry-picks when used without --keep-base 657 658 * --update-refs 658 - * --root when used in combination with --onto 659 + * --root when used without --onto 659 660 660 661 In addition, the following pairs of options are incompatible: 661 662
+58 -21
builtin/rebase.c
··· 122 122 int reapply_cherry_picks; 123 123 int fork_point; 124 124 int update_refs; 125 + int config_autosquash; 126 + int config_update_refs; 125 127 }; 126 128 127 129 #define REBASE_OPTIONS_INIT { \ ··· 134 136 .exec = STRING_LIST_INIT_NODUP, \ 135 137 .git_format_patch_opt = STRBUF_INIT, \ 136 138 .fork_point = -1, \ 139 + .reapply_cherry_picks = -1, \ 140 + .allow_empty_message = 1, \ 141 + .autosquash = -1, \ 142 + .config_autosquash = -1, \ 143 + .update_refs = -1, \ 144 + .config_update_refs = -1, \ 137 145 } 138 146 139 147 static struct replay_opts get_replay_opts(const struct rebase_options *opts) ··· 776 784 } 777 785 778 786 if (!strcmp(var, "rebase.autosquash")) { 779 - opts->autosquash = git_config_bool(var, value); 787 + opts->config_autosquash = git_config_bool(var, value); 780 788 return 0; 781 789 } 782 790 ··· 793 801 } 794 802 795 803 if (!strcmp(var, "rebase.updaterefs")) { 796 - opts->update_refs = git_config_bool(var, value); 804 + opts->config_update_refs = git_config_bool(var, value); 797 805 return 0; 798 806 } 799 807 ··· 907 915 BUG_ON_OPT_NEG(unset); 908 916 BUG_ON_OPT_ARG(arg); 909 917 918 + if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_APPLY) 919 + die(_("apply options and merge options cannot be used together")); 920 + 910 921 opts->type = REBASE_APPLY; 911 922 912 923 return 0; ··· 920 931 BUG_ON_OPT_NEG(unset); 921 932 BUG_ON_OPT_ARG(arg); 922 933 923 - if (!is_merge(opts)) 924 - opts->type = REBASE_MERGE; 934 + if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE) 935 + die(_("apply options and merge options cannot be used together")); 936 + 937 + opts->type = REBASE_MERGE; 925 938 926 939 return 0; 927 940 } ··· 934 947 935 948 BUG_ON_OPT_NEG(unset); 936 949 BUG_ON_OPT_ARG(arg); 950 + 951 + if (opts->type != REBASE_UNSPECIFIED && opts->type != REBASE_MERGE) 952 + die(_("apply options and merge options cannot be used together")); 937 953 938 954 opts->type = REBASE_MERGE; 939 955 opts->flags |= REBASE_INTERACTIVE_EXPLICIT; ··· 1150 1166 prepare_repo_settings(the_repository); 1151 1167 the_repository->settings.command_requires_full_index = 0; 1152 1168 1153 - options.reapply_cherry_picks = -1; 1154 - options.allow_empty_message = 1; 1155 1169 git_config(rebase_config, &options); 1156 1170 /* options.gpg_sign_opt will be either "-S" or NULL */ 1157 1171 gpg_sign = options.gpg_sign_opt ? "" : NULL; ··· 1216 1230 if (options.fork_point < 0) 1217 1231 options.fork_point = 0; 1218 1232 } 1219 - /* 1220 - * --keep-base defaults to --reapply-cherry-picks to avoid losing 1221 - * commits when using this option. 1222 - */ 1223 - if (options.reapply_cherry_picks < 0) 1224 - options.reapply_cherry_picks = keep_base; 1225 - 1226 1233 if (options.root && options.fork_point > 0) 1227 1234 die(_("options '%s' and '%s' cannot be used together"), "--root", "--fork-point"); 1228 1235 ··· 1365 1372 if ((options.flags & REBASE_INTERACTIVE_EXPLICIT) || 1366 1373 (options.action != ACTION_NONE) || 1367 1374 (options.exec.nr > 0) || 1368 - options.autosquash) { 1375 + (options.autosquash == -1 && options.config_autosquash == 1) || 1376 + options.autosquash == 1) { 1369 1377 allow_preemptive_ff = 0; 1370 1378 } 1371 1379 if (options.committer_date_is_author_date || options.ignore_date) ··· 1398 1406 if (options.empty != EMPTY_UNSPECIFIED) 1399 1407 imply_merge(&options, "--empty"); 1400 1408 1401 - /* 1402 - * --keep-base implements --reapply-cherry-picks by altering upstream so 1403 - * it works with both backends. 1404 - */ 1405 - if (options.reapply_cherry_picks && !keep_base) 1406 - imply_merge(&options, "--reapply-cherry-picks"); 1409 + if (options.reapply_cherry_picks < 0) 1410 + /* 1411 + * We default to --no-reapply-cherry-picks unless 1412 + * --keep-base is given; when --keep-base is given, we want 1413 + * to default to --reapply-cherry-picks. 1414 + */ 1415 + options.reapply_cherry_picks = keep_base; 1416 + else if (!keep_base) 1417 + /* 1418 + * The apply backend always searches for and drops cherry 1419 + * picks. This is often not wanted with --keep-base, so 1420 + * --keep-base allows --reapply-cherry-picks to be 1421 + * simulated by altering the upstream such that 1422 + * cherry-picks cannot be detected and thus all commits are 1423 + * reapplied. Thus, --[no-]reapply-cherry-picks is 1424 + * supported when --keep-base is specified, but not when 1425 + * --keep-base is left out. 1426 + */ 1427 + imply_merge(&options, options.reapply_cherry_picks ? 1428 + "--reapply-cherry-picks" : 1429 + "--no-reapply-cherry-picks"); 1407 1430 1408 1431 if (gpg_sign) 1409 1432 options.gpg_sign_opt = xstrfmt("-S%s", gpg_sign); ··· 1483 1506 if (strcmp(options.git_am_opts.v[i], "-q")) 1484 1507 break; 1485 1508 1486 - if (i >= 0) { 1509 + if (i >= 0 || options.type == REBASE_APPLY) { 1487 1510 if (is_merge(&options)) 1488 1511 die(_("apply options and merge options " 1489 1512 "cannot be used together")); 1513 + else if (options.autosquash == -1 && options.config_autosquash == 1) 1514 + die(_("apply options are incompatible with rebase.autosquash. Consider adding --no-autosquash")); 1515 + else if (options.update_refs == -1 && options.config_update_refs == 1) 1516 + die(_("apply options are incompatible with rebase.updateRefs. Consider adding --no-update-refs")); 1490 1517 else 1491 1518 options.type = REBASE_APPLY; 1492 1519 } 1493 1520 } 1521 + 1522 + if (options.update_refs == 1) 1523 + imply_merge(&options, "--update-refs"); 1524 + options.update_refs = (options.update_refs >= 0) ? options.update_refs : 1525 + ((options.config_update_refs >= 0) ? options.config_update_refs : 0); 1526 + 1527 + if (options.autosquash == 1) 1528 + imply_merge(&options, "--autosquash"); 1529 + options.autosquash = (options.autosquash >= 0) ? options.autosquash : 1530 + ((options.config_autosquash >= 0) ? options.config_autosquash : 0); 1494 1531 1495 1532 if (options.type == REBASE_UNSPECIFIED) { 1496 1533 if (!strcmp(options.default_backend, "merge"))
+66 -5
t/t3422-rebase-incompatible-options.sh
··· 25 25 ' 26 26 27 27 # 28 - # Rebase has lots of useful options like --whitepsace=fix, which are 29 - # actually all built in terms of flags to git-am. Since neither 30 - # --merge nor --interactive (nor any options that imply those two) use 31 - # git-am, using them together will result in flags like --whitespace=fix 32 - # being ignored. Make sure rebase warns the user and aborts instead. 28 + # Rebase has a couple options which are specific to the apply backend, 29 + # and several options which are specific to the merge backend. Flags 30 + # from the different sets cannot work together, and we do not want to 31 + # just ignore one of the sets of flags. Make sure rebase warns the 32 + # user and aborts instead. 33 33 # 34 34 35 35 test_rebase_am_only () { ··· 50 50 test_must_fail git rebase $opt --strategy-option=ours A 51 51 " 52 52 53 + test_expect_success "$opt incompatible with --autosquash" " 54 + git checkout B^0 && 55 + test_must_fail git rebase $opt --autosquash A 56 + " 57 + 53 58 test_expect_success "$opt incompatible with --interactive" " 54 59 git checkout B^0 && 55 60 test_must_fail git rebase $opt --interactive A ··· 60 65 test_must_fail git rebase $opt --exec 'true' A 61 66 " 62 67 68 + test_expect_success "$opt incompatible with --keep-empty" " 69 + git checkout B^0 && 70 + test_must_fail git rebase $opt --keep-empty A 71 + " 72 + 73 + test_expect_success "$opt incompatible with --empty=..." " 74 + git checkout B^0 && 75 + test_must_fail git rebase $opt --empty=ask A 76 + " 77 + 78 + test_expect_success "$opt incompatible with --no-reapply-cherry-picks" " 79 + git checkout B^0 && 80 + test_must_fail git rebase $opt --no-reapply-cherry-picks A 81 + " 82 + 83 + test_expect_success "$opt incompatible with --reapply-cherry-picks" " 84 + git checkout B^0 && 85 + test_must_fail git rebase $opt --reapply-cherry-picks A 86 + " 87 + 88 + test_expect_success "$opt incompatible with --update-refs" " 89 + git checkout B^0 && 90 + test_must_fail git rebase $opt --update-refs A 91 + " 92 + 93 + test_expect_success "$opt incompatible with --root without --onto" " 94 + git checkout B^0 && 95 + test_must_fail git rebase $opt --root A 96 + " 97 + 98 + test_expect_success "$opt incompatible with rebase.autosquash" " 99 + git checkout B^0 && 100 + test_must_fail git -c rebase.autosquash=true rebase $opt A 2>err && 101 + grep -e --no-autosquash err 102 + " 103 + 104 + test_expect_success "$opt incompatible with rebase.updateRefs" " 105 + git checkout B^0 && 106 + test_must_fail git -c rebase.updateRefs=true rebase $opt A 2>err && 107 + grep -e --no-update-refs err 108 + " 109 + 110 + test_expect_success "$opt okay with overridden rebase.autosquash" " 111 + test_when_finished \"git reset --hard B^0\" && 112 + git checkout B^0 && 113 + git -c rebase.autosquash=true rebase --no-autosquash $opt A 114 + " 115 + 116 + test_expect_success "$opt okay with overridden rebase.updateRefs" " 117 + test_when_finished \"git reset --hard B^0\" && 118 + git checkout B^0 && 119 + git -c rebase.updateRefs=true rebase --no-update-refs $opt A 120 + " 63 121 } 64 122 123 + # Check options which imply --apply 65 124 test_rebase_am_only --whitespace=fix 66 125 test_rebase_am_only -C4 126 + # Also check an explicit --apply 127 + test_rebase_am_only --apply 67 128 68 129 test_done