diff options
author | Patrick Steinhardt <ps@pks.im> | 2018-11-30 09:46:14 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2018-11-30 09:46:14 +0100 |
commit | 0ddc60944ca4727246414e8bcf3170fe8286f854 (patch) | |
tree | 54b2eac923cc1b711ac42d383777c7ca07bcc04c | |
parent | e7873eb2cc0e96c9eebe653b6a5ba41daea4a28e (diff) | |
parent | cb71a9cec2e5b857c18aea678664c5f2e65c8308 (diff) | |
download | libgit2-0ddc60944ca4727246414e8bcf3170fe8286f854.tar.gz |
Merge pull request #4770 from tiennou/feature/merge-analysis-any-branch
Allow merge analysis against any reference
-rw-r--r-- | include/git2/merge.h | 19 | ||||
-rw-r--r-- | src/merge.c | 45 | ||||
-rw-r--r-- | src/refs.c | 24 | ||||
-rw-r--r-- | src/refs.h | 2 | ||||
-rw-r--r-- | src/repository.c | 2 | ||||
-rw-r--r-- | tests/merge/workdir/analysis.c | 69 |
6 files changed, 131 insertions, 30 deletions
diff --git a/include/git2/merge.h b/include/git2/merge.h index 47bf32daa..862721370 100644 --- a/include/git2/merge.h +++ b/include/git2/merge.h @@ -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' diff --git a/src/merge.c b/src/merge.c index a36981723..23a593cd8 100644 --- a/src/merge.c +++ b/src/merge.c @@ -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"); diff --git a/src/refs.c b/src/refs.c index b3e94d8e8..933921099 100644 --- a/src/refs.c +++ b/src/refs.c @@ -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; +} diff --git a/src/refs.h b/src/refs.h index 84daf8b53..f4f934222 100644 --- a/src/refs.h +++ b/src/refs.h @@ -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 diff --git a/src/repository.c b/src/repository.c index cac25ad9b..2305bfef3 100644 --- a/src/repository.c +++ b/src/repository.c @@ -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; diff --git a/tests/merge/workdir/analysis.c b/tests/merge/workdir/analysis.c index f87fc58b0..27d7dba84 100644 --- a/tests/merge/workdir/analysis.c +++ b/tests/merge/workdir/analysis.c @@ -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); +} |