summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-06-12 11:55:27 -0700
committerRussell Belfer <rb@github.com>2013-06-12 11:55:27 -0700
commitf9c824c592d7a23f7cc385c25c95a5d0c5c8687e (patch)
treea9041574778f0bb2341d97c56357d280ed71f06c
parent54faddd299ccb6187a9747c1d3ee18d33e5edf7a (diff)
downloadlibgit2-f9c824c592d7a23f7cc385c25c95a5d0c5c8687e.tar.gz
Add patch from blobs API
This adds two new public APIs: git_diff_patch_from_blobs and git_diff_patch_from_blob_and_buffer, plus it refactors the code for git_diff_blobs and git_diff_blob_to_buffer so that they code is almost entirely shared between these APIs, and adds tests for the new APIs.
-rw-r--r--include/git2/diff.h47
-rw-r--r--src/diff_patch.c195
-rw-r--r--tests-clar/diff/blob.c238
3 files changed, 428 insertions, 52 deletions
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 40e65b1e4..8113a56be 100644
--- a/include/git2/diff.h
+++ b/include/git2/diff.h
@@ -860,7 +860,7 @@ GIT_EXTERN(size_t) git_diff_patch_num_hunks(
* @param total_additions Count of addition lines in output, can be NULL.
* @param total_deletions Count of deletion lines in output, can be NULL.
* @param patch The git_diff_patch object
- * @return Number of lines in hunk or -1 if invalid hunk index
+ * @return 0 on success, <0 on error
*/
GIT_EXTERN(int) git_diff_patch_line_stats(
size_t *total_context,
@@ -1001,6 +1001,26 @@ GIT_EXTERN(int) git_diff_blobs(
void *payload);
/**
+ * Directly generate a patch from the difference between two blobs.
+ *
+ * This is just like `git_diff_blobs()` except it generates a patch object
+ * for the difference instead of directly making callbacks. You can use the
+ * standard `git_diff_patch` accessor functions to read the patch data, and
+ * you must call `git_diff_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param new_blob Blob for new side of diff, or NULL for empty blob
+ * @param options Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_diff_patch_from_blobs(
+ git_diff_patch **out,
+ const git_blob *old_blob,
+ const git_blob *new_blob,
+ const git_diff_options *opts);
+
+/**
* Directly run a diff between a blob and a buffer.
*
* As with `git_diff_blobs`, comparing a blob and buffer lacks some context,
@@ -1013,7 +1033,7 @@ GIT_EXTERN(int) git_diff_blobs(
* the reverse, with GIT_DELTA_REMOVED and blob content removed.
*
* @param old_blob Blob for old side of diff, or NULL for empty blob
- * @param buffer Raw data for new side of diff
+ * @param buffer Raw data for new side of diff, or NULL for empty
* @param buffer_len Length of raw data for new side of diff
* @param options Options for diff, or NULL for default options
* @param file_cb Callback for "file"; made once if there is a diff; can be NULL
@@ -1032,6 +1052,29 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
git_diff_data_cb data_cb,
void *payload);
+/**
+ * Directly generate a patch from the difference between a blob and a buffer.
+ *
+ * This is just like `git_diff_blob_to_buffer()` except it generates a patch
+ * object for the difference instead of directly making callbacks. You can
+ * use the standard `git_diff_patch` accessor functions to read the patch
+ * data, and you must call `git_diff_patch_free()` on the patch when done.
+ *
+ * @param out The generated patch; NULL on error
+ * @param old_blob Blob for old side of diff, or NULL for empty blob
+ * @param buffer Raw data for new side of diff, or NULL for empty
+ * @param buffer_len Length of raw data for new side of diff
+ * @param options Options for diff, or NULL for default options
+ * @return 0 on success or error code < 0
+ */
+GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer(
+ git_diff_patch **out,
+ const git_blob *old_blob,
+ const char *buf,
+ size_t buflen,
+ const git_diff_options *opts);
+
+
GIT_END_DECL
/** @} */
diff --git a/src/diff_patch.c b/src/diff_patch.c
index fe22d678c..4c0b9a70c 100644
--- a/src/diff_patch.c
+++ b/src/diff_patch.c
@@ -265,33 +265,32 @@ int git_diff_foreach(
}
typedef struct {
- git_xdiff_output xo;
git_diff_patch patch;
git_diff_delta delta;
-} diff_single_info;
+} diff_patch_with_delta;
-static int diff_single_generate(diff_single_info *info)
+static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
{
int error = 0;
- git_diff_patch *patch = &info->patch;
+ git_diff_patch *patch = &pd->patch;
bool has_old = ((patch->ofile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
- info->delta.status = has_new ?
+ pd->delta.status = has_new ?
(has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
(has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
if (git_oid_equal(&patch->nfile.file.oid, &patch->ofile.file.oid))
- info->delta.status = GIT_DELTA_UNMODIFIED;
+ pd->delta.status = GIT_DELTA_UNMODIFIED;
- patch->delta = &info->delta;
+ patch->delta = &pd->delta;
diff_patch_init_common(patch);
- error = diff_patch_file_callback(patch, (git_diff_output *)&info->xo);
+ error = diff_patch_file_callback(patch, (git_diff_output *)xo);
if (!error)
- error = diff_patch_generate(patch, (git_diff_output *)&info->xo);
+ error = diff_patch_generate(patch, (git_diff_output *)xo);
if (error == GIT_EUSER)
giterr_clear(); /* don't leave error message set invalidly */
@@ -299,24 +298,23 @@ static int diff_single_generate(diff_single_info *info)
return error;
}
-int git_diff_blobs(
+static int diff_patch_from_blobs(
+ diff_patch_with_delta *pd,
+ git_xdiff_output *xo,
const git_blob *old_blob,
const git_blob *new_blob,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
- void *payload)
+ const git_diff_options *opts)
{
int error = 0;
- diff_single_info info;
git_repository *repo =
new_blob ? git_object_owner((const git_object *)new_blob) :
old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
- if (!repo) /* Hmm, given two NULL blobs, silently do no callbacks? */
+ pd->patch.delta = &pd->delta;
+
+ if (!repo) /* return two NULL items as UNMODIFIED delta */
return 0;
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
@@ -325,64 +323,163 @@ int git_diff_blobs(
new_blob = swap;
}
- memset(&info, 0, sizeof(info));
+ if ((error = diff_file_content_init_from_blob(
+ &pd->patch.ofile, repo, opts, old_blob)) < 0 ||
+ (error = diff_file_content_init_from_blob(
+ &pd->patch.nfile, repo, opts, new_blob)) < 0)
+ return error;
- diff_output_init((git_diff_output *)&info.xo,
- opts, file_cb, hunk_cb, data_cb, payload);
- git_xdiff_init(&info.xo, opts);
+ return diff_single_generate(pd, xo);
+}
- if (!(error = diff_file_content_init_from_blob(
- &info.patch.ofile, repo, opts, old_blob)) &&
- !(error = diff_file_content_init_from_blob(
- &info.patch.nfile, repo, opts, new_blob)))
- error = diff_single_generate(&info);
+int git_diff_blobs(
+ const git_blob *old_blob,
+ const git_blob *new_blob,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_data_cb data_cb,
+ void *payload)
+{
+ int error = 0;
+ diff_patch_with_delta pd;
+ git_xdiff_output xo;
- git_diff_patch_free(&info.patch);
+ memset(&pd, 0, sizeof(pd));
+ memset(&xo, 0, sizeof(xo));
+
+ diff_output_init(
+ (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+ git_xdiff_init(&xo, opts);
+
+ error = diff_patch_from_blobs(&pd, &xo, old_blob, new_blob, opts);
+
+ git_diff_patch_free((git_diff_patch *)&pd);
return error;
}
-int git_diff_blob_to_buffer(
+int git_diff_patch_from_blobs(
+ git_diff_patch **out,
+ const git_blob *old_blob,
+ const git_blob *new_blob,
+ const git_diff_options *opts)
+{
+ int error = 0;
+ diff_patch_with_delta *pd;
+ git_xdiff_output xo;
+
+ assert(out);
+ *out = NULL;
+
+ pd = git__calloc(1, sizeof(*pd));
+ GITERR_CHECK_ALLOC(pd);
+ pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
+
+ memset(&xo, 0, sizeof(xo));
+
+ diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+ git_xdiff_init(&xo, opts);
+
+ if (!(error = diff_patch_from_blobs(pd, &xo, old_blob, new_blob, opts)))
+ *out = (git_diff_patch *)pd;
+ else
+ git_diff_patch_free((git_diff_patch *)pd);
+
+ return error;
+}
+
+static int diff_patch_from_blob_and_buffer(
+ diff_patch_with_delta *pd,
+ git_xdiff_output *xo,
const git_blob *old_blob,
const char *buf,
size_t buflen,
- const git_diff_options *opts,
- git_diff_file_cb file_cb,
- git_diff_hunk_cb hunk_cb,
- git_diff_data_cb data_cb,
- void *payload)
+ const git_diff_options *opts)
{
int error = 0;
- diff_single_info info;
git_repository *repo =
old_blob ? git_object_owner((const git_object *)old_blob) : NULL;
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
- if (!repo && !buf) /* Hmm, given NULLs, silently do no callbacks? */
- return 0;
-
- memset(&info, 0, sizeof(info));
+ pd->patch.delta = &pd->delta;
- diff_output_init((git_diff_output *)&info.xo,
- opts, file_cb, hunk_cb, data_cb, payload);
- git_xdiff_init(&info.xo, opts);
+ if (!repo && !buf) /* return two NULL items as UNMODIFIED delta */
+ return 0;
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
if (!(error = diff_file_content_init_from_raw(
- &info.patch.ofile, repo, opts, buf, buflen)))
+ &pd->patch.ofile, repo, opts, buf, buflen)))
error = diff_file_content_init_from_blob(
- &info.patch.nfile, repo, opts, old_blob);
+ &pd->patch.nfile, repo, opts, old_blob);
} else {
if (!(error = diff_file_content_init_from_blob(
- &info.patch.ofile, repo, opts, old_blob)))
+ &pd->patch.ofile, repo, opts, old_blob)))
error = diff_file_content_init_from_raw(
- &info.patch.nfile, repo, opts, buf, buflen);
+ &pd->patch.nfile, repo, opts, buf, buflen);
}
- error = diff_single_generate(&info);
+ return diff_single_generate(pd, xo);
+}
+
+int git_diff_blob_to_buffer(
+ const git_blob *old_blob,
+ const char *buf,
+ size_t buflen,
+ const git_diff_options *opts,
+ git_diff_file_cb file_cb,
+ git_diff_hunk_cb hunk_cb,
+ git_diff_data_cb data_cb,
+ void *payload)
+{
+ int error = 0;
+ diff_patch_with_delta pd;
+ git_xdiff_output xo;
- git_diff_patch_free(&info.patch);
+ memset(&pd, 0, sizeof(pd));
+ memset(&xo, 0, sizeof(xo));
+
+ diff_output_init(
+ (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
+ git_xdiff_init(&xo, opts);
+
+ error = diff_patch_from_blob_and_buffer(
+ &pd, &xo, old_blob, buf, buflen, opts);
+
+ git_diff_patch_free((git_diff_patch *)&pd);
+
+ return error;
+}
+
+int git_diff_patch_from_blob_and_buffer(
+ git_diff_patch **out,
+ const git_blob *old_blob,
+ const char *buf,
+ size_t buflen,
+ const git_diff_options *opts)
+{
+ int error = 0;
+ diff_patch_with_delta *pd;
+ git_xdiff_output xo;
+
+ assert(out);
+ *out = NULL;
+
+ pd = git__calloc(1, sizeof(*pd));
+ GITERR_CHECK_ALLOC(pd);
+ pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
+
+ memset(&xo, 0, sizeof(xo));
+
+ diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
+ git_xdiff_init(&xo, opts);
+
+ if (!(error = diff_patch_from_blob_and_buffer(
+ pd, &xo, old_blob, buf, buflen, opts)))
+ *out = (git_diff_patch *)pd;
+ else
+ git_diff_patch_free((git_diff_patch *)pd);
return error;
}
@@ -599,9 +696,7 @@ static int diff_patch_file_cb(
float progress,
void *payload)
{
- GIT_UNUSED(delta);
- GIT_UNUSED(progress);
- GIT_UNUSED(payload);
+ GIT_UNUSED(delta); GIT_UNUSED(progress); GIT_UNUSED(payload);
return 0;
}
diff --git a/tests-clar/diff/blob.c b/tests-clar/diff/blob.c
index 2ac8dbc51..b12186d98 100644
--- a/tests-clar/diff/blob.c
+++ b/tests-clar/diff/blob.c
@@ -120,6 +120,93 @@ void test_diff_blob__can_compare_text_blobs(void)
git_blob_free(c);
}
+void test_diff_blob__can_compare_text_blobs_with_patch(void)
+{
+ git_blob *a, *b, *c;
+ git_oid a_oid, b_oid, c_oid;
+ git_diff_patch *p;
+ size_t tc, ta, td;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
+
+ /* tests/resources/attr/root_test2 */
+ cl_git_pass(git_oid_fromstrn(&b_oid, "4d713dc4", 8));
+ cl_git_pass(git_blob_lookup_prefix(&b, g_repo, &b_oid, 4));
+
+ /* tests/resources/attr/root_test3 */
+ cl_git_pass(git_oid_fromstrn(&c_oid, "c96bbb2c2557a832", 16));
+ cl_git_pass(git_blob_lookup_prefix(&c, g_repo, &c_oid, 8));
+
+ /* Doing the equivalent of a `git diff -U1` on these files */
+
+ /* diff on tests/resources/attr/root_test1 */
+ cl_git_pass(git_diff_patch_from_blobs(&p, a, b, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(1, (int)tc);
+ cl_assert_equal_i(5, (int)ta);
+ cl_assert_equal_i(0, (int)td);
+
+ git_diff_patch_free(p);
+
+ /* diff on tests/resources/attr/root_test2 */
+ cl_git_pass(git_diff_patch_from_blobs(&p, b, c, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(15, git_diff_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(3, (int)tc);
+ cl_assert_equal_i(9, (int)ta);
+ cl_assert_equal_i(3, (int)td);
+
+ git_diff_patch_free(p);
+
+ /* diff on tests/resources/attr/root_test3 */
+ cl_git_pass(git_diff_patch_from_blobs(&p, a, c, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(13, git_diff_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(0, (int)tc);
+ cl_assert_equal_i(12, (int)ta);
+ cl_assert_equal_i(1, (int)td);
+
+ git_diff_patch_free(p);
+
+ /* one more */
+ cl_git_pass(git_diff_patch_from_blobs(&p, c, d, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(5, git_diff_patch_num_lines_in_hunk(p, 0));
+ cl_assert_equal_i(9, git_diff_patch_num_lines_in_hunk(p, 1));
+
+ cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(4, (int)tc);
+ cl_assert_equal_i(6, (int)ta);
+ cl_assert_equal_i(4, (int)td);
+
+ git_diff_patch_free(p);
+
+ git_blob_free(a);
+ git_blob_free(b);
+ git_blob_free(c);
+}
+
void test_diff_blob__can_compare_against_null_blobs(void)
{
git_blob *e = NULL;
@@ -175,6 +262,66 @@ void test_diff_blob__can_compare_against_null_blobs(void)
cl_assert_equal_i(0, expected.lines);
}
+void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
+{
+ git_blob *e = NULL;
+ git_diff_patch *p;
+ int line;
+ char origin;
+
+ cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
+
+ for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) {
+ cl_git_pass(git_diff_patch_get_line_in_hunk(
+ &origin, NULL, NULL, NULL, NULL, p, 0, line));
+ cl_assert_equal_i(GIT_DIFF_LINE_DELETION, (int)origin);
+ }
+
+ git_diff_patch_free(p);
+
+ opts.flags |= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
+
+ for (line = 0; line < git_diff_patch_num_lines_in_hunk(p, 0); ++line) {
+ cl_git_pass(git_diff_patch_get_line_in_hunk(
+ &origin, NULL, NULL, NULL, NULL, p, 0, line));
+ cl_assert_equal_i(GIT_DIFF_LINE_ADDITION, (int)origin);
+ }
+
+ git_diff_patch_free(p);
+
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status);
+ cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0);
+ cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+
+ git_diff_patch_free(p);
+
+ cl_git_pass(git_diff_patch_from_blobs(&p, NULL, alien, &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
+ cl_assert((git_diff_patch_delta(p)->flags & GIT_DIFF_FLAG_BINARY) != 0);
+ cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+
+ git_diff_patch_free(p);
+}
+
static void assert_identical_blobs_comparison(diff_expects *expected)
{
cl_assert_equal_i(1, expected->files);
@@ -206,6 +353,29 @@ void test_diff_blob__can_compare_identical_blobs(void)
assert_identical_blobs_comparison(&expected);
}
+void test_diff_blob__can_compare_identical_blobs_with_patch(void)
+{
+ git_diff_patch *p;
+
+ cl_git_pass(git_diff_patch_from_blobs(&p, d, d, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+ git_diff_patch_free(p);
+
+ cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+ git_diff_patch_free(p);
+
+ cl_git_pass(git_diff_patch_from_blobs(&p, alien, alien, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+ git_diff_patch_free(p);
+}
+
static void assert_binary_blobs_comparison(diff_expects *expected)
{
cl_assert(expected->files_binary > 0);
@@ -428,6 +598,74 @@ void test_diff_blob__can_compare_blob_to_buffer(void)
git_blob_free(a);
}
+void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
+{
+ git_diff_patch *p;
+ git_blob *a;
+ git_oid a_oid;
+ const char *a_content = "Hello from the root\n";
+ const char *b_content = "Hello from the root\n\nSome additional lines\n\nDown here below\n\n";
+ size_t tc, ta, td;
+
+ /* tests/resources/attr/root_test1 */
+ cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
+ cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
+
+ /* diff from blob a to content of b */
+ cl_git_pass(git_diff_patch_from_blob_and_buffer(
+ &p, a, b_content, strlen(b_content), &opts));
+
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0));
+
+ cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
+ cl_assert_equal_i(1, (int)tc);
+ cl_assert_equal_i(5, (int)ta);
+ cl_assert_equal_i(0, (int)td);
+
+ git_diff_patch_free(p);
+
+ /* diff from blob a to content of a */
+ cl_git_pass(git_diff_patch_from_blob_and_buffer(
+ &p, a, a_content, strlen(a_content), &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
+ git_diff_patch_free(p);
+
+ /* diff from NULL blob to content of a */
+ cl_git_pass(git_diff_patch_from_blob_and_buffer(
+ &p, NULL, a_content, strlen(a_content), &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
+ git_diff_patch_free(p);
+
+ /* diff from blob a to NULL buffer */
+ cl_git_pass(git_diff_patch_from_blob_and_buffer(
+ &p, a, NULL, 0, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
+ git_diff_patch_free(p);
+
+ /* diff with reverse */
+ opts.flags ^= GIT_DIFF_REVERSE;
+
+ cl_git_pass(git_diff_patch_from_blob_and_buffer(
+ &p, a, NULL, 0, &opts));
+ cl_assert(p != NULL);
+ cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
+ cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p));
+ cl_assert_equal_i(1, git_diff_patch_num_lines_in_hunk(p, 0));
+ git_diff_patch_free(p);
+
+ git_blob_free(a);
+}
static void assert_one_modified_with_lines(diff_expects *expected, int lines)
{