Commit f335ecd6 by Russell Belfer

Diff iterators

This refactors the diff output code so that an iterator object
can be used to traverse and generate the diffs, instead of just
the `foreach()` style with callbacks.  The code has been rearranged
so that the two styles can still share most functions.

This also replaces `GIT_REVWALKOVER` with `GIT_ITEROVER` and uses
that as a common error code for marking the end of iteration when
using a iterator style of object.
parent 4d383403
...@@ -169,7 +169,7 @@ enum { ...@@ -169,7 +169,7 @@ enum {
GIT_DIFF_LINE_CONTEXT = ' ', GIT_DIFF_LINE_CONTEXT = ' ',
GIT_DIFF_LINE_ADDITION = '+', GIT_DIFF_LINE_ADDITION = '+',
GIT_DIFF_LINE_DELETION = '-', GIT_DIFF_LINE_DELETION = '-',
GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< DEPRECATED */ GIT_DIFF_LINE_ADD_EOFNL = '\n', /**< DEPRECATED - will not be returned */
GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */ GIT_DIFF_LINE_DEL_EOFNL = '\0', /**< LF was removed at end of file */
/* The following values will only be sent to a `git_diff_data_fn` when /* The following values will only be sent to a `git_diff_data_fn` when
...@@ -197,6 +197,11 @@ typedef int (*git_diff_data_fn)( ...@@ -197,6 +197,11 @@ typedef int (*git_diff_data_fn)(
const char *content, const char *content,
size_t content_len); size_t content_len);
/**
* The diff iterator object is used to scan a diff list.
*/
typedef struct git_diff_iterator git_diff_iterator;
/** @name Diff List Generator Functions /** @name Diff List Generator Functions
* *
* These are the functions you would use to create (or destroy) a * These are the functions you would use to create (or destroy) a
...@@ -322,6 +327,60 @@ GIT_EXTERN(int) git_diff_merge( ...@@ -322,6 +327,60 @@ GIT_EXTERN(int) git_diff_merge(
/**@{*/ /**@{*/
/** /**
* Create a diff iterator object that can be used to traverse a diff.
*/
GIT_EXTERN(int) git_diff_iterator_new(
git_diff_iterator **iterator,
git_diff_list *diff);
GIT_EXTERN(void) git_diff_iterator_free(git_diff_iterator *iter);
/**
* Return the number of files in the diff.
*/
GIT_EXTERN(int) git_diff_iterator_num_files(git_diff_iterator *iterator);
GIT_EXTERN(int) git_diff_iterator_num_hunks_in_file(git_diff_iterator *iterator);
GIT_EXTERN(int) git_diff_iterator_num_lines_in_hunk(git_diff_iterator *iterator);
/**
* Return the delta information for the next file in the diff.
*
* This will return a pointer to the next git_diff_delta` to be processed or
* NULL if the iterator is at the end of the diff, then advance.
*/
GIT_EXTERN(int) git_diff_iterator_next_file(
git_diff_delta **delta,
git_diff_iterator *iterator);
/**
* Return the hunk information for the next hunk in the current file.
*
* It is recommended that you not call this if the file is a binary
* file, but it is allowed to do so.
*
* Warning! Call this function for the first time on a file is when the
* actual text diff will be computed (it cannot be computed incrementally)
* so the first call for a new file is expensive (at least in relative
* terms - in reality, it is still pretty darn fast).
*/
GIT_EXTERN(int) git_diff_iterator_next_hunk(
git_diff_range **range,
const char **header,
size_t *header_len,
git_diff_iterator *iterator);
/**
* Return the next line of the current hunk of diffs.
*/
GIT_EXTERN(int) git_diff_iterator_next_line(
char *line_origin, /**< GIT_DIFF_LINE_... value from above */
const char **content,
size_t *content_len,
git_diff_iterator *iterator);
/**
* Iterate over a diff list issuing callbacks. * Iterate over a diff list issuing callbacks.
* *
* This will iterate through all of the files described in a diff. You * This will iterate through all of the files described in a diff. You
......
...@@ -28,7 +28,7 @@ enum { ...@@ -28,7 +28,7 @@ enum {
GIT_EUSER = -7, GIT_EUSER = -7,
GIT_PASSTHROUGH = -30, GIT_PASSTHROUGH = -30,
GIT_REVWALKOVER = -31, GIT_ITEROVER = -31,
}; };
typedef struct { typedef struct {
......
...@@ -201,7 +201,7 @@ GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname); ...@@ -201,7 +201,7 @@ GIT_EXTERN(int) git_revwalk_hide_ref(git_revwalk *walk, const char *refname);
* @param oid Pointer where to store the oid of the next commit * @param oid Pointer where to store the oid of the next commit
* @param walk the walker to pop the commit from. * @param walk the walker to pop the commit from.
* @return 0 if the next commit was found; * @return 0 if the next commit was found;
* GIT_REVWALKOVER if there are no commits left to iterate * GIT_ITEROVER if there are no commits left to iterate
*/ */
GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk); GIT_EXTERN(int) git_revwalk_next(git_oid *oid, git_revwalk *walk);
......
...@@ -188,6 +188,7 @@ int git_attr_foreach( ...@@ -188,6 +188,7 @@ int git_attr_foreach(
error = callback(assign->name, assign->value, payload); error = callback(assign->name, assign->value, payload);
if (error) { if (error) {
giterr_clear();
error = GIT_EUSER; error = GIT_EUSER;
goto cleanup; goto cleanup;
} }
......
...@@ -221,6 +221,7 @@ static int file_foreach( ...@@ -221,6 +221,7 @@ static int file_foreach(
/* abort iterator on non-zero return value */ /* abort iterator on non-zero return value */
if (fn(key, var->value, data)) { if (fn(key, var->value, data)) {
giterr_clear();
result = GIT_EUSER; result = GIT_EUSER;
goto cleanup; goto cleanup;
} }
......
...@@ -316,6 +316,7 @@ static git_diff_list *git_diff_list_alloc( ...@@ -316,6 +316,7 @@ static git_diff_list *git_diff_list_alloc(
if (diff == NULL) if (diff == NULL)
return NULL; return NULL;
GIT_REFCOUNT_INC(diff);
diff->repo = repo; diff->repo = repo;
if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 || if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
...@@ -391,15 +392,12 @@ fail: ...@@ -391,15 +392,12 @@ fail:
return NULL; return NULL;
} }
void git_diff_list_free(git_diff_list *diff) static void diff_list_free(git_diff_list *diff)
{ {
git_diff_delta *delta; git_diff_delta *delta;
git_attr_fnmatch *match; git_attr_fnmatch *match;
unsigned int i; unsigned int i;
if (!diff)
return;
git_vector_foreach(&diff->deltas, i, delta) { git_vector_foreach(&diff->deltas, i, delta) {
git__free(delta); git__free(delta);
diff->deltas.contents[i] = NULL; diff->deltas.contents[i] = NULL;
...@@ -416,6 +414,14 @@ void git_diff_list_free(git_diff_list *diff) ...@@ -416,6 +414,14 @@ void git_diff_list_free(git_diff_list *diff)
git__free(diff); git__free(diff);
} }
void git_diff_list_free(git_diff_list *diff)
{
if (!diff)
return;
GIT_REFCOUNT_DEC(diff, diff_list_free);
}
static int oid_for_workdir_item( static int oid_for_workdir_item(
git_repository *repo, git_repository *repo,
const git_index_entry *item, const git_index_entry *item,
......
...@@ -26,6 +26,7 @@ enum { ...@@ -26,6 +26,7 @@ enum {
}; };
struct git_diff_list { struct git_diff_list {
git_refcount rc;
git_repository *repo; git_repository *repo;
git_diff_options opts; git_diff_options opts;
git_vector pathspec; git_vector pathspec;
......
...@@ -221,7 +221,7 @@ int git_fetch_negotiate(git_remote *remote) ...@@ -221,7 +221,7 @@ int git_fetch_negotiate(git_remote *remote)
} }
} }
if (error < 0 && error != GIT_REVWALKOVER) if (error < 0 && error != GIT_ITEROVER)
goto on_error; goto on_error;
/* Tell the other end that we're done negotiating */ /* Tell the other end that we're done negotiating */
......
...@@ -76,6 +76,17 @@ extern void git_pool_swap(git_pool *a, git_pool *b); ...@@ -76,6 +76,17 @@ extern void git_pool_swap(git_pool *a, git_pool *b);
extern void *git_pool_malloc(git_pool *pool, uint32_t items); extern void *git_pool_malloc(git_pool *pool, uint32_t items);
/** /**
* Allocate space and zero it out.
*/
GIT_INLINE(void *) git_pool_mallocz(git_pool *pool, uint32_t items)
{
void *ptr = git_pool_malloc(pool, items);
if (ptr)
memset(ptr, 0, (size_t)items * (size_t)pool->item_size);
return ptr;
}
/**
* Allocate space and duplicate string data into it. * Allocate space and duplicate string data into it.
* *
* This is allowed only for pools with item_size == sizeof(char) * This is allowed only for pools with item_size == sizeof(char)
......
...@@ -1643,7 +1643,6 @@ int git_reference_normalize_name( ...@@ -1643,7 +1643,6 @@ int git_reference_normalize_name(
} }
} }
*buffer_out++ = *current++; *buffer_out++ = *current++;
buffer_size--; buffer_size--;
} }
......
...@@ -493,7 +493,7 @@ static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex) ...@@ -493,7 +493,7 @@ static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex)
git_object_free(obj); git_object_free(obj);
} }
if (error < 0 && error == GIT_REVWALKOVER) if (error < 0 && error == GIT_ITEROVER)
error = GIT_ENOTFOUND; error = GIT_ENOTFOUND;
return error; return error;
......
...@@ -449,6 +449,7 @@ int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *tw ...@@ -449,6 +449,7 @@ int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *tw
if (!result) { if (!result) {
git_revwalk_free(walk); git_revwalk_free(walk);
giterr_clear();
return GIT_ENOTFOUND; return GIT_ENOTFOUND;
} }
...@@ -682,7 +683,8 @@ static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk) ...@@ -682,7 +683,8 @@ static int revwalk_next_timesort(commit_object **object_out, git_revwalk *walk)
} }
} }
return GIT_REVWALKOVER; giterr_clear();
return GIT_ITEROVER;
} }
static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
...@@ -700,7 +702,8 @@ static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk) ...@@ -700,7 +702,8 @@ static int revwalk_next_unsorted(commit_object **object_out, git_revwalk *walk)
} }
} }
return GIT_REVWALKOVER; giterr_clear();
return GIT_ITEROVER;
} }
static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
...@@ -710,8 +713,10 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) ...@@ -710,8 +713,10 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
for (;;) { for (;;) {
next = commit_list_pop(&walk->iterator_topo); next = commit_list_pop(&walk->iterator_topo);
if (next == NULL) if (next == NULL) {
return GIT_REVWALKOVER; giterr_clear();
return GIT_ITEROVER;
}
if (next->in_degree > 0) { if (next->in_degree > 0) {
next->topo_delay = 1; next->topo_delay = 1;
...@@ -736,7 +741,7 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk) ...@@ -736,7 +741,7 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk) static int revwalk_next_reverse(commit_object **object_out, git_revwalk *walk)
{ {
*object_out = commit_list_pop(&walk->iterator_reverse); *object_out = commit_list_pop(&walk->iterator_reverse);
return *object_out ? 0 : GIT_REVWALKOVER; return *object_out ? 0 : GIT_ITEROVER;
} }
...@@ -751,8 +756,10 @@ static int prepare_walk(git_revwalk *walk) ...@@ -751,8 +756,10 @@ static int prepare_walk(git_revwalk *walk)
* If walk->one is NULL, there were no positive references, * If walk->one is NULL, there were no positive references,
* so we know that the walk is already over. * so we know that the walk is already over.
*/ */
if (walk->one == NULL) if (walk->one == NULL) {
return GIT_REVWALKOVER; giterr_clear();
return GIT_ITEROVER;
}
/* first figure out what the merge bases are */ /* first figure out what the merge bases are */
if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0) if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0)
...@@ -780,7 +787,7 @@ static int prepare_walk(git_revwalk *walk) ...@@ -780,7 +787,7 @@ static int prepare_walk(git_revwalk *walk)
return -1; return -1;
} }
if (error != GIT_REVWALKOVER) if (error != GIT_ITEROVER)
return error; return error;
walk->get_next = &revwalk_next_toposort; walk->get_next = &revwalk_next_toposort;
...@@ -792,7 +799,7 @@ static int prepare_walk(git_revwalk *walk) ...@@ -792,7 +799,7 @@ static int prepare_walk(git_revwalk *walk)
if (commit_list_insert(next, &walk->iterator_reverse) == NULL) if (commit_list_insert(next, &walk->iterator_reverse) == NULL)
return -1; return -1;
if (error != GIT_REVWALKOVER) if (error != GIT_ITEROVER)
return error; return error;
walk->get_next = &revwalk_next_reverse; walk->get_next = &revwalk_next_reverse;
...@@ -891,9 +898,10 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk) ...@@ -891,9 +898,10 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk)
error = walk->get_next(&next, walk); error = walk->get_next(&next, walk);
if (error == GIT_REVWALKOVER) { if (error == GIT_ITEROVER) {
git_revwalk_reset(walk); git_revwalk_reset(walk);
return GIT_REVWALKOVER; giterr_clear();
return GIT_ITEROVER;
} }
if (!error) if (!error)
......
...@@ -151,6 +151,9 @@ cleanup: ...@@ -151,6 +151,9 @@ cleanup:
git_diff_list_free(idx2head); git_diff_list_free(idx2head);
git_diff_list_free(wd2idx); git_diff_list_free(wd2idx);
if (err == GIT_EUSER)
giterr_clear();
return err; return err;
} }
......
...@@ -163,6 +163,7 @@ int git_submodule_foreach( ...@@ -163,6 +163,7 @@ int git_submodule_foreach(
} }
if (callback(sm, sm->name, payload)) { if (callback(sm, sm->name, payload)) {
giterr_clear();
error = GIT_EUSER; error = GIT_EUSER;
break; break;
} }
......
...@@ -58,59 +58,59 @@ void test_diff_blob__can_compare_text_blobs(void) ...@@ -58,59 +58,59 @@ void test_diff_blob__can_compare_text_blobs(void)
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
a, b, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); a, b, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_mods == 1); cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false); cl_assert(expected.at_least_one_of_them_is_binary == false);
cl_assert(expected.hunks == 1); cl_assert_equal_i(1, expected.hunks);
cl_assert(expected.lines == 6); cl_assert_equal_i(6, expected.lines);
cl_assert(expected.line_ctxt == 1); cl_assert_equal_i(1, expected.line_ctxt);
cl_assert(expected.line_adds == 5); cl_assert_equal_i(5, expected.line_adds);
cl_assert(expected.line_dels == 0); cl_assert_equal_i(0, expected.line_dels);
/* diff on tests/resources/attr/root_test2 */ /* diff on tests/resources/attr/root_test2 */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
b, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); b, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_mods == 1); cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false); cl_assert(expected.at_least_one_of_them_is_binary == false);
cl_assert(expected.hunks == 1); cl_assert_equal_i(1, expected.hunks);
cl_assert(expected.lines == 15); cl_assert_equal_i(15, expected.lines);
cl_assert(expected.line_ctxt == 3); cl_assert_equal_i(3, expected.line_ctxt);
cl_assert(expected.line_adds == 9); cl_assert_equal_i(9, expected.line_adds);
cl_assert(expected.line_dels == 3); cl_assert_equal_i(3, expected.line_dels);
/* diff on tests/resources/attr/root_test3 */ /* diff on tests/resources/attr/root_test3 */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
a, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); a, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_mods == 1); cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false); cl_assert(expected.at_least_one_of_them_is_binary == false);
cl_assert(expected.hunks == 1); cl_assert_equal_i(1, expected.hunks);
cl_assert(expected.lines == 13); cl_assert_equal_i(13, expected.lines);
cl_assert(expected.line_ctxt == 0); cl_assert_equal_i(0, expected.line_ctxt);
cl_assert(expected.line_adds == 12); cl_assert_equal_i(12, expected.line_adds);
cl_assert(expected.line_dels == 1); cl_assert_equal_i(1, expected.line_dels);
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
c, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); c, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_mods == 1); cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false); cl_assert(expected.at_least_one_of_them_is_binary == false);
cl_assert(expected.hunks == 2); cl_assert_equal_i(2, expected.hunks);
cl_assert(expected.lines == 14); cl_assert_equal_i(14, expected.lines);
cl_assert(expected.line_ctxt == 4); cl_assert_equal_i(4, expected.line_ctxt);
cl_assert(expected.line_adds == 6); cl_assert_equal_i(6, expected.line_adds);
cl_assert(expected.line_dels == 4); cl_assert_equal_i(4, expected.line_dels);
git_blob_free(a); git_blob_free(a);
git_blob_free(b); git_blob_free(b);
...@@ -124,14 +124,14 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -124,14 +124,14 @@ void test_diff_blob__can_compare_against_null_blobs(void)
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_dels == 1); cl_assert_equal_i(1, expected.file_dels);
cl_assert(expected.at_least_one_of_them_is_binary == false); cl_assert(expected.at_least_one_of_them_is_binary == false);
cl_assert(expected.hunks == 1); cl_assert_equal_i(1, expected.hunks);
cl_assert(expected.hunk_old_lines == 14); cl_assert_equal_i(14, expected.hunk_old_lines);
cl_assert(expected.lines == 14); cl_assert_equal_i(14, expected.lines);
cl_assert(expected.line_dels == 14); cl_assert_equal_i(14, expected.line_dels);
opts.flags |= GIT_DIFF_REVERSE; opts.flags |= GIT_DIFF_REVERSE;
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
...@@ -139,14 +139,14 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -139,14 +139,14 @@ void test_diff_blob__can_compare_against_null_blobs(void)
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_adds == 1); cl_assert_equal_i(1, expected.file_adds);
cl_assert(expected.at_least_one_of_them_is_binary == false); cl_assert(expected.at_least_one_of_them_is_binary == false);
cl_assert(expected.hunks == 1); cl_assert_equal_i(1, expected.hunks);
cl_assert(expected.hunk_new_lines == 14); cl_assert_equal_i(14, expected.hunk_new_lines);
cl_assert(expected.lines == 14); cl_assert_equal_i(14, expected.lines);
cl_assert(expected.line_adds == 14); cl_assert_equal_i(14, expected.line_adds);
opts.flags ^= GIT_DIFF_REVERSE; opts.flags ^= GIT_DIFF_REVERSE;
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
...@@ -156,10 +156,10 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -156,10 +156,10 @@ void test_diff_blob__can_compare_against_null_blobs(void)
cl_assert(expected.at_least_one_of_them_is_binary == true); cl_assert(expected.at_least_one_of_them_is_binary == true);
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_dels == 1); cl_assert_equal_i(1, expected.file_dels);
cl_assert(expected.hunks == 0); cl_assert_equal_i(0, expected.hunks);
cl_assert(expected.lines == 0); cl_assert_equal_i(0, expected.lines);
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
...@@ -168,18 +168,18 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -168,18 +168,18 @@ void test_diff_blob__can_compare_against_null_blobs(void)
cl_assert(expected.at_least_one_of_them_is_binary == true); cl_assert(expected.at_least_one_of_them_is_binary == true);
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_adds == 1); cl_assert_equal_i(1, expected.file_adds);
cl_assert(expected.hunks == 0); cl_assert_equal_i(0, expected.hunks);
cl_assert(expected.lines == 0); cl_assert_equal_i(0, expected.lines);
} }
static void assert_identical_blobs_comparison(diff_expects expected) static void assert_identical_blobs_comparison(diff_expects expected)
{ {
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_unmodified == 1); cl_assert_equal_i(1, expected.file_unmodified);
cl_assert(expected.hunks == 0); cl_assert_equal_i(0, expected.hunks);
cl_assert(expected.lines == 0); cl_assert_equal_i(0, expected.lines);
} }
void test_diff_blob__can_compare_identical_blobs(void) void test_diff_blob__can_compare_identical_blobs(void)
...@@ -209,10 +209,10 @@ static void assert_binary_blobs_comparison(diff_expects expected) ...@@ -209,10 +209,10 @@ static void assert_binary_blobs_comparison(diff_expects expected)
{ {
cl_assert(expected.at_least_one_of_them_is_binary == true); cl_assert(expected.at_least_one_of_them_is_binary == true);
cl_assert(expected.files == 1); cl_assert_equal_i(1, expected.files);
cl_assert(expected.file_mods == 1); cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.hunks == 0); cl_assert_equal_i(0, expected.hunks);
cl_assert(expected.lines == 0); cl_assert_equal_i(0, expected.lines);
} }
void test_diff_blob__can_compare_two_binary_blobs(void) void test_diff_blob__can_compare_two_binary_blobs(void)
...@@ -292,7 +292,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) ...@@ -292,7 +292,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.hunks == 2); cl_assert_equal_i(2, expected.hunks);
/* Test with inter-hunk-context explicitly set to 0 */ /* Test with inter-hunk-context explicitly set to 0 */
opts.interhunk_lines = 0; opts.interhunk_lines = 0;
...@@ -300,7 +300,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) ...@@ -300,7 +300,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.hunks == 2); cl_assert_equal_i(2, expected.hunks);
/* Test with inter-hunk-context explicitly set to 1 */ /* Test with inter-hunk-context explicitly set to 1 */
opts.interhunk_lines = 1; opts.interhunk_lines = 1;
...@@ -308,7 +308,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) ...@@ -308,7 +308,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn)); old_d, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(expected.hunks == 1); cl_assert_equal_i(1, expected.hunks);
git_blob_free(old_d); git_blob_free(old_d);
} }
...@@ -103,3 +103,74 @@ int diff_line_fn( ...@@ -103,3 +103,74 @@ int diff_line_fn(
} }
return 0; return 0;
} }
int diff_foreach_via_iterator(
git_diff_list *diff,
void *data,
git_diff_file_fn file_cb,
git_diff_hunk_fn hunk_cb,
git_diff_data_fn line_cb)
{
int error, curr, total;
git_diff_iterator *iter;
git_diff_delta *delta;
if ((error = git_diff_iterator_new(&iter, diff)) < 0)
return error;
curr = 0;
total = git_diff_iterator_num_files(iter);
while (!(error = git_diff_iterator_next_file(&delta, iter))) {
git_diff_range *range;
const char *hdr;
size_t hdr_len;
/* call file_cb for this file */
if (file_cb != NULL && file_cb(data, delta, (float)curr / total) != 0)
goto abort;
if (!hunk_cb && !line_cb)
continue;
while (!(error = git_diff_iterator_next_hunk(
&range, &hdr, &hdr_len, iter))) {
char origin;
const char *line;
size_t line_len;
if (hunk_cb && hunk_cb(data, delta, range, hdr, hdr_len) != 0)
goto abort;
if (!line_cb)
continue;
while (!(error = git_diff_iterator_next_line(
&origin, &line, &line_len, iter))) {
if (line_cb(data, delta, range, origin, line, line_len) != 0)
goto abort;
}
if (error && error != GIT_ITEROVER)
goto done;
}
if (error && error != GIT_ITEROVER)
goto done;
}
done:
git_diff_iterator_free(iter);
if (error == GIT_ITEROVER)
error = 0;
return error;
abort:
git_diff_iterator_free(iter);
giterr_clear();
return GIT_EUSER;
}
...@@ -45,3 +45,9 @@ extern int diff_line_fn( ...@@ -45,3 +45,9 @@ extern int diff_line_fn(
const char *content, const char *content,
size_t content_len); size_t content_len);
extern int diff_foreach_via_iterator(
git_diff_list *diff,
void *data,
git_diff_file_fn file_cb,
git_diff_hunk_fn hunk_cb,
git_diff_data_fn line_cb);
#include "clar_libgit2.h"
#include "diff_helpers.h"
void test_diff_diffiter__initialize(void)
{
}
void test_diff_diffiter__cleanup(void)
{
cl_git_sandbox_cleanup();
}
void test_diff_diffiter__create(void)
{
git_repository *repo = cl_git_sandbox_init("attr");
git_diff_list *diff;
git_diff_iterator *iter;
cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff));
cl_git_pass(git_diff_iterator_new(&iter, diff));
git_diff_iterator_free(iter);
git_diff_list_free(diff);
}
void test_diff_diffiter__iterate_files(void)
{
git_repository *repo = cl_git_sandbox_init("attr");
git_diff_list *diff;
git_diff_iterator *iter;
git_diff_delta *delta;
int error, count = 0;
cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff));
cl_git_pass(git_diff_iterator_new(&iter, diff));
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
cl_assert_equal_i(0, error);
cl_assert(delta != NULL);
count++;
}
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert(delta == NULL);
cl_assert_equal_i(6, count);
git_diff_iterator_free(iter);
git_diff_list_free(diff);
}
void test_diff_diffiter__iterate_files_2(void)
{
git_repository *repo = cl_git_sandbox_init("status");
git_diff_list *diff;
git_diff_iterator *iter;
git_diff_delta *delta;
int error, count = 0;
cl_git_pass(git_diff_workdir_to_index(repo, NULL, &diff));
cl_git_pass(git_diff_iterator_new(&iter, diff));
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
cl_assert_equal_i(0, error);
cl_assert(delta != NULL);
count++;
}
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert(delta == NULL);
cl_assert_equal_i(8, count);
git_diff_iterator_free(iter);
git_diff_list_free(diff);
}
void test_diff_diffiter__iterate_files_and_hunks(void)
{
git_repository *repo = cl_git_sandbox_init("status");
git_diff_options opts = {0};
git_diff_list *diff = NULL;
git_diff_iterator *iter;
git_diff_delta *delta;
git_diff_range *range;
const char *header;
size_t header_len;
int error, file_count = 0, hunk_count = 0;
opts.context_lines = 3;
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
cl_git_pass(git_diff_workdir_to_index(repo, &opts, &diff));
cl_git_pass(git_diff_iterator_new(&iter, diff));
while ((error = git_diff_iterator_next_file(&delta, iter)) != GIT_ITEROVER) {
cl_assert_equal_i(0, error);
cl_assert(delta);
file_count++;
while ((error = git_diff_iterator_next_hunk(
&range, &header, &header_len, iter)) != GIT_ITEROVER) {
cl_assert_equal_i(0, error);
cl_assert(range);
hunk_count++;
}
}
cl_assert_equal_i(GIT_ITEROVER, error);
cl_assert(delta == NULL);
cl_assert_equal_i(13, file_count);
cl_assert_equal_i(8, hunk_count);
git_diff_iterator_free(iter);
git_diff_list_free(diff);
}
...@@ -44,17 +44,17 @@ void test_diff_index__0(void) ...@@ -44,17 +44,17 @@ void test_diff_index__0(void)
* - git diff -U1 --cached 26a125ee1bf * - git diff -U1 --cached 26a125ee1bf
* - mv .git .gitted * - mv .git .gitted
*/ */
cl_assert(exp.files == 8); cl_assert_equal_i(8, exp.files);
cl_assert(exp.file_adds == 3); cl_assert_equal_i(3, exp.file_adds);
cl_assert(exp.file_dels == 2); cl_assert_equal_i(2, exp.file_dels);
cl_assert(exp.file_mods == 3); cl_assert_equal_i(3, exp.file_mods);
cl_assert(exp.hunks == 8); cl_assert_equal_i(8, exp.hunks);
cl_assert(exp.lines == 11); cl_assert_equal_i(11, exp.lines);
cl_assert(exp.line_ctxt == 3); cl_assert_equal_i(3, exp.line_ctxt);
cl_assert(exp.line_adds == 6); cl_assert_equal_i(6, exp.line_adds);
cl_assert(exp.line_dels == 2); cl_assert_equal_i(2, exp.line_dels);
git_diff_list_free(diff); git_diff_list_free(diff);
diff = NULL; diff = NULL;
...@@ -72,17 +72,17 @@ void test_diff_index__0(void) ...@@ -72,17 +72,17 @@ void test_diff_index__0(void)
* - git diff -U1 --cached 0017bd4ab1ec3 * - git diff -U1 --cached 0017bd4ab1ec3
* - mv .git .gitted * - mv .git .gitted
*/ */
cl_assert(exp.files == 12); cl_assert_equal_i(12, exp.files);
cl_assert(exp.file_adds == 7); cl_assert_equal_i(7, exp.file_adds);
cl_assert(exp.file_dels == 2); cl_assert_equal_i(2, exp.file_dels);
cl_assert(exp.file_mods == 3); cl_assert_equal_i(3, exp.file_mods);
cl_assert(exp.hunks == 12); cl_assert_equal_i(12, exp.hunks);
cl_assert(exp.lines == 16); cl_assert_equal_i(16, exp.lines);
cl_assert(exp.line_ctxt == 3); cl_assert_equal_i(3, exp.line_ctxt);
cl_assert(exp.line_adds == 11); cl_assert_equal_i(11, exp.line_adds);
cl_assert(exp.line_dels == 2); cl_assert_equal_i(2, exp.line_dels);
git_diff_list_free(diff); git_diff_list_free(diff);
diff = NULL; diff = NULL;
...@@ -132,7 +132,7 @@ void test_diff_index__1(void) ...@@ -132,7 +132,7 @@ void test_diff_index__1(void)
git_diff_foreach(diff, &exp, diff_stop_after_2_files, NULL, NULL) git_diff_foreach(diff, &exp, diff_stop_after_2_files, NULL, NULL)
); );
cl_assert(exp.files == 2); cl_assert_equal_i(2, exp.files);
git_diff_list_free(diff); git_diff_list_free(diff);
diff = NULL; diff = NULL;
......
...@@ -39,17 +39,17 @@ void test_diff_tree__0(void) ...@@ -39,17 +39,17 @@ void test_diff_tree__0(void)
cl_git_pass(git_diff_foreach( cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 5); cl_assert_equal_i(5, exp.files);
cl_assert(exp.file_adds == 2); cl_assert_equal_i(2, exp.file_adds);
cl_assert(exp.file_dels == 1); cl_assert_equal_i(1, exp.file_dels);
cl_assert(exp.file_mods == 2); cl_assert_equal_i(2, exp.file_mods);
cl_assert(exp.hunks == 5); cl_assert_equal_i(5, exp.hunks);
cl_assert(exp.lines == 7 + 24 + 1 + 6 + 6); cl_assert_equal_i(7 + 24 + 1 + 6 + 6, exp.lines);
cl_assert(exp.line_ctxt == 1); cl_assert_equal_i(1, exp.line_ctxt);
cl_assert(exp.line_adds == 24 + 1 + 5 + 5); cl_assert_equal_i(24 + 1 + 5 + 5, exp.line_adds);
cl_assert(exp.line_dels == 7 + 1); cl_assert_equal_i(7 + 1, exp.line_dels);
git_diff_list_free(diff); git_diff_list_free(diff);
diff = NULL; diff = NULL;
...@@ -61,17 +61,17 @@ void test_diff_tree__0(void) ...@@ -61,17 +61,17 @@ void test_diff_tree__0(void)
cl_git_pass(git_diff_foreach( cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 2); cl_assert_equal_i(2, exp.files);
cl_assert(exp.file_adds == 0); cl_assert_equal_i(0, exp.file_adds);
cl_assert(exp.file_dels == 0); cl_assert_equal_i(0, exp.file_dels);
cl_assert(exp.file_mods == 2); cl_assert_equal_i(2, exp.file_mods);
cl_assert(exp.hunks == 2); cl_assert_equal_i(2, exp.hunks);
cl_assert(exp.lines == 8 + 15); cl_assert_equal_i(8 + 15, exp.lines);
cl_assert(exp.line_ctxt == 1); cl_assert_equal_i(1, exp.line_ctxt);
cl_assert(exp.line_adds == 1); cl_assert_equal_i(1, exp.line_adds);
cl_assert(exp.line_dels == 7 + 14); cl_assert_equal_i(7 + 14, exp.line_dels);
git_diff_list_free(diff); git_diff_list_free(diff);
...@@ -192,17 +192,17 @@ void test_diff_tree__bare(void) ...@@ -192,17 +192,17 @@ void test_diff_tree__bare(void)
cl_git_pass(git_diff_foreach( cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 3); cl_assert_equal_i(3, exp.files);
cl_assert(exp.file_adds == 2); cl_assert_equal_i(2, exp.file_adds);
cl_assert(exp.file_dels == 0); cl_assert_equal_i(0, exp.file_dels);
cl_assert(exp.file_mods == 1); cl_assert_equal_i(1, exp.file_mods);
cl_assert(exp.hunks == 3); cl_assert_equal_i(3, exp.hunks);
cl_assert(exp.lines == 4); cl_assert_equal_i(4, exp.lines);
cl_assert(exp.line_ctxt == 0); cl_assert_equal_i(0, exp.line_ctxt);
cl_assert(exp.line_adds == 3); cl_assert_equal_i(3, exp.line_adds);
cl_assert(exp.line_dels == 1); cl_assert_equal_i(1, exp.line_dels);
git_diff_list_free(diff); git_diff_list_free(diff);
git_tree_free(a); git_tree_free(a);
...@@ -242,17 +242,17 @@ void test_diff_tree__merge(void) ...@@ -242,17 +242,17 @@ void test_diff_tree__merge(void)
cl_git_pass(git_diff_foreach( cl_git_pass(git_diff_foreach(
diff1, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn)); diff1, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
cl_assert(exp.files == 6); cl_assert_equal_i(6, exp.files);
cl_assert(exp.file_adds == 2); cl_assert_equal_i(2, exp.file_adds);
cl_assert(exp.file_dels == 1); cl_assert_equal_i(1, exp.file_dels);
cl_assert(exp.file_mods == 3); cl_assert_equal_i(3, exp.file_mods);
cl_assert(exp.hunks == 6); cl_assert_equal_i(6, exp.hunks);
cl_assert(exp.lines == 59); cl_assert_equal_i(59, exp.lines);
cl_assert(exp.line_ctxt == 1); cl_assert_equal_i(1, exp.line_ctxt);
cl_assert(exp.line_adds == 36); cl_assert_equal_i(36, exp.line_adds);
cl_assert(exp.line_dels == 22); cl_assert_equal_i(22, exp.line_dels);
git_diff_list_free(diff1); git_diff_list_free(diff1);
} }
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