@matheusbsbs / matheus-bagliano / commits / 134748353b
Fork 0

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>

Björn Gustavsson committed Oct 29, 2009 at 23:08 UTC 134748353b2a71a34f899c9b1326ccf7ae082412
4 files changed +61 -3
Documentation/merge-options.txt
+5
@@ -60,6 +60,11 @@
60 a fast-forward, only update the branch pointer. This is
61 the default behavior of git-merge.
62
63 +--ff-only::
64 + Refuse to merge and exit with a non-zero status unless the
65 + current `HEAD` is already up-to-date or the merge can be
66 + resolved as a fast-forward.
67 +
68 -s <strategy>::
69 --strategy=<strategy>::
70 Use the given merge strategy; can be supplied more than
builtin-merge.c
+10 -1
@@ -43,6 +43,7 @@ static const char * const builtin_merge_usage[] = {
43
44 static int show_diffstat = 1, option_log, squash;
45 static int option_commit = 1, allow_fast_forward = 1;
46 +static int fast_forward_only;
47 static int allow_trivial = 1, have_message;
48 static struct strbuf merge_msg;
49 static struct commit_list *remoteheads;
@@ -167,6 +168,8 @@ static struct option builtin_merge_options[] = {
168 "perform a commit if the merge succeeds (default)"),
169 OPT_BOOLEAN(0, "ff", &allow_fast_forward,
170 "allow fast forward (default)"),
171 + OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
172 + "abort if fast forward is not possible"),
173 OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
174 "merge strategy to use", option_parse_strategy),
175 OPT_CALLBACK('m', "message", &merge_msg, "message",
@@ -874,6 +877,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
877 option_commit = 0;
878 }
879
880 + if (!allow_fast_forward && fast_forward_only)
881 + die("You cannot combine --no-ff with --ff-only.");
882 +
883 if (!argc)
884 usage_with_options(builtin_merge_usage,
885 builtin_merge_options);
@@ -1040,7 +1046,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
1046 * only one common.
1047 */
1048 refresh_cache(REFRESH_QUIET);
1043 - if (allow_trivial) {
1049 + if (allow_trivial && !fast_forward_only) {
1050 /* See if it is really trivial. */
1051 git_committer_info(IDENT_ERROR_ON_NO_NAME);
1052 printf("Trying really trivial in-index merge...\n");
@@ -1079,6 +1085,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
1085 }
1086 }
1087
1088 + if (fast_forward_only)
1089 + die("Not possible to fast forward, aborting.");
1090 +
1091 /* We are going to make a new commit. */
1092 git_committer_info(IDENT_ERROR_ON_NO_NAME);
1093
git-pull.sh
+5 -2
@@ -16,7 +16,8 @@ cd_to_toplevel
16 test -z "$(git ls-files -u)" ||
17 die "You are in the middle of a conflicted merge."
18
19 -strategy_args= diffstat= no_commit= squash= no_ff= log_arg= verbosity=
19 +strategy_args= diffstat= no_commit= squash= no_ff= ff_only=
20 +log_arg= verbosity=
21 curr_branch=$(git symbolic-ref -q HEAD)
22 curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
23 rebase=$(git config --bool branch.$curr_branch_short.rebase)
@@ -45,6 +46,8 @@ do
46 no_ff=--ff ;;
47 --no-ff)
48 no_ff=--no-ff ;;
49 + --ff-only)
50 + ff_only=--ff-only ;;
51 -s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
52 --strateg=*|--strategy=*|\
53 -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
218 test true = "$rebase" &&
219 exec git-rebase $diffstat $strategy_args --onto $merge_head \
220 ${oldremoteref:-$merge_head}
218 -exec git-merge $diffstat $no_commit $squash $no_ff $log_arg $strategy_args \
221 +exec git-merge $diffstat $no_commit $squash $no_ff $ff_only $log_arg $strategy_args \
222 "$merge_name" HEAD $merge_head $verbosity
t/t7600-merge.sh
+41
@@ -243,6 +243,16 @@ test_expect_success 'merge c0 with c1' '
243
244 test_debug 'gitk --all'
245
246 +test_expect_success 'merge c0 with c1 with --ff-only' '
247 + git reset --hard c0 &&
248 + git merge --ff-only c1 &&
249 + git merge --ff-only HEAD c0 c1 &&
250 + verify_merge file result.1 &&
251 + verify_head "$c1"
252 +'
253 +
254 +test_debug 'gitk --all'
255 +
256 test_expect_success 'merge c1 with c2' '
257 git reset --hard c1 &&
258 test_tick &&
@@ -263,6 +273,14 @@ test_expect_success 'merge c1 with c2 and c3' '
273
274 test_debug 'gitk --all'
275
276 +test_expect_success 'failing merges with --ff-only' '
277 + git reset --hard c1 &&
278 + test_tick &&
279 + test_must_fail git merge --ff-only c2 &&
280 + test_must_fail git merge --ff-only c3 &&
281 + test_must_fail git merge --ff-only c2 c3
282 +'
283 +
284 test_expect_success 'merge c0 with c1 (no-commit)' '
285 git reset --hard c0 &&
286 git merge --no-commit c1 &&
@@ -303,6 +321,17 @@ test_expect_success 'merge c0 with c1 (squash)' '
321
322 test_debug 'gitk --all'
323
324 +test_expect_success 'merge c0 with c1 (squash, ff-only)' '
325 + git reset --hard c0 &&
326 + git merge --squash --ff-only c1 &&
327 + verify_merge file result.1 &&
328 + verify_head $c0 &&
329 + verify_no_mergehead &&
330 + verify_diff squash.1 .git/SQUASH_MSG "[OOPS] bad squash message"
331 +'
332 +
333 +test_debug 'gitk --all'
334 +
335 test_expect_success 'merge c1 with c2 (squash)' '
336 git reset --hard c1 &&
337 git merge --squash c2 &&
@@ -314,6 +343,13 @@ test_expect_success 'merge c1 with c2 (squash)' '
343
344 test_debug 'gitk --all'
345
346 +test_expect_success 'unsuccesful merge of c1 with c2 (squash, ff-only)' '
347 + git reset --hard c1 &&
348 + test_must_fail git merge --squash --ff-only c2
349 +'
350 +
351 +test_debug 'gitk --all'
352 +
353 test_expect_success 'merge c1 with c2 and c3 (squash)' '
354 git reset --hard c1 &&
355 git merge --squash c2 c3 &&
@@ -432,6 +468,11 @@ test_expect_success 'combining --squash and --no-ff is refused' '
468 test_must_fail git merge --no-ff --squash c1
469 '
470
471 +test_expect_success 'combining --ff-only and --no-ff is refused' '
472 + test_must_fail git merge --ff-only --no-ff c1 &&
473 + test_must_fail git merge --no-ff --ff-only c1
474 +'
475 +
476 test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
477 git reset --hard c0 &&
478 git config branch.master.mergeoptions "--no-ff" &&