summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/git2/diff.h20
-rw-r--r--src/diff.c30
-rw-r--r--src/diff.h3
-rw-r--r--src/diff_output.c87
-rw-r--r--tests-clar/diff/diff_helpers.c8
-rw-r--r--tests-clar/diff/diffiter.c12
6 files changed, 105 insertions, 55 deletions
diff --git a/include/git2/diff.h b/include/git2/diff.h
index d216c1303..154264546 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -401,6 +401,20 @@ GIT_EXTERN(int) git_diff_print_compact(
git_diff_data_fn print_cb);
/**
+ * Look up the single character abbreviation for a delta status code.
+ *
+ * When you call `git_diff_print_compact` it prints single letter codes into
+ * the output such as 'A' for added, 'D' for deleted, 'M' for modified, etc.
+ * It is sometimes convenient to convert a git_delta_t value into these
+ * letters for your own purposes. This function does just that. By the
+ * way, unmodified will return a space (i.e. ' ').
+ *
+ * @param delta_t The git_delta_t value to look up
+ * @return The single character label for that code
+ */
+GIT_EXTERN(char) git_diff_status_char(git_delta_t status);
+
+/**
* Iterate over a diff generating text output like "git diff".
*
* This is a super easy way to generate a patch from a diff.
@@ -453,9 +467,9 @@ GIT_EXTERN(size_t) git_diff_num_deltas_of_type(
* done with it. You can use the patch object to loop over all the hunks
* and lines in the diff of the one delta.
*
- * For a binary file, no `git_diff_patch` will be created, the output will
- * be set to NULL, and the `binary` flag will be set true in the
- * `git_diff_delta` structure.
+ * For an unchanged file or a binary file, no `git_diff_patch` will be
+ * created, the output will be set to NULL, and the `binary` flag will be
+ * set true in the `git_diff_delta` structure.
*
* The `git_diff_delta` pointer points to internal data and you do not have
* to release it when you are done with it. It will go away when the
diff --git a/src/diff.c b/src/diff.c
index 7c8e2a9bb..7ee919b5a 100644
--- a/src/diff.c
+++ b/src/diff.c
@@ -807,6 +807,28 @@ on_error:
return -1;
}
+
+bool git_diff_delta__should_skip(
+ git_diff_options *opts, git_diff_delta *delta)
+{
+ uint32_t flags = opts ? opts->flags : 0;
+
+ if (delta->status == GIT_DELTA_UNMODIFIED &&
+ (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
+ return true;
+
+ if (delta->status == GIT_DELTA_IGNORED &&
+ (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
+ return true;
+
+ if (delta->status == GIT_DELTA_UNTRACKED &&
+ (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
+ return true;
+
+ return false;
+}
+
+
int git_diff_merge(
git_diff_list *onto,
const git_diff_list *from)
@@ -843,6 +865,14 @@ int git_diff_merge(
j++;
}
+ /* the ignore rules for the target may not match the source
+ * or the result of a merged delta could be skippable...
+ */
+ if (git_diff_delta__should_skip(&onto->opts, delta)) {
+ git__free(delta);
+ continue;
+ }
+
if ((error = !delta ? -1 : git_vector_insert(&onto_new, delta)) < 0)
break;
}
diff --git a/src/diff.h b/src/diff.h
index 862c33c1b..64fe00928 100644
--- a/src/diff.h
+++ b/src/diff.h
@@ -50,5 +50,8 @@ extern void git_diff__cleanup_modes(
extern void git_diff_list_addref(git_diff_list *diff);
+extern bool git_diff_delta__should_skip(
+ git_diff_options *opts, git_diff_delta *delta);
+
#endif
diff --git a/src/diff_output.c b/src/diff_output.c
index b84b0c2a4..141859742 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -51,26 +51,6 @@ static int parse_hunk_header(git_diff_range *range, const char *header)
return 0;
}
-static bool diff_delta_should_skip(
- diff_context *ctxt, git_diff_delta *delta)
-{
- uint32_t flags = ctxt->opts ? ctxt->opts->flags : 0;
-
- if (delta->status == GIT_DELTA_UNMODIFIED &&
- (flags & GIT_DIFF_INCLUDE_UNMODIFIED) == 0)
- return true;
-
- if (delta->status == GIT_DELTA_IGNORED &&
- (flags & GIT_DIFF_INCLUDE_IGNORED) == 0)
- return true;
-
- if (delta->status == GIT_DELTA_UNTRACKED &&
- (flags & GIT_DIFF_INCLUDE_UNTRACKED) == 0)
- return true;
-
- return false;
-}
-
#define KNOWN_BINARY_FLAGS (GIT_DIFF_FILE_BINARY|GIT_DIFF_FILE_NOT_BINARY)
#define NOT_BINARY_FLAGS (GIT_DIFF_FILE_NOT_BINARY|GIT_DIFF_FILE_NO_DATA)
@@ -411,7 +391,7 @@ static void diff_context_init(
setup_xdiff_options(ctxt->opts, &ctxt->xdiff_config, &ctxt->xdiff_params);
}
-static bool diff_delta_file_callback(
+static int diff_delta_file_callback(
diff_context *ctxt, git_diff_delta *delta, size_t idx)
{
float progress;
@@ -419,18 +399,18 @@ static bool diff_delta_file_callback(
if (!ctxt->file_cb)
return 0;
- progress = (float)idx / ctxt->diff->deltas.length;
+ progress = ctxt->diff ? ((float)idx / ctxt->diff->deltas.length) : 1.0f;
if (ctxt->file_cb(ctxt->cb_data, delta, progress) != 0)
- return GIT_EUSER;
+ ctxt->cb_error = GIT_EUSER;
- return 0;
+ return ctxt->cb_error;
}
static void diff_patch_init(
diff_context *ctxt, git_diff_patch *patch)
{
- memset(patch, 0, sizeof(*patch));
+ memset(patch, 0, sizeof(git_diff_patch));
patch->diff = ctxt->diff;
patch->ctxt = ctxt;
@@ -454,7 +434,7 @@ static git_diff_patch *diff_patch_alloc(
git_diff_list_addref(patch->diff);
- GIT_REFCOUNT_INC(&patch);
+ GIT_REFCOUNT_INC(patch);
patch->delta = delta;
patch->flags = GIT_DIFF_PATCH_ALLOCATED;
@@ -836,6 +816,8 @@ static int diff_patch_line_cb(
}
}
+ hunk->line_count++;
+
return 0;
}
@@ -847,7 +829,7 @@ int git_diff_foreach(
git_diff_hunk_fn hunk_cb,
git_diff_data_fn data_cb)
{
- int error;
+ int error = 0;
diff_context ctxt;
size_t idx;
git_diff_patch patch;
@@ -861,14 +843,20 @@ int git_diff_foreach(
git_vector_foreach(&diff->deltas, idx, patch.delta) {
/* check flags against patch status */
- if (diff_delta_should_skip(&ctxt, patch.delta))
+ if (git_diff_delta__should_skip(ctxt.opts, patch.delta))
continue;
- if (!(error = diff_patch_load(&ctxt, &patch)) &&
- !(error = diff_delta_file_callback(&ctxt, patch.delta, idx)))
- error = diff_patch_generate(&ctxt, &patch);
+ if (!(error = diff_patch_load(&ctxt, &patch))) {
- diff_patch_unload(&patch);
+ /* invoke file callback */
+ error = diff_delta_file_callback(&ctxt, patch.delta, idx);
+
+ /* generate diffs and invoke hunk and line callbacks */
+ if (!error)
+ error = diff_patch_generate(&ctxt, &patch);
+
+ diff_patch_unload(&patch);
+ }
if (error < 0)
break;
@@ -899,25 +887,32 @@ static char pick_suffix(int mode)
return ' ';
}
+char git_diff_status_char(git_delta_t status)
+{
+ char code;
+
+ switch (status) {
+ case GIT_DELTA_ADDED: code = 'A'; break;
+ case GIT_DELTA_DELETED: code = 'D'; break;
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
+ case GIT_DELTA_RENAMED: code = 'R'; break;
+ case GIT_DELTA_COPIED: code = 'C'; break;
+ case GIT_DELTA_IGNORED: code = 'I'; break;
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
+ default: code = ' '; break;
+ }
+
+ return code;
+}
+
static int print_compact(void *data, git_diff_delta *delta, float progress)
{
diff_print_info *pi = data;
- char code, old_suffix, new_suffix;
+ char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
GIT_UNUSED(progress);
- switch (delta->status) {
- case GIT_DELTA_ADDED: code = 'A'; break;
- case GIT_DELTA_DELETED: code = 'D'; break;
- case GIT_DELTA_MODIFIED: code = 'M'; break;
- case GIT_DELTA_RENAMED: code = 'R'; break;
- case GIT_DELTA_COPIED: code = 'C'; break;
- case GIT_DELTA_IGNORED: code = 'I'; break;
- case GIT_DELTA_UNTRACKED: code = '?'; break;
- default: code = 0;
- }
-
- if (!code)
+ if (code == ' ')
return 0;
old_suffix = pick_suffix(delta->old_file.mode);
@@ -1288,7 +1283,7 @@ int git_diff_get_patch(
&ctxt, diff, diff->repo, &diff->opts,
NULL, NULL, diff_patch_hunk_cb, diff_patch_line_cb);
- if (diff_delta_should_skip(&ctxt, delta))
+ if (git_diff_delta__should_skip(ctxt.opts, delta))
return 0;
patch = diff_patch_alloc(&ctxt, delta);
diff --git a/tests-clar/diff/diff_helpers.c b/tests-clar/diff/diff_helpers.c
index 1c0435975..0c4721897 100644
--- a/tests-clar/diff/diff_helpers.c
+++ b/tests-clar/diff/diff_helpers.c
@@ -120,7 +120,7 @@ int diff_foreach_via_iterator(
size_t h, num_h;
cl_git_pass(git_diff_get_patch(&patch, &delta, diff, d));
- cl_assert(delta && patch);
+ cl_assert(delta);
/* call file_cb for this file */
if (file_cb != NULL && file_cb(data, delta, (float)d / num_d) != 0) {
@@ -128,6 +128,12 @@ int diff_foreach_via_iterator(
goto abort;
}
+ /* if there are no changes, then the patch will be NULL */
+ if (!patch) {
+ cl_assert(delta->status == GIT_DELTA_UNMODIFIED || delta->binary == 1);
+ continue;
+ }
+
if (!hunk_cb && !line_cb) {
git_diff_patch_free(patch);
continue;
diff --git a/tests-clar/diff/diffiter.c b/tests-clar/diff/diffiter.c
index 9e33d91e1..4273b16dd 100644
--- a/tests-clar/diff/diffiter.c
+++ b/tests-clar/diff/diffiter.c
@@ -256,21 +256,23 @@ void test_diff_diffiter__iterate_all(void)
cl_assert_equal_i(13, exp.files);
cl_assert_equal_i(8, exp.hunks);
- cl_assert_equal_i(13, exp.lines);
+ cl_assert_equal_i(14, exp.lines);
git_diff_list_free(diff);
}
static void iterate_over_patch(git_diff_patch *patch, diff_expects *exp)
{
- size_t h, num_h = git_diff_patch_num_hunks(patch);
+ size_t h, num_h = git_diff_patch_num_hunks(patch), num_l;
exp->files++;
exp->hunks += num_h;
/* let's iterate in reverse, just because we can! */
- for (h = 1; h <= num_h; ++h)
- exp->lines += git_diff_patch_num_lines_in_hunk(patch, num_h - h);
+ for (h = 1, num_l = 0; h <= num_h; ++h)
+ num_l += git_diff_patch_num_lines_in_hunk(patch, num_h - h);
+
+ exp->lines += num_l;
}
#define PATCH_CACHE 5
@@ -338,5 +340,5 @@ void test_diff_diffiter__iterate_randomly_while_saving_state(void)
/* hopefully it all still added up right */
cl_assert_equal_i(13, exp.files);
cl_assert_equal_i(8, exp.hunks);
- cl_assert_equal_i(13, exp.lines);
+ cl_assert_equal_i(14, exp.lines);
}