Commit 6789b7a7 by Russell Belfer

Add buffer to buffer diff and patch APIs

This adds `git_diff_buffers` and `git_patch_from_buffers`.  This
also includes a bunch of internal refactoring to increase the
shared code between these functions and the blob-to-blob and
blob-to-buffer APIs, as well as some higher level assert helpers
in the tests to also remove redundancy.
parent d8839992
...@@ -1013,6 +1013,39 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( ...@@ -1013,6 +1013,39 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
git_diff_line_cb line_cb, git_diff_line_cb line_cb,
void *payload); void *payload);
/**
* Directly run a diff between two buffers.
*
* Even more than with `git_diff_blobs`, comparing two buffer lacks
* context, so the `git_diff_file` parameters to the callbacks will be
* faked a la the rules for `git_diff_blobs()`.
*
* @param old_buffer Raw data for old side of diff, or NULL for empty
* @param old_len Length of the raw data for old side of the diff
* @param old_as_path Treat old buffer as if it had this filename; can be NULL
* @param new_buffer Raw data for new side of diff, or NULL for empty
* @param new_len Length of raw data for new side of diff
* @param new_as_path Treat buffer as if it had this filename; can be NULL
* @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
* @param hunk_cb Callback for each hunk in diff; can be NULL
* @param line_cb Callback for each line in diff; can be NULL
* @param payload Payload passed to each callback function
* @return 0 on success, non-zero callback return value, or error code
*/
GIT_EXTERN(int) git_diff_buffers(
const void *old_buffer,
size_t old_len,
const char *old_as_path,
const void *new_buffer,
size_t new_len,
const char *new_as_path,
const git_diff_options *options,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb line_cb,
void *payload);
GIT_END_DECL GIT_END_DECL
......
...@@ -106,6 +106,34 @@ GIT_EXTERN(int) git_patch_from_blob_and_buffer( ...@@ -106,6 +106,34 @@ GIT_EXTERN(int) git_patch_from_blob_and_buffer(
const git_diff_options *opts); const git_diff_options *opts);
/** /**
* Directly generate a patch from the difference between two buffers.
*
* This is just like `git_diff_buffers()` except it generates a patch
* object for the difference instead of directly making callbacks. You can
* use the standard `git_patch` accessor functions to read the patch
* data, and you must call `git_patch_free()` on the patch when done.
*
* @param out The generated patch; NULL on error
* @param old_buffer Raw data for old side of diff, or NULL for empty
* @param old_len Length of the raw data for old side of the diff
* @param old_as_path Treat old buffer as if it had this filename; can be NULL
* @param new_buffer Raw data for new side of diff, or NULL for empty
* @param new_len Length of raw data for new side of diff
* @param new_as_path Treat buffer as if it had this filename; can be NULL
* @param opts Options for diff, or NULL for default options
* @return 0 on success or error code < 0
*/
GIT_EXTERN(int) git_patch_from_buffers(
git_patch **out,
const void *old_buffer,
size_t old_len,
const char *old_as_path,
const char *new_buffer,
size_t new_len,
const char *new_as_path,
const git_diff_options *opts);
/**
* Free a git_patch object. * Free a git_patch object.
*/ */
GIT_EXTERN(void) git_patch_free(git_patch *patch); GIT_EXTERN(void) git_patch_free(git_patch *patch);
......
...@@ -127,57 +127,38 @@ int git_diff_file_content__init_from_diff( ...@@ -127,57 +127,38 @@ int git_diff_file_content__init_from_diff(
return diff_file_content_init_common(fc, &diff->opts); return diff_file_content_init_common(fc, &diff->opts);
} }
int git_diff_file_content__init_from_blob( int git_diff_file_content__init_from_src(
git_diff_file_content *fc, git_diff_file_content *fc,
git_repository *repo, git_repository *repo,
const git_diff_options *opts, const git_diff_options *opts,
const git_blob *blob, const git_diff_file_content_src *src,
git_diff_file *as_file) git_diff_file *as_file)
{ {
memset(fc, 0, sizeof(*fc)); memset(fc, 0, sizeof(*fc));
fc->repo = repo; fc->repo = repo;
fc->file = as_file; fc->file = as_file;
fc->blob = blob; fc->blob = src->blob;
if (!blob) { if (!src->blob && !src->buf) {
fc->flags |= GIT_DIFF_FLAG__NO_DATA; fc->flags |= GIT_DIFF_FLAG__NO_DATA;
} else { } else {
fc->flags |= GIT_DIFF_FLAG__LOADED; fc->flags |= GIT_DIFF_FLAG__LOADED;
fc->file->flags |= GIT_DIFF_FLAG_VALID_ID; fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
fc->file->size = git_blob_rawsize(blob);
fc->file->mode = GIT_FILEMODE_BLOB; fc->file->mode = GIT_FILEMODE_BLOB;
git_oid_cpy(&fc->file->id, git_blob_id(blob));
fc->map.len = (size_t)fc->file->size; if (src->blob) {
fc->map.data = (char *)git_blob_rawcontent(blob); fc->file->size = git_blob_rawsize(src->blob);
} git_oid_cpy(&fc->file->id, git_blob_id(src->blob));
return diff_file_content_init_common(fc, opts); fc->map.len = (size_t)fc->file->size;
} fc->map.data = (char *)git_blob_rawcontent(src->blob);
} else {
fc->file->size = src->buflen;
git_odb_hash(&fc->file->id, src->buf, src->buflen, GIT_OBJ_BLOB);
int git_diff_file_content__init_from_raw( fc->map.len = src->buflen;
git_diff_file_content *fc, fc->map.data = (char *)src->buf;
git_repository *repo, }
const git_diff_options *opts,
const char *buf,
size_t buflen,
git_diff_file *as_file)
{
memset(fc, 0, sizeof(*fc));
fc->repo = repo;
fc->file = as_file;
if (!buf) {
fc->flags |= GIT_DIFF_FLAG__NO_DATA;
} else {
fc->flags |= GIT_DIFF_FLAG__LOADED;
fc->file->flags |= GIT_DIFF_FLAG_VALID_ID;
fc->file->size = buflen;
fc->file->mode = GIT_FILEMODE_BLOB;
git_odb_hash(&fc->file->id, buf, buflen, GIT_OBJ_BLOB);
fc->map.len = buflen;
fc->map.data = (char *)buf;
} }
return diff_file_content_init_common(fc, opts); return diff_file_content_init_common(fc, opts);
......
...@@ -31,19 +31,21 @@ extern int git_diff_file_content__init_from_diff( ...@@ -31,19 +31,21 @@ extern int git_diff_file_content__init_from_diff(
size_t delta_index, size_t delta_index,
bool use_old); bool use_old);
extern int git_diff_file_content__init_from_blob( typedef struct {
git_diff_file_content *fc, const git_blob *blob;
git_repository *repo, const void *buf;
const git_diff_options *opts, size_t buflen;
const git_blob *blob, const char *as_path;
git_diff_file *as_file); } git_diff_file_content_src;
#define GIT_DIFF_FILE_CONTENT_SRC__BLOB(BLOB,PATH) { (BLOB),NULL,0,(PATH) }
#define GIT_DIFF_FILE_CONTENT_SRC__BUF(BUF,LEN,PATH) { NULL,(BUF),(LEN),(PATH) }
extern int git_diff_file_content__init_from_raw( extern int git_diff_file_content__init_from_src(
git_diff_file_content *fc, git_diff_file_content *fc,
git_repository *repo, git_repository *repo,
const git_diff_options *opts, const git_diff_options *opts,
const char *buf, const git_diff_file_content_src *src,
size_t buflen,
git_diff_file *as_file); git_diff_file *as_file);
/* this loads the blob/file-on-disk as needed */ /* this loads the blob/file-on-disk as needed */
......
...@@ -5,6 +5,7 @@ ...@@ -5,6 +5,7 @@
* a Linking Exception. For full terms see the included COPYING file. * a Linking Exception. For full terms see the included COPYING file.
*/ */
#include "common.h" #include "common.h"
#include "git2/blob.h"
#include "diff.h" #include "diff.h"
#include "diff_file.h" #include "diff_file.h"
#include "diff_driver.h" #include "diff_driver.h"
...@@ -334,38 +335,45 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) ...@@ -334,38 +335,45 @@ static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
return error; return error;
} }
static int diff_patch_from_blobs( static int diff_patch_from_sources(
diff_patch_with_delta *pd, diff_patch_with_delta *pd,
git_xdiff_output *xo, git_xdiff_output *xo,
const git_blob *old_blob, git_diff_file_content_src *oldsrc,
const char *old_path, git_diff_file_content_src *newsrc,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; int error = 0;
git_repository *repo = git_repository *repo =
new_blob ? git_object_owner((const git_object *)new_blob) : oldsrc->blob ? git_blob_owner(oldsrc->blob) :
old_blob ? git_object_owner((const git_object *)old_blob) : NULL; newsrc->blob ? git_blob_owner(newsrc->blob) : NULL;
git_diff_file *lfile = &pd->delta.old_file, *rfile = &pd->delta.new_file;
git_diff_file_content *ldata = &pd->patch.ofile, *rdata = &pd->patch.nfile;
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
const git_blob *tmp_blob; void *tmp = lfile; lfile = rfile; rfile = tmp;
const char *tmp_path; tmp = ldata; ldata = rdata; rdata = tmp;
tmp_blob = old_blob; old_blob = new_blob; new_blob = tmp_blob;
tmp_path = old_path; old_path = new_path; new_path = tmp_path;
} }
pd->patch.delta = &pd->delta; pd->patch.delta = &pd->delta;
pd->delta.old_file.path = old_path; if (!oldsrc->as_path) {
pd->delta.new_file.path = new_path; if (newsrc->as_path)
oldsrc->as_path = newsrc->as_path;
else
oldsrc->as_path = newsrc->as_path = "file";
}
else if (!newsrc->as_path)
newsrc->as_path = oldsrc->as_path;
lfile->path = oldsrc->as_path;
rfile->path = newsrc->as_path;
if ((error = git_diff_file_content__init_from_blob( if ((error = git_diff_file_content__init_from_src(
&pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)) < 0 || ldata, repo, opts, oldsrc, lfile)) < 0 ||
(error = git_diff_file_content__init_from_blob( (error = git_diff_file_content__init_from_src(
&pd->patch.nfile, repo, opts, new_blob, &pd->delta.new_file)) < 0) rdata, repo, opts, newsrc, rfile)) < 0)
return error; return error;
return diff_single_generate(pd, xo); return diff_single_generate(pd, xo);
...@@ -400,11 +408,9 @@ static int diff_patch_with_delta_alloc( ...@@ -400,11 +408,9 @@ static int diff_patch_with_delta_alloc(
return 0; return 0;
} }
int git_diff_blobs( static int diff_from_sources(
const git_blob *old_blob, git_diff_file_content_src *oldsrc,
const char *old_path, git_diff_file_content_src *newsrc,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts, const git_diff_options *opts,
git_diff_file_cb file_cb, git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb, git_diff_hunk_cb hunk_cb,
...@@ -420,26 +426,19 @@ int git_diff_blobs( ...@@ -420,26 +426,19 @@ int git_diff_blobs(
&xo.output, opts, file_cb, hunk_cb, data_cb, payload); &xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
if (!old_path && new_path)
old_path = new_path;
else if (!new_path && old_path)
new_path = old_path;
memset(&pd, 0, sizeof(pd)); memset(&pd, 0, sizeof(pd));
error = diff_patch_from_blobs(
&pd, &xo, old_blob, old_path, new_blob, new_path, opts); error = diff_patch_from_sources(&pd, &xo, oldsrc, newsrc, opts);
git_patch_free(&pd.patch); git_patch_free(&pd.patch);
return error; return error;
} }
int git_patch_from_blobs( static int patch_from_sources(
git_patch **out, git_patch **out,
const git_blob *old_blob, git_diff_file_content_src *oldsrc,
const char *old_path, git_diff_file_content_src *newsrc,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; int error = 0;
...@@ -449,17 +448,15 @@ int git_patch_from_blobs( ...@@ -449,17 +448,15 @@ int git_patch_from_blobs(
assert(out); assert(out);
*out = NULL; *out = NULL;
if (diff_patch_with_delta_alloc(&pd, &old_path, &new_path) < 0) if ((error = diff_patch_with_delta_alloc(
return -1; &pd, &oldsrc->as_path, &newsrc->as_path)) < 0)
return error;
memset(&xo, 0, sizeof(xo)); memset(&xo, 0, sizeof(xo));
diff_output_to_patch(&xo.output, &pd->patch); diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
error = diff_patch_from_blobs( if (!(error = diff_patch_from_sources(pd, &xo, oldsrc, newsrc, opts)))
pd, &xo, old_blob, old_path, new_blob, new_path, opts);
if (!error)
*out = (git_patch *)pd; *out = (git_patch *)pd;
else else
git_patch_free((git_patch *)pd); git_patch_free((git_patch *)pd);
...@@ -467,46 +464,38 @@ int git_patch_from_blobs( ...@@ -467,46 +464,38 @@ int git_patch_from_blobs(
return error; return error;
} }
static int diff_patch_from_blob_and_buffer( int git_diff_blobs(
diff_patch_with_delta *pd,
git_xdiff_output *xo,
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path, const char *old_path,
const char *buf, const git_blob *new_blob,
size_t buflen, const char *new_path,
const char *buf_path, const git_diff_options *opts,
const git_diff_options *opts) git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb data_cb,
void *payload)
{ {
int error = 0; git_diff_file_content_src osrc =
git_repository *repo = GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
old_blob ? git_object_owner((const git_object *)old_blob) : NULL; git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); return diff_from_sources(
&osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload);
pd->patch.delta = &pd->delta; }
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
pd->delta.old_file.path = buf_path;
pd->delta.new_file.path = old_path;
if (!(error = git_diff_file_content__init_from_raw(
&pd->patch.ofile, repo, opts, buf, buflen, &pd->delta.old_file)))
error = git_diff_file_content__init_from_blob(
&pd->patch.nfile, repo, opts, old_blob, &pd->delta.new_file);
} else {
pd->delta.old_file.path = old_path;
pd->delta.new_file.path = buf_path;
if (!(error = git_diff_file_content__init_from_blob(
&pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)))
error = git_diff_file_content__init_from_raw(
&pd->patch.nfile, repo, opts, buf, buflen, &pd->delta.new_file);
}
if (error < 0)
return error;
return diff_single_generate(pd, xo); int git_patch_from_blobs(
git_patch **out,
const git_blob *old_blob,
const char *old_path,
const git_blob *new_blob,
const char *new_path,
const git_diff_options *opts)
{
git_diff_file_content_src osrc =
GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BLOB(new_blob, new_path);
return patch_from_sources(out, &osrc, &nsrc, opts);
} }
int git_diff_blob_to_buffer( int git_diff_blob_to_buffer(
...@@ -521,27 +510,12 @@ int git_diff_blob_to_buffer( ...@@ -521,27 +510,12 @@ int git_diff_blob_to_buffer(
git_diff_line_cb data_cb, git_diff_line_cb data_cb,
void *payload) void *payload)
{ {
int error = 0; git_diff_file_content_src osrc =
diff_patch_with_delta pd; GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
git_xdiff_output xo; git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
memset(&xo, 0, sizeof(xo)); return diff_from_sources(
diff_output_init( &osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload);
&xo.output, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts);
if (!old_path && buf_path)
old_path = buf_path;
else if (!buf_path && old_path)
buf_path = old_path;
memset(&pd, 0, sizeof(pd));
error = diff_patch_from_blob_and_buffer(
&pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
git_patch_free(&pd.patch);
return error;
} }
int git_patch_from_blob_and_buffer( int git_patch_from_blob_and_buffer(
...@@ -553,29 +527,49 @@ int git_patch_from_blob_and_buffer( ...@@ -553,29 +527,49 @@ int git_patch_from_blob_and_buffer(
const char *buf_path, const char *buf_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; git_diff_file_content_src osrc =
diff_patch_with_delta *pd; GIT_DIFF_FILE_CONTENT_SRC__BLOB(old_blob, old_path);
git_xdiff_output xo; git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(buf, buflen, buf_path);
assert(out); return patch_from_sources(out, &osrc, &nsrc, opts);
*out = NULL; }
if (diff_patch_with_delta_alloc(&pd, &old_path, &buf_path) < 0)
return -1;
memset(&xo, 0, sizeof(xo));
diff_output_to_patch(&xo.output, &pd->patch);
git_xdiff_init(&xo, opts);
error = diff_patch_from_blob_and_buffer(
pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
if (!error) int git_diff_buffers(
*out = (git_patch *)pd; const void *old_buf,
else size_t old_len,
git_patch_free((git_patch *)pd); const char *old_path,
const void *new_buf,
size_t new_len,
const char *new_path,
const git_diff_options *opts,
git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb,
git_diff_line_cb data_cb,
void *payload)
{
git_diff_file_content_src osrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
return diff_from_sources(
&osrc, &nsrc, opts, file_cb, hunk_cb, data_cb, payload);
}
return error; int git_patch_from_buffers(
git_patch **out,
const void *old_buf,
size_t old_len,
const char *old_path,
const char *new_buf,
size_t new_len,
const char *new_path,
const git_diff_options *opts)
{
git_diff_file_content_src osrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(old_buf, old_len, old_path);
git_diff_file_content_src nsrc =
GIT_DIFF_FILE_CONTENT_SRC__BUF(new_buf, new_len, new_path);
return patch_from_sources(out, &osrc, &nsrc, opts);
} }
int git_patch_from_diff( int git_patch_from_diff(
......
...@@ -51,6 +51,20 @@ void test_diff_blob__cleanup(void) ...@@ -51,6 +51,20 @@ void test_diff_blob__cleanup(void)
cl_git_sandbox_cleanup(); cl_git_sandbox_cleanup();
} }
static void assert_one_modified(
int hunks, int lines, int ctxt, int adds, int dels, diff_expects *exp)
{
cl_assert_equal_i(1, exp->files);
cl_assert_equal_i(1, exp->file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, exp->files_binary);
cl_assert_equal_i(hunks, exp->hunks);
cl_assert_equal_i(lines, exp->lines);
cl_assert_equal_i(ctxt, exp->line_ctxt);
cl_assert_equal_i(adds, exp->line_adds);
cl_assert_equal_i(dels, exp->line_dels);
}
void test_diff_blob__can_compare_text_blobs(void) void test_diff_blob__can_compare_text_blobs(void)
{ {
git_blob *a, *b, *c; git_blob *a, *b, *c;
...@@ -71,79 +85,81 @@ void test_diff_blob__can_compare_text_blobs(void) ...@@ -71,79 +85,81 @@ void test_diff_blob__can_compare_text_blobs(void)
/* Doing the equivalent of a `git diff -U1` on these files */ /* Doing the equivalent of a `git diff -U1` on these files */
/* diff on tests/resources/attr/root_test1 */ /* diff on tests/resources/attr/root_test1 */
memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
a, NULL, b, NULL, &opts, a, NULL, b, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified(1, 6, 1, 5, 0, &expected);
cl_assert_equal_i(1, expected.files); /* same diff but use direct buffers */
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); memset(&expected, 0, sizeof(expected));
cl_assert_equal_i(0, expected.files_binary); cl_git_pass(git_diff_buffers(
git_blob_rawcontent(a), (size_t)git_blob_rawsize(a), NULL,
cl_assert_equal_i(1, expected.hunks); git_blob_rawcontent(b), (size_t)git_blob_rawsize(b), NULL, &opts,
cl_assert_equal_i(6, expected.lines); diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.line_ctxt); assert_one_modified(1, 6, 1, 5, 0, &expected);
cl_assert_equal_i(5, expected.line_adds);
cl_assert_equal_i(0, expected.line_dels);
/* diff on tests/resources/attr/root_test2 */ /* diff on tests/resources/attr/root_test2 */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
b, NULL, c, NULL, &opts, b, NULL, c, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified(1, 15, 3, 9, 3, &expected);
cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary);
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 */ /* diff on tests/resources/attr/root_test3 */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
a, NULL, c, NULL, &opts, a, NULL, c, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified(1, 13, 0, 12, 1, &expected);
cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary);
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)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
c, NULL, d, NULL, &opts, c, NULL, d, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified(2, 14, 4, 6, 4, &expected);
cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary);
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(a);
git_blob_free(b); git_blob_free(b);
git_blob_free(c); git_blob_free(c);
} }
static void assert_patch_matches_blobs(
git_patch *p, git_blob *a, git_blob *b,
int hunks, int l0, int l1, int ctxt, int adds, int dels)
{
const git_diff_delta *delta;
size_t tc, ta, td;
cl_assert(p != NULL);
delta = git_patch_get_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.id));
cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.id));
cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
cl_assert_equal_i(hunks, (int)git_patch_num_hunks(p));
if (hunks > 0)
cl_assert_equal_i(l0, git_patch_num_lines_in_hunk(p, 0));
if (hunks > 1)
cl_assert_equal_i(l1, git_patch_num_lines_in_hunk(p, 1));
cl_git_pass(git_patch_line_stats(&tc, &ta, &td, p));
cl_assert_equal_i(ctxt, (int)tc);
cl_assert_equal_i(adds, (int)ta);
cl_assert_equal_i(dels, (int)td);
}
void test_diff_blob__can_compare_text_blobs_with_patch(void) void test_diff_blob__can_compare_text_blobs_with_patch(void)
{ {
git_blob *a, *b, *c; git_blob *a, *b, *c;
git_oid a_oid, b_oid, c_oid; git_oid a_oid, b_oid, c_oid;
git_patch *p; git_patch *p;
const git_diff_delta *delta;
size_t tc, ta, td;
/* tests/resources/attr/root_test1 */ /* tests/resources/attr/root_test1 */
cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8)); cl_git_pass(git_oid_fromstrn(&a_oid, "45141a79", 8));
...@@ -161,92 +177,22 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) ...@@ -161,92 +177,22 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void)
/* diff on tests/resources/attr/root_test1 */ /* diff on tests/resources/attr/root_test1 */
cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, &opts)); cl_git_pass(git_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
assert_patch_matches_blobs(p, a, b, 1, 6, 0, 1, 5, 0);
cl_assert(p != NULL);
delta = git_patch_get_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.id));
cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.id));
cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
cl_assert_equal_i(6, git_patch_num_lines_in_hunk(p, 0));
cl_git_pass(git_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_patch_free(p); git_patch_free(p);
/* diff on tests/resources/attr/root_test2 */ /* diff on tests/resources/attr/root_test2 */
cl_git_pass(git_patch_from_blobs(&p, b, NULL, c, NULL, &opts)); cl_git_pass(git_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
assert_patch_matches_blobs(p, b, c, 1, 15, 0, 3, 9, 3);
cl_assert(p != NULL);
delta = git_patch_get_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
cl_assert(git_oid_equal(git_blob_id(b), &delta->old_file.id));
cl_assert_equal_sz(git_blob_rawsize(b), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.id));
cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
cl_assert_equal_i(1, (int)git_patch_num_hunks(p));
cl_assert_equal_i(15, git_patch_num_lines_in_hunk(p, 0));
cl_git_pass(git_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_patch_free(p); git_patch_free(p);
/* diff on tests/resources/attr/root_test3 */ /* diff on tests/resources/attr/root_test3 */
cl_git_pass(git_patch_from_blobs(&p, a, NULL, c, NULL, &opts)); cl_git_pass(git_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
assert_patch_matches_blobs(p, a, c, 1, 13, 0, 0, 12, 1);
cl_assert(p != NULL);
delta = git_patch_get_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
cl_assert(git_oid_equal(git_blob_id(a), &delta->old_file.id));
cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.id));
cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
cl_git_pass(git_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_patch_free(p); git_patch_free(p);
/* one more */ /* one more */
cl_git_pass(git_patch_from_blobs(&p, c, NULL, d, NULL, &opts)); cl_git_pass(git_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
assert_patch_matches_blobs(p, c, d, 2, 5, 9, 4, 6, 4);
cl_assert(p != NULL);
delta = git_patch_get_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, delta->status);
cl_assert(git_oid_equal(git_blob_id(c), &delta->old_file.id));
cl_assert_equal_sz(git_blob_rawsize(c), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.id));
cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
cl_assert_equal_i(2, (int)git_patch_num_hunks(p));
cl_assert_equal_i(5, git_patch_num_lines_in_hunk(p, 0));
cl_assert_equal_i(9, git_patch_num_lines_in_hunk(p, 1));
cl_git_pass(git_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_patch_free(p); git_patch_free(p);
git_blob_free(a); git_blob_free(a);
...@@ -656,14 +602,7 @@ void test_diff_blob__can_compare_blob_to_buffer(void) ...@@ -656,14 +602,7 @@ void test_diff_blob__can_compare_blob_to_buffer(void)
/* diff from blob a to content of b */ /* diff from blob a to content of b */
quick_diff_blob_to_str(a, NULL, b_content, 0, NULL); quick_diff_blob_to_str(a, NULL, b_content, 0, NULL);
cl_assert_equal_i(1, expected.files); assert_one_modified(1, 6, 1, 5, 0, &expected);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary);
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 from blob a to content of a */ /* diff from blob a to content of a */
opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED; opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
...@@ -910,14 +849,7 @@ void test_diff_blob__using_path_and_attributes(void) ...@@ -910,14 +849,7 @@ void test_diff_blob__using_path_and_attributes(void)
changed = "Hello from the root\nMore lines\nAnd more\nGo here\n"; changed = "Hello from the root\nMore lines\nAnd more\nGo here\n";
quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL); quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL);
cl_assert_equal_i(1, expected.files); assert_one_modified(1, 3, 0, 3, 0, &expected);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary);
cl_assert_equal_i(1, expected.hunks);
cl_assert_equal_i(3, expected.lines);
cl_assert_equal_i(0, expected.line_ctxt);
cl_assert_equal_i(3, expected.line_adds);
cl_assert_equal_i(0, expected.line_dels);
quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL); quick_diff_blob_to_str(nonbin, "foo/bar.binary", changed, 0, NULL);
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
...@@ -925,29 +857,12 @@ void test_diff_blob__using_path_and_attributes(void) ...@@ -925,29 +857,12 @@ void test_diff_blob__using_path_and_attributes(void)
cl_assert_equal_i(1, expected.files_binary); cl_assert_equal_i(1, expected.files_binary);
cl_assert_equal_i(0, expected.hunks); cl_assert_equal_i(0, expected.hunks);
cl_assert_equal_i(0, expected.lines); cl_assert_equal_i(0, expected.lines);
cl_assert_equal_i(0, expected.line_ctxt);
cl_assert_equal_i(0, expected.line_adds);
cl_assert_equal_i(0, expected.line_dels);
quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL); quick_diff_blob_to_str(nonbin, "foo/bar.textary", changed, 0, NULL);
cl_assert_equal_i(1, expected.files); assert_one_modified(1, 3, 0, 3, 0, &expected);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary);
cl_assert_equal_i(1, expected.hunks);
cl_assert_equal_i(3, expected.lines);
cl_assert_equal_i(0, expected.line_ctxt);
cl_assert_equal_i(3, expected.line_adds);
cl_assert_equal_i(0, expected.line_dels);
quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL); quick_diff_blob_to_str(nonbin, "foo/bar.alphary", changed, 0, NULL);
cl_assert_equal_i(1, expected.files); assert_one_modified(1, 3, 0, 3, 0, &expected);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary);
cl_assert_equal_i(1, expected.hunks);
cl_assert_equal_i(3, expected.lines);
cl_assert_equal_i(0, expected.line_ctxt);
cl_assert_equal_i(3, expected.line_adds);
cl_assert_equal_i(0, expected.line_dels);
cl_git_pass(git_patch_from_blob_and_buffer( cl_git_pass(git_patch_from_blob_and_buffer(
&p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts)); &p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts));
...@@ -1066,3 +981,28 @@ void test_diff_blob__using_path_and_attributes(void) ...@@ -1066,3 +981,28 @@ void test_diff_blob__using_path_and_attributes(void)
git_blob_free(nonbin); git_blob_free(nonbin);
git_blob_free(bin); git_blob_free(bin);
} }
void test_diff_blob__can_compare_buffer_to_buffer(void)
{
const char *a = "a\nb\nc\nd\ne\nf\ng\nh\ni\nj\n";
const char *b = "a\nB\nc\nd\nE\nF\nh\nj\nk\n";
opts.interhunk_lines = 0;
opts.context_lines = 0;
memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_buffers(
a, strlen(a), NULL, b, strlen(b), NULL,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified(4, 9, 0, 4, 5, &expected);
opts.flags ^= GIT_DIFF_REVERSE;
memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_buffers(
a, strlen(a), NULL, b, strlen(b), NULL,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified(4, 9, 0, 5, 4, &expected);
}
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