Skip to content

Commit 3cc3351

Browse files
committed
Merge branch 'bg/merge-ff-only'
* bg/merge-ff-only: Teach 'git merge' and 'git pull' the option --ff-only
2 parents 740b6ad + 1347483 commit 3cc3351

File tree

4 files changed

+61
-3
lines changed

4 files changed

+61
-3
lines changed

Documentation/merge-options.txt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,11 @@ merge.
4949
With --no-squash perform the merge and commit the result. This
5050
option can be used to override --squash.
5151

52+
--ff-only::
53+
Refuse to merge and exit with a non-zero status unless the
54+
current `HEAD` is already up-to-date or the merge can be
55+
resolved as a fast-forward.
56+
5257
-s <strategy>::
5358
--strategy=<strategy>::
5459
Use the given merge strategy; can be supplied more than

builtin-merge.c

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ static const char * const builtin_merge_usage[] = {
4343

4444
static int show_diffstat = 1, option_log, squash;
4545
static int option_commit = 1, allow_fast_forward = 1;
46+
static int fast_forward_only;
4647
static int allow_trivial = 1, have_message;
4748
static struct strbuf merge_msg;
4849
static struct commit_list *remoteheads;
@@ -167,6 +168,8 @@ static struct option builtin_merge_options[] = {
167168
"perform a commit if the merge succeeds (default)"),
168169
OPT_BOOLEAN(0, "ff", &allow_fast_forward,
169170
"allow fast forward (default)"),
171+
OPT_BOOLEAN(0, "ff-only", &fast_forward_only,
172+
"abort if fast forward is not possible"),
170173
OPT_CALLBACK('s', "strategy", &use_strategies, "strategy",
171174
"merge strategy to use", option_parse_strategy),
172175
OPT_CALLBACK('m', "message", &merge_msg, "message",
@@ -877,6 +880,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
877880
option_commit = 0;
878881
}
879882

883+
if (!allow_fast_forward && fast_forward_only)
884+
die("You cannot combine --no-ff with --ff-only.");
885+
880886
if (!argc)
881887
usage_with_options(builtin_merge_usage,
882888
builtin_merge_options);
@@ -1043,7 +1049,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
10431049
* only one common.
10441050
*/
10451051
refresh_cache(REFRESH_QUIET);
1046-
if (allow_trivial) {
1052+
if (allow_trivial && !fast_forward_only) {
10471053
/* See if it is really trivial. */
10481054
git_committer_info(IDENT_ERROR_ON_NO_NAME);
10491055
printf("Trying really trivial in-index merge...\n");
@@ -1082,6 +1088,9 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
10821088
}
10831089
}
10841090

1091+
if (fast_forward_only)
1092+
die("Not possible to fast forward, aborting.");
1093+
10851094
/* We are going to make a new commit. */
10861095
git_committer_info(IDENT_ERROR_ON_NO_NAME);
10871096

git-pull.sh

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,8 @@ cd_to_toplevel
1616
test -z "$(git ls-files -u)" ||
1717
die "You are in the middle of a conflicted merge."
1818

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=
2021
curr_branch=$(git symbolic-ref -q HEAD)
2122
curr_branch_short=$(echo "$curr_branch" | sed "s|refs/heads/||")
2223
rebase=$(git config --bool branch.$curr_branch_short.rebase)
@@ -45,6 +46,8 @@ do
4546
no_ff=--ff ;;
4647
--no-ff)
4748
no_ff=--no-ff ;;
49+
--ff-only)
50+
ff_only=--ff-only ;;
4851
-s=*|--s=*|--st=*|--str=*|--stra=*|--strat=*|--strate=*|\
4952
--strateg=*|--strategy=*|\
5053
-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
215218
test true = "$rebase" &&
216219
exec git-rebase $diffstat $strategy_args --onto $merge_head \
217220
${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 \
219222
"$merge_name" HEAD $merge_head $verbosity

t/t7600-merge.sh

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -243,6 +243,16 @@ test_expect_success 'merge c0 with c1' '
243243

244244
test_debug 'gitk --all'
245245

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+
246256
test_expect_success 'merge c1 with c2' '
247257
git reset --hard c1 &&
248258
test_tick &&
@@ -263,6 +273,14 @@ test_expect_success 'merge c1 with c2 and c3' '
263273

264274
test_debug 'gitk --all'
265275

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+
266284
test_expect_success 'merge c0 with c1 (no-commit)' '
267285
git reset --hard c0 &&
268286
git merge --no-commit c1 &&
@@ -303,6 +321,17 @@ test_expect_success 'merge c0 with c1 (squash)' '
303321

304322
test_debug 'gitk --all'
305323

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+
306335
test_expect_success 'merge c1 with c2 (squash)' '
307336
git reset --hard c1 &&
308337
git merge --squash c2 &&
@@ -314,6 +343,13 @@ test_expect_success 'merge c1 with c2 (squash)' '
314343

315344
test_debug 'gitk --all'
316345

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+
317353
test_expect_success 'merge c1 with c2 and c3 (squash)' '
318354
git reset --hard c1 &&
319355
git merge --squash c2 c3 &&
@@ -432,6 +468,11 @@ test_expect_success 'combining --squash and --no-ff is refused' '
432468
test_must_fail git merge --no-ff --squash c1
433469
'
434470

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+
435476
test_expect_success 'merge c0 with c1 (ff overrides no-ff)' '
436477
git reset --hard c0 &&
437478
git config branch.master.mergeoptions "--no-ff" &&

0 commit comments

Comments
 (0)