Unverified Commit 0ddc6094 by Patrick Steinhardt Committed by GitHub

Merge pull request #4770 from tiennou/feature/merge-analysis-any-branch

Allow merge analysis against any reference
parents e7873eb2 cb71a9ce
......@@ -389,6 +389,25 @@ GIT_EXTERN(int) git_merge_analysis(
size_t their_heads_len);
/**
* Analyzes the given branch(es) and determines the opportunities for
* merging them into a reference.
*
* @param analysis_out analysis enumeration that the result is written into
* @param repo the repository to merge
* @param our_ref the reference to perform the analysis from
* @param their_heads the heads to merge into
* @param their_heads_len the number of heads to merge
* @return 0 on success or error code
*/
GIT_EXTERN(int) git_merge_analysis_for_ref(
git_merge_analysis_t *analysis_out,
git_merge_preference_t *preference_out,
git_repository *repo,
git_reference *our_ref,
const git_annotated_commit **their_heads,
size_t their_heads_len);
/**
* Find a merge base between two commits
*
* @param out the OID of a merge base between 'one' and 'two'
......
......@@ -3110,11 +3110,11 @@ static int merge_heads(
git_annotated_commit **ancestor_head_out,
git_annotated_commit **our_head_out,
git_repository *repo,
git_reference *our_ref,
const git_annotated_commit **their_heads,
size_t their_heads_len)
{
git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
git_reference *our_ref = NULL;
int error = 0;
*ancestor_head_out = NULL;
......@@ -3123,8 +3123,7 @@ static int merge_heads(
if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
goto done;
if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
(error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0)
if ((error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0)
goto done;
if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) {
......@@ -3144,8 +3143,6 @@ done:
git_annotated_commit_free(our_head);
}
git_reference_free(our_ref);
return error;
}
......@@ -3182,17 +3179,19 @@ done:
return error;
}
int git_merge_analysis(
int git_merge_analysis_for_ref(
git_merge_analysis_t *analysis_out,
git_merge_preference_t *preference_out,
git_repository *repo,
git_reference *our_ref,
const git_annotated_commit **their_heads,
size_t their_heads_len)
{
git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
int error = 0;
bool unborn;
assert(analysis_out && preference_out && repo && their_heads);
assert(analysis_out && preference_out && repo && their_heads && their_heads_len > 0);
if (their_heads_len != 1) {
giterr_set(GITERR_MERGE, "can only merge a single branch");
......@@ -3205,12 +3204,16 @@ int git_merge_analysis(
if ((error = merge_preference(preference_out, repo)) < 0)
goto done;
if (git_repository_head_unborn(repo)) {
if ((error = git_reference__is_unborn_head(&unborn, our_ref, repo)) < 0)
goto done;
if (unborn) {
*analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
error = 0;
goto done;
}
if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
if ((error = merge_heads(&ancestor_head, &our_head, repo, our_ref, their_heads, their_heads_len)) < 0)
goto done;
/* We're up-to-date if we're trying to merge our own common ancestor. */
......@@ -3233,6 +3236,28 @@ done:
return error;
}
int git_merge_analysis(
git_merge_analysis_t *analysis_out,
git_merge_preference_t *preference_out,
git_repository *repo,
const git_annotated_commit **their_heads,
size_t their_heads_len)
{
git_reference *head_ref = NULL;
int error = 0;
if ((error = git_reference_lookup(&head_ref, repo, GIT_HEAD_FILE)) < 0) {
giterr_set(GITERR_MERGE, "failed to lookup HEAD reference");
return error;
}
error = git_merge_analysis_for_ref(analysis_out, preference_out, repo, head_ref, their_heads, their_heads_len);
git_reference_free(head_ref);
return error;
}
int git_merge(
git_repository *repo,
const git_annotated_commit **their_heads,
......@@ -3248,7 +3273,7 @@ int git_merge(
unsigned int checkout_strategy;
int error = 0;
assert(repo && their_heads);
assert(repo && their_heads && their_heads_len > 0);
if (their_heads_len != 1) {
giterr_set(GITERR_MERGE, "can only merge a single branch");
......
......@@ -1430,3 +1430,27 @@ const char *git_reference_shorthand(const git_reference *ref)
{
return git_reference__shorthand(ref->name);
}
int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo)
{
int error;
git_reference *tmp_ref;
assert(unborn && ref && repo);
if (ref->type == GIT_REF_OID) {
*unborn = 0;
return 0;
}
error = git_reference_lookup_resolved(&tmp_ref, repo, ref->name, -1);
git_reference_free(tmp_ref);
if (error != 0 && error != GIT_ENOTFOUND)
return error;
else if (error == GIT_ENOTFOUND && git__strcmp(ref->name, GIT_HEAD_FILE) == 0)
*unborn = true;
else
*unborn = false;
return 0;
}
......@@ -136,4 +136,6 @@ int git_reference__update_for_commit(
const git_oid *id,
const char *operation);
int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo);
#endif
......@@ -2157,6 +2157,8 @@ int git_repository_head(git_reference **head_out, git_repository *repo)
git_reference *head;
int error;
assert(head_out);
if ((error = git_reference_lookup(&head, repo, GIT_HEAD_FILE)) < 0)
return error;
......
......@@ -40,21 +40,33 @@ void test_merge_workdir_analysis__cleanup(void)
static void analysis_from_branch(
git_merge_analysis_t *merge_analysis,
git_merge_preference_t *merge_pref,
const char *branchname)
const char *our_branchname,
const char *their_branchname)
{
git_buf refname = GIT_BUF_INIT;
git_buf our_refname = GIT_BUF_INIT;
git_buf their_refname = GIT_BUF_INIT;
git_reference *our_ref;
git_reference *their_ref;
git_annotated_commit *their_head;
git_buf_printf(&refname, "%s%s", GIT_REFS_HEADS_DIR, branchname);
if (our_branchname != NULL) {
cl_git_pass(git_buf_printf(&our_refname, "%s%s", GIT_REFS_HEADS_DIR, our_branchname));
cl_git_pass(git_reference_lookup(&our_ref, repo, git_buf_cstr(&our_refname)));
} else {
cl_git_pass(git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE));
}
cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&refname)));
cl_git_pass(git_buf_printf(&their_refname, "%s%s", GIT_REFS_HEADS_DIR, their_branchname));
cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&their_refname)));
cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref));
cl_git_pass(git_merge_analysis(merge_analysis, merge_pref, repo, (const git_annotated_commit **)&their_head, 1));
cl_git_pass(git_merge_analysis_for_ref(merge_analysis, merge_pref, repo, our_ref, (const git_annotated_commit **)&their_head, 1));
git_buf_dispose(&refname);
git_buf_dispose(&our_refname);
git_buf_dispose(&their_refname);
git_annotated_commit_free(their_head);
git_reference_free(our_ref);
git_reference_free(their_ref);
}
......@@ -63,9 +75,8 @@ void test_merge_workdir_analysis__fastforward(void)
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
analysis_from_branch(&merge_analysis, &merge_pref, NULL, FASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL|GIT_MERGE_ANALYSIS_FASTFORWARD, merge_analysis);
}
void test_merge_workdir_analysis__no_fastforward(void)
......@@ -73,7 +84,7 @@ void test_merge_workdir_analysis__no_fastforward(void)
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
}
......@@ -82,7 +93,7 @@ void test_merge_workdir_analysis__uptodate(void)
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis_from_branch(&merge_analysis, &merge_pref, UPTODATE_BRANCH);
analysis_from_branch(&merge_analysis, &merge_pref, NULL, UPTODATE_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
}
......@@ -91,7 +102,7 @@ void test_merge_workdir_analysis__uptodate_merging_prev_commit(void)
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis_from_branch(&merge_analysis, &merge_pref, PREVIOUS_BRANCH);
analysis_from_branch(&merge_analysis, &merge_pref, NULL, PREVIOUS_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
}
......@@ -104,9 +115,8 @@ void test_merge_workdir_analysis__unborn(void)
git_buf_joinpath(&master, git_repository_path(repo), "refs/heads/master");
p_unlink(git_buf_cstr(&master));
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (merge_analysis & GIT_MERGE_ANALYSIS_UNBORN));
analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD|GIT_MERGE_ANALYSIS_UNBORN, merge_analysis);
git_buf_dispose(&master);
}
......@@ -120,9 +130,9 @@ void test_merge_workdir_analysis__fastforward_with_config_noff(void)
git_repository_config(&config, repo);
git_config_set_string(config, "merge.ff", "false");
analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
analysis_from_branch(&merge_analysis, &merge_pref, NULL, FASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL|GIT_MERGE_ANALYSIS_FASTFORWARD, merge_analysis);
cl_assert_equal_i(GIT_MERGE_PREFERENCE_NO_FASTFORWARD, (merge_pref & GIT_MERGE_PREFERENCE_NO_FASTFORWARD));
}
......@@ -135,7 +145,26 @@ void test_merge_workdir_analysis__no_fastforward_with_config_ffonly(void)
git_repository_config(&config, repo);
git_config_set_string(config, "merge.ff", "only");
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
cl_assert_equal_i(GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, (merge_pref & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY));
}
void test_merge_workdir_analysis__between_uptodate_refs(void)
{
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH, PREVIOUS_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
}
void test_merge_workdir_analysis__between_noff_refs(void)
{
git_merge_analysis_t merge_analysis;
git_merge_preference_t merge_pref;
analysis_from_branch(&merge_analysis, &merge_pref, "branch", FASTFORWARD_BRANCH);
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment