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 {
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
......@@ -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.
*
* This will iterate through all of the files described in a diff. You
......
......@@ -28,7 +28,7 @@ enum {
GIT_EUSER = -7,
GIT_PASSTHROUGH = -30,
GIT_REVWALKOVER = -31,
GIT_ITEROVER = -31,
};
typedef struct {
......
......@@ -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);
......
......@@ -188,6 +188,7 @@ int git_attr_foreach(
error = callback(assign->name, assign->value, payload);
if (error) {
giterr_clear();
error = GIT_EUSER;
goto cleanup;
}
......
......@@ -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;
}
......
......@@ -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,
......
......@@ -26,6 +26,7 @@ enum {
};
struct git_diff_list {
git_refcount rc;
git_repository *repo;
git_diff_options opts;
git_vector pathspec;
......
......@@ -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,370 @@ 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);
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;
size_t line_count;
diffiter_line *line_curr;
};
typedef struct {
git_diff_iterator *iter;
diffiter_hunk *last_hunk;
diffiter_line *last_line;
} diffiter_cb_info;
fill_map_from_mmfile(&old_map, &old_data);
fill_map_from_mmfile(&new_map, &new_data);
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 (file_is_binary_by_content(&delta, &old_map, &new_map) < 0)
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));
iter->hunk_count++;
if (iter->hunk_head == NULL)
iter->hunk_curr = iter->hunk_head = hunk;
return 0;
}
/* don't do hunk and line diffs if file is binary */
if (delta.binary == 1)
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++;
iter->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);
if (error == GIT_EUSER)
error = iter->ctxt.cb_error;
return error;
}
xdl_diff(&old_data, &new_data, &xdiff_params, &xdiff_config, &xdiff_callback);
static void diffiter_do_unload_file(git_diff_iterator *iter)
{
if (iter->ctxt.loaded) {
diff_delta_unload(&iter->ctxt);
git_pool_clear(&iter->lines);
git_pool_clear(&iter->hunks);
}
iter->ctxt.delta = NULL;
iter->hunk_head = NULL;
iter->hunk_count = 0;
iter->line_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);
return (error != 0) ? error : (int)iter->line_count;
}
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);
return info.error;
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;
}
......@@ -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 */
......
......@@ -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)
......
......@@ -1643,7 +1643,6 @@ int git_reference_normalize_name(
}
}
*buffer_out++ = *current++;
buffer_size--;
}
......
......@@ -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;
......
......@@ -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)
......
......@@ -151,6 +151,9 @@ cleanup:
git_diff_list_free(idx2head);
git_diff_list_free(wd2idx);
if (err == GIT_EUSER)
giterr_clear();
return err;
}
......
......@@ -163,6 +163,7 @@ int git_submodule_foreach(
}
if (callback(sm, sm->name, payload)) {
giterr_clear();
error = GIT_EUSER;
break;
}
......
......@@ -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);
}
......@@ -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;
}
......@@ -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);
#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)
* - 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;
......
......@@ -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);
}
......@@ -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);
}
......
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