diff --git a/include/git2/merge.h b/include/git2/merge.h index 7915050b0..dc63ed588 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -266,6 +266,18 @@ typedef enum { * to simply set HEAD to the target commit(s). */ GIT_MERGE_ANALYSIS_UNBORN = (1 << 3), + + /** + * There is a `merge.ff=false` configuration setting, suggesting that + * the user does not want to allow a fast-forward merge. + */ + GIT_MERGE_CONFIG_NO_FASTFORWARD = (1 << 4), + + /** + * There is a `merge.ff=only` configuration setting, suggesting that + * the user only wants fast-forward merges. + */ + GIT_MERGE_CONFIG_FASTFORWARD_ONLY = (1 << 5), } git_merge_analysis_t; /** diff --git a/src/merge.c b/src/merge.c index 6a8e5874f..85b74483b 100644 --- a/src/merge.c +++ b/src/merge.c @@ -2564,6 +2564,37 @@ done: return error; } +int analysis_config(git_merge_analysis_t *out, git_repository *repo) +{ + git_config *config; + const char *value; + int bool_value, error = 0; + + if ((error = git_repository_config(&config, repo)) < 0) + goto done; + + if ((error = git_config_get_string(&value, config, "merge.ff")) < 0) { + if (error == GIT_ENOTFOUND) { + giterr_clear(); + error = 0; + } + + goto done; + } + + if (git_config_parse_bool(&bool_value, value) == 0) { + if (!bool_value) + *out |= GIT_MERGE_CONFIG_NO_FASTFORWARD; + } else { + if (strcasecmp(value, "only") == 0) + *out |= GIT_MERGE_CONFIG_FASTFORWARD_ONLY; + } + +done: + git_config_free(config); + return error; +} + int git_merge_analysis( git_merge_analysis_t *out, git_repository *repo, @@ -2575,33 +2606,36 @@ int git_merge_analysis( assert(out && repo && their_heads); - *out = GIT_MERGE_ANALYSIS_NONE; - - if (git_repository_head_unborn(repo)) { - *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN; - goto done; - } - if (their_heads_len != 1) { giterr_set(GITERR_MERGE, "Can only merge a single branch"); error = -1; goto done; } + *out = GIT_MERGE_ANALYSIS_NONE; + + if ((error = analysis_config(out, repo)) < 0) + goto done; + + if (git_repository_head_unborn(repo)) { + *out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN; + goto done; + } + if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0) goto done; /* We're up-to-date if we're trying to merge our own common ancestor. */ if (ancestor_head && git_oid_equal(&ancestor_head->oid, &their_heads[0]->oid)) - *out = GIT_MERGE_ANALYSIS_UP_TO_DATE; + *out |= GIT_MERGE_ANALYSIS_UP_TO_DATE; /* We're fastforwardable if we're our own common ancestor. */ else if (ancestor_head && git_oid_equal(&ancestor_head->oid, &our_head->oid)) - *out = GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL; + *out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_NORMAL; /* Otherwise, just a normal merge is possible. */ else - *out = GIT_MERGE_ANALYSIS_NORMAL; + *out |= GIT_MERGE_ANALYSIS_NORMAL; done: git_merge_head_free(ancestor_head); diff --git a/tests/merge/workdir/analysis.c b/tests/merge/workdir/analysis.c index 0e937857f..daa4e9dd8 100644 --- a/tests/merge/workdir/analysis.c +++ b/tests/merge/workdir/analysis.c @@ -105,3 +105,29 @@ void test_merge_workdir_analysis__unborn(void) git_buf_free(&master); } +void test_merge_workdir_analysis__fastforward_with_config_noff(void) +{ + git_config *config; + git_merge_analysis_t analysis; + + git_repository_config(&config, repo); + git_config_set_string(config, "merge.ff", "false"); + + analysis = analysis_from_branch(FASTFORWARD_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (analysis & GIT_MERGE_ANALYSIS_FASTFORWARD)); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (analysis & GIT_MERGE_ANALYSIS_NORMAL)); + cl_assert_equal_i(GIT_MERGE_CONFIG_NO_FASTFORWARD, (analysis & GIT_MERGE_CONFIG_NO_FASTFORWARD)); +} + +void test_merge_workdir_analysis__no_fastforward_with_config_ffonly(void) +{ + git_config *config; + git_merge_analysis_t analysis; + + git_repository_config(&config, repo); + git_config_set_string(config, "merge.ff", "only"); + + analysis = analysis_from_branch(NOFASTFORWARD_BRANCH); + cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (analysis & GIT_MERGE_ANALYSIS_NORMAL)); + cl_assert_equal_i(GIT_MERGE_CONFIG_FASTFORWARD_ONLY, (analysis & GIT_MERGE_CONFIG_FASTFORWARD_ONLY)); +}