summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorVicent Marti <tanoku@gmail.com>2012-09-06 10:08:14 +0200
committerVicent Marti <tanoku@gmail.com>2012-09-06 10:08:14 +0200
commit2e4a9ea9cf8cf12199ea17b0aab50219380b8311 (patch)
tree7bccb1732b38799a51e5e99fa1a8cc50839b0c7f
parent27730eefb9b0b4619006fd7d590bca54c0537090 (diff)
parent510f1bac6b94ce19459498ae78f87fc4f4552305 (diff)
downloadlibgit2-2e4a9ea9cf8cf12199ea17b0aab50219380b8311.tar.gz
Merge remote-tracking branch 'arrbee/diff-iterator' into development
-rw-r--r--include/git2/diff.h127
-rw-r--r--include/git2/errors.h2
-rw-r--r--include/git2/revwalk.h2
-rw-r--r--src/attr.c1
-rw-r--r--src/config_file.c1
-rw-r--r--src/diff.c14
-rw-r--r--src/diff.h1
-rw-r--r--src/diff_output.c1015
-rw-r--r--src/fetch.c2
-rw-r--r--src/pool.h11
-rw-r--r--src/refs.c1
-rw-r--r--src/revparse.c2
-rw-r--r--src/revwalk.c30
-rw-r--r--src/status.c3
-rw-r--r--src/submodule.c1
-rw-r--r--tests-clar/diff/blob.c118
-rw-r--r--tests-clar/diff/diff_helpers.c71
-rw-r--r--tests-clar/diff/diff_helpers.h6
-rw-r--r--tests-clar/diff/diffiter.c116
-rw-r--r--tests-clar/diff/index.c38
-rw-r--r--tests-clar/diff/tree.c72
-rw-r--r--tests-clar/diff/workdir.c485
-rw-r--r--tests-clar/resources/attr/.gitted/indexbin1856 -> 1856 bytes
-rw-r--r--tests-clar/resources/issue_592/.gitted/indexbin392 -> 392 bytes
-rw-r--r--tests-clar/resources/status/.gitted/indexbin1160 -> 1160 bytes
25 files changed, 1549 insertions, 570 deletions
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 088e1ecf..d1455061 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -169,7 +169,7 @@ enum {
GIT_DIFF_LINE_CONTEXT = ' ',
GIT_DIFF_LINE_ADDITION = '+',
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 */
/* The following values will only be sent to a `git_diff_data_fn` when
@@ -197,6 +197,11 @@ typedef int (*git_diff_data_fn)(
const char *content,
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
*
* These are the functions you would use to create (or destroy) a
@@ -354,6 +359,126 @@ GIT_EXTERN(int) git_diff_foreach(
git_diff_data_fn line_cb);
/**
+ * Create a diff iterator object that can be used to traverse a diff.
+ *
+ * This iterator can be used instead of `git_diff_foreach` in situations
+ * where callback functions are awkward to use. Because of the way that
+ * diffs are calculated internally, using an iterator will use somewhat
+ * more memory than `git_diff_foreach` would.
+ *
+ * @param iterator Output parameter of newly created iterator.
+ * @param diff Diff over which you wish to iterate.
+ * @return 0 on success, < 0 on error
+ */
+GIT_EXTERN(int) git_diff_iterator_new(
+ git_diff_iterator **iterator,
+ git_diff_list *diff);
+
+/**
+ * Release the iterator object.
+ *
+ * Call this when you are done using the iterator.
+ *
+ * @param iterator The diff iterator to be freed.
+ */
+GIT_EXTERN(void) git_diff_iterator_free(git_diff_iterator *iterator);
+
+/**
+ * Return the number of files in the diff.
+ *
+ * Note that there is an uncommon scenario where this number might be too
+ * high -- if a file in the working directory has been "touched" on disk but
+ * the contents were then reverted, it might have been added to the
+ * `git_diff_list` as a MODIFIED file along with a note that the status
+ * needs to be confirmed when the file contents are loaded into memory. In
+ * that case, when the file is loaded, we will check the contents and might
+ * switch it back to UNMODIFIED. The loading of the file is deferred until
+ * as late as possible. As a result, this might return a value what was too
+ * high in those circumstances.
+ *
+ * This is true of `git_diff_foreach` as well, but the only implication
+ * there is that the `progress` value would not advance evenly.
+ *
+ * @param iterator The iterator object
+ * @return The maximum number of files to be iterated over
+ */
+GIT_EXTERN(int) git_diff_iterator_num_files(git_diff_iterator *iterator);
+
+/**
+ * Return the number of hunks in the current file
+ *
+ * This will return the number of diff hunks in the current file. If the
+ * diff has not been performed yet, this may result in loading the file and
+ * performing the diff.
+ *
+ * @param iterator The iterator object
+ * @return The number of hunks in the current file or <0 on loading failure
+ */
+GIT_EXTERN(int) git_diff_iterator_num_hunks_in_file(git_diff_iterator *iterator);
+
+/**
+ * Return the number of lines in the hunk currently being examined.
+ *
+ * This will return the number of lines in the current hunk. If the diff
+ * has not been performed yet, this may result in loading the file and
+ * performing the diff.
+ *
+ * @param iterator The iterator object
+ * @return The number of lines in the current hunk (context, added, and
+ * removed all added together) or <0 on loading failure
+ */
+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. This
+ * returns the value `GIT_ITEROVER` after processing the last file.
+ *
+ * @param delta Output parameter for the next delta object
+ * @param iterator The iterator object
+ * @return 0 on success, GIT_ITEROVER when done, other value < 0 on error
+ */
+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).
+ *
+ * @param iterator The iterator object
+ * @return 0 on success, GIT_ITEROVER when done with current file, other
+ * value < 0 on error
+ */
+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.
+ *
+ * @param iterator The iterator object
+ * @return 0 on success, GIT_ITEROVER when done with current hunk, other
+ * value < 0 on error
+ */
+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 generating text output like "git diff --name-status".
*
* Returning a non-zero value from the callbacks will terminate the
diff --git a/include/git2/errors.h b/include/git2/errors.h
index b55f8c30..f6671c49 100644
--- a/include/git2/errors.h
+++ b/include/git2/errors.h
@@ -28,7 +28,7 @@ enum {
GIT_EUSER = -7,
GIT_PASSTHROUGH = -30,
- GIT_REVWALKOVER = -31,
+ GIT_ITEROVER = -31,
};
typedef struct {
diff --git a/include/git2/revwalk.h b/include/git2/revwalk.h
index d86bb28e..0a85a4c6 100644
--- a/include/git2/revwalk.h
+++ b/include/git2/revwalk.h
@@ -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 walk the walker to pop the commit from.
* @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);
diff --git a/src/attr.c b/src/attr.c
index 99322066..68f8d7de 100644
--- a/src/attr.c
+++ b/src/attr.c
@@ -188,6 +188,7 @@ int git_attr_foreach(
error = callback(assign->name, assign->value, payload);
if (error) {
+ giterr_clear();
error = GIT_EUSER;
goto cleanup;
}
diff --git a/src/config_file.c b/src/config_file.c
index d3fb56aa..c575649a 100644
--- a/src/config_file.c
+++ b/src/config_file.c
@@ -221,6 +221,7 @@ static int file_foreach(
/* abort iterator on non-zero return value */
if (fn(key, var->value, data)) {
+ giterr_clear();
result = GIT_EUSER;
goto cleanup;
}
diff --git a/src/diff.c b/src/diff.c
index 430f52e0..f8a01086 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -316,6 +316,7 @@ static git_diff_list *git_diff_list_alloc(
if (diff == NULL)
return NULL;
+ GIT_REFCOUNT_INC(diff);
diff->repo = repo;
if (git_vector_init(&diff->deltas, 0, diff_delta__cmp) < 0 ||
@@ -391,15 +392,12 @@ fail:
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_attr_fnmatch *match;
unsigned int i;
- if (!diff)
- return;
-
git_vector_foreach(&diff->deltas, i, delta) {
git__free(delta);
diff->deltas.contents[i] = NULL;
@@ -416,6 +414,14 @@ void git_diff_list_free(git_diff_list *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(
git_repository *repo,
const git_index_entry *item,
diff --git a/src/diff.h b/src/diff.h
index 6cc854fb..2785fa42 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -26,6 +26,7 @@ enum {
};
struct git_diff_list {
+ git_refcount rc;
git_repository *repo;
git_diff_options opts;
git_vector pathspec;
diff --git a/src/diff_output.c b/src/diff_output.c
index 1f2c7233..d715f9ef 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -16,16 +16,35 @@
#include "fileops.h"
#include "filter.h"
+/*
+ * A diff_delta_context represents all of the information that goes into
+ * processing the diff of an observed file change. In the case of the
+ * git_diff_foreach() call it is an emphemeral structure that is filled
+ * in to execute each diff. In the case of a git_diff_iterator, it holds
+ * most of the information for the diff in progress.
+ */
typedef struct {
- git_diff_list *diff;
- void *cb_data;
- git_diff_hunk_fn hunk_cb;
- git_diff_data_fn line_cb;
- unsigned int index;
+ git_repository *repo;
+ git_diff_options *opts;
+ xdemitconf_t xdiff_config;
+ xpparam_t xdiff_params;
git_diff_delta *delta;
+ uint32_t prepped : 1;
+ uint32_t loaded : 1;
+ uint32_t diffable : 1;
+ uint32_t diffed : 1;
+ git_iterator_type_t old_src;
+ git_iterator_type_t new_src;
+ git_blob *old_blob;
+ git_blob *new_blob;
+ git_map old_data;
+ git_map new_data;
+ void *cb_data;
+ git_diff_hunk_fn per_hunk;
+ git_diff_data_fn per_line;
+ int cb_error;
git_diff_range range;
- int error;
-} diff_output_info;
+} diff_delta_context;
static int read_next_int(const char **str, int *value)
{
@@ -41,71 +60,89 @@ static int read_next_int(const char **str, int *value)
return (digits > 0) ? 0 : -1;
}
-static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
+static int parse_hunk_header(git_diff_range *range, const char *header)
{
- diff_output_info *info = priv;
-
- if (len == 1 && info->hunk_cb) {
- git_diff_range range = { -1, 0, -1, 0 };
- const char *scan = bufs[0].ptr;
+ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
+ if (*header != '@')
+ return -1;
+ if (read_next_int(&header, &range->old_start) < 0)
+ return -1;
+ if (*header == ',') {
+ if (read_next_int(&header, &range->old_lines) < 0)
+ return -1;
+ } else
+ range->old_lines = 1;
+ if (read_next_int(&header, &range->new_start) < 0)
+ return -1;
+ if (*header == ',') {
+ if (read_next_int(&header, &range->new_lines) < 0)
+ return -1;
+ } else
+ range->new_lines = 1;
+ if (range->old_start < 0 || range->new_start < 0)
+ return -1;
- /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
- if (*scan != '@')
- info->error = -1;
- else if (read_next_int(&scan, &range.old_start) < 0)
- info->error = -1;
- else if (*scan == ',' && read_next_int(&scan, &range.old_lines) < 0)
- info->error = -1;
- else if (read_next_int(&scan, &range.new_start) < 0)
- info->error = -1;
- else if (*scan == ',' && read_next_int(&scan, &range.new_lines) < 0)
- info->error = -1;
- else if (range.old_start < 0 || range.new_start < 0)
- info->error = -1;
- else {
- memcpy(&info->range, &range, sizeof(git_diff_range));
+ return 0;
+}
- if (info->hunk_cb(
- info->cb_data, info->delta, &range, bufs[0].ptr, bufs[0].size))
- info->error = GIT_EUSER;
- }
+static int format_hunk_header(char *header, size_t len, git_diff_range *range)
+{
+ if (range->old_lines != 1) {
+ if (range->new_lines != 1)
+ return snprintf(
+ header, len, "@@ -%d,%d +%d,%d @@",
+ range->old_start, range->old_lines,
+ range->new_start, range->new_lines);
+ else
+ return snprintf(
+ header, len, "@@ -%d,%d +%d @@",
+ range->old_start, range->old_lines, range->new_start);
+ } else {
+ if (range->new_lines != 1)
+ return snprintf(
+ header, len, "@@ -%d +%d,%d @@",
+ range->old_start, range->new_start, range->new_lines);
+ else
+ return snprintf(
+ header, len, "@@ -%d +%d @@",
+ range->old_start, range->new_start);
}
+}
- if ((len == 2 || len == 3) && info->line_cb) {
- int origin;
-
- /* expect " "/"-"/"+", then data, then maybe newline */
- origin =
- (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
- (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
- GIT_DIFF_LINE_CONTEXT;
+static bool diff_delta_is_ambiguous(git_diff_delta *delta)
+{
+ return (git_oid_iszero(&delta->new_file.oid) &&
+ (delta->new_file.flags & GIT_DIFF_FILE_VALID_OID) == 0 &&
+ delta->status == GIT_DELTA_MODIFIED);
+}
- if (info->line_cb(
- info->cb_data, info->delta, &info->range, origin, bufs[1].ptr, bufs[1].size))
- info->error = GIT_EUSER;
+static bool diff_delta_should_skip(git_diff_options *opts, git_diff_delta *delta)
+{
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ (opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return true;
- /* This should only happen if we are adding a line that does not
- * have a newline at the end and the old code did. In that case,
- * we have a ADD with a DEL_EOFNL as a pair.
- */
- else if (len == 3) {
- origin = (origin == GIT_DIFF_LINE_ADDITION) ?
- GIT_DIFF_LINE_DEL_EOFNL : GIT_DIFF_LINE_ADD_EOFNL;
+ if (delta->status == GIT_DELTA_IGNORED &&
+ (opts->flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return true;
- if (info->line_cb(
- info->cb_data, info->delta, &info->range, origin, bufs[2].ptr, bufs[2].size))
- info->error = GIT_EUSER;
- }
- }
+ if (delta->status == GIT_DELTA_UNTRACKED &&
+ (opts->flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return true;
- return info->error;
+ return false;
}
#define BINARY_DIFF_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
-static int update_file_is_binary_by_attr(git_repository *repo, git_diff_file *file)
+static int update_file_is_binary_by_attr(
+ git_repository *repo, git_diff_file *file)
{
const char *value;
+
+ if (!repo)
+ return 0;
+
if (git_attr_get(&value, repo, 0, file->path, "diff") < 0)
return -1;
@@ -129,11 +166,10 @@ static void update_delta_is_binary(git_diff_delta *delta)
/* otherwise leave delta->binary value untouched */
}
-static int file_is_binary_by_attr(
- git_diff_list *diff,
- git_diff_delta *delta)
+static int diff_delta_is_binary_by_attr(diff_delta_context *ctxt)
{
int error = 0, mirror_new;
+ git_diff_delta *delta = ctxt->delta;
delta->binary = -1;
@@ -148,7 +184,7 @@ static int file_is_binary_by_attr(
}
/* check if user is forcing us to text diff these files */
- if (diff->opts.flags & GIT_DIFF_FORCE_TEXT) {
+ if (ctxt->opts->flags & GIT_DIFF_FORCE_TEXT) {
delta->old_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
delta->new_file.flags |= GIT_DIFF_FILE_NOT_BINARY;
delta->binary = 0;
@@ -156,7 +192,7 @@ static int file_is_binary_by_attr(
}
/* check diff attribute +, -, or 0 */
- if (update_file_is_binary_by_attr(diff->repo, &delta->old_file) < 0)
+ if (update_file_is_binary_by_attr(ctxt->repo, &delta->old_file) < 0)
return -1;
mirror_new = (delta->new_file.path == delta->old_file.path ||
@@ -164,23 +200,21 @@ static int file_is_binary_by_attr(
if (mirror_new)
delta->new_file.flags |= (delta->old_file.flags & BINARY_DIFF_FLAGS);
else
- error = update_file_is_binary_by_attr(diff->repo, &delta->new_file);
+ error = update_file_is_binary_by_attr(ctxt->repo, &delta->new_file);
update_delta_is_binary(delta);
return error;
}
-static int file_is_binary_by_content(
- git_diff_delta *delta,
- git_map *old_data,
- git_map *new_data)
+static int diff_delta_is_binary_by_content(diff_delta_context *ctxt)
{
+ git_diff_delta *delta = ctxt->delta;
git_buf search;
if ((delta->old_file.flags & BINARY_DIFF_FLAGS) == 0) {
- search.ptr = old_data->data;
- search.size = min(old_data->len, 4000);
+ search.ptr = ctxt->old_data.data;
+ search.size = min(ctxt->old_data.len, 4000);
if (git_buf_is_binary(&search))
delta->old_file.flags |= GIT_DIFF_FILE_BINARY;
@@ -189,8 +223,8 @@ static int file_is_binary_by_content(
}
if ((delta->new_file.flags & BINARY_DIFF_FLAGS) == 0) {
- search.ptr = new_data->data;
- search.size = min(new_data->len, 4000);
+ search.ptr = ctxt->new_data.data;
+ search.size = min(ctxt->new_data.len, 4000);
if (git_buf_is_binary(&search))
delta->new_file.flags |= GIT_DIFF_FILE_BINARY;
@@ -256,16 +290,21 @@ static int get_workdir_content(
return -1;
if (S_ISLNK(file->mode)) {
- ssize_t read_len;
+ ssize_t alloc_len, read_len;
file->flags |= GIT_DIFF_FILE_FREE_DATA;
file->flags |= GIT_DIFF_FILE_BINARY;
- map->data = git__malloc((size_t)file->size + 1);
+ /* link path on disk could be UTF-16, so prepare a buffer that is
+ * big enough to handle some UTF-8 data expansion
+ */
+ alloc_len = (ssize_t)(file->size * 2) + 1;
+
+ map->data = git__malloc(alloc_len);
GITERR_CHECK_ALLOC(map->data);
- read_len = p_readlink(path.ptr, map->data, (size_t)file->size + 1);
- if (read_len != (ssize_t)file->size) {
+ read_len = p_readlink(path.ptr, map->data, (int)alloc_len);
+ if (read_len < 0) {
giterr_set(GITERR_OS, "Failed to read symlink '%s'", file->path);
error = -1;
} else
@@ -286,189 +325,304 @@ static void release_content(git_diff_file *file, git_map *map, git_blob *blob)
if (file->flags & GIT_DIFF_FILE_FREE_DATA) {
git__free(map->data);
- map->data = NULL;
+ map->data = "";
+ map->len = 0;
file->flags &= ~GIT_DIFF_FILE_FREE_DATA;
}
else if (file->flags & GIT_DIFF_FILE_UNMAP_DATA) {
git_futils_mmap_free(map);
- map->data = NULL;
+ map->data = "";
+ map->len = 0;
file->flags &= ~GIT_DIFF_FILE_UNMAP_DATA;
}
}
-static void fill_map_from_mmfile(git_map *dst, mmfile_t *src) {
- assert(dst && src);
+static void diff_delta_init_context(
+ diff_delta_context *ctxt,
+ git_repository *repo,
+ git_diff_options *opts,
+ git_iterator_type_t old_src,
+ git_iterator_type_t new_src)
+{
+ memset(ctxt, 0, sizeof(diff_delta_context));
+
+ ctxt->repo = repo;
+ ctxt->opts = opts;
+ ctxt->old_src = old_src;
+ ctxt->new_src = new_src;
- dst->data = src->ptr;
- dst->len = src->size;
-#ifdef GIT_WIN32
- dst->fmh = NULL;
-#endif
+ setup_xdiff_options(opts, &ctxt->xdiff_config, &ctxt->xdiff_params);
}
-int git_diff_foreach(
- git_diff_list *diff,
- void *data,
- git_diff_file_fn file_cb,
- git_diff_hunk_fn hunk_cb,
- git_diff_data_fn line_cb)
+static void diff_delta_init_context_from_diff_list(
+ diff_delta_context *ctxt,
+ git_diff_list *diff)
{
- int error = 0;
- diff_output_info info;
- git_diff_delta *delta;
- xpparam_t xdiff_params;
- xdemitconf_t xdiff_config;
- xdemitcb_t xdiff_callback;
+ diff_delta_init_context(
+ ctxt, diff->repo, &diff->opts, diff->old_src, diff->new_src);
+}
- memset(&info, 0, sizeof(info));
- info.diff = diff;
- info.cb_data = data;
- info.hunk_cb = hunk_cb;
- info.line_cb = line_cb;
+static void diff_delta_unload(diff_delta_context *ctxt)
+{
+ ctxt->diffed = 0;
- setup_xdiff_options(&diff->opts, &xdiff_config, &xdiff_params);
- memset(&xdiff_callback, 0, sizeof(xdiff_callback));
- xdiff_callback.outf = diff_output_cb;
- xdiff_callback.priv = &info;
+ if (ctxt->loaded) {
+ release_content(&ctxt->delta->old_file, &ctxt->old_data, ctxt->old_blob);
+ release_content(&ctxt->delta->new_file, &ctxt->new_data, ctxt->new_blob);
+ ctxt->loaded = 0;
+ }
- git_vector_foreach(&diff->deltas, info.index, delta) {
- git_blob *old_blob = NULL, *new_blob = NULL;
- git_map old_data, new_data;
- mmfile_t old_xdiff_data, new_xdiff_data;
+ ctxt->delta = NULL;
+ ctxt->prepped = 0;
+}
- if (delta->status == GIT_DELTA_UNMODIFIED &&
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
- continue;
+static int diff_delta_prep(diff_delta_context *ctxt)
+{
+ int error;
- if (delta->status == GIT_DELTA_IGNORED &&
- (diff->opts.flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
- continue;
+ if (ctxt->prepped || !ctxt->delta)
+ return 0;
- if (delta->status == GIT_DELTA_UNTRACKED &&
- (diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
- continue;
+ error = diff_delta_is_binary_by_attr(ctxt);
- if ((error = file_is_binary_by_attr(diff, delta)) < 0)
- goto cleanup;
+ ctxt->prepped = !error;
- old_data.data = "";
- old_data.len = 0;
- new_data.data = "";
- new_data.len = 0;
+ return error;
+}
- /* TODO: Partial blob reading to defer loading whole blob.
- * I.e. I want a blob with just the first 4kb loaded, then
- * later on I will read the rest of the blob if needed.
- */
+static int diff_delta_load(diff_delta_context *ctxt)
+{
+ int error = 0;
+ git_diff_delta *delta = ctxt->delta;
- /* map files */
- if (delta->binary != 1 &&
- (hunk_cb || line_cb || git_oid_iszero(&delta->old_file.oid)) &&
- (delta->status == GIT_DELTA_DELETED ||
- delta->status == GIT_DELTA_MODIFIED))
- {
- if (diff->old_src == GIT_ITERATOR_WORKDIR)
- error = get_workdir_content(diff->repo, &delta->old_file, &old_data);
- else
- error = get_blob_content(
- diff->repo, &delta->old_file.oid, &old_data, &old_blob);
+ if (ctxt->loaded || !ctxt->delta)
+ return 0;
- if (error < 0)
- goto cleanup;
+ if (!ctxt->prepped && (error = diff_delta_prep(ctxt)) < 0)
+ goto cleanup;
+
+ ctxt->old_data.data = "";
+ ctxt->old_data.len = 0;
+ ctxt->old_blob = NULL;
+
+ if (!error && delta->binary != 1 &&
+ (delta->status == GIT_DELTA_DELETED ||
+ delta->status == GIT_DELTA_MODIFIED))
+ {
+ if (ctxt->old_src == GIT_ITERATOR_WORKDIR)
+ error = get_workdir_content(
+ ctxt->repo, &delta->old_file, &ctxt->old_data);
+ else {
+ error = get_blob_content(
+ ctxt->repo, &delta->old_file.oid,
+ &ctxt->old_data, &ctxt->old_blob);
+
+ if (ctxt->new_src == GIT_ITERATOR_WORKDIR) {
+ /* TODO: convert crlf of blob content */
+ }
}
+ }
- if (delta->binary != 1 &&
- (hunk_cb || line_cb || git_oid_iszero(&delta->new_file.oid)) &&
- (delta->status == GIT_DELTA_ADDED ||
- delta->status == GIT_DELTA_MODIFIED))
- {
- if (diff->new_src == GIT_ITERATOR_WORKDIR)
- error = get_workdir_content(diff->repo, &delta->new_file, &new_data);
- else
- error = get_blob_content(
- diff->repo, &delta->new_file.oid, &new_data, &new_blob);
+ ctxt->new_data.data = "";
+ ctxt->new_data.len = 0;
+ ctxt->new_blob = NULL;
+
+ if (!error && delta->binary != 1 &&
+ (delta->status == GIT_DELTA_ADDED ||
+ delta->status == GIT_DELTA_MODIFIED))
+ {
+ if (ctxt->new_src == GIT_ITERATOR_WORKDIR)
+ error = get_workdir_content(
+ ctxt->repo, &delta->new_file, &ctxt->new_data);
+ else {
+ error = get_blob_content(
+ ctxt->repo, &delta->new_file.oid,
+ &ctxt->new_data, &ctxt->new_blob);
+ if (ctxt->old_src == GIT_ITERATOR_WORKDIR) {
+ /* TODO: convert crlf of blob content */
+ }
+ }
+
+ if (!error && !(delta->new_file.flags & GIT_DIFF_FILE_VALID_OID)) {
+ error = git_odb_hash(
+ &delta->new_file.oid, ctxt->new_data.data,
+ ctxt->new_data.len, GIT_OBJ_BLOB);
if (error < 0)
goto cleanup;
- if ((delta->new_file.flags & GIT_DIFF_FILE_VALID_OID) == 0) {
- error = git_odb_hash(
- &delta->new_file.oid, new_data.data, new_data.len, GIT_OBJ_BLOB);
+ delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
- if (error < 0)
+ /* since we did not have the definitive oid, we may have
+ * incorrect status and need to skip this item.
+ */
+ if (delta->old_file.mode == delta->new_file.mode &&
+ !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
+ {
+ delta->status = GIT_DELTA_UNMODIFIED;
+
+ if ((ctxt->opts->flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
goto cleanup;
- delta->new_file.flags |= GIT_DIFF_FILE_VALID_OID;
-
- /* since we did not have the definitive oid, we may have
- * incorrect status and need to skip this item.
- */
- if (delta->old_file.mode == delta->new_file.mode &&
- !git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
- {
- delta->status = GIT_DELTA_UNMODIFIED;
- if ((diff->opts.flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
- goto cleanup;
- }
}
}
+ }
+
+ /* if we have not already decided whether file is binary,
+ * check the first 4K for nul bytes to decide...
+ */
+ if (!error && delta->binary == -1)
+ error = diff_delta_is_binary_by_content(ctxt);
+
+cleanup:
+ ctxt->loaded = !error;
+
+ /* flag if we would want to diff the contents of these files */
+ if (ctxt->loaded)
+ ctxt->diffable =
+ (delta->binary != 1 &&
+ delta->status != GIT_DELTA_UNMODIFIED &&
+ (ctxt->old_data.len || ctxt->new_data.len) &&
+ git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid));
+
+ return error;
+}
+
+static int diff_delta_cb(void *priv, mmbuffer_t *bufs, int len)
+{
+ diff_delta_context *ctxt = priv;
+
+ if (len == 1) {
+ if ((ctxt->cb_error = parse_hunk_header(&ctxt->range, bufs[0].ptr)) < 0)
+ return ctxt->cb_error;
+
+ if (ctxt->per_hunk != NULL &&
+ ctxt->per_hunk(ctxt->cb_data, ctxt->delta, &ctxt->range,
+ bufs[0].ptr, bufs[0].size))
+ ctxt->cb_error = GIT_EUSER;
+ }
- /* if we have not already decided whether file is binary,
- * check the first 4K for nul bytes to decide...
+ if (len == 2 || len == 3) {
+ /* expect " "/"-"/"+", then data */
+ char origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
+ GIT_DIFF_LINE_CONTEXT;
+
+ if (ctxt->per_line != NULL &&
+ ctxt->per_line(ctxt->cb_data, ctxt->delta, &ctxt->range, origin,
+ bufs[1].ptr, bufs[1].size))
+ ctxt->cb_error = GIT_EUSER;
+ }
+
+ if (len == 3 && !ctxt->cb_error) {
+ /* This should only happen if we are adding a line that does not
+ * have a newline at the end and the old code did. In that case,
+ * we have a ADD with a DEL_EOFNL as a pair.
*/
- if (delta->binary == -1) {
- error = file_is_binary_by_content(
- delta, &old_data, &new_data);
- if (error < 0)
+ char origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_DEL_EOFNL :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_ADD_EOFNL :
+ GIT_DIFF_LINE_CONTEXT;
+
+ if (ctxt->per_line != NULL &&
+ ctxt->per_line(ctxt->cb_data, ctxt->delta, &ctxt->range, origin,
+ bufs[2].ptr, bufs[2].size))
+ ctxt->cb_error = GIT_EUSER;
+ }
+
+ return ctxt->cb_error;
+}
+
+static int diff_delta_exec(
+ diff_delta_context *ctxt,
+ void *cb_data,
+ git_diff_hunk_fn per_hunk,
+ git_diff_data_fn per_line)
+{
+ int error = 0;
+ xdemitcb_t xdiff_callback;
+ mmfile_t old_xdiff_data, new_xdiff_data;
+
+ if (ctxt->diffed || !ctxt->delta)
+ return 0;
+
+ if (!ctxt->loaded && (error = diff_delta_load(ctxt)) < 0)
+ goto cleanup;
+
+ if (!ctxt->diffable)
+ return 0;
+
+ ctxt->cb_data = cb_data;
+ ctxt->per_hunk = per_hunk;
+ ctxt->per_line = per_line;
+ ctxt->cb_error = 0;
+
+ memset(&xdiff_callback, 0, sizeof(xdiff_callback));
+ xdiff_callback.outf = diff_delta_cb;
+ xdiff_callback.priv = ctxt;
+
+ old_xdiff_data.ptr = ctxt->old_data.data;
+ old_xdiff_data.size = ctxt->old_data.len;
+ new_xdiff_data.ptr = ctxt->new_data.data;
+ new_xdiff_data.size = ctxt->new_data.len;
+
+ xdl_diff(&old_xdiff_data, &new_xdiff_data,
+ &ctxt->xdiff_params, &ctxt->xdiff_config, &xdiff_callback);
+
+ error = ctxt->cb_error;
+
+cleanup:
+ ctxt->diffed = !error;
+
+ return error;
+}
+
+int git_diff_foreach(
+ 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 = 0;
+ diff_delta_context ctxt;
+ size_t idx;
+
+ diff_delta_init_context_from_diff_list(&ctxt, diff);
+
+ git_vector_foreach(&diff->deltas, idx, ctxt.delta) {
+ if (diff_delta_is_ambiguous(ctxt.delta))
+ if ((error = diff_delta_load(&ctxt)) < 0)
goto cleanup;
- }
- /* TODO: if ignore_whitespace is set, then we *must* do text
- * diffs to tell if a file has really been changed.
- */
+ if (diff_delta_should_skip(ctxt.opts, ctxt.delta))
+ continue;
+
+ if ((error = diff_delta_load(&ctxt)) < 0)
+ goto cleanup;
if (file_cb != NULL &&
- file_cb(data, delta, (float)info.index / diff->deltas.length))
+ file_cb(data, ctxt.delta, (float)idx / diff->deltas.length) != 0)
{
error = GIT_EUSER;
goto cleanup;
}
- /* don't do hunk and line diffs if file is binary */
- if (delta->binary == 1)
- goto cleanup;
-
- /* nothing to do if we did not get data */
- if (!old_data.len && !new_data.len)
- goto cleanup;
-
- /* nothing to do if only diff was a mode change */
- if (!git_oid_cmp(&delta->old_file.oid, &delta->new_file.oid))
- goto cleanup;
-
- assert(hunk_cb || line_cb);
-
- info.delta = delta;
- old_xdiff_data.ptr = old_data.data;
- old_xdiff_data.size = old_data.len;
- new_xdiff_data.ptr = new_data.data;
- new_xdiff_data.size = new_data.len;
-
- xdl_diff(&old_xdiff_data, &new_xdiff_data,
- &xdiff_params, &xdiff_config, &xdiff_callback);
- error = info.error;
+ error = diff_delta_exec(&ctxt, data, hunk_cb, line_cb);
cleanup:
- release_content(&delta->old_file, &old_data, old_blob);
- release_content(&delta->new_file, &new_data, new_blob);
+ diff_delta_unload(&ctxt);
if (error < 0)
break;
}
+ if (error == GIT_EUSER)
+ giterr_clear();
+
return error;
}
-
typedef struct {
git_diff_list *diff;
git_diff_data_fn print_cb;
@@ -531,7 +685,10 @@ static int print_compact(void *data, git_diff_delta *delta, float progress)
if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
+ {
+ giterr_clear();
return GIT_EUSER;
+ }
return 0;
}
@@ -628,7 +785,10 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
return -1;
if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_FILE_HDR, git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
+ {
+ giterr_clear();
return GIT_EUSER;
+ }
if (delta->binary != 1)
return 0;
@@ -642,7 +802,10 @@ static int print_patch_file(void *data, git_diff_delta *delta, float progress)
if (pi->print_cb(pi->cb_data, delta, NULL, GIT_DIFF_LINE_BINARY,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
+ {
+ giterr_clear();
return GIT_EUSER;
+ }
return 0;
}
@@ -662,7 +825,10 @@ static int print_patch_hunk(
if (pi->print_cb(pi->cb_data, d, r, GIT_DIFF_LINE_HUNK_HDR,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
+ {
+ giterr_clear();
return GIT_EUSER;
+ }
return 0;
}
@@ -691,7 +857,10 @@ static int print_patch_line(
if (pi->print_cb(pi->cb_data, delta, range, line_origin,
git_buf_cstr(pi->buf), git_buf_len(pi->buf)))
+ {
+ giterr_clear();
return GIT_EUSER;
+ }
return 0;
}
@@ -726,17 +895,36 @@ int git_diff_entrycount(git_diff_list *diff, int delta_t)
assert(diff);
- if (delta_t < 0)
- return (int)diff->deltas.length;
-
git_vector_foreach(&diff->deltas, i, delta) {
- if (delta->status == (git_delta_t)delta_t)
+ if (diff_delta_should_skip(&diff->opts, delta))
+ continue;
+
+ if (delta_t < 0 || delta->status == (git_delta_t)delta_t)
count++;
}
+ /* It is possible that this has overcounted the number of diffs because
+ * there may be entries that are marked as MODIFIED due to differences
+ * in stat() output that will turn out to be the same once we calculate
+ * the actual SHA of the data on disk.
+ */
+
return count;
}
+static void set_data_from_blob(
+ git_blob *blob, git_map *map, git_diff_file *file)
+{
+ if (blob) {
+ map->data = (char *)git_blob_rawcontent(blob);
+ file->size = map->len = git_blob_rawsize(blob);
+ git_oid_cpy(&file->oid, git_object_id((const git_object *)blob));
+ } else {
+ map->data = "";
+ file->size = map->len = 0;
+ }
+}
+
int git_diff_blobs(
git_blob *old_blob,
git_blob *new_blob,
@@ -746,17 +934,11 @@ int git_diff_blobs(
git_diff_hunk_fn hunk_cb,
git_diff_data_fn line_cb)
{
- diff_output_info info;
+ int error;
+ diff_delta_context ctxt;
git_diff_delta delta;
- mmfile_t old_data, new_data;
- git_map old_map, new_map;
- xpparam_t xdiff_params;
- xdemitconf_t xdiff_config;
- xdemitcb_t xdiff_callback;
git_blob *new, *old;
- memset(&delta, 0, sizeof(delta));
-
new = new_blob;
old = old_blob;
@@ -766,25 +948,16 @@ int git_diff_blobs(
new = swap;
}
- if (old) {
- old_data.ptr = (char *)git_blob_rawcontent(old);
- old_data.size = git_blob_rawsize(old);
- git_oid_cpy(&delta.old_file.oid, git_object_id((const git_object *)old));
- } else {
- old_data.ptr = "";
- old_data.size = 0;
- }
-
- if (new) {
- new_data.ptr = (char *)git_blob_rawcontent(new);
- new_data.size = git_blob_rawsize(new);
- git_oid_cpy(&delta.new_file.oid, git_object_id((const git_object *)new));
- } else {
- new_data.ptr = "";
- new_data.size = 0;
- }
+ diff_delta_init_context(
+ &ctxt, NULL, options, GIT_ITERATOR_TREE, GIT_ITERATOR_TREE);
/* populate a "fake" delta record */
+
+ memset(&delta, 0, sizeof(delta));
+
+ set_data_from_blob(old, &ctxt.old_data, &delta.old_file);
+ set_data_from_blob(new, &ctxt.new_data, &delta.new_file);
+
delta.status = new ?
(old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
(old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
@@ -792,39 +965,369 @@ int git_diff_blobs(
if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0)
delta.status = GIT_DELTA_UNMODIFIED;
- delta.old_file.size = old_data.size;
- delta.new_file.size = new_data.size;
+ ctxt.delta = &delta;
+
+ if ((error = diff_delta_prep(&ctxt)) < 0)
+ goto cleanup;
+
+ if (delta.binary == -1 &&
+ (error = diff_delta_is_binary_by_content(&ctxt)) < 0)
+ goto cleanup;
+
+ ctxt.loaded = 1;
+ ctxt.diffable = (delta.binary != 1 && delta.status != GIT_DELTA_UNMODIFIED);
+
+ /* do diffs */
+
+ if (file_cb != NULL && file_cb(cb_data, &delta, 1)) {
+ error = GIT_EUSER;
+ goto cleanup;
+ }
+
+ error = diff_delta_exec(&ctxt, cb_data, hunk_cb, line_cb);
- fill_map_from_mmfile(&old_map, &old_data);
- fill_map_from_mmfile(&new_map, &new_data);
+cleanup:
+ if (error == GIT_EUSER)
+ giterr_clear();
+
+ diff_delta_unload(&ctxt);
+
+ return error;
+}
+
+typedef struct diffiter_line diffiter_line;
+struct diffiter_line {
+ diffiter_line *next;
+ char origin;
+ const char *ptr;
+ size_t len;
+};
+
+typedef struct diffiter_hunk diffiter_hunk;
+struct diffiter_hunk {
+ diffiter_hunk *next;
+ git_diff_range range;
+ diffiter_line *line_head;
+ size_t line_count;
+};
+
+struct git_diff_iterator {
+ git_diff_list *diff;
+ diff_delta_context ctxt;
+ size_t file_index;
+ size_t next_index;
+ size_t file_count;
+ git_pool hunks;
+ size_t hunk_count;
+ diffiter_hunk *hunk_head;
+ diffiter_hunk *hunk_curr;
+ char hunk_header[128];
+ git_pool lines;
+ diffiter_line *line_curr;
+};
+
+typedef struct {
+ git_diff_iterator *iter;
+ diffiter_hunk *last_hunk;
+ diffiter_line *last_line;
+} diffiter_cb_info;
- if (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)
+static int diffiter_hunk_cb(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ const char *header,
+ size_t header_len)
+{
+ diffiter_cb_info *info = cb_data;
+ git_diff_iterator *iter = info->iter;
+ diffiter_hunk *hunk;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(header);
+ GIT_UNUSED(header_len);
+
+ if ((hunk = git_pool_mallocz(&iter->hunks, 1)) == NULL) {
+ iter->ctxt.cb_error = -1;
return -1;
+ }
- if (file_cb != NULL && file_cb(cb_data, &delta, 1))
- return GIT_EUSER;
+ if (info->last_hunk)
+ info->last_hunk->next = hunk;
+ info->last_hunk = hunk;
- /* don't do hunk and line diffs if the two blobs are identical */
- if (delta.status == GIT_DELTA_UNMODIFIED)
- return 0;
+ memcpy(&hunk->range, range, sizeof(hunk->range));
- /* don't do hunk and line diffs if file is binary */
- if (delta.binary == 1)
+ iter->hunk_count++;
+
+ if (iter->hunk_head == NULL)
+ iter->hunk_curr = iter->hunk_head = hunk;
+
+ return 0;
+}
+
+static int diffiter_line_cb(
+ void *cb_data,
+ git_diff_delta *delta,
+ git_diff_range *range,
+ char line_origin,
+ const char *content,
+ size_t content_len)
+{
+ diffiter_cb_info *info = cb_data;
+ git_diff_iterator *iter = info->iter;
+ diffiter_line *line;
+
+ GIT_UNUSED(delta);
+ GIT_UNUSED(range);
+
+ if ((line = git_pool_mallocz(&iter->lines, 1)) == NULL) {
+ iter->ctxt.cb_error = -1;
+ return -1;
+ }
+
+ if (info->last_line)
+ info->last_line->next = line;
+ info->last_line = line;
+
+ line->origin = line_origin;
+ line->ptr = content;
+ line->len = content_len;
+
+ info->last_hunk->line_count++;
+
+ if (info->last_hunk->line_head == NULL)
+ info->last_hunk->line_head = line;
+
+ return 0;
+}
+
+static int diffiter_do_diff_file(git_diff_iterator *iter)
+{
+ int error;
+ diffiter_cb_info info;
+
+ if (iter->ctxt.diffed || !iter->ctxt.delta)
return 0;
memset(&info, 0, sizeof(info));
- info.diff = NULL;
- info.delta = &delta;
- info.cb_data = cb_data;
- info.hunk_cb = hunk_cb;
- info.line_cb = line_cb;
+ info.iter = iter;
- setup_xdiff_options(options, &xdiff_config, &xdiff_params);
- memset(&xdiff_callback, 0, sizeof(xdiff_callback));
- xdiff_callback.outf = diff_output_cb;
- xdiff_callback.priv = &info;
+ error = diff_delta_exec(
+ &iter->ctxt, &info, diffiter_hunk_cb, diffiter_line_cb);
- xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
+ if (error == GIT_EUSER)
+ error = iter->ctxt.cb_error;
+
+ return error;
+}
+
+static void diffiter_do_unload_file(git_diff_iterator *iter)
+{
+ if (iter->ctxt.loaded) {
+ diff_delta_unload(&iter->ctxt);
- return info.error;
+ git_pool_clear(&iter->lines);
+ git_pool_clear(&iter->hunks);
+ }
+
+ iter->ctxt.delta = NULL;
+ iter->hunk_head = NULL;
+ iter->hunk_count = 0;
+}
+
+int git_diff_iterator_new(
+ git_diff_iterator **iterator_ptr,
+ git_diff_list *diff)
+{
+ size_t i;
+ git_diff_delta *delta;
+ git_diff_iterator *iter;
+
+ assert(diff && iterator_ptr);
+
+ *iterator_ptr = NULL;
+
+ iter = git__malloc(sizeof(git_diff_iterator));
+ GITERR_CHECK_ALLOC(iter);
+
+ memset(iter, 0, sizeof(*iter));
+
+ iter->diff = diff;
+ GIT_REFCOUNT_INC(iter->diff);
+
+ diff_delta_init_context_from_diff_list(&iter->ctxt, diff);
+
+ if (git_pool_init(&iter->hunks, sizeof(diffiter_hunk), 0) < 0 ||
+ git_pool_init(&iter->lines, sizeof(diffiter_line), 0) < 0)
+ goto fail;
+
+ git_vector_foreach(&diff->deltas, i, delta) {
+ if (diff_delta_should_skip(iter->ctxt.opts, delta))
+ continue;
+ iter->file_count++;
+ }
+
+ *iterator_ptr = iter;
+
+ return 0;
+
+fail:
+ git_diff_iterator_free(iter);
+
+ return -1;
+}
+
+void git_diff_iterator_free(git_diff_iterator *iter)
+{
+ diffiter_do_unload_file(iter);
+ git_diff_list_free(iter->diff); /* decrement ref count */
+ git__free(iter);
+}
+
+int git_diff_iterator_num_files(git_diff_iterator *iter)
+{
+ return (int)iter->file_count;
+}
+
+int git_diff_iterator_num_hunks_in_file(git_diff_iterator *iter)
+{
+ int error = diffiter_do_diff_file(iter);
+ return (error != 0) ? error : (int)iter->hunk_count;
+}
+
+int git_diff_iterator_num_lines_in_hunk(git_diff_iterator *iter)
+{
+ int error = diffiter_do_diff_file(iter);
+ if (!error && iter->hunk_curr)
+ error = iter->hunk_curr->line_count;
+ return error;
+}
+
+int git_diff_iterator_next_file(
+ git_diff_delta **delta_ptr,
+ git_diff_iterator *iter)
+{
+ int error = 0;
+
+ assert(iter);
+
+ iter->file_index = iter->next_index;
+
+ diffiter_do_unload_file(iter);
+
+ while (!error) {
+ iter->ctxt.delta = git_vector_get(&iter->diff->deltas, iter->file_index);
+ if (!iter->ctxt.delta) {
+ error = GIT_ITEROVER;
+ break;
+ }
+
+ if (diff_delta_is_ambiguous(iter->ctxt.delta) &&
+ (error = diff_delta_load(&iter->ctxt)) < 0)
+ break;
+
+ if (!diff_delta_should_skip(iter->ctxt.opts, iter->ctxt.delta))
+ break;
+
+ iter->file_index++;
+ }
+
+ if (!error) {
+ iter->next_index = iter->file_index + 1;
+
+ error = diff_delta_prep(&iter->ctxt);
+ }
+
+ if (iter->ctxt.delta == NULL) {
+ iter->hunk_curr = NULL;
+ iter->line_curr = NULL;
+ }
+
+ if (delta_ptr != NULL)
+ *delta_ptr = !error ? iter->ctxt.delta : NULL;
+
+ return error;
+}
+
+int git_diff_iterator_next_hunk(
+ git_diff_range **range_ptr,
+ const char **header,
+ size_t *header_len,
+ git_diff_iterator *iter)
+{
+ int error = diffiter_do_diff_file(iter);
+ git_diff_range *range;
+
+ if (error)
+ return error;
+
+ if (iter->hunk_curr == NULL) {
+ if (range_ptr) *range_ptr = NULL;
+ if (header) *header = NULL;
+ if (header_len) *header_len = 0;
+ iter->line_curr = NULL;
+ return GIT_ITEROVER;
+ }
+
+ range = &iter->hunk_curr->range;
+
+ if (range_ptr)
+ *range_ptr = range;
+
+ if (header) {
+ int out = format_hunk_header(
+ iter->hunk_header, sizeof(iter->hunk_header), range);
+
+ /* TODO: append function name to header */
+
+ *(iter->hunk_header + out++) = '\n';
+
+ *header = iter->hunk_header;
+
+ if (header_len)
+ *header_len = (size_t)out;
+ }
+
+ iter->line_curr = iter->hunk_curr->line_head;
+ iter->hunk_curr = iter->hunk_curr->next;
+
+ return error;
+}
+
+int git_diff_iterator_next_line(
+ char *line_origin, /**< GIT_DIFF_LINE_... value from above */
+ const char **content_ptr,
+ size_t *content_len,
+ git_diff_iterator *iter)
+{
+ int error = diffiter_do_diff_file(iter);
+
+ if (error)
+ return error;
+
+ /* if the user has not called next_hunk yet, call it implicitly (OK?) */
+ if (iter->hunk_curr == iter->hunk_head) {
+ error = git_diff_iterator_next_hunk(NULL, NULL, NULL, iter);
+ if (error)
+ return error;
+ }
+
+ if (iter->line_curr == NULL) {
+ if (line_origin) *line_origin = GIT_DIFF_LINE_CONTEXT;
+ if (content_ptr) *content_ptr = NULL;
+ if (content_len) *content_len = 0;
+ return GIT_ITEROVER;
+ }
+
+ if (line_origin)
+ *line_origin = iter->line_curr->origin;
+ if (content_ptr)
+ *content_ptr = iter->line_curr->ptr;
+ if (content_len)
+ *content_len = iter->line_curr->len;
+
+ iter->line_curr = iter->line_curr->next;
+
+ return error;
}
diff --git a/src/fetch.c b/src/fetch.c
index 278ba3c5..98e1f0b1 100644
--- a/src/fetch.c
+++ b/src/fetch.c
@@ -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;
/* Tell the other end that we're done negotiating */
diff --git a/src/pool.h b/src/pool.h
index 05d33924..dee6ecda 100644
--- a/src/pool.h
+++ b/src/pool.h
@@ -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);
/**
+ * 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.
*
* This is allowed only for pools with item_size == sizeof(char)
diff --git a/src/refs.c b/src/refs.c
index 1589bc37..211a5870 100644
--- a/src/refs.c
+++ b/src/refs.c
@@ -1643,7 +1643,6 @@ int git_reference_normalize_name(
}
}
-
*buffer_out++ = *current++;
buffer_size--;
}
diff --git a/src/revparse.c b/src/revparse.c
index 3855c29f..17266b94 100644
--- a/src/revparse.c
+++ b/src/revparse.c
@@ -493,7 +493,7 @@ static int walk_and_search(git_object **out, git_revwalk *walk, regex_t *regex)
git_object_free(obj);
}
- if (error < 0 && error == GIT_REVWALKOVER)
+ if (error < 0 && error == GIT_ITEROVER)
error = GIT_ENOTFOUND;
return error;
diff --git a/src/revwalk.c b/src/revwalk.c
index 8b0e93ba..1a092771 100644
--- a/src/revwalk.c
+++ b/src/revwalk.c
@@ -449,6 +449,7 @@ int git_merge_base(git_oid *out, git_repository *repo, git_oid *one, git_oid *tw
if (!result) {
git_revwalk_free(walk);
+ giterr_clear();
return GIT_ENOTFOUND;
}
@@ -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)
@@ -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)
@@ -710,8 +713,10 @@ static int revwalk_next_toposort(commit_object **object_out, git_revwalk *walk)
for (;;) {
next = commit_list_pop(&walk->iterator_topo);
- if (next == NULL)
- return GIT_REVWALKOVER;
+ if (next == NULL) {
+ giterr_clear();
+ return GIT_ITEROVER;
+ }
if (next->in_degree > 0) {
next->topo_delay = 1;
@@ -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)
{
*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)
* If walk->one is NULL, there were no positive references,
* so we know that the walk is already over.
*/
- if (walk->one == NULL)
- return GIT_REVWALKOVER;
+ if (walk->one == NULL) {
+ giterr_clear();
+ return GIT_ITEROVER;
+ }
/* first figure out what the merge bases are */
if (merge_bases_many(&bases, walk, walk->one, &walk->twos) < 0)
@@ -780,7 +787,7 @@ static int prepare_walk(git_revwalk *walk)
return -1;
}
- if (error != GIT_REVWALKOVER)
+ if (error != GIT_ITEROVER)
return error;
walk->get_next = &revwalk_next_toposort;
@@ -792,7 +799,7 @@ static int prepare_walk(git_revwalk *walk)
if (commit_list_insert(next, &walk->iterator_reverse) == NULL)
return -1;
- if (error != GIT_REVWALKOVER)
+ if (error != GIT_ITEROVER)
return error;
walk->get_next = &revwalk_next_reverse;
@@ -891,9 +898,10 @@ int git_revwalk_next(git_oid *oid, git_revwalk *walk)
error = walk->get_next(&next, walk);
- if (error == GIT_REVWALKOVER) {
+ if (error == GIT_ITEROVER) {
git_revwalk_reset(walk);
- return GIT_REVWALKOVER;
+ giterr_clear();
+ return GIT_ITEROVER;
}
if (!error)
diff --git a/src/status.c b/src/status.c
index 3d3d15d7..0a5fbdcb 100644
--- a/src/status.c
+++ b/src/status.c
@@ -151,6 +151,9 @@ cleanup:
git_diff_list_free(idx2head);
git_diff_list_free(wd2idx);
+ if (err == GIT_EUSER)
+ giterr_clear();
+
return err;
}
diff --git a/src/submodule.c b/src/submodule.c
index a9de9ee6..66f1f84b 100644
--- a/src/submodule.c
+++ b/src/submodule.c
@@ -163,6 +163,7 @@ int git_submodule_foreach(
}
if (callback(sm, sm->name, payload)) {
+ giterr_clear();
error = GIT_EUSER;
break;
}
diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c
index 5d3ab8d5..d5cf41e9 100644
--- a/tests-clar/diff/blob.c
+++ b/tests-clar/diff/blob.c
@@ -58,59 +58,59 @@ void test_diff_blob__can_compare_text_blobs(void)
cl_git_pass(git_diff_blobs(
a, b, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(expected.files == 1);
- cl_assert(expected.file_mods == 1);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false);
- cl_assert(expected.hunks == 1);
- cl_assert(expected.lines == 6);
- cl_assert(expected.line_ctxt == 1);
- cl_assert(expected.line_adds == 5);
- cl_assert(expected.line_dels == 0);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(6, expected.lines);
+ cl_assert_equal_i(1, expected.line_ctxt);
+ cl_assert_equal_i(5, expected.line_adds);
+ cl_assert_equal_i(0, expected.line_dels);
/* diff on tests/resources/attr/root_test2 */
memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs(
b, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(expected.files == 1);
- cl_assert(expected.file_mods == 1);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false);
- cl_assert(expected.hunks == 1);
- cl_assert(expected.lines == 15);
- cl_assert(expected.line_ctxt == 3);
- cl_assert(expected.line_adds == 9);
- cl_assert(expected.line_dels == 3);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(15, expected.lines);
+ cl_assert_equal_i(3, expected.line_ctxt);
+ cl_assert_equal_i(9, expected.line_adds);
+ cl_assert_equal_i(3, expected.line_dels);
/* diff on tests/resources/attr/root_test3 */
memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs(
a, c, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(expected.files == 1);
- cl_assert(expected.file_mods == 1);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false);
- cl_assert(expected.hunks == 1);
- cl_assert(expected.lines == 13);
- cl_assert(expected.line_ctxt == 0);
- cl_assert(expected.line_adds == 12);
- cl_assert(expected.line_dels == 1);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(13, expected.lines);
+ cl_assert_equal_i(0, expected.line_ctxt);
+ cl_assert_equal_i(12, expected.line_adds);
+ cl_assert_equal_i(1, expected.line_dels);
memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs(
c, d, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(expected.files == 1);
- cl_assert(expected.file_mods == 1);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_mods);
cl_assert(expected.at_least_one_of_them_is_binary == false);
- cl_assert(expected.hunks == 2);
- cl_assert(expected.lines == 14);
- cl_assert(expected.line_ctxt == 4);
- cl_assert(expected.line_adds == 6);
- cl_assert(expected.line_dels == 4);
+ cl_assert_equal_i(2, expected.hunks);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(4, expected.line_ctxt);
+ cl_assert_equal_i(6, expected.line_adds);
+ cl_assert_equal_i(4, expected.line_dels);
git_blob_free(a);
git_blob_free(b);
@@ -124,14 +124,14 @@ void test_diff_blob__can_compare_against_null_blobs(void)
cl_git_pass(git_diff_blobs(
d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(expected.files == 1);
- cl_assert(expected.file_dels == 1);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_dels);
cl_assert(expected.at_least_one_of_them_is_binary == false);
- cl_assert(expected.hunks == 1);
- cl_assert(expected.hunk_old_lines == 14);
- cl_assert(expected.lines == 14);
- cl_assert(expected.line_dels == 14);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(14, expected.hunk_old_lines);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(14, expected.line_dels);
opts.flags |= GIT_DIFF_REVERSE;
memset(&expected, 0, sizeof(expected));
@@ -139,14 +139,14 @@ void test_diff_blob__can_compare_against_null_blobs(void)
cl_git_pass(git_diff_blobs(
d, e, &opts, &expected, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(expected.files == 1);
- cl_assert(expected.file_adds == 1);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_adds);
cl_assert(expected.at_least_one_of_them_is_binary == false);
- cl_assert(expected.hunks == 1);
- cl_assert(expected.hunk_new_lines == 14);
- cl_assert(expected.lines == 14);
- cl_assert(expected.line_adds == 14);
+ cl_assert_equal_i(1, expected.hunks);
+ cl_assert_equal_i(14, expected.hunk_new_lines);
+ cl_assert_equal_i(14, expected.lines);
+ cl_assert_equal_i(14, expected.line_adds);
opts.flags ^= GIT_DIFF_REVERSE;
memset(&expected, 0, sizeof(expected));
@@ -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.files == 1);
- cl_assert(expected.file_dels == 1);
- cl_assert(expected.hunks == 0);
- cl_assert(expected.lines == 0);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_dels);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
memset(&expected, 0, sizeof(expected));
@@ -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.files == 1);
- cl_assert(expected.file_adds == 1);
- cl_assert(expected.hunks == 0);
- cl_assert(expected.lines == 0);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_adds);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
}
static void assert_identical_blobs_comparison(diff_expects expected)
{
- cl_assert(expected.files == 1);
- cl_assert(expected.file_unmodified == 1);
- cl_assert(expected.hunks == 0);
- cl_assert(expected.lines == 0);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_unmodified);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
}
void test_diff_blob__can_compare_identical_blobs(void)
@@ -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.files == 1);
- cl_assert(expected.file_mods == 1);
- cl_assert(expected.hunks == 0);
- cl_assert(expected.lines == 0);
+ cl_assert_equal_i(1, expected.files);
+ cl_assert_equal_i(1, expected.file_mods);
+ cl_assert_equal_i(0, expected.hunks);
+ cl_assert_equal_i(0, expected.lines);
}
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)
cl_git_pass(git_diff_blobs(
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 */
opts.interhunk_lines = 0;
@@ -300,7 +300,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
cl_git_pass(git_diff_blobs(
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 */
opts.interhunk_lines = 1;
@@ -308,7 +308,7 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
cl_git_pass(git_diff_blobs(
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);
}
diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c
index 7b391262..59e01802 100644
--- a/tests-clar/diff/diff_helpers.c
+++ b/tests-clar/diff/diff_helpers.c
@@ -103,3 +103,74 @@ int diff_line_fn(
}
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;
+}
diff --git a/tests-clar/diff/diff_helpers.h b/tests-clar/diff/diff_helpers.h
index 0aaa6c11..79e14092 100644
--- a/tests-clar/diff/diff_helpers.h
+++ b/tests-clar/diff/diff_helpers.h
@@ -45,3 +45,9 @@ extern int diff_line_fn(
const char *content,
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);
diff --git a/tests-clar/diff/diffiter.c b/tests-clar/diff/diffiter.c
new file mode 100644
index 00000000..56c25474
--- /dev/null
+++ b/tests-clar/diff/diffiter.c
@@ -0,0 +1,116 @@
+#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);
+}
diff --git a/tests-clar/diff/index.c b/tests-clar/diff/index.c
index 89e65e3b..2c6e89c4 100644
--- a/tests-clar/diff/index.c
+++ b/tests-clar/diff/index.c
@@ -44,17 +44,17 @@ void test_diff_index__0(void)
* - git diff -U1 --cached 26a125ee1bf
* - mv .git .gitted
*/
- cl_assert(exp.files == 8);
- cl_assert(exp.file_adds == 3);
- cl_assert(exp.file_dels == 2);
- cl_assert(exp.file_mods == 3);
+ cl_assert_equal_i(8, exp.files);
+ cl_assert_equal_i(3, exp.file_adds);
+ cl_assert_equal_i(2, exp.file_dels);
+ 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(exp.line_ctxt == 3);
- cl_assert(exp.line_adds == 6);
- cl_assert(exp.line_dels == 2);
+ cl_assert_equal_i(11, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(6, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
git_diff_list_free(diff);
diff = NULL;
@@ -72,17 +72,17 @@ void test_diff_index__0(void)
* - git diff -U1 --cached 0017bd4ab1ec3
* - mv .git .gitted
*/
- cl_assert(exp.files == 12);
- cl_assert(exp.file_adds == 7);
- cl_assert(exp.file_dels == 2);
- cl_assert(exp.file_mods == 3);
+ cl_assert_equal_i(12, exp.files);
+ cl_assert_equal_i(7, exp.file_adds);
+ cl_assert_equal_i(2, exp.file_dels);
+ 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(exp.line_ctxt == 3);
- cl_assert(exp.line_adds == 11);
- cl_assert(exp.line_dels == 2);
+ cl_assert_equal_i(16, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(11, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
git_diff_list_free(diff);
diff = NULL;
@@ -132,7 +132,7 @@ void test_diff_index__1(void)
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);
diff = NULL;
diff --git a/tests-clar/diff/tree.c b/tests-clar/diff/tree.c
index be9eb6c1..3003374a 100644
--- a/tests-clar/diff/tree.c
+++ b/tests-clar/diff/tree.c
@@ -39,17 +39,17 @@ void test_diff_tree__0(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(exp.files == 5);
- cl_assert(exp.file_adds == 2);
- cl_assert(exp.file_dels == 1);
- cl_assert(exp.file_mods == 2);
+ cl_assert_equal_i(5, exp.files);
+ cl_assert_equal_i(2, exp.file_adds);
+ cl_assert_equal_i(1, exp.file_dels);
+ 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(exp.line_ctxt == 1);
- cl_assert(exp.line_adds == 24 + 1 + 5 + 5);
- cl_assert(exp.line_dels == 7 + 1);
+ cl_assert_equal_i(7 + 24 + 1 + 6 + 6, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(24 + 1 + 5 + 5, exp.line_adds);
+ cl_assert_equal_i(7 + 1, exp.line_dels);
git_diff_list_free(diff);
diff = NULL;
@@ -61,17 +61,17 @@ void test_diff_tree__0(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(exp.files == 2);
- cl_assert(exp.file_adds == 0);
- cl_assert(exp.file_dels == 0);
- cl_assert(exp.file_mods == 2);
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ 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(exp.line_ctxt == 1);
- cl_assert(exp.line_adds == 1);
- cl_assert(exp.line_dels == 7 + 14);
+ cl_assert_equal_i(8 + 15, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(7 + 14, exp.line_dels);
git_diff_list_free(diff);
@@ -192,17 +192,17 @@ void test_diff_tree__bare(void)
cl_git_pass(git_diff_foreach(
diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(exp.files == 3);
- cl_assert(exp.file_adds == 2);
- cl_assert(exp.file_dels == 0);
- cl_assert(exp.file_mods == 1);
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(2, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ 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(exp.line_ctxt == 0);
- cl_assert(exp.line_adds == 3);
- cl_assert(exp.line_dels == 1);
+ cl_assert_equal_i(4, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(3, exp.line_adds);
+ cl_assert_equal_i(1, exp.line_dels);
git_diff_list_free(diff);
git_tree_free(a);
@@ -242,17 +242,17 @@ void test_diff_tree__merge(void)
cl_git_pass(git_diff_foreach(
diff1, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert(exp.files == 6);
- cl_assert(exp.file_adds == 2);
- cl_assert(exp.file_dels == 1);
- cl_assert(exp.file_mods == 3);
+ cl_assert_equal_i(6, exp.files);
+ cl_assert_equal_i(2, exp.file_adds);
+ cl_assert_equal_i(1, exp.file_dels);
+ 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(exp.line_ctxt == 1);
- cl_assert(exp.line_adds == 36);
- cl_assert(exp.line_dels == 22);
+ cl_assert_equal_i(59, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(36, exp.line_adds);
+ cl_assert_equal_i(22, exp.line_dels);
git_diff_list_free(diff1);
}
diff --git a/tests-clar/diff/workdir.c b/tests-clar/diff/workdir.c
index 801439e3..eac7eb87 100644
--- a/tests-clar/diff/workdir.c
+++ b/tests-clar/diff/workdir.c
@@ -17,6 +17,7 @@ void test_diff_workdir__to_index(void)
git_diff_options opts = {0};
git_diff_list *diff = NULL;
diff_expects exp;
+ int use_iterator;
g_repo = cl_git_sandbox_init("status");
@@ -24,33 +25,39 @@ void test_diff_workdir__to_index(void)
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- memset(&exp, 0, sizeof(exp));
-
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
-
- /* to generate these values:
- * - cd to tests/resources/status,
- * - mv .gitted .git
- * - git diff --name-status
- * - git diff
- * - mv .git .gitted
- */
- cl_assert_equal_i(13, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(4, exp.file_dels);
- cl_assert_equal_i(4, exp.file_mods);
- cl_assert_equal_i(1, exp.file_ignored);
- cl_assert_equal_i(4, exp.file_untracked);
-
- cl_assert_equal_i(8, exp.hunks);
-
- cl_assert_equal_i(14, exp.lines);
- cl_assert_equal_i(5, exp.line_ctxt);
- cl_assert_equal_i(4, exp.line_adds);
- cl_assert_equal_i(5, exp.line_dels);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ /* to generate these values:
+ * - cd to tests/resources/status,
+ * - mv .gitted .git
+ * - git diff --name-status
+ * - git diff
+ * - mv .git .gitted
+ */
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(4, exp.file_dels);
+ cl_assert_equal_i(4, exp.file_mods);
+ cl_assert_equal_i(1, exp.file_ignored);
+ cl_assert_equal_i(4, exp.file_untracked);
+
+ cl_assert_equal_i(8, exp.hunks);
+
+ cl_assert_equal_i(14, exp.lines);
+ cl_assert_equal_i(5, exp.line_ctxt);
+ cl_assert_equal_i(4, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+ }
git_diff_list_free(diff);
}
@@ -65,6 +72,7 @@ void test_diff_workdir__to_tree(void)
git_diff_list *diff = NULL;
git_diff_list *diff2 = NULL;
diff_expects exp;
+ int use_iterator;
g_repo = cl_git_sandbox_init("status");
@@ -75,8 +83,6 @@ void test_diff_workdir__to_tree(void)
opts.interhunk_lines = 1;
opts.flags |= GIT_DIFF_INCLUDE_IGNORED | GIT_DIFF_INCLUDE_UNTRACKED;
- memset(&exp, 0, sizeof(exp));
-
/* You can't really generate the equivalent of git_diff_workdir_to_tree()
* using C git. It really wants to interpose the index into the diff.
*
@@ -89,15 +95,23 @@ void test_diff_workdir__to_tree(void)
*/
cl_git_pass(git_diff_workdir_to_tree(g_repo, &opts, a, &diff));
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
- cl_assert_equal_i(14, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(4, exp.file_dels);
- cl_assert_equal_i(4, exp.file_mods);
- cl_assert_equal_i(1, exp.file_ignored);
- cl_assert_equal_i(5, exp.file_untracked);
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(14, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(4, exp.file_dels);
+ cl_assert_equal_i(4, exp.file_mods);
+ cl_assert_equal_i(1, exp.file_ignored);
+ cl_assert_equal_i(5, exp.file_untracked);
+ }
/* Since there is no git diff equivalent, let's just assume that the
* text diffs produced by git_diff_foreach are accurate here. We will
@@ -117,22 +131,30 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_merge(diff, diff2));
git_diff_list_free(diff2);
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(15, exp.files);
- cl_assert_equal_i(2, exp.file_adds);
- cl_assert_equal_i(5, exp.file_dels);
- cl_assert_equal_i(4, exp.file_mods);
- cl_assert_equal_i(1, exp.file_ignored);
- cl_assert_equal_i(3, exp.file_untracked);
+ cl_assert_equal_i(15, exp.files);
+ cl_assert_equal_i(2, exp.file_adds);
+ cl_assert_equal_i(5, exp.file_dels);
+ cl_assert_equal_i(4, exp.file_mods);
+ cl_assert_equal_i(1, exp.file_ignored);
+ cl_assert_equal_i(3, exp.file_untracked);
- cl_assert_equal_i(11, exp.hunks);
+ cl_assert_equal_i(11, exp.hunks);
- cl_assert_equal_i(17, exp.lines);
- cl_assert_equal_i(4, exp.line_ctxt);
- cl_assert_equal_i(8, exp.line_adds);
- cl_assert_equal_i(5, exp.line_dels);
+ cl_assert_equal_i(17, exp.lines);
+ cl_assert_equal_i(4, exp.line_ctxt);
+ cl_assert_equal_i(8, exp.line_adds);
+ cl_assert_equal_i(5, exp.line_dels);
+ }
git_diff_list_free(diff);
diff = NULL;
@@ -146,22 +168,30 @@ void test_diff_workdir__to_tree(void)
cl_git_pass(git_diff_merge(diff, diff2));
git_diff_list_free(diff2);
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
- cl_assert_equal_i(16, exp.files);
- cl_assert_equal_i(5, exp.file_adds);
- cl_assert_equal_i(4, exp.file_dels);
- cl_assert_equal_i(3, exp.file_mods);
- cl_assert_equal_i(1, exp.file_ignored);
- cl_assert_equal_i(3, exp.file_untracked);
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(12, exp.hunks);
+ cl_assert_equal_i(16, exp.files);
+ cl_assert_equal_i(5, exp.file_adds);
+ cl_assert_equal_i(4, exp.file_dels);
+ cl_assert_equal_i(3, exp.file_mods);
+ cl_assert_equal_i(1, exp.file_ignored);
+ cl_assert_equal_i(3, exp.file_untracked);
- cl_assert_equal_i(19, exp.lines);
- cl_assert_equal_i(3, exp.line_ctxt);
- cl_assert_equal_i(12, exp.line_adds);
- cl_assert_equal_i(4, exp.line_dels);
+ cl_assert_equal_i(12, exp.hunks);
+
+ cl_assert_equal_i(19, exp.lines);
+ cl_assert_equal_i(3, exp.line_ctxt);
+ cl_assert_equal_i(12, exp.line_adds);
+ cl_assert_equal_i(4, exp.line_dels);
+ }
git_diff_list_free(diff);
@@ -175,6 +205,7 @@ void test_diff_workdir__to_index_with_pathspec(void)
git_diff_list *diff = NULL;
diff_expects exp;
char *pathspec = NULL;
+ int use_iterator;
g_repo = cl_git_sandbox_init("status");
@@ -184,62 +215,93 @@ void test_diff_workdir__to_index_with_pathspec(void)
opts.pathspec.strings = &pathspec;
opts.pathspec.count = 1;
- memset(&exp, 0, sizeof(exp));
-
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
- cl_assert_equal_i(13, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(4, exp.file_dels);
- cl_assert_equal_i(4, exp.file_mods);
- cl_assert_equal_i(1, exp.file_ignored);
- cl_assert_equal_i(4, exp.file_untracked);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, NULL, NULL));
+ else
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(13, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(4, exp.file_dels);
+ cl_assert_equal_i(4, exp.file_mods);
+ cl_assert_equal_i(1, exp.file_ignored);
+ cl_assert_equal_i(4, exp.file_untracked);
+ }
git_diff_list_free(diff);
- memset(&exp, 0, sizeof(exp));
pathspec = "modified_file";
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(0, exp.file_dels);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(0, exp.file_ignored);
- cl_assert_equal_i(0, exp.file_untracked);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, NULL, NULL));
+ else
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(0, exp.file_ignored);
+ cl_assert_equal_i(0, exp.file_untracked);
+ }
git_diff_list_free(diff);
- memset(&exp, 0, sizeof(exp));
pathspec = "subdir";
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
- cl_assert_equal_i(3, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(1, exp.file_dels);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(0, exp.file_ignored);
- cl_assert_equal_i(1, exp.file_untracked);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, NULL, NULL));
+ else
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(3, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(1, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(0, exp.file_ignored);
+ cl_assert_equal_i(1, exp.file_untracked);
+ }
git_diff_list_free(diff);
- memset(&exp, 0, sizeof(exp));
pathspec = "*_deleted";
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
- cl_assert_equal_i(2, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(2, exp.file_dels);
- cl_assert_equal_i(0, exp.file_mods);
- cl_assert_equal_i(0, exp.file_ignored);
- cl_assert_equal_i(0, exp.file_untracked);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, NULL, NULL));
+ else
+ cl_git_pass(git_diff_foreach(diff, &exp, diff_file_fn, NULL, NULL));
+
+ cl_assert_equal_i(2, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(2, exp.file_dels);
+ cl_assert_equal_i(0, exp.file_mods);
+ cl_assert_equal_i(0, exp.file_ignored);
+ cl_assert_equal_i(0, exp.file_untracked);
+ }
git_diff_list_free(diff);
}
@@ -249,6 +311,7 @@ void test_diff_workdir__filemode_changes(void)
git_config *cfg;
git_diff_list *diff = NULL;
diff_expects exp;
+ int use_iterator;
if (!cl_is_chmod_supported())
return;
@@ -262,13 +325,20 @@ void test_diff_workdir__filemode_changes(void)
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.file_mods);
- cl_assert_equal_i(0, exp.hunks);
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_mods);
+ cl_assert_equal_i(0, exp.hunks);
+ }
git_diff_list_free(diff);
@@ -278,13 +348,20 @@ void test_diff_workdir__filemode_changes(void)
cl_git_pass(git_diff_workdir_to_index(g_repo, NULL, &diff));
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(0, exp.hunks);
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(0, exp.hunks);
+ }
git_diff_list_free(diff);
@@ -347,6 +424,7 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void)
diff_expects exp;
char *pathspec = "staged_changes_modified_file";
git_tree *tree;
+ int use_iterator;
/* For this file,
* - head->index diff has 1 line of context, 1 line of diff
@@ -366,46 +444,70 @@ void test_diff_workdir__head_index_and_workdir_all_differ(void)
cl_git_pass(git_diff_index_to_tree(g_repo, &opts, tree, &diff_i2t));
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff_w2i));
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(0, exp.file_dels);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(2, exp.lines);
- cl_assert_equal_i(1, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
-
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(0, exp.file_dels);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(3, exp.lines);
- cl_assert_equal_i(2, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(2, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
+
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_w2i, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(2, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
cl_git_pass(git_diff_merge(diff_i2t, diff_w2i));
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(0, exp.file_dels);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(3, exp.lines);
- cl_assert_equal_i(1, exp.line_ctxt);
- cl_assert_equal_i(2, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff_i2t, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(2, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
git_diff_list_free(diff_i2t);
git_diff_list_free(diff_w2i);
@@ -419,6 +521,7 @@ void test_diff_workdir__eof_newline_changes(void)
git_diff_list *diff = NULL;
diff_expects exp;
char *pathspec = "current_file";
+ int use_iterator;
g_repo = cl_git_sandbox_init("status");
@@ -427,18 +530,26 @@ void test_diff_workdir__eof_newline_changes(void)
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(0, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(0, exp.file_dels);
- cl_assert_equal_i(0, exp.file_mods);
- cl_assert_equal_i(0, exp.hunks);
- cl_assert_equal_i(0, exp.lines);
- cl_assert_equal_i(0, exp.line_ctxt);
- cl_assert_equal_i(0, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(0, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(0, exp.file_mods);
+ cl_assert_equal_i(0, exp.hunks);
+ cl_assert_equal_i(0, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(0, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
git_diff_list_free(diff);
@@ -446,18 +557,26 @@ void test_diff_workdir__eof_newline_changes(void)
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(0, exp.file_dels);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(2, exp.lines);
- cl_assert_equal_i(1, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(0, exp.line_dels);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(2, exp.lines);
+ cl_assert_equal_i(1, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(0, exp.line_dels);
+ }
git_diff_list_free(diff);
@@ -465,18 +584,26 @@ void test_diff_workdir__eof_newline_changes(void)
cl_git_pass(git_diff_workdir_to_index(g_repo, &opts, &diff));
- memset(&exp, 0, sizeof(exp));
- cl_git_pass(git_diff_foreach(
- diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
- cl_assert_equal_i(1, exp.files);
- cl_assert_equal_i(0, exp.file_adds);
- cl_assert_equal_i(0, exp.file_dels);
- cl_assert_equal_i(1, exp.file_mods);
- cl_assert_equal_i(1, exp.hunks);
- cl_assert_equal_i(3, exp.lines);
- cl_assert_equal_i(0, exp.line_ctxt);
- cl_assert_equal_i(1, exp.line_adds);
- cl_assert_equal_i(2, exp.line_dels);
+ for (use_iterator = 0; use_iterator <= 1; use_iterator++) {
+ memset(&exp, 0, sizeof(exp));
+
+ if (use_iterator)
+ cl_git_pass(diff_foreach_via_iterator(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+ else
+ cl_git_pass(git_diff_foreach(
+ diff, &exp, diff_file_fn, diff_hunk_fn, diff_line_fn));
+
+ cl_assert_equal_i(1, exp.files);
+ cl_assert_equal_i(0, exp.file_adds);
+ cl_assert_equal_i(0, exp.file_dels);
+ cl_assert_equal_i(1, exp.file_mods);
+ cl_assert_equal_i(1, exp.hunks);
+ cl_assert_equal_i(3, exp.lines);
+ cl_assert_equal_i(0, exp.line_ctxt);
+ cl_assert_equal_i(1, exp.line_adds);
+ cl_assert_equal_i(2, exp.line_dels);
+ }
git_diff_list_free(diff);
}
diff --git a/tests-clar/resources/attr/.gitted/index b/tests-clar/resources/attr/.gitted/index
index 943e2243..439ffb15 100644
--- a/tests-clar/resources/attr/.gitted/index
+++ b/tests-clar/resources/attr/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/issue_592/.gitted/index b/tests-clar/resources/issue_592/.gitted/index
index eaeb5d76..be7a29d9 100644
--- a/tests-clar/resources/issue_592/.gitted/index
+++ b/tests-clar/resources/issue_592/.gitted/index
Binary files differ
diff --git a/tests-clar/resources/status/.gitted/index b/tests-clar/resources/status/.gitted/index
index 9a383ec0..2af99a18 100644
--- a/tests-clar/resources/status/.gitted/index
+++ b/tests-clar/resources/status/.gitted/index
Binary files differ