Commit 1b82f7b6 by Edward Thomson Committed by Edward Thomson

merge: compute octopus merge bases

parent fccad82e
...@@ -1923,114 +1923,131 @@ done: ...@@ -1923,114 +1923,131 @@ done:
return error; return error;
} }
#define INSERT_VIRTUAL_BASE_PARENT(commit, parent_id) \ static int merge_trees_with_heads(
do { \ git_index **out,
id = git_array_alloc(commit->parent_ids); \ git_repository *repo,
GITERR_CHECK_ALLOC(id); \ const git_tree *ours,
git_oid_cpy(id, parent_id); \ const git_tree *theirs,
const git_oid heads[],
size_t heads_len,
const git_merge_options *opts);
#define INSERT_ID(_ar, _id) do { \
git_oid *_alloced = git_array_alloc(_ar); \
GITERR_CHECK_ALLOC(_alloced); \
git_oid_cpy(_alloced, _id); \
} while(0) } while(0)
static int build_virtual_base( static int compute_base(
git_commit **out, git_tree **out,
git_repository *repo, git_repository *repo,
const git_commit *one, const git_oid heads[],
bool one_is_virtual, size_t heads_len,
const git_oid *two_id) const git_merge_options *opts)
{ {
git_commit *two = NULL, *result; git_commit_list *base_list = NULL;
git_revwalk *walk = NULL;
git_commit *base_commit = NULL, *next_commit = NULL;
git_tree *base_tree = NULL, *next_tree = NULL;
git_array_t(git_oid) base_ids = GIT_ARRAY_INIT;
git_index *index = NULL; git_index *index = NULL;
git_oid tree_id, *id; bool recursive = !opts || (opts->flags & GIT_MERGE_NO_RECURSIVE) == 0;
int error; int error = 0;
*out = NULL;
/* TODO: propagate merge options */ if ((error = merge_bases_many(&base_list, &walk, repo,
if ((error = git_commit_lookup(&two, repo, two_id)) < 0 || heads_len, heads)) < 0)
(error = git_merge_commits(&index, repo, one, two, NULL)) < 0) return error;
if (error == GIT_ENOTFOUND) {
giterr_clear();
error = 0;
goto done; goto done;
}
if ((error = git_index_write_tree_to(&tree_id, index, repo)) < 0) if ((error = git_commit_lookup(&base_commit, repo,
&base_list->item->oid)) < 0 ||
(error = git_commit_tree(&base_tree, base_commit)) < 0)
goto done; goto done;
if ((result = git__calloc(1, sizeof(git_commit))) == NULL) INSERT_ID(base_ids, git_commit_id(base_commit));
while (recursive && base_list->next) {
git_tree *new_tree;
git_oid new_tree_id;
base_list = base_list->next;
if ((error = git_commit_lookup(&next_commit, repo,
&base_list->item->oid)) < 0 ||
(error = git_commit_tree(&next_tree, next_commit)) < 0)
goto done; goto done;
result->object.repo = repo; INSERT_ID(base_ids, git_commit_id(next_commit));
/* if the initial commit we were given is virtual, we are octopus if ((error = merge_trees_with_heads(&index, repo, base_tree,
* merging - that virtual base's parents should actually be the next_tree, base_ids.ptr, base_ids.size, opts)) < 0)
* parents that we use for our new virtual commit. otherwise, it goto done;
* is an actual parent.
*/
if (one_is_virtual) {
size_t i, cnt = git_commit_parentcount(one);
for (i = 0; i < cnt; i++) /* TODO: conflicts!! */
INSERT_VIRTUAL_BASE_PARENT(result, git_commit_parent_id(one, i));
} else {
INSERT_VIRTUAL_BASE_PARENT(result, git_commit_id(one));
}
INSERT_VIRTUAL_BASE_PARENT(result, two_id); if ((error = git_index_write_tree_to(&new_tree_id, index, repo)) < 0 ||
(error = git_tree_lookup(&new_tree, repo, &new_tree_id)) < 0)
goto done;
git_oid_cpy(&result->tree_id, &tree_id); git_index_free(index);
index = NULL;
*out = result; git_tree_free(next_tree);
next_tree = NULL;
git_commit_free(next_commit);
next_commit = NULL;
git_tree_free(base_tree);
base_tree = new_tree;
}
*out = base_tree;
base_tree = NULL;
done: done:
git_index_free(index); git_index_free(index);
git_commit_free(two); git_tree_free(next_tree);
git_tree_free(base_tree);
git_commit_free(next_commit);
git_commit_free(base_commit);
git_commit_list_free(&base_list);
git_revwalk_free(walk);
git_array_clear(base_ids);
return error; return error;
} }
#undef INSERT_VIRTUAL_BASE_PARENT static int merge_trees_with_heads(
git_index **out,
static int compute_base_tree(
git_tree **out,
git_repository *repo, git_repository *repo,
const git_commit *our_commit, const git_tree *ours,
const git_commit *their_commit, const git_tree *theirs,
bool recursive) const git_oid heads[],
size_t heads_len,
const git_merge_options *opts)
{ {
git_commit_list *base_list; git_tree *ancestor = NULL;
git_revwalk *walk;
git_commit *base = NULL;
bool base_virtual = false;
int error = 0; int error = 0;
*out = NULL; if ((error = compute_base(&ancestor, repo, heads, heads_len, opts)) < 0) {
if (error == GIT_ENOTFOUND)
if ((error = merge_bases(&base_list, &walk, repo,
git_commit_id(our_commit), git_commit_id(their_commit))) < 0)
return error;
if (error == GIT_ENOTFOUND) {
giterr_clear(); giterr_clear();
error = 0; else
goto done;
}
if ((error = git_commit_lookup(&base, repo, &base_list->item->oid)) < 0)
goto done;
while (recursive && base_list->next) {
git_commit *new_base;
base_list = base_list->next;
if ((error = build_virtual_base(&new_base, repo, base, base_virtual,
&base_list->item->oid)) < 0)
goto done; goto done;
git_commit_free(base);
base = new_base;
base_virtual = true;
} }
error = git_commit_tree(out, base); error = git_merge_trees(out, repo, ancestor, ours, theirs, opts);
done: done:
git_commit_free(base); git_tree_free(ancestor);
git_commit_list_free(&base_list);
git_revwalk_free(walk);
return error; return error;
} }
...@@ -2042,30 +2059,24 @@ int git_merge_commits( ...@@ -2042,30 +2059,24 @@ int git_merge_commits(
const git_commit *their_commit, const git_commit *their_commit,
const git_merge_options *opts) const git_merge_options *opts)
{ {
git_tree *our_tree = NULL, *their_tree = NULL, *ancestor_tree = NULL; git_tree *our_tree = NULL, *their_tree = NULL;
bool recursive; git_oid heads[2];
int error = 0; int error = 0;
recursive = !opts || (opts->flags & GIT_MERGE_NO_RECURSIVE) == 0; *out = NULL;
if ((error = compute_base_tree(&ancestor_tree, repo,
our_commit, their_commit, recursive)) < 0) {
if (error == GIT_ENOTFOUND) git_oid_cpy(&heads[0], git_commit_id(our_commit));
giterr_clear(); git_oid_cpy(&heads[1], git_commit_id(their_commit));
else
goto done;
}
if ((error = git_commit_tree(&our_tree, our_commit)) < 0 || if ((error = git_commit_tree(&our_tree, our_commit)) < 0 ||
(error = git_commit_tree(&their_tree, their_commit)) < 0 || (error = git_commit_tree(&their_tree, their_commit)) < 0 ||
(error = git_merge_trees(out, repo, ancestor_tree, our_tree, their_tree, opts)) < 0) (error = merge_trees_with_heads(out, repo, our_tree, their_tree,
heads, 2, opts)) < 0)
goto done; goto done;
done: done:
git_tree_free(our_tree); git_tree_free(our_tree);
git_tree_free(their_tree); git_tree_free(their_tree);
git_tree_free(ancestor_tree);
return error; return error;
} }
......
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