Skip to content

Commit 6e9fb04

Browse files
committed
merge: make analysis possible against a non-HEAD reference
This moves the current merge analysis code into a more generic version that can work against any reference. Also change the tests to check returned analysis values exactly.
1 parent 5c0c8fd commit 6e9fb04

File tree

5 files changed

+127
-28
lines changed

5 files changed

+127
-28
lines changed

include/git2/merge.h

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,25 @@ GIT_EXTERN(int) git_merge_analysis(
388388
const git_annotated_commit **their_heads,
389389
size_t their_heads_len);
390390

391+
/**
392+
* Analyzes the given branch(es) and determines the opportunities for
393+
* merging them into a reference.
394+
*
395+
* @param analysis_out analysis enumeration that the result is written into
396+
* @param repo the repository to merge
397+
* @param our_ref the reference to perform the analysis from
398+
* @param their_heads the heads to merge into
399+
* @param their_heads_len the number of heads to merge
400+
* @return 0 on success or error code
401+
*/
402+
GIT_EXTERN(int) git_merge_analysis_for_ref(
403+
git_merge_analysis_t *analysis_out,
404+
git_merge_preference_t *preference_out,
405+
git_repository *repo,
406+
git_reference *our_ref,
407+
const git_annotated_commit **their_heads,
408+
size_t their_heads_len);
409+
391410
/**
392411
* Find a merge base between two commits
393412
*

src/merge.c

Lines changed: 33 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3110,11 +3110,11 @@ static int merge_heads(
31103110
git_annotated_commit **ancestor_head_out,
31113111
git_annotated_commit **our_head_out,
31123112
git_repository *repo,
3113+
git_reference *our_ref,
31133114
const git_annotated_commit **their_heads,
31143115
size_t their_heads_len)
31153116
{
31163117
git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
3117-
git_reference *our_ref = NULL;
31183118
int error = 0;
31193119

31203120
*ancestor_head_out = NULL;
@@ -3123,8 +3123,7 @@ static int merge_heads(
31233123
if ((error = git_repository__ensure_not_bare(repo, "merge")) < 0)
31243124
goto done;
31253125

3126-
if ((error = git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE)) < 0 ||
3127-
(error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0)
3126+
if ((error = git_annotated_commit_from_ref(&our_head, repo, our_ref)) < 0)
31283127
goto done;
31293128

31303129
if ((error = merge_ancestor_head(&ancestor_head, repo, our_head, their_heads, their_heads_len)) < 0) {
@@ -3144,8 +3143,6 @@ static int merge_heads(
31443143
git_annotated_commit_free(our_head);
31453144
}
31463145

3147-
git_reference_free(our_ref);
3148-
31493146
return error;
31503147
}
31513148

@@ -3182,15 +3179,17 @@ static int merge_preference(git_merge_preference_t *out, git_repository *repo)
31823179
return error;
31833180
}
31843181

3185-
int git_merge_analysis(
3182+
int git_merge_analysis_for_ref(
31863183
git_merge_analysis_t *analysis_out,
31873184
git_merge_preference_t *preference_out,
31883185
git_repository *repo,
3186+
git_reference *our_ref,
31893187
const git_annotated_commit **their_heads,
31903188
size_t their_heads_len)
31913189
{
31923190
git_annotated_commit *ancestor_head = NULL, *our_head = NULL;
31933191
int error = 0;
3192+
bool unborn;
31943193

31953194
assert(analysis_out && preference_out && repo && their_heads);
31963195

@@ -3205,12 +3204,16 @@ int git_merge_analysis(
32053204
if ((error = merge_preference(preference_out, repo)) < 0)
32063205
goto done;
32073206

3208-
if (git_repository_head_unborn(repo)) {
3207+
if ((error = git_reference__is_unborn_head(&unborn, our_ref, repo)) < 0)
3208+
goto done;
3209+
3210+
if (unborn) {
32093211
*analysis_out |= GIT_MERGE_ANALYSIS_FASTFORWARD | GIT_MERGE_ANALYSIS_UNBORN;
3212+
error = 0;
32103213
goto done;
32113214
}
32123215

3213-
if ((error = merge_heads(&ancestor_head, &our_head, repo, their_heads, their_heads_len)) < 0)
3216+
if ((error = merge_heads(&ancestor_head, &our_head, repo, our_ref, their_heads, their_heads_len)) < 0)
32143217
goto done;
32153218

32163219
/* We're up-to-date if we're trying to merge our own common ancestor. */
@@ -3233,6 +3236,28 @@ int git_merge_analysis(
32333236
return error;
32343237
}
32353238

3239+
int git_merge_analysis(
3240+
git_merge_analysis_t *analysis_out,
3241+
git_merge_preference_t *preference_out,
3242+
git_repository *repo,
3243+
const git_annotated_commit **their_heads,
3244+
size_t their_heads_len)
3245+
{
3246+
git_reference *head_ref = NULL;
3247+
int error = 0;
3248+
3249+
if ((error = git_reference_lookup(&head_ref, repo, GIT_HEAD_FILE)) < 0) {
3250+
giterr_set(GITERR_MERGE, "failed to lookup HEAD reference");
3251+
return error;
3252+
}
3253+
3254+
error = git_merge_analysis_for_ref(analysis_out, preference_out, repo, head_ref, their_heads, their_heads_len);
3255+
3256+
git_reference_free(head_ref);
3257+
3258+
return error;
3259+
}
3260+
32363261
int git_merge(
32373262
git_repository *repo,
32383263
const git_annotated_commit **their_heads,

src/refs.c

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1430,3 +1430,27 @@ const char *git_reference_shorthand(const git_reference *ref)
14301430
{
14311431
return git_reference__shorthand(ref->name);
14321432
}
1433+
1434+
int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo)
1435+
{
1436+
int error;
1437+
git_reference *tmp_ref;
1438+
assert(unborn && ref && repo);
1439+
1440+
if (ref->type == GIT_REF_OID) {
1441+
*unborn = 0;
1442+
return 0;
1443+
}
1444+
1445+
error = git_reference_lookup_resolved(&tmp_ref, repo, ref->name, -1);
1446+
git_reference_free(tmp_ref);
1447+
1448+
if (error != 0 && error != GIT_ENOTFOUND)
1449+
return error;
1450+
else if (error == GIT_ENOTFOUND && git__strcmp(ref->name, GIT_HEAD_FILE) == 0)
1451+
*unborn = true;
1452+
else
1453+
*unborn = false;
1454+
1455+
return 0;
1456+
}

src/refs.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,4 +136,6 @@ int git_reference__update_for_commit(
136136
const git_oid *id,
137137
const char *operation);
138138

139+
int git_reference__is_unborn_head(bool *unborn, const git_reference *ref, git_repository *repo);
140+
139141
#endif

tests/merge/workdir/analysis.c

Lines changed: 49 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -40,21 +40,33 @@ void test_merge_workdir_analysis__cleanup(void)
4040
static void analysis_from_branch(
4141
git_merge_analysis_t *merge_analysis,
4242
git_merge_preference_t *merge_pref,
43-
const char *branchname)
43+
const char *our_branchname,
44+
const char *their_branchname)
4445
{
45-
git_buf refname = GIT_BUF_INIT;
46+
git_buf our_refname = GIT_BUF_INIT;
47+
git_buf their_refname = GIT_BUF_INIT;
48+
git_reference *our_ref;
4649
git_reference *their_ref;
4750
git_annotated_commit *their_head;
4851

49-
git_buf_printf(&refname, "%s%s", GIT_REFS_HEADS_DIR, branchname);
52+
if (our_branchname != NULL) {
53+
cl_git_pass(git_buf_printf(&our_refname, "%s%s", GIT_REFS_HEADS_DIR, our_branchname));
54+
cl_git_pass(git_reference_lookup(&our_ref, repo, git_buf_cstr(&our_refname)));
55+
} else {
56+
cl_git_pass(git_reference_lookup(&our_ref, repo, GIT_HEAD_FILE));
57+
}
5058

51-
cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&refname)));
59+
cl_git_pass(git_buf_printf(&their_refname, "%s%s", GIT_REFS_HEADS_DIR, their_branchname));
60+
61+
cl_git_pass(git_reference_lookup(&their_ref, repo, git_buf_cstr(&their_refname)));
5262
cl_git_pass(git_annotated_commit_from_ref(&their_head, repo, their_ref));
5363

54-
cl_git_pass(git_merge_analysis(merge_analysis, merge_pref, repo, (const git_annotated_commit **)&their_head, 1));
64+
cl_git_pass(git_merge_analysis_for_ref(merge_analysis, merge_pref, repo, our_ref, (const git_annotated_commit **)&their_head, 1));
5565

56-
git_buf_dispose(&refname);
66+
git_buf_dispose(&our_refname);
67+
git_buf_dispose(&their_refname);
5768
git_annotated_commit_free(their_head);
69+
git_reference_free(our_ref);
5870
git_reference_free(their_ref);
5971
}
6072

@@ -63,17 +75,16 @@ void test_merge_workdir_analysis__fastforward(void)
6375
git_merge_analysis_t merge_analysis;
6476
git_merge_preference_t merge_pref;
6577

66-
analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
67-
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
68-
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
78+
analysis_from_branch(&merge_analysis, &merge_pref, NULL, FASTFORWARD_BRANCH);
79+
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL|GIT_MERGE_ANALYSIS_FASTFORWARD, merge_analysis);
6980
}
7081

7182
void test_merge_workdir_analysis__no_fastforward(void)
7283
{
7384
git_merge_analysis_t merge_analysis;
7485
git_merge_preference_t merge_pref;
7586

76-
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
87+
analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
7788
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
7889
}
7990

@@ -82,7 +93,7 @@ void test_merge_workdir_analysis__uptodate(void)
8293
git_merge_analysis_t merge_analysis;
8394
git_merge_preference_t merge_pref;
8495

85-
analysis_from_branch(&merge_analysis, &merge_pref, UPTODATE_BRANCH);
96+
analysis_from_branch(&merge_analysis, &merge_pref, NULL, UPTODATE_BRANCH);
8697
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
8798
}
8899

@@ -91,7 +102,7 @@ void test_merge_workdir_analysis__uptodate_merging_prev_commit(void)
91102
git_merge_analysis_t merge_analysis;
92103
git_merge_preference_t merge_pref;
93104

94-
analysis_from_branch(&merge_analysis, &merge_pref, PREVIOUS_BRANCH);
105+
analysis_from_branch(&merge_analysis, &merge_pref, NULL, PREVIOUS_BRANCH);
95106
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
96107
}
97108

@@ -104,9 +115,8 @@ void test_merge_workdir_analysis__unborn(void)
104115
git_buf_joinpath(&master, git_repository_path(repo), "refs/heads/master");
105116
p_unlink(git_buf_cstr(&master));
106117

107-
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
108-
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
109-
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UNBORN, (merge_analysis & GIT_MERGE_ANALYSIS_UNBORN));
118+
analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
119+
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD|GIT_MERGE_ANALYSIS_UNBORN, merge_analysis);
110120

111121
git_buf_dispose(&master);
112122
}
@@ -120,9 +130,9 @@ void test_merge_workdir_analysis__fastforward_with_config_noff(void)
120130
git_repository_config(&config, repo);
121131
git_config_set_string(config, "merge.ff", "false");
122132

123-
analysis_from_branch(&merge_analysis, &merge_pref, FASTFORWARD_BRANCH);
124-
cl_assert_equal_i(GIT_MERGE_ANALYSIS_FASTFORWARD, (merge_analysis & GIT_MERGE_ANALYSIS_FASTFORWARD));
125-
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
133+
analysis_from_branch(&merge_analysis, &merge_pref, NULL, FASTFORWARD_BRANCH);
134+
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL|GIT_MERGE_ANALYSIS_FASTFORWARD, merge_analysis);
135+
126136
cl_assert_equal_i(GIT_MERGE_PREFERENCE_NO_FASTFORWARD, (merge_pref & GIT_MERGE_PREFERENCE_NO_FASTFORWARD));
127137
}
128138

@@ -135,7 +145,26 @@ void test_merge_workdir_analysis__no_fastforward_with_config_ffonly(void)
135145
git_repository_config(&config, repo);
136146
git_config_set_string(config, "merge.ff", "only");
137147

138-
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH);
139-
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, (merge_analysis & GIT_MERGE_ANALYSIS_NORMAL));
148+
analysis_from_branch(&merge_analysis, &merge_pref, NULL, NOFASTFORWARD_BRANCH);
149+
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
150+
140151
cl_assert_equal_i(GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY, (merge_pref & GIT_MERGE_PREFERENCE_FASTFORWARD_ONLY));
141152
}
153+
154+
void test_merge_workdir_analysis__between_uptodate_refs(void)
155+
{
156+
git_merge_analysis_t merge_analysis;
157+
git_merge_preference_t merge_pref;
158+
159+
analysis_from_branch(&merge_analysis, &merge_pref, NOFASTFORWARD_BRANCH, PREVIOUS_BRANCH);
160+
cl_assert_equal_i(GIT_MERGE_ANALYSIS_UP_TO_DATE, merge_analysis);
161+
}
162+
163+
void test_merge_workdir_analysis__between_noff_refs(void)
164+
{
165+
git_merge_analysis_t merge_analysis;
166+
git_merge_preference_t merge_pref;
167+
168+
analysis_from_branch(&merge_analysis, &merge_pref, "branch", FASTFORWARD_BRANCH);
169+
cl_assert_equal_i(GIT_MERGE_ANALYSIS_NORMAL, merge_analysis);
170+
}

0 commit comments

Comments
 (0)