Teach 'git merge' and 'git pull' the option --ff-only
For convenience in scripts and aliases, add the option --ff-only to only allow fast-forwards (and up-to-date, despite the name). Disallow combining --ff-only and --no-ff, since they flatly contradict each other. Allow all other options to be combined with --ff-only (i.e. do not add any code to handle them specially), including the following options: * --strategy (one or more): As long as the chosen merge strategy results in up-to-date or fast-forward, the command will succeed. * --squash: I cannot imagine why anyone would want to squash commits only if fast-forward is possible, but I also see no reason why it should not be allowed. * --message: The message will always be ignored, but I see no need to explicitly disallow providing a redundant message. Acknowledgements: I did look at Yuval Kogman's earlier patch (107768 in gmane), mainly as shortcut to find my way in the code, but I did not copy anything directly. Signed-off-by: Björn Gustavsson <bgustavsson@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
cd0f8e6d63
commit
134748353b
@ -60,6 +60,11 @@
|
|||||||
a fast-forward, only update the branch pointer. This is
|
a fast-forward, only update the branch pointer. This is
|
||||||
the default behavior of git-merge.
|
the default behavior of git-merge.
|
||||||
|
|
||||||
|
--ff-only::
|
||||||
|
Refuse to merge and exit with a non-zero status unless the
|
||||||
|
current `HEAD` is already up-to-date or the merge can be
|
||||||
|
resolved as a fast-forward.
|
||||||
|
|
||||||
-s <strategy>::
|
-s <strategy>::
|
||||||
--strategy=<strategy>::
|
--strategy=<strategy>::
|
||||||
Use the given merge strategy; can be supplied more than
|
Use the given merge strategy; can be supplied more than
|
||||||
|
@ -43,6 +43,7 @@ static const char * const builtin_merge_usage[] = {
|
|||||||
|
|
||||||
static int show_diffstat = 1, option_log, squash;
|
static int show_diffstat = 1, option_log, squash;
|
||||||
static int option_commit = 1, allow_fast_forward = 1;
|
static int option_commit = 1, allow_fast_forward = 1;
|
||||||
|
static int fast_forward_only;
|
||||||
static int allow_trivial = 1, have_message;
|
static int allow_trivial = 1, have_message;
|
||||||
static struct strbuf merge_msg;
|
static struct strbuf merge_msg;
|
||||||
static struct commit_list *remoteheads;
|
static struct commit_list *remoteheads;
|
||||||
@ -167,6 +168,8 @@ static struct option builtin_merge_options[] = {
|
|||||||
"perform a commit if the merge succeeds (default)"),
|
"perform a commit if the merge succeeds (default)"),
|
||||||
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
|
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
|
||||||
"allow fast forward (default)"),
|
"allow fast forward (default)"),
|
||||||
|
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
|
||||||
|
"abort if fast forward is not possible"),
|
||||||
OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
|
OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
|
||||||
"merge strategy to use", option_parse_strategy),
|
"merge strategy to use", option_parse_strategy),
|
||||||
OPT_CALLBACK('m', "message", &merge_msg, "message",
|
OPT_CALLBACK('m', "message", &merge_msg, "message",
|
||||||
@ -874,6 +877,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||||||
option_commit = 0;
|
option_commit = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!allow_fast_forward && fast_forward_only)
|
||||||
|
die("You cannot combine --no-ff with --ff-only.");
|
||||||
|
|
||||||
if (!argc)
|
if (!argc)
|
||||||
usage_with_options(builtin_merge_usage,
|
usage_with_options(builtin_merge_usage,
|
||||||
builtin_merge_options);
|
builtin_merge_options);
|
||||||
@ -1040,7 +1046,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||||||
* only one common.
|
* only one common.
|
||||||
*/
|
*/
|
||||||
refresh_cache(REFRESH_QUIET);
|
refresh_cache(REFRESH_QUIET);
|
||||||
if (allow_trivial) {
|
if (allow_trivial && !fast_forward_only) {
|
||||||
/* See if it is really trivial. */
|
/* See if it is really trivial. */
|
||||||
git_committer_info(IDENT_ERROR_ON_NO_NAME);
|
git_committer_info(IDENT_ERROR_ON_NO_NAME);
|
||||||
printf("Trying really trivial in-index merge...\n");
|
printf("Trying really trivial in-index merge...\n");
|
||||||
@ -1079,6 +1085,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (fast_forward_only)
|
||||||
|
die("Not possible to fast forward, aborting.");
|
||||||
|
|
||||||
/* We are going to make a new commit. */
|
/* We are going to make a new commit. */
|
||||||
git_committer_info(IDENT_ERROR_ON_NO_NAME);
|
git_committer_info(IDENT_ERROR_ON_NO_NAME);
|
||||||
|
|
||||||
|
@ -16,7 +16,8 @@ cd_to_toplevel
|
|||||||
test -z "$(git ls-files -u)" ||
|
test -z "$(git ls-files -u)" ||
|
||||||
die "You are in the middle of a conflicted merge."
|
die "You are in the middle of a conflicted merge."
|
||||||
|
|
||||||
strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity=
|
strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
|
||||||
|
log_arg= verbosity=
|
||||||
curr_branch=$(git symbolic-ref -q HEAD)
|
curr_branch=$(git symbolic-ref -q HEAD)
|
||||||
curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
|
curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
|
||||||
rebase=$(git config --bool branch.$curr_branch_short.rebase)
|
rebase=$(git config --bool branch.$curr_branch_short.rebase)
|
||||||
@ -45,6 +46,8 @@ do
|
|||||||
no_ff=--ff ;;
|
no_ff=--ff ;;
|
||||||
--no-ff)
|
--no-ff)
|
||||||
no_ff=--no-ff ;;
|
no_ff=--no-ff ;;
|
||||||
|
--ff-only)
|
||||||
|
ff_only=--ff-only ;;
|
||||||
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
|
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
|
||||||
--strateg=*|--strategy=*|\
|
--strateg=*|--strategy=*|\
|
||||||
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
|
-s|--s|--st|--str|--stra|--strat|--strate|--strateg|--strategy)
|
||||||
@ -215,5 +218,5 @@ merge_name=$(git fmt-merge-msg $log_arg <"$GIT_DIR/FETCH_HEAD") || exit
|
|||||||
test true = "$rebase" &&
|
test true = "$rebase" &&
|
||||||
exec git-rebase $diffstat $strategy_args --onto $merge_head \
|
exec git-rebase $diffstat $strategy_args --onto $merge_head \
|
||||||
${oldremoteref:-$merge_head}
|
${oldremoteref:-$merge_head}
|
||||||
exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \
|
exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \
|
||||||
"$merge_name" HEAD $merge_head $verbosity
|
"$merge_name" HEAD $merge_head $verbosity
|
||||||
|
@ -243,6 +243,16 @@ test_expect_success 'merge c0 with c1' '
|
|||||||
|
|
||||||
test_debug 'gitk --all'
|
test_debug 'gitk --all'
|
||||||
|
|
||||||
|
test_expect_success 'merge c0 with c1 with --ff-only' '
|
||||||
|
git reset --hard c0 &&
|
||||||
|
git merge --ff-only c1 &&
|
||||||
|
git merge --ff-only HEAD c0 c1 &&
|
||||||
|
verify_merge file result.1 &&
|
||||||
|
verify_head "$c1"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_debug 'gitk --all'
|
||||||
|
|
||||||
test_expect_success 'merge c1 with c2' '
|
test_expect_success 'merge c1 with c2' '
|
||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
test_tick &&
|
test_tick &&
|
||||||
@ -263,6 +273,14 @@ test_expect_success 'merge c1 with c2 and c3' '
|
|||||||
|
|
||||||
test_debug 'gitk --all'
|
test_debug 'gitk --all'
|
||||||
|
|
||||||
|
test_expect_success 'failing merges with --ff-only' '
|
||||||
|
git reset --hard c1 &&
|
||||||
|
test_tick &&
|
||||||
|
test_must_fail git merge --ff-only c2 &&
|
||||||
|
test_must_fail git merge --ff-only c3 &&
|
||||||
|
test_must_fail git merge --ff-only c2 c3
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'merge c0 with c1 (no-commit)' '
|
test_expect_success 'merge c0 with c1 (no-commit)' '
|
||||||
git reset --hard c0 &&
|
git reset --hard c0 &&
|
||||||
git merge --no-commit c1 &&
|
git merge --no-commit c1 &&
|
||||||
@ -303,6 +321,17 @@ test_expect_success 'merge c0 with c1 (squash)' '
|
|||||||
|
|
||||||
test_debug 'gitk --all'
|
test_debug 'gitk --all'
|
||||||
|
|
||||||
|
test_expect_success 'merge c0 with c1 (squash, ff-only)' '
|
||||||
|
git reset --hard c0 &&
|
||||||
|
git merge --squash --ff-only c1 &&
|
||||||
|
verify_merge file result.1 &&
|
||||||
|
verify_head $c0 &&
|
||||||
|
verify_no_mergehead &&
|
||||||
|
verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
|
||||||
|
'
|
||||||
|
|
||||||
|
test_debug 'gitk --all'
|
||||||
|
|
||||||
test_expect_success 'merge c1 with c2 (squash)' '
|
test_expect_success 'merge c1 with c2 (squash)' '
|
||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
git merge --squash c2 &&
|
git merge --squash c2 &&
|
||||||
@ -314,6 +343,13 @@ test_expect_success 'merge c1 with c2 (squash)' '
|
|||||||
|
|
||||||
test_debug 'gitk --all'
|
test_debug 'gitk --all'
|
||||||
|
|
||||||
|
test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
|
||||||
|
git reset --hard c1 &&
|
||||||
|
test_must_fail git merge --squash --ff-only c2
|
||||||
|
'
|
||||||
|
|
||||||
|
test_debug 'gitk --all'
|
||||||
|
|
||||||
test_expect_success 'merge c1 with c2 and c3 (squash)' '
|
test_expect_success 'merge c1 with c2 and c3 (squash)' '
|
||||||
git reset --hard c1 &&
|
git reset --hard c1 &&
|
||||||
git merge --squash c2 c3 &&
|
git merge --squash c2 c3 &&
|
||||||
@ -432,6 +468,11 @@ test_expect_success 'combining --squash and --no-ff is refused' '
|
|||||||
test_must_fail git merge --no-ff --squash c1
|
test_must_fail git merge --no-ff --squash c1
|
||||||
'
|
'
|
||||||
|
|
||||||
|
test_expect_success 'combining --ff-only and --no-ff is refused' '
|
||||||
|
test_must_fail git merge --ff-only --no-ff c1 &&
|
||||||
|
test_must_fail git merge --no-ff --ff-only c1
|
||||||
|
'
|
||||||
|
|
||||||
test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
|
test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
|
||||||
git reset --hard c0 &&
|
git reset --hard c0 &&
|
||||||
git config branch.master.mergeoptions "--no-ff" &&
|
git config branch.master.mergeoptions "--no-ff" &&
|
||||||
|
Loading…
Reference in New Issue
Block a user