Merge branch 'bg/merge-ff-only'
authorJunio C Hamano <gitster@pobox.com>
Tue, 10 Nov 2009 20:32:59 +0000 (12:32 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 10 Nov 2009 20:32:59 +0000 (12:32 -0800)
* bg/merge-ff-only:
Teach 'git merge' and 'git pull' the option --ff-only

Documentation/merge-options.txt
builtin-merge.c
git-pull.sh
t/t7600-merge.sh
index 48d04a5d8833a2c83f1dc754c798688b194de5b9..fec33943058ea3e139aff0b898ef55a677d4067e 100644 (file)
@@ -49,6 +49,11 @@ merge.
 With --no-squash perform the merge and commit the result. This
 option can be used to override --squash.
 
+--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>::
 --strategy=<strategy>::
        Use the given merge strategy; can be supplied more than
index c69a3051f38eb70f148015471d0c2df1df8db33b..1ff7b15803fef8791c5c47209ba6f983d59f3065 100644 (file)
@@ -43,6 +43,7 @@ static const char * const builtin_merge_usage[] = {
 
 static int show_diffstat = 1, option_log, squash;
 static int option_commit = 1, allow_fast_forward = 1;
+static int fast_forward_only;
 static int allow_trivial = 1, have_message;
 static struct strbuf merge_msg;
 static struct commit_list *remoteheads;
@@ -167,6 +168,8 @@ static struct option builtin_merge_options[] = {
                "perform a commit if the merge succeeds (default)"),
        OPT_BOOLEAN(0, "ff", &allow_fast_forward,
                "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",
                "merge strategy to use", option_parse_strategy),
        OPT_CALLBACK('m', "message", &merge_msg, "message",
@@ -877,6 +880,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                option_commit = 0;
        }
 
+       if (!allow_fast_forward && fast_forward_only)
+               die("You cannot combine --no-ff with --ff-only.");
+
        if (!argc)
                usage_with_options(builtin_merge_usage,
                        builtin_merge_options);
@@ -1043,7 +1049,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                 * only one common.
                 */
                refresh_cache(REFRESH_QUIET);
-               if (allow_trivial) {
+               if (allow_trivial && !fast_forward_only) {
                        /* See if it is really trivial. */
                        git_committer_info(IDENT_ERROR_ON_NO_NAME);
                        printf("Trying really trivial in-index merge...\n");
@@ -1082,6 +1088,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. */
        git_committer_info(IDENT_ERROR_ON_NO_NAME);
 
index fc78592ae04283db2af633ec080f99c6404ceda7..37f3d9301715cd9db92c346c6dcbfe6602a19a35 100755 (executable)
@@ -16,7 +16,8 @@ cd_to_toplevel
 test -z "$(git ls-files -u)" ||
        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_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
 rebase=$(git config --bool branch.$curr_branch_short.rebase)
@@ -45,6 +46,8 @@ do
                no_ff=--ff ;;
        --no-ff)
                no_ff=--no-ff ;;
+       --ff-only)
+               ff_only=--ff-only ;;
        -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" &&
        exec git-rebase $diffstat $strategy_args --onto $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
index e5b210bc960c8433d6758f3932a86647208297ef..57f6d2bae7c63f13ee18e11737dbdcc0c080ab10 100755 (executable)
@@ -243,6 +243,16 @@ test_expect_success 'merge c0 with c1' '
 
 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' '
        git reset --hard c1 &&
        test_tick &&
@@ -263,6 +273,14 @@ test_expect_success 'merge c1 with c2 and c3' '
 
 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)' '
        git reset --hard c0 &&
        git merge --no-commit c1 &&
@@ -303,6 +321,17 @@ test_expect_success 'merge c0 with c1 (squash)' '
 
 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)' '
        git reset --hard c1 &&
        git merge --squash c2 &&
@@ -314,6 +343,13 @@ test_expect_success 'merge c1 with c2 (squash)' '
 
 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)' '
        git reset --hard c1 &&
        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_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)' '
        git reset --hard c0 &&
        git config branch.master.mergeoptions "--no-ff" &&