Commit 8960dc1e by Edward Thomson

iterator: provide git_iterator_walk

Provide `git_iterator_walk` to walk each iterator in lockstep,
returning each iterator's idea of the contents of the next path.
parent 82b1c93d
......@@ -1843,3 +1843,91 @@ int git_iterator_advance_over_with_status(
return error;
}
int git_iterator_walk(
git_iterator **iterators,
size_t cnt,
git_iterator_walk_cb cb,
void *data)
{
const git_index_entry **iterator_item; /* next in each iterator */
const git_index_entry **cur_items; /* current path in each iter */
const git_index_entry *first_match;
int cur_item_modified;
size_t i, j;
int error = 0;
iterator_item = git__calloc(cnt, sizeof(git_index_entry *));
cur_items = git__calloc(cnt, sizeof(git_index_entry *));
GITERR_CHECK_ALLOC(iterator_item);
GITERR_CHECK_ALLOC(cur_items);
/* Set up the iterators */
for (i = 0; i < cnt; i++) {
error = git_iterator_current(&iterator_item[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
while (true) {
for (i = 0; i < cnt; i++)
cur_items[i] = NULL;
first_match = NULL;
cur_item_modified = 0;
/* Find the next path(s) to consume from each iterator */
for (i = 0; i < cnt; i++) {
if (iterator_item[i] == NULL)
continue;
if (first_match == NULL) {
first_match = iterator_item[i];
cur_items[i] = iterator_item[i];
} else {
int path_diff = git_index_entry_cmp(iterator_item[i], first_match);
if (path_diff < 0) {
/* Found an index entry that sorts before the one we're
* looking at. Forget that we've seen the other and
* look at the other iterators for this path.
*/
for (j = 0; j < i; j++)
cur_items[j] = NULL;
first_match = iterator_item[i];
cur_items[i] = iterator_item[i];
} else if (path_diff > 0) {
/* No entry for the current item, this is modified */
cur_item_modified = 1;
} else if (path_diff == 0) {
cur_items[i] = iterator_item[i];
}
}
}
if (first_match == NULL)
break;
if ((error = cb(cur_items, data)) != 0)
goto done;
/* Advance each iterator that participated */
for (i = 0; i < cnt; i++) {
if (cur_items[i] == NULL)
continue;
error = git_iterator_advance(&iterator_item[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
}
done:
if (error == GIT_ITEROVER)
error = 0;
return error;
}
......@@ -294,4 +294,19 @@ extern int git_iterator_advance_over_with_status(
*/
extern int git_iterator_index(git_index **out, git_iterator *iter);
typedef int (*git_iterator_walk_cb)(
const git_index_entry **entries,
void *data);
/**
* Walk the given iterators in lock-step. The given callback will be
* called for each unique path, with the index entry in each iterator
* (or NULL if the given iterator does not contain that path).
*/
extern int git_iterator_walk(
git_iterator **iterators,
size_t cnt,
git_iterator_walk_cb cb,
void *data);
#endif
......@@ -1449,100 +1449,44 @@ static int merge_diff_list_insert_unmodified(
return error;
}
int git_merge_diff_list__find_differences(
git_merge_diff_list *diff_list,
git_iterator *ancestor_iter,
git_iterator *our_iter,
git_iterator *their_iter)
{
git_iterator *iterators[3] = { ancestor_iter, our_iter, their_iter };
const git_index_entry *items[3] = {0}, *best_cur_item, *cur_items[3];
git_vector_cmp entry_compare = git_index_entry_cmp;
struct merge_diff_df_data df_data = {0};
int cur_item_modified;
size_t i, j;
int error = 0;
assert(diff_list && (our_iter || their_iter));
/* Set up the iterators */
for (i = 0; i < 3; i++) {
error = git_iterator_current(&items[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
while (true) {
for (i = 0; i < 3; i++)
cur_items[i] = NULL;
best_cur_item = NULL;
cur_item_modified = 0;
struct merge_diff_find_data {
git_merge_diff_list *diff_list;
struct merge_diff_df_data df_data;
};
/* Find the next path(s) to consume from each iterator */
for (i = 0; i < 3; i++) {
if (items[i] == NULL) {
cur_item_modified = 1;
continue;
}
static int queue_difference(const git_index_entry **entries, void *data)
{
struct merge_diff_find_data *find_data = data;
bool item_modified = false;
size_t i;
if (best_cur_item == NULL) {
best_cur_item = items[i];
cur_items[i] = items[i];
if (!entries[0] || !entries[1] || !entries[2]) {
item_modified = true;
} else {
int path_diff = entry_compare(items[i], best_cur_item);
if (path_diff < 0) {
/*
* Found an item that sorts before our current item, make
* our current item this one.
*/
for (j = 0; j < i; j++)
cur_items[j] = NULL;
cur_item_modified = 1;
best_cur_item = items[i];
cur_items[i] = items[i];
} else if (path_diff > 0) {
/* No entry for the current item, this is modified */
cur_item_modified = 1;
} else if (path_diff == 0) {
cur_items[i] = items[i];
if (!cur_item_modified)
cur_item_modified = index_entry_cmp(best_cur_item, items[i]);
for (i = 1; i < 3; i++) {
if (index_entry_cmp(entries[0], entries[i]) != 0) {
item_modified = true;
break;
}
}
}
if (best_cur_item == NULL)
break;
if (cur_item_modified)
error = merge_diff_list_insert_conflict(diff_list, &df_data, cur_items);
else
error = merge_diff_list_insert_unmodified(diff_list, cur_items);
if (error < 0)
goto done;
/* Advance each iterator that participated */
for (i = 0; i < 3; i++) {
if (cur_items[i] == NULL)
continue;
error = git_iterator_advance(&items[i], iterators[i]);
if (error < 0 && error != GIT_ITEROVER)
goto done;
}
}
return item_modified ?
merge_diff_list_insert_conflict(
find_data->diff_list, &find_data->df_data, entries) :
merge_diff_list_insert_unmodified(find_data->diff_list, entries);
}
done:
if (error == GIT_ITEROVER)
error = 0;
int git_merge_diff_list__find_differences(
git_merge_diff_list *diff_list,
git_iterator *ancestor_iter,
git_iterator *our_iter,
git_iterator *their_iter)
{
git_iterator *iterators[3] = { ancestor_iter, our_iter, their_iter };
struct merge_diff_find_data find_data = { diff_list };
return error;
return git_iterator_walk(iterators, 3, queue_difference, &find_data);
}
git_merge_diff_list *git_merge_diff_list__alloc(git_repository *repo)
......
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