Commit 74ded024 by Russell Belfer

Add "as_path" parameters to blob and buffer diffs

This adds parameters to the four functions that allow for blob-to-
blob and blob-to-buffer differencing (either via callbacks or by
making a git_diff_patch object).  These parameters let you say
that filename we should pretend the blob has while doing the diff.
If you pass NULL, there should be no change from the existing
behavior, which is to skip using attributes for file type checks
and just look at content.  With the parameters, you can plug into
the new diff driver functionality and get binary or non-binary
behavior, plus function context regular expressions, etc.

This commit also fixes things so that the git_diff_delta that is
generated by these functions will actually be populated with the
data that we know about the blobs (or buffers) so you can use it
appropriately.  It also fixes a bug in generating patches from
the git_diff_patch objects created via these functions.

Lastly, there is one other behavior change that may matter.  If
there is no difference between the two blobs, these functions no
longer generate any diff callbacks / patches unless you have
passed in GIT_DIFF_INCLUDE_UNMODIFIED.  This is pretty natural,
but could potentially change the behavior of existing usage.
parent c09810ee
...@@ -983,7 +983,9 @@ GIT_EXTERN(int) git_diff_patch_to_str( ...@@ -983,7 +983,9 @@ GIT_EXTERN(int) git_diff_patch_to_str(
* `GIT_DIFF_FORCE_TEXT` of course). * `GIT_DIFF_FORCE_TEXT` of course).
* *
* @param old_blob Blob for old side of diff, or NULL for empty blob * @param old_blob Blob for old side of diff, or NULL for empty blob
* @param old_as_path Treat old blob as if it had this filename; can be NULL
* @param new_blob Blob for new side of diff, or NULL for empty blob * @param new_blob Blob for new side of diff, or NULL for empty blob
* @param new_as_path Treat new blob as if it had this filename; can be NULL
* @param options Options for diff, or NULL for default options * @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 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 hunk_cb Callback for each hunk in diff; can be NULL
...@@ -993,7 +995,9 @@ GIT_EXTERN(int) git_diff_patch_to_str( ...@@ -993,7 +995,9 @@ GIT_EXTERN(int) git_diff_patch_to_str(
*/ */
GIT_EXTERN(int) git_diff_blobs( GIT_EXTERN(int) git_diff_blobs(
const git_blob *old_blob, const git_blob *old_blob,
const char *old_as_path,
const git_blob *new_blob, const git_blob *new_blob,
const char *new_as_path,
const git_diff_options *options, const git_diff_options *options,
git_diff_file_cb file_cb, git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb, git_diff_hunk_cb hunk_cb,
...@@ -1010,14 +1014,18 @@ GIT_EXTERN(int) git_diff_blobs( ...@@ -1010,14 +1014,18 @@ GIT_EXTERN(int) git_diff_blobs(
* *
* @param out The generated patch; NULL on error * @param out The generated patch; NULL on error
* @param old_blob Blob for old side of diff, or NULL for empty blob * @param old_blob Blob for old side of diff, or NULL for empty blob
* @param old_as_path Treat old blob as if it had this filename; can be NULL
* @param new_blob Blob for new side of diff, or NULL for empty blob * @param new_blob Blob for new side of diff, or NULL for empty blob
* @param new_as_path Treat new blob as if it had this filename; can be NULL
* @param options Options for diff, or NULL for default options * @param options Options for diff, or NULL for default options
* @return 0 on success or error code < 0 * @return 0 on success or error code < 0
*/ */
GIT_EXTERN(int) git_diff_patch_from_blobs( GIT_EXTERN(int) git_diff_patch_from_blobs(
git_diff_patch **out, git_diff_patch **out,
const git_blob *old_blob, const git_blob *old_blob,
const char *old_as_path,
const git_blob *new_blob, const git_blob *new_blob,
const char *new_as_path,
const git_diff_options *opts); const git_diff_options *opts);
/** /**
...@@ -1033,8 +1041,10 @@ GIT_EXTERN(int) git_diff_patch_from_blobs( ...@@ -1033,8 +1041,10 @@ GIT_EXTERN(int) git_diff_patch_from_blobs(
* the reverse, with GIT_DELTA_REMOVED and blob content removed. * 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 old_blob Blob for old side of diff, or NULL for empty blob
* @param old_as_path Treat old blob as if it had this filename; can be NULL
* @param buffer Raw data for new side of diff, or NULL for empty * @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 buffer_len Length of raw data for new side of diff
* @param buffer_as_path Treat buffer as if it had this filename; can be NULL
* @param options Options for diff, or NULL for default options * @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 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 hunk_cb Callback for each hunk in diff; can be NULL
...@@ -1044,8 +1054,10 @@ GIT_EXTERN(int) git_diff_patch_from_blobs( ...@@ -1044,8 +1054,10 @@ GIT_EXTERN(int) git_diff_patch_from_blobs(
*/ */
GIT_EXTERN(int) git_diff_blob_to_buffer( GIT_EXTERN(int) git_diff_blob_to_buffer(
const git_blob *old_blob, const git_blob *old_blob,
const char *old_as_path,
const char *buffer, const char *buffer,
size_t buffer_len, size_t buffer_len,
const char *buffer_as_path,
const git_diff_options *options, const git_diff_options *options,
git_diff_file_cb file_cb, git_diff_file_cb file_cb,
git_diff_hunk_cb hunk_cb, git_diff_hunk_cb hunk_cb,
...@@ -1062,16 +1074,20 @@ GIT_EXTERN(int) git_diff_blob_to_buffer( ...@@ -1062,16 +1074,20 @@ GIT_EXTERN(int) git_diff_blob_to_buffer(
* *
* @param out The generated patch; NULL on error * @param out The generated patch; NULL on error
* @param old_blob Blob for old side of diff, or NULL for empty blob * @param old_blob Blob for old side of diff, or NULL for empty blob
* @param old_as_path Treat old blob as if it had this filename; can be NULL
* @param buffer Raw data for new side of diff, or NULL for empty * @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 buffer_len Length of raw data for new side of diff
* @param buffer_as_path Treat buffer as if it had this filename; can be NULL
* @param options Options for diff, or NULL for default options * @param options Options for diff, or NULL for default options
* @return 0 on success or error code < 0 * @return 0 on success or error code < 0
*/ */
GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer( GIT_EXTERN(int) git_diff_patch_from_blob_and_buffer(
git_diff_patch **out, git_diff_patch **out,
const git_blob *old_blob, const git_blob *old_blob,
const char *buf, const char *old_as_path,
size_t buflen, const char *buffer,
size_t buffer_len,
const char *buffer_as_path,
const git_diff_options *opts); const git_diff_options *opts);
......
...@@ -18,23 +18,23 @@ ...@@ -18,23 +18,23 @@
static bool diff_file_content_binary_by_size(git_diff_file_content *fc) static bool diff_file_content_binary_by_size(git_diff_file_content *fc)
{ {
/* if we have diff opts, check max_size vs file size */ /* if we have diff opts, check max_size vs file size */
if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) == 0 && if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) == 0 &&
fc->opts_max_size > 0 && fc->opts_max_size > 0 &&
fc->file.size > fc->opts_max_size) fc->file->size > fc->opts_max_size)
fc->file.flags |= GIT_DIFF_FLAG_BINARY; fc->file->flags |= GIT_DIFF_FLAG_BINARY;
return ((fc->file.flags & GIT_DIFF_FLAG_BINARY) != 0); return ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0);
} }
static void diff_file_content_binary_by_content(git_diff_file_content *fc) static void diff_file_content_binary_by_content(git_diff_file_content *fc)
{ {
if ((fc->file.flags & DIFF_FLAGS_KNOWN_BINARY) != 0) if ((fc->file->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
return; return;
switch (git_diff_driver_content_is_binary( switch (git_diff_driver_content_is_binary(
fc->driver, fc->map.data, fc->map.len)) { fc->driver, fc->map.data, fc->map.len)) {
case 0: fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; break; case 0: fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY; break;
case 1: fc->file.flags |= GIT_DIFF_FLAG_BINARY; break; case 1: fc->file->flags |= GIT_DIFF_FLAG_BINARY; break;
default: break; default: break;
} }
} }
...@@ -48,38 +48,39 @@ static int diff_file_content_init_common( ...@@ -48,38 +48,39 @@ static int diff_file_content_init_common(
fc->opts_max_size = opts->max_size ? fc->opts_max_size = opts->max_size ?
opts->max_size : DIFF_MAX_FILESIZE; opts->max_size : DIFF_MAX_FILESIZE;
if (!fc->driver) { if (fc->src == GIT_ITERATOR_TYPE_EMPTY)
if (git_diff_driver_lookup(&fc->driver, fc->repo, "") < 0)
return -1;
fc->src = GIT_ITERATOR_TYPE_TREE; fc->src = GIT_ITERATOR_TYPE_TREE;
}
if (!fc->driver &&
git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
return -1;
/* give driver a chance to modify options */ /* give driver a chance to modify options */
git_diff_driver_update_options(&fc->opts_flags, fc->driver); git_diff_driver_update_options(&fc->opts_flags, fc->driver);
/* make sure file is conceivable mmap-able */ /* make sure file is conceivable mmap-able */
if ((git_off_t)((size_t)fc->file.size) != fc->file.size) if ((git_off_t)((size_t)fc->file->size) != fc->file->size)
fc->file.flags |= GIT_DIFF_FLAG_BINARY; fc->file->flags |= GIT_DIFF_FLAG_BINARY;
/* check if user is forcing text diff the file */ /* check if user is forcing text diff the file */
else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) { else if (fc->opts_flags & GIT_DIFF_FORCE_TEXT) {
fc->file.flags &= ~GIT_DIFF_FLAG_BINARY; fc->file->flags &= ~GIT_DIFF_FLAG_BINARY;
fc->file.flags |= GIT_DIFF_FLAG_NOT_BINARY; fc->file->flags |= GIT_DIFF_FLAG_NOT_BINARY;
} }
/* check if user is forcing binary diff the file */ /* check if user is forcing binary diff the file */
else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) { else if (fc->opts_flags & GIT_DIFF_FORCE_BINARY) {
fc->file.flags &= ~GIT_DIFF_FLAG_NOT_BINARY; fc->file->flags &= ~GIT_DIFF_FLAG_NOT_BINARY;
fc->file.flags |= GIT_DIFF_FLAG_BINARY; fc->file->flags |= GIT_DIFF_FLAG_BINARY;
} }
diff_file_content_binary_by_size(fc); diff_file_content_binary_by_size(fc);
if ((fc->file.flags & GIT_DIFF_FLAG__NO_DATA) != 0) { if ((fc->flags & GIT_DIFF_FLAG__NO_DATA) != 0) {
fc->file.flags |= GIT_DIFF_FLAG__LOADED; fc->flags |= GIT_DIFF_FLAG__LOADED;
fc->map.len = 0; fc->map.len = 0;
fc->map.data = ""; fc->map.data = "";
} }
if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0) if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
diff_file_content_binary_by_content(fc); diff_file_content_binary_by_content(fc);
return 0; return 0;
...@@ -92,15 +93,14 @@ int git_diff_file_content__init_from_diff( ...@@ -92,15 +93,14 @@ int git_diff_file_content__init_from_diff(
bool use_old) bool use_old)
{ {
git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index); git_diff_delta *delta = git_vector_get(&diff->deltas, delta_index);
git_diff_file *file = use_old ? &delta->old_file : &delta->new_file;
bool has_data = true; bool has_data = true;
memset(fc, 0, sizeof(*fc)); memset(fc, 0, sizeof(*fc));
fc->repo = diff->repo; fc->repo = diff->repo;
fc->file = use_old ? &delta->old_file : &delta->new_file;
fc->src = use_old ? diff->old_src : diff->new_src; fc->src = use_old ? diff->old_src : diff->new_src;
memcpy(&fc->file, file, sizeof(fc->file));
if (git_diff_driver_lookup(&fc->driver, fc->repo, file->path) < 0) if (git_diff_driver_lookup(&fc->driver, fc->repo, fc->file->path) < 0)
return -1; return -1;
switch (delta->status) { switch (delta->status) {
...@@ -122,7 +122,7 @@ int git_diff_file_content__init_from_diff( ...@@ -122,7 +122,7 @@ int git_diff_file_content__init_from_diff(
} }
if (!has_data) if (!has_data)
fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; fc->flags |= GIT_DIFF_FLAG__NO_DATA;
return diff_file_content_init_common(fc, &diff->opts); return diff_file_content_init_common(fc, &diff->opts);
} }
...@@ -131,21 +131,24 @@ int git_diff_file_content__init_from_blob( ...@@ -131,21 +131,24 @@ int git_diff_file_content__init_from_blob(
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_blob *blob,
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->blob = blob; fc->blob = blob;
if (!blob) { if (!blob) {
fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; fc->flags |= GIT_DIFF_FLAG__NO_DATA;
} else { } else {
fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID; fc->flags |= GIT_DIFF_FLAG__LOADED;
fc->file.size = git_blob_rawsize(blob); fc->file->flags |= GIT_DIFF_FLAG_VALID_OID;
fc->file.mode = 0644; fc->file->size = git_blob_rawsize(blob);
git_oid_cpy(&fc->file.oid, git_blob_id(blob)); fc->file->mode = GIT_FILEMODE_BLOB;
git_oid_cpy(&fc->file->oid, git_blob_id(blob));
fc->map.len = (size_t)fc->file.size; fc->map.len = (size_t)fc->file->size;
fc->map.data = (char *)git_blob_rawcontent(blob); fc->map.data = (char *)git_blob_rawcontent(blob);
} }
...@@ -157,18 +160,21 @@ int git_diff_file_content__init_from_raw( ...@@ -157,18 +160,21 @@ int git_diff_file_content__init_from_raw(
git_repository *repo, git_repository *repo,
const git_diff_options *opts, const git_diff_options *opts,
const char *buf, const char *buf,
size_t buflen) size_t buflen,
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;
if (!buf) { if (!buf) {
fc->file.flags |= GIT_DIFF_FLAG__NO_DATA; fc->flags |= GIT_DIFF_FLAG__NO_DATA;
} else { } else {
fc->file.flags |= GIT_DIFF_FLAG__LOADED | GIT_DIFF_FLAG_VALID_OID; fc->flags |= GIT_DIFF_FLAG__LOADED;
fc->file.size = buflen; fc->file->flags |= GIT_DIFF_FLAG_VALID_OID;
fc->file.mode = 0644; fc->file->size = buflen;
git_odb_hash(&fc->file.oid, buf, buflen, GIT_OBJ_BLOB); fc->file->mode = GIT_FILEMODE_BLOB;
git_odb_hash(&fc->file->oid, buf, buflen, GIT_OBJ_BLOB);
fc->map.len = buflen; fc->map.len = buflen;
fc->map.data = (char *)buf; fc->map.data = (char *)buf;
...@@ -190,7 +196,7 @@ static int diff_file_content_commit_to_str( ...@@ -190,7 +196,7 @@ static int diff_file_content_commit_to_str(
unsigned int sm_status = 0; unsigned int sm_status = 0;
const git_oid *sm_head; const git_oid *sm_head;
if ((error = git_submodule_lookup(&sm, fc->repo, fc->file.path)) < 0 || if ((error = git_submodule_lookup(&sm, fc->repo, fc->file->path)) < 0 ||
(error = git_submodule_status(&sm_status, sm)) < 0) { (error = git_submodule_status(&sm_status, sm)) < 0) {
/* GIT_EEXISTS means a "submodule" that has not been git added */ /* GIT_EEXISTS means a "submodule" that has not been git added */
if (error == GIT_EEXISTS) if (error == GIT_EEXISTS)
...@@ -199,25 +205,25 @@ static int diff_file_content_commit_to_str( ...@@ -199,25 +205,25 @@ static int diff_file_content_commit_to_str(
} }
/* update OID if we didn't have it previously */ /* update OID if we didn't have it previously */
if ((fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0 && if ((fc->file->flags & GIT_DIFF_FLAG_VALID_OID) == 0 &&
((sm_head = git_submodule_wd_id(sm)) != NULL || ((sm_head = git_submodule_wd_id(sm)) != NULL ||
(sm_head = git_submodule_head_id(sm)) != NULL)) (sm_head = git_submodule_head_id(sm)) != NULL))
{ {
git_oid_cpy(&fc->file.oid, sm_head); git_oid_cpy(&fc->file->oid, sm_head);
fc->file.flags |= GIT_DIFF_FLAG_VALID_OID; fc->file->flags |= GIT_DIFF_FLAG_VALID_OID;
} }
if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status)) if (GIT_SUBMODULE_STATUS_IS_WD_DIRTY(sm_status))
status = "-dirty"; status = "-dirty";
} }
git_oid_tostr(oid, sizeof(oid), &fc->file.oid); git_oid_tostr(oid, sizeof(oid), &fc->file->oid);
if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0) if (git_buf_printf(&content, "Subproject commit %s%s\n", oid, status) < 0)
return -1; return -1;
fc->map.len = git_buf_len(&content); fc->map.len = git_buf_len(&content);
fc->map.data = git_buf_detach(&content); fc->map.data = git_buf_detach(&content);
fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
return 0; return 0;
} }
...@@ -227,27 +233,27 @@ static int diff_file_content_load_blob(git_diff_file_content *fc) ...@@ -227,27 +233,27 @@ static int diff_file_content_load_blob(git_diff_file_content *fc)
int error = 0; int error = 0;
git_odb_object *odb_obj = NULL; git_odb_object *odb_obj = NULL;
if (git_oid_iszero(&fc->file.oid)) if (git_oid_iszero(&fc->file->oid))
return 0; return 0;
if (fc->file.mode == GIT_FILEMODE_COMMIT) if (fc->file->mode == GIT_FILEMODE_COMMIT)
return diff_file_content_commit_to_str(fc, false); return diff_file_content_commit_to_str(fc, false);
/* if we don't know size, try to peek at object header first */ /* if we don't know size, try to peek at object header first */
if (!fc->file.size) { if (!fc->file->size) {
git_odb *odb; git_odb *odb;
size_t len; size_t len;
git_otype type; git_otype type;
if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) { if (!(error = git_repository_odb__weakptr(&odb, fc->repo))) {
error = git_odb__read_header_or_object( error = git_odb__read_header_or_object(
&odb_obj, &len, &type, odb, &fc->file.oid); &odb_obj, &len, &type, odb, &fc->file->oid);
git_odb_free(odb); git_odb_free(odb);
} }
if (error) if (error)
return error; return error;
fc->file.size = len; fc->file->size = len;
} }
if (diff_file_content_binary_by_size(fc)) if (diff_file_content_binary_by_size(fc))
...@@ -259,11 +265,11 @@ static int diff_file_content_load_blob(git_diff_file_content *fc) ...@@ -259,11 +265,11 @@ static int diff_file_content_load_blob(git_diff_file_content *fc)
git_odb_object_free(odb_obj); git_odb_object_free(odb_obj);
} else { } else {
error = git_blob_lookup( error = git_blob_lookup(
(git_blob **)&fc->blob, fc->repo, &fc->file.oid); (git_blob **)&fc->blob, fc->repo, &fc->file->oid);
} }
if (!error) { if (!error) {
fc->file.flags |= GIT_DIFF_FLAG__FREE_BLOB; fc->flags |= GIT_DIFF_FLAG__FREE_BLOB;
fc->map.data = (void *)git_blob_rawcontent(fc->blob); fc->map.data = (void *)git_blob_rawcontent(fc->blob);
fc->map.len = (size_t)git_blob_rawsize(fc->blob); fc->map.len = (size_t)git_blob_rawsize(fc->blob);
} }
...@@ -279,16 +285,16 @@ static int diff_file_content_load_workdir_symlink( ...@@ -279,16 +285,16 @@ static int diff_file_content_load_workdir_symlink(
/* link path on disk could be UTF-16, so prepare a buffer that is /* link path on disk could be UTF-16, so prepare a buffer that is
* big enough to handle some UTF-8 data expansion * big enough to handle some UTF-8 data expansion
*/ */
alloc_len = (ssize_t)(fc->file.size * 2) + 1; alloc_len = (ssize_t)(fc->file->size * 2) + 1;
fc->map.data = git__calloc(alloc_len, sizeof(char)); fc->map.data = git__calloc(alloc_len, sizeof(char));
GITERR_CHECK_ALLOC(fc->map.data); GITERR_CHECK_ALLOC(fc->map.data);
fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len); read_len = p_readlink(git_buf_cstr(path), fc->map.data, alloc_len);
if (read_len < 0) { if (read_len < 0) {
giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file.path); giterr_set(GITERR_OS, "Failed to read symlink '%s'", fc->file->path);
return -1; return -1;
} }
...@@ -307,28 +313,28 @@ static int diff_file_content_load_workdir_file( ...@@ -307,28 +313,28 @@ static int diff_file_content_load_workdir_file(
if (fd < 0) if (fd < 0)
return fd; return fd;
if (!fc->file.size && if (!fc->file->size &&
!(fc->file.size = git_futils_filesize(fd))) !(fc->file->size = git_futils_filesize(fd)))
goto cleanup; goto cleanup;
if (diff_file_content_binary_by_size(fc)) if (diff_file_content_binary_by_size(fc))
goto cleanup; goto cleanup;
if ((error = git_filters_load( if ((error = git_filters_load(
&filters, fc->repo, fc->file.path, GIT_FILTER_TO_ODB)) < 0) &filters, fc->repo, fc->file->path, GIT_FILTER_TO_ODB)) < 0)
goto cleanup; goto cleanup;
/* error >= is a filter count */ /* error >= is a filter count */
if (error == 0) { if (error == 0) {
if (!(error = git_futils_mmap_ro( if (!(error = git_futils_mmap_ro(
&fc->map, fd, 0, (size_t)fc->file.size))) &fc->map, fd, 0, (size_t)fc->file->size)))
fc->file.flags |= GIT_DIFF_FLAG__UNMAP_DATA; fc->flags |= GIT_DIFF_FLAG__UNMAP_DATA;
else /* fall through to try readbuffer below */ else /* fall through to try readbuffer below */
giterr_clear(); giterr_clear();
} }
if (error != 0) { if (error != 0) {
error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file.size); error = git_futils_readbuffer_fd(&raw, fd, (size_t)fc->file->size);
if (error < 0) if (error < 0)
goto cleanup; goto cleanup;
...@@ -340,7 +346,7 @@ static int diff_file_content_load_workdir_file( ...@@ -340,7 +346,7 @@ static int diff_file_content_load_workdir_file(
if (!error) { if (!error) {
fc->map.len = git_buf_len(&filtered); fc->map.len = git_buf_len(&filtered);
fc->map.data = git_buf_detach(&filtered); fc->map.data = git_buf_detach(&filtered);
fc->file.flags |= GIT_DIFF_FLAG__FREE_DATA; fc->flags |= GIT_DIFF_FLAG__FREE_DATA;
} }
git_buf_free(&raw); git_buf_free(&raw);
...@@ -359,26 +365,26 @@ static int diff_file_content_load_workdir(git_diff_file_content *fc) ...@@ -359,26 +365,26 @@ static int diff_file_content_load_workdir(git_diff_file_content *fc)
int error = 0; int error = 0;
git_buf path = GIT_BUF_INIT; git_buf path = GIT_BUF_INIT;
if (fc->file.mode == GIT_FILEMODE_COMMIT) if (fc->file->mode == GIT_FILEMODE_COMMIT)
return diff_file_content_commit_to_str(fc, true); return diff_file_content_commit_to_str(fc, true);
if (fc->file.mode == GIT_FILEMODE_TREE) if (fc->file->mode == GIT_FILEMODE_TREE)
return 0; return 0;
if (git_buf_joinpath( if (git_buf_joinpath(
&path, git_repository_workdir(fc->repo), fc->file.path) < 0) &path, git_repository_workdir(fc->repo), fc->file->path) < 0)
return -1; return -1;
if (S_ISLNK(fc->file.mode)) if (S_ISLNK(fc->file->mode))
error = diff_file_content_load_workdir_symlink(fc, &path); error = diff_file_content_load_workdir_symlink(fc, &path);
else else
error = diff_file_content_load_workdir_file(fc, &path); error = diff_file_content_load_workdir_file(fc, &path);
/* once data is loaded, update OID if we didn't have it previously */ /* once data is loaded, update OID if we didn't have it previously */
if (!error && (fc->file.flags & GIT_DIFF_FLAG_VALID_OID) == 0) { if (!error && (fc->file->flags & GIT_DIFF_FLAG_VALID_OID) == 0) {
error = git_odb_hash( error = git_odb_hash(
&fc->file.oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB); &fc->file->oid, fc->map.data, fc->map.len, GIT_OBJ_BLOB);
fc->file.flags |= GIT_DIFF_FLAG_VALID_OID; fc->file->flags |= GIT_DIFF_FLAG_VALID_OID;
} }
git_buf_free(&path); git_buf_free(&path);
...@@ -389,10 +395,10 @@ int git_diff_file_content__load(git_diff_file_content *fc) ...@@ -389,10 +395,10 @@ int git_diff_file_content__load(git_diff_file_content *fc)
{ {
int error = 0; int error = 0;
if ((fc->file.flags & GIT_DIFF_FLAG__LOADED) != 0) if ((fc->flags & GIT_DIFF_FLAG__LOADED) != 0)
return 0; return 0;
if (fc->file.flags & GIT_DIFF_FLAG_BINARY) if ((fc->file->flags & GIT_DIFF_FLAG_BINARY) != 0)
return 0; return 0;
if (fc->src == GIT_ITERATOR_TYPE_WORKDIR) if (fc->src == GIT_ITERATOR_TYPE_WORKDIR)
...@@ -402,7 +408,7 @@ int git_diff_file_content__load(git_diff_file_content *fc) ...@@ -402,7 +408,7 @@ int git_diff_file_content__load(git_diff_file_content *fc)
if (error) if (error)
return error; return error;
fc->file.flags |= GIT_DIFF_FLAG__LOADED; fc->flags |= GIT_DIFF_FLAG__LOADED;
diff_file_content_binary_by_content(fc); diff_file_content_binary_by_content(fc);
...@@ -411,26 +417,26 @@ int git_diff_file_content__load(git_diff_file_content *fc) ...@@ -411,26 +417,26 @@ int git_diff_file_content__load(git_diff_file_content *fc)
void git_diff_file_content__unload(git_diff_file_content *fc) void git_diff_file_content__unload(git_diff_file_content *fc)
{ {
if (fc->file.flags & GIT_DIFF_FLAG__FREE_DATA) { if (fc->flags & GIT_DIFF_FLAG__FREE_DATA) {
git__free(fc->map.data); git__free(fc->map.data);
fc->map.data = ""; fc->map.data = "";
fc->map.len = 0; fc->map.len = 0;
fc->file.flags &= ~GIT_DIFF_FLAG__FREE_DATA; fc->flags &= ~GIT_DIFF_FLAG__FREE_DATA;
} }
else if (fc->file.flags & GIT_DIFF_FLAG__UNMAP_DATA) { else if (fc->flags & GIT_DIFF_FLAG__UNMAP_DATA) {
git_futils_mmap_free(&fc->map); git_futils_mmap_free(&fc->map);
fc->map.data = ""; fc->map.data = "";
fc->map.len = 0; fc->map.len = 0;
fc->file.flags &= ~GIT_DIFF_FLAG__UNMAP_DATA; fc->flags &= ~GIT_DIFF_FLAG__UNMAP_DATA;
} }
if (fc->file.flags & GIT_DIFF_FLAG__FREE_BLOB) { if (fc->flags & GIT_DIFF_FLAG__FREE_BLOB) {
git_blob_free((git_blob *)fc->blob); git_blob_free((git_blob *)fc->blob);
fc->blob = NULL; fc->blob = NULL;
fc->file.flags &= ~GIT_DIFF_FLAG__FREE_BLOB; fc->flags &= ~GIT_DIFF_FLAG__FREE_BLOB;
} }
fc->file.flags &= ~GIT_DIFF_FLAG__LOADED; fc->flags &= ~GIT_DIFF_FLAG__LOADED;
} }
void git_diff_file_content__clear(git_diff_file_content *fc) void git_diff_file_content__clear(git_diff_file_content *fc)
......
...@@ -15,8 +15,9 @@ ...@@ -15,8 +15,9 @@
/* expanded information for one side of a delta */ /* expanded information for one side of a delta */
typedef struct { typedef struct {
git_repository *repo; git_repository *repo;
git_diff_file file; git_diff_file *file;
git_diff_driver *driver; git_diff_driver *driver;
uint32_t flags;
uint32_t opts_flags; uint32_t opts_flags;
git_off_t opts_max_size; git_off_t opts_max_size;
git_iterator_type_t src; git_iterator_type_t src;
...@@ -34,14 +35,16 @@ extern int git_diff_file_content__init_from_blob( ...@@ -34,14 +35,16 @@ extern int git_diff_file_content__init_from_blob(
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_blob *blob,
git_diff_file *as_file);
extern int git_diff_file_content__init_from_raw( extern int git_diff_file_content__init_from_raw(
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 char *buf,
size_t buflen); size_t buflen,
git_diff_file *as_file);
/* this loads the blob/file-on-disk as needed */ /* this loads the blob/file-on-disk as needed */
extern int git_diff_file_content__load(git_diff_file_content *fc); extern int git_diff_file_content__load(git_diff_file_content *fc);
......
...@@ -64,12 +64,12 @@ static void diff_patch_update_binary(git_diff_patch *patch) ...@@ -64,12 +64,12 @@ static void diff_patch_update_binary(git_diff_patch *patch)
if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0) if ((patch->delta->flags & DIFF_FLAGS_KNOWN_BINARY) != 0)
return; return;
if ((patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0 || if ((patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0 ||
(patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
patch->delta->flags |= GIT_DIFF_FLAG_BINARY; patch->delta->flags |= GIT_DIFF_FLAG_BINARY;
else if ((patch->ofile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0 && else if ((patch->ofile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0 &&
(patch->nfile.file.flags & DIFF_FLAGS_NOT_BINARY) != 0) (patch->nfile.file->flags & DIFF_FLAGS_NOT_BINARY) != 0)
patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY; patch->delta->flags |= GIT_DIFF_FLAG_NOT_BINARY;
} }
...@@ -143,42 +143,42 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output) ...@@ -143,42 +143,42 @@ static int diff_patch_load(git_diff_patch *patch, git_diff_output *output)
output && !output->hunk_cb && !output->data_cb) output && !output->hunk_cb && !output->data_cb)
return 0; return 0;
#define DIFF_FLAGS_KNOWN_DATA (GIT_DIFF_FLAG__NO_DATA|GIT_DIFF_FLAG_VALID_OID)
incomplete_data = incomplete_data =
((patch->ofile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0 && (((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
(patch->nfile.file.flags & DIFF_FLAGS_KNOWN_DATA) != 0); (patch->ofile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0) &&
((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) != 0 ||
(patch->nfile.file->flags & GIT_DIFF_FLAG_VALID_OID) != 0));
/* always try to load workdir content first because filtering may /* always try to load workdir content first because filtering may
* need 2x data size and this minimizes peak memory footprint * need 2x data size and this minimizes peak memory footprint
*/ */
if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) { if (patch->ofile.src == GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = git_diff_file_content__load(&patch->ofile)) < 0 || if ((error = git_diff_file_content__load(&patch->ofile)) < 0 ||
(patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) (patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
goto cleanup; goto cleanup;
} }
if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) { if (patch->nfile.src == GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = git_diff_file_content__load(&patch->nfile)) < 0 || if ((error = git_diff_file_content__load(&patch->nfile)) < 0 ||
(patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
goto cleanup; goto cleanup;
} }
/* once workdir has been tried, load other data as needed */ /* once workdir has been tried, load other data as needed */
if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) { if (patch->ofile.src != GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = git_diff_file_content__load(&patch->ofile)) < 0 || if ((error = git_diff_file_content__load(&patch->ofile)) < 0 ||
(patch->ofile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) (patch->ofile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
goto cleanup; goto cleanup;
} }
if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) { if (patch->nfile.src != GIT_ITERATOR_TYPE_WORKDIR) {
if ((error = git_diff_file_content__load(&patch->nfile)) < 0 || if ((error = git_diff_file_content__load(&patch->nfile)) < 0 ||
(patch->nfile.file.flags & GIT_DIFF_FLAG_BINARY) != 0) (patch->nfile.file->flags & GIT_DIFF_FLAG_BINARY) != 0)
goto cleanup; goto cleanup;
} }
/* if we were previously missing an oid, update MODIFIED->UNMODIFIED */ /* if we were previously missing an oid, update MODIFIED->UNMODIFIED */
if (incomplete_data && if (incomplete_data &&
patch->ofile.file.mode == patch->nfile.file.mode && patch->ofile.file->mode == patch->nfile.file->mode &&
git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid) && git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid) &&
patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */ patch->delta->status == GIT_DELTA_MODIFIED) /* not RENAMED/COPIED! */
patch->delta->status = GIT_DELTA_UNMODIFIED; patch->delta->status = GIT_DELTA_UNMODIFIED;
...@@ -193,7 +193,7 @@ cleanup: ...@@ -193,7 +193,7 @@ cleanup:
patch->delta->status != GIT_DELTA_UNMODIFIED && patch->delta->status != GIT_DELTA_UNMODIFIED &&
(patch->ofile.map.len || patch->nfile.map.len) && (patch->ofile.map.len || patch->nfile.map.len) &&
(patch->ofile.map.len != patch->nfile.map.len || (patch->ofile.map.len != patch->nfile.map.len ||
!git_oid_equal(&patch->ofile.file.oid, &patch->nfile.file.oid))) !git_oid_equal(&patch->ofile.file->oid, &patch->nfile.file->oid)))
patch->flags |= GIT_DIFF_PATCH_DIFFABLE; patch->flags |= GIT_DIFF_PATCH_DIFFABLE;
patch->flags |= GIT_DIFF_PATCH_LOADED; patch->flags |= GIT_DIFF_PATCH_LOADED;
...@@ -312,26 +312,31 @@ int git_diff_foreach( ...@@ -312,26 +312,31 @@ int git_diff_foreach(
typedef struct { typedef struct {
git_diff_patch patch; git_diff_patch patch;
git_diff_delta delta; git_diff_delta delta;
char paths[GIT_FLEX_ARRAY];
} diff_patch_with_delta; } diff_patch_with_delta;
static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo) static int diff_single_generate(diff_patch_with_delta *pd, git_xdiff_output *xo)
{ {
int error = 0; int error = 0;
git_diff_patch *patch = &pd->patch; git_diff_patch *patch = &pd->patch;
bool has_old = ((patch->ofile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); bool has_old = ((patch->ofile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
bool has_new = ((patch->nfile.file.flags & GIT_DIFF_FLAG__NO_DATA) == 0); bool has_new = ((patch->nfile.flags & GIT_DIFF_FLAG__NO_DATA) == 0);
pd->delta.status = has_new ? pd->delta.status = has_new ?
(has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : (has_old ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) :
(has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); (has_old ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED);
if (git_oid_equal(&patch->nfile.file.oid, &patch->ofile.file.oid)) if (git_oid_equal(&patch->nfile.file->oid, &patch->ofile.file->oid))
pd->delta.status = GIT_DELTA_UNMODIFIED; pd->delta.status = GIT_DELTA_UNMODIFIED;
patch->delta = &pd->delta; patch->delta = &pd->delta;
diff_patch_init_common(patch); diff_patch_init_common(patch);
if (pd->delta.status == GIT_DELTA_UNMODIFIED &&
!(patch->ofile.opts_flags & GIT_DIFF_INCLUDE_UNMODIFIED))
return error;
error = diff_patch_file_callback(patch, (git_diff_output *)xo); error = diff_patch_file_callback(patch, (git_diff_output *)xo);
if (!error) if (!error)
...@@ -347,7 +352,9 @@ static int diff_patch_from_blobs( ...@@ -347,7 +352,9 @@ static int diff_patch_from_blobs(
diff_patch_with_delta *pd, diff_patch_with_delta *pd,
git_xdiff_output *xo, git_xdiff_output *xo,
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path,
const git_blob *new_blob, 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;
...@@ -357,29 +364,61 @@ static int diff_patch_from_blobs( ...@@ -357,29 +364,61 @@ static int diff_patch_from_blobs(
GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); GITERR_CHECK_VERSION(opts, GIT_DIFF_OPTIONS_VERSION, "git_diff_options");
pd->patch.delta = &pd->delta;
if (!repo) /* return two NULL items as UNMODIFIED delta */
return 0;
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) {
const git_blob *swap = old_blob; const git_blob *tmp_blob;
old_blob = new_blob; const char *tmp_path;
new_blob = swap; 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->delta.old_file.path = old_path;
pd->delta.new_file.path = new_path;
if ((error = git_diff_file_content__init_from_blob( if ((error = git_diff_file_content__init_from_blob(
&pd->patch.ofile, repo, opts, old_blob)) < 0 || &pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)) < 0 ||
(error = git_diff_file_content__init_from_blob( (error = git_diff_file_content__init_from_blob(
&pd->patch.nfile, repo, opts, new_blob)) < 0) &pd->patch.nfile, repo, opts, new_blob, &pd->delta.new_file)) < 0)
return error; return error;
return diff_single_generate(pd, xo); return diff_single_generate(pd, xo);
} }
static int diff_patch_with_delta_alloc(
diff_patch_with_delta **out,
const char **old_path,
const char **new_path)
{
diff_patch_with_delta *pd;
size_t old_len = *old_path ? strlen(*old_path) : 0;
size_t new_len = *new_path ? strlen(*new_path) : 0;
*out = pd = git__calloc(1, sizeof(*pd) + old_len + new_len + 2);
GITERR_CHECK_ALLOC(pd);
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
if (*old_path) {
memcpy(&pd->paths[0], *old_path, old_len);
*old_path = &pd->paths[0];
} else if (*new_path)
*old_path = &pd->paths[old_len + 1];
if (*new_path) {
memcpy(&pd->paths[old_len + 1], *new_path, new_len);
*new_path = &pd->paths[old_len + 1];
} else if (*old_path)
*new_path = &pd->paths[0];
return 0;
}
int git_diff_blobs( int git_diff_blobs(
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path,
const git_blob *new_blob, 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,
...@@ -397,7 +436,13 @@ int git_diff_blobs( ...@@ -397,7 +436,13 @@ int git_diff_blobs(
(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload); (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
error = diff_patch_from_blobs(&pd, &xo, old_blob, new_blob, opts); if (!old_path && new_path)
old_path = new_path;
else if (!new_path && old_path)
new_path = old_path;
error = diff_patch_from_blobs(
&pd, &xo, old_blob, old_path, new_blob, new_path, opts);
git_diff_patch_free((git_diff_patch *)&pd); git_diff_patch_free((git_diff_patch *)&pd);
...@@ -407,7 +452,9 @@ int git_diff_blobs( ...@@ -407,7 +452,9 @@ int git_diff_blobs(
int git_diff_patch_from_blobs( int git_diff_patch_from_blobs(
git_diff_patch **out, git_diff_patch **out,
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path,
const git_blob *new_blob, 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;
...@@ -417,16 +464,18 @@ int git_diff_patch_from_blobs( ...@@ -417,16 +464,18 @@ int git_diff_patch_from_blobs(
assert(out); assert(out);
*out = NULL; *out = NULL;
pd = git__calloc(1, sizeof(*pd)); if (diff_patch_with_delta_alloc(&pd, &old_path, &new_path) < 0)
GITERR_CHECK_ALLOC(pd); return -1;
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
memset(&xo, 0, sizeof(xo)); memset(&xo, 0, sizeof(xo));
diff_output_to_patch((git_diff_output *)&xo, &pd->patch); diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
if (!(error = diff_patch_from_blobs(pd, &xo, old_blob, new_blob, opts))) error = diff_patch_from_blobs(
pd, &xo, old_blob, old_path, new_blob, new_path, opts);
if (!error)
*out = (git_diff_patch *)pd; *out = (git_diff_patch *)pd;
else else
git_diff_patch_free((git_diff_patch *)pd); git_diff_patch_free((git_diff_patch *)pd);
...@@ -438,8 +487,10 @@ static int diff_patch_from_blob_and_buffer( ...@@ -438,8 +487,10 @@ static int diff_patch_from_blob_and_buffer(
diff_patch_with_delta *pd, diff_patch_with_delta *pd,
git_xdiff_output *xo, git_xdiff_output *xo,
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path,
const char *buf, const char *buf,
size_t buflen, size_t buflen,
const char *buf_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; int error = 0;
...@@ -450,28 +501,36 @@ static int diff_patch_from_blob_and_buffer( ...@@ -450,28 +501,36 @@ static int diff_patch_from_blob_and_buffer(
pd->patch.delta = &pd->delta; pd->patch.delta = &pd->delta;
if (!repo && !buf) /* return two NULL items as UNMODIFIED delta */
return 0;
if (opts && (opts->flags & GIT_DIFF_REVERSE) != 0) { 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( if (!(error = git_diff_file_content__init_from_raw(
&pd->patch.ofile, repo, opts, buf, buflen))) &pd->patch.ofile, repo, opts, buf, buflen, &pd->delta.old_file)))
error = git_diff_file_content__init_from_blob( error = git_diff_file_content__init_from_blob(
&pd->patch.nfile, repo, opts, old_blob); &pd->patch.nfile, repo, opts, old_blob, &pd->delta.new_file);
} else { } else {
pd->delta.old_file.path = old_path;
pd->delta.new_file.path = buf_path;
if (!(error = git_diff_file_content__init_from_blob( if (!(error = git_diff_file_content__init_from_blob(
&pd->patch.ofile, repo, opts, old_blob))) &pd->patch.ofile, repo, opts, old_blob, &pd->delta.old_file)))
error = git_diff_file_content__init_from_raw( error = git_diff_file_content__init_from_raw(
&pd->patch.nfile, repo, opts, buf, buflen); &pd->patch.nfile, repo, opts, buf, buflen, &pd->delta.new_file);
} }
if (error < 0)
return error;
return diff_single_generate(pd, xo); return diff_single_generate(pd, xo);
} }
int git_diff_blob_to_buffer( int git_diff_blob_to_buffer(
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path,
const char *buf, const char *buf,
size_t buflen, size_t buflen,
const char *buf_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,
...@@ -489,8 +548,13 @@ int git_diff_blob_to_buffer( ...@@ -489,8 +548,13 @@ int git_diff_blob_to_buffer(
(git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload); (git_diff_output *)&xo, opts, file_cb, hunk_cb, data_cb, payload);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
if (!old_path && buf_path)
old_path = buf_path;
else if (!buf_path && old_path)
buf_path = old_path;
error = diff_patch_from_blob_and_buffer( error = diff_patch_from_blob_and_buffer(
&pd, &xo, old_blob, buf, buflen, opts); &pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
git_diff_patch_free((git_diff_patch *)&pd); git_diff_patch_free((git_diff_patch *)&pd);
...@@ -500,8 +564,10 @@ int git_diff_blob_to_buffer( ...@@ -500,8 +564,10 @@ int git_diff_blob_to_buffer(
int git_diff_patch_from_blob_and_buffer( int git_diff_patch_from_blob_and_buffer(
git_diff_patch **out, git_diff_patch **out,
const git_blob *old_blob, const git_blob *old_blob,
const char *old_path,
const char *buf, const char *buf,
size_t buflen, size_t buflen,
const char *buf_path,
const git_diff_options *opts) const git_diff_options *opts)
{ {
int error = 0; int error = 0;
...@@ -511,17 +577,18 @@ int git_diff_patch_from_blob_and_buffer( ...@@ -511,17 +577,18 @@ int git_diff_patch_from_blob_and_buffer(
assert(out); assert(out);
*out = NULL; *out = NULL;
pd = git__calloc(1, sizeof(*pd)); if (diff_patch_with_delta_alloc(&pd, &old_path, &buf_path) < 0)
GITERR_CHECK_ALLOC(pd); return -1;
pd->patch.flags = GIT_DIFF_PATCH_ALLOCATED;
memset(&xo, 0, sizeof(xo)); memset(&xo, 0, sizeof(xo));
diff_output_to_patch((git_diff_output *)&xo, &pd->patch); diff_output_to_patch((git_diff_output *)&xo, &pd->patch);
git_xdiff_init(&xo, opts); git_xdiff_init(&xo, opts);
if (!(error = diff_patch_from_blob_and_buffer( error = diff_patch_from_blob_and_buffer(
pd, &xo, old_blob, buf, buflen, opts))) pd, &xo, old_blob, old_path, buf, buflen, buf_path, opts);
if (!error)
*out = (git_diff_patch *)pd; *out = (git_diff_patch *)pd;
else else
git_diff_patch_free((git_diff_patch *)pd); git_diff_patch_free((git_diff_patch *)pd);
......
...@@ -21,14 +21,15 @@ static int diff_print_info_init( ...@@ -21,14 +21,15 @@ static int diff_print_info_init(
diff_print_info *pi, diff_print_info *pi,
git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload) git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
{ {
assert(diff && diff->repo);
pi->diff = diff; pi->diff = diff;
pi->print_cb = cb; pi->print_cb = cb;
pi->payload = payload; pi->payload = payload;
pi->buf = out; pi->buf = out;
if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0) if (!diff || !diff->repo)
pi->oid_strlen = GIT_ABBREV_DEFAULT;
else if (git_repository__cvar(
&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
return -1; return -1;
pi->oid_strlen += 1; /* for NUL byte */ pi->oid_strlen += 1; /* for NUL byte */
...@@ -82,6 +83,8 @@ static int diff_print_one_compact( ...@@ -82,6 +83,8 @@ static int diff_print_one_compact(
diff_print_info *pi = data; diff_print_info *pi = data;
git_buf *out = pi->buf; git_buf *out = pi->buf;
char old_suffix, new_suffix, code = git_diff_status_char(delta->status); char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
int (*strcomp)(const char *, const char *) =
pi->diff ? pi->diff->strcomp : git__strcmp;
GIT_UNUSED(progress); GIT_UNUSED(progress);
...@@ -94,7 +97,7 @@ static int diff_print_one_compact( ...@@ -94,7 +97,7 @@ static int diff_print_one_compact(
git_buf_clear(out); git_buf_clear(out);
if (delta->old_file.path != delta->new_file.path && if (delta->old_file.path != delta->new_file.path &&
pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0) strcomp(delta->old_file.path,delta->new_file.path) != 0)
git_buf_printf(out, "%c\t%s%c -> %s%c\n", code, git_buf_printf(out, "%c\t%s%c -> %s%c\n", code,
delta->old_file.path, old_suffix, delta->new_file.path, new_suffix); delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
else if (delta->old_file.mode != delta->new_file.mode && else if (delta->old_file.mode != delta->new_file.mode &&
...@@ -229,10 +232,11 @@ static int diff_print_patch_file( ...@@ -229,10 +232,11 @@ static int diff_print_patch_file(
const git_diff_delta *delta, float progress, void *data) const git_diff_delta *delta, float progress, void *data)
{ {
diff_print_info *pi = data; diff_print_info *pi = data;
const char *oldpfx = pi->diff->opts.old_prefix; const char *oldpfx = pi->diff ? pi->diff->opts.old_prefix : NULL;
const char *oldpath = delta->old_file.path; const char *oldpath = delta->old_file.path;
const char *newpfx = pi->diff->opts.new_prefix; const char *newpfx = pi->diff ? pi->diff->opts.new_prefix : NULL;
const char *newpath = delta->new_file.path; const char *newpath = delta->new_file.path;
uint32_t opts_flags = pi->diff ? pi->diff->opts.flags : GIT_DIFF_NORMAL;
GIT_UNUSED(progress); GIT_UNUSED(progress);
...@@ -240,17 +244,17 @@ static int diff_print_patch_file( ...@@ -240,17 +244,17 @@ static int diff_print_patch_file(
delta->status == GIT_DELTA_UNMODIFIED || delta->status == GIT_DELTA_UNMODIFIED ||
delta->status == GIT_DELTA_IGNORED || delta->status == GIT_DELTA_IGNORED ||
(delta->status == GIT_DELTA_UNTRACKED && (delta->status == GIT_DELTA_UNTRACKED &&
(pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0)) (opts_flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
return 0; return 0;
if (!oldpfx) if (!oldpfx)
oldpfx = DIFF_OLD_PREFIX_DEFAULT; oldpfx = DIFF_OLD_PREFIX_DEFAULT;
if (!newpfx) if (!newpfx)
newpfx = DIFF_NEW_PREFIX_DEFAULT; newpfx = DIFF_NEW_PREFIX_DEFAULT;
git_buf_clear(pi->buf); git_buf_clear(pi->buf);
git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path); git_buf_printf(pi->buf, "diff --git %s%s %s%s\n",
oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
if (diff_print_oid_range(pi, delta) < 0) if (diff_print_oid_range(pi, delta) < 0)
return -1; return -1;
......
...@@ -6,6 +6,20 @@ static diff_expects expected; ...@@ -6,6 +6,20 @@ static diff_expects expected;
static git_diff_options opts; static git_diff_options opts;
static git_blob *d, *alien; static git_blob *d, *alien;
static void quick_diff_blob_to_str(
const git_blob *blob, const char *blob_path,
const char *str, size_t len, const char *str_path)
{
memset(&expected, 0, sizeof(expected));
if (str && !len)
len = strlen(str);
cl_git_pass(git_diff_blob_to_buffer(
blob, blob_path, str, len, str_path,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
}
void test_diff_blob__initialize(void) void test_diff_blob__initialize(void)
{ {
git_oid oid; git_oid oid;
...@@ -59,7 +73,8 @@ void test_diff_blob__can_compare_text_blobs(void) ...@@ -59,7 +73,8 @@ void test_diff_blob__can_compare_text_blobs(void)
/* diff on tests/resources/attr/root_test1 */ /* diff on tests/resources/attr/root_test1 */
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
a, b, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); a, NULL, b, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
...@@ -74,7 +89,8 @@ void test_diff_blob__can_compare_text_blobs(void) ...@@ -74,7 +89,8 @@ void test_diff_blob__can_compare_text_blobs(void)
/* 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, c, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); b, NULL, c, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
...@@ -89,7 +105,8 @@ void test_diff_blob__can_compare_text_blobs(void) ...@@ -89,7 +105,8 @@ void test_diff_blob__can_compare_text_blobs(void)
/* 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, c, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); a, NULL, c, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
...@@ -103,7 +120,8 @@ void test_diff_blob__can_compare_text_blobs(void) ...@@ -103,7 +120,8 @@ void test_diff_blob__can_compare_text_blobs(void)
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
c, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); c, NULL, d, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
...@@ -125,6 +143,7 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) ...@@ -125,6 +143,7 @@ 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_diff_patch *p; git_diff_patch *p;
const git_diff_delta *delta;
size_t tc, ta, td; size_t tc, ta, td;
/* tests/resources/attr/root_test1 */ /* tests/resources/attr/root_test1 */
...@@ -142,10 +161,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) ...@@ -142,10 +161,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(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 */
cl_git_pass(git_diff_patch_from_blobs(&p, a, b, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, b, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
delta = git_diff_patch_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.oid));
cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(b), &delta->new_file.oid));
cl_assert_equal_sz(git_blob_rawsize(b), delta->new_file.size);
cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); 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_assert_equal_i(6, git_diff_patch_num_lines_in_hunk(p, 0));
...@@ -157,10 +184,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) ...@@ -157,10 +184,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void)
git_diff_patch_free(p); git_diff_patch_free(p);
/* diff on tests/resources/attr/root_test2 */ /* diff on tests/resources/attr/root_test2 */
cl_git_pass(git_diff_patch_from_blobs(&p, b, c, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, b, NULL, c, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
delta = git_diff_patch_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.oid));
cl_assert_equal_sz(git_blob_rawsize(b), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); 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_assert_equal_i(15, git_diff_patch_num_lines_in_hunk(p, 0));
...@@ -172,12 +207,17 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) ...@@ -172,12 +207,17 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void)
git_diff_patch_free(p); git_diff_patch_free(p);
/* diff on tests/resources/attr/root_test3 */ /* diff on tests/resources/attr/root_test3 */
cl_git_pass(git_diff_patch_from_blobs(&p, a, c, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, a, NULL, c, NULL, &opts));
cl_assert(p != NULL); 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)); delta = git_diff_patch_delta(p);
cl_assert_equal_i(13, git_diff_patch_num_lines_in_hunk(p, 0)); 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.oid));
cl_assert_equal_sz(git_blob_rawsize(a), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(c), &delta->new_file.oid));
cl_assert_equal_sz(git_blob_rawsize(c), delta->new_file.size);
cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p)); cl_git_pass(git_diff_patch_line_stats(&tc, &ta, &td, p));
cl_assert_equal_i(0, (int)tc); cl_assert_equal_i(0, (int)tc);
...@@ -187,10 +227,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void) ...@@ -187,10 +227,18 @@ void test_diff_blob__can_compare_text_blobs_with_patch(void)
git_diff_patch_free(p); git_diff_patch_free(p);
/* one more */ /* one more */
cl_git_pass(git_diff_patch_from_blobs(&p, c, d, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, c, NULL, d, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
delta = git_diff_patch_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.oid));
cl_assert_equal_sz(git_blob_rawsize(c), delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
cl_assert_equal_i(2, (int)git_diff_patch_num_hunks(p)); 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(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_assert_equal_i(9, git_diff_patch_num_lines_in_hunk(p, 1));
...@@ -212,7 +260,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -212,7 +260,8 @@ void test_diff_blob__can_compare_against_null_blobs(void)
git_blob *e = NULL; git_blob *e = NULL;
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
d, e, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); d, NULL, e, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_DELETED]);
...@@ -227,7 +276,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -227,7 +276,8 @@ void test_diff_blob__can_compare_against_null_blobs(void)
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
d, e, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); d, NULL, e, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_ADDED]);
...@@ -242,7 +292,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -242,7 +292,8 @@ void test_diff_blob__can_compare_against_null_blobs(void)
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
alien, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); alien, NULL, NULL, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.files_binary); cl_assert_equal_i(1, expected.files_binary);
...@@ -253,7 +304,8 @@ void test_diff_blob__can_compare_against_null_blobs(void) ...@@ -253,7 +304,8 @@ void test_diff_blob__can_compare_against_null_blobs(void)
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
NULL, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); NULL, NULL, alien, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.files_binary); cl_assert_equal_i(1, expected.files_binary);
...@@ -266,13 +318,22 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) ...@@ -266,13 +318,22 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
{ {
git_blob *e = NULL; git_blob *e = NULL;
git_diff_patch *p; git_diff_patch *p;
const git_diff_delta *delta;
int line; int line;
char origin; char origin;
cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status);
delta = git_diff_patch_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid));
cl_assert_equal_sz(git_blob_rawsize(d), delta->old_file.size);
cl_assert(git_oid_iszero(&delta->new_file.oid));
cl_assert_equal_sz(0, delta->new_file.size);
cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); 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)); cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
...@@ -286,10 +347,18 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) ...@@ -286,10 +347,18 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
opts.flags |= GIT_DIFF_REVERSE; opts.flags |= GIT_DIFF_REVERSE;
cl_git_pass(git_diff_patch_from_blobs(&p, d, e, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, e, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status);
delta = git_diff_patch_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
cl_assert(git_oid_iszero(&delta->old_file.oid));
cl_assert_equal_sz(0, delta->old_file.size);
cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
cl_assert_equal_sz(git_blob_rawsize(d), delta->new_file.size);
cl_assert_equal_i(1, (int)git_diff_patch_num_hunks(p)); 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)); cl_assert_equal_i(14, git_diff_patch_num_lines_in_hunk(p, 0));
...@@ -303,20 +372,28 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void) ...@@ -303,20 +372,28 @@ void test_diff_blob__can_compare_against_null_blobs_with_patch(void)
opts.flags ^= GIT_DIFF_REVERSE; opts.flags ^= GIT_DIFF_REVERSE;
cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, NULL, NULL, &opts));
cl_assert(p != NULL); 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); delta = git_diff_patch_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_DELETED, delta->status);
cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
git_diff_patch_free(p); git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blobs(&p, NULL, alien, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, alien, NULL, &opts));
cl_assert(p != NULL); 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); delta = git_diff_patch_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_ADDED, delta->status);
cl_assert((delta->flags & GIT_DIFF_FLAG_BINARY) != 0);
cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
git_diff_patch_free(p); git_diff_patch_free(p);
...@@ -332,44 +409,66 @@ static void assert_identical_blobs_comparison(diff_expects *expected) ...@@ -332,44 +409,66 @@ static void assert_identical_blobs_comparison(diff_expects *expected)
void test_diff_blob__can_compare_identical_blobs(void) void test_diff_blob__can_compare_identical_blobs(void)
{ {
opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); d, NULL, d, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(0, expected.files_binary);
assert_identical_blobs_comparison(&expected); assert_identical_blobs_comparison(&expected);
cl_assert_equal_i(0, expected.files_binary);
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
NULL, NULL, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); NULL, NULL, NULL, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_identical_blobs_comparison(&expected);
cl_assert_equal_i(0, expected.files_binary); cl_assert_equal_i(0, expected.files_binary);
cl_assert_equal_i(0, expected.files); /* NULLs mean no callbacks, period */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
alien, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); alien, NULL, alien, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert(expected.files_binary > 0);
assert_identical_blobs_comparison(&expected); assert_identical_blobs_comparison(&expected);
cl_assert(expected.files_binary > 0);
} }
void test_diff_blob__can_compare_identical_blobs_with_patch(void) void test_diff_blob__can_compare_identical_blobs_with_patch(void)
{ {
git_diff_patch *p; git_diff_patch *p;
const git_diff_delta *delta;
cl_git_pass(git_diff_patch_from_blobs(&p, d, d, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, d, NULL, d, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
delta = git_diff_patch_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
cl_assert_equal_sz(delta->old_file.size, git_blob_rawsize(d));
cl_assert(git_oid_equal(git_blob_id(d), &delta->old_file.oid));
cl_assert_equal_sz(delta->new_file.size, git_blob_rawsize(d));
cl_assert(git_oid_equal(git_blob_id(d), &delta->new_file.oid));
cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
git_diff_patch_free(p); git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, NULL, NULL, NULL, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status);
delta = git_diff_patch_delta(p);
cl_assert(delta != NULL);
cl_assert_equal_i(GIT_DELTA_UNMODIFIED, delta->status);
cl_assert_equal_sz(0, delta->old_file.size);
cl_assert(git_oid_iszero(&delta->old_file.oid));
cl_assert_equal_sz(0, delta->new_file.size);
cl_assert(git_oid_iszero(&delta->new_file.oid));
cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p)); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
git_diff_patch_free(p); git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blobs(&p, alien, alien, &opts)); cl_git_pass(git_diff_patch_from_blobs(&p, alien, NULL, alien, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); 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)); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
...@@ -396,14 +495,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void) ...@@ -396,14 +495,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void)
cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 4)); cl_git_pass(git_blob_lookup_prefix(&heart, g_repo, &h_oid, 4));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
alien, heart, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); alien, NULL, heart, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_binary_blobs_comparison(&expected); assert_binary_blobs_comparison(&expected);
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
heart, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); heart, NULL, alien, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_binary_blobs_comparison(&expected); assert_binary_blobs_comparison(&expected);
...@@ -413,14 +514,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void) ...@@ -413,14 +514,16 @@ void test_diff_blob__can_compare_two_binary_blobs(void)
void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void) void test_diff_blob__can_compare_a_binary_blob_and_a_text_blob(void)
{ {
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
alien, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); alien, NULL, d, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_binary_blobs_comparison(&expected); assert_binary_blobs_comparison(&expected);
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); d, NULL, alien, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_binary_blobs_comparison(&expected); assert_binary_blobs_comparison(&expected);
} }
...@@ -461,7 +564,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) ...@@ -461,7 +564,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
/* Test with default inter-hunk-context (not set) => default is 0 */ /* Test with default inter-hunk-context (not set) => default is 0 */
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); old_d, NULL, d, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(2, expected.hunks); cl_assert_equal_i(2, expected.hunks);
...@@ -469,7 +573,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) ...@@ -469,7 +573,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
opts.interhunk_lines = 0; opts.interhunk_lines = 0;
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); old_d, NULL, d, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(2, expected.hunks); cl_assert_equal_i(2, expected.hunks);
...@@ -477,7 +582,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void) ...@@ -477,7 +582,8 @@ void test_diff_blob__comparing_two_text_blobs_honors_interhunkcontext(void)
opts.interhunk_lines = 1; opts.interhunk_lines = 1;
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
old_d, d, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); old_d, NULL, d, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.hunks); cl_assert_equal_i(1, expected.hunks);
...@@ -490,7 +596,8 @@ void test_diff_blob__checks_options_version_too_low(void) ...@@ -490,7 +596,8 @@ void test_diff_blob__checks_options_version_too_low(void)
opts.version = 0; opts.version = 0;
cl_git_fail(git_diff_blobs( cl_git_fail(git_diff_blobs(
d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); d, NULL, alien, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
err = giterr_last(); err = giterr_last();
cl_assert_equal_i(GITERR_INVALID, err->klass); cl_assert_equal_i(GITERR_INVALID, err->klass);
} }
...@@ -501,7 +608,8 @@ void test_diff_blob__checks_options_version_too_high(void) ...@@ -501,7 +608,8 @@ void test_diff_blob__checks_options_version_too_high(void)
opts.version = 1024; opts.version = 1024;
cl_git_fail(git_diff_blobs( cl_git_fail(git_diff_blobs(
d, alien, &opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); d, NULL, alien, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
err = giterr_last(); err = giterr_last();
cl_assert_equal_i(GITERR_INVALID, err->klass); cl_assert_equal_i(GITERR_INVALID, err->klass);
} }
...@@ -548,10 +656,7 @@ void test_diff_blob__can_compare_blob_to_buffer(void) ...@@ -548,10 +656,7 @@ void test_diff_blob__can_compare_blob_to_buffer(void)
cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4)); cl_git_pass(git_blob_lookup_prefix(&a, g_repo, &a_oid, 4));
/* diff from blob a to content of b */ /* diff from blob a to content of b */
cl_git_pass(git_diff_blob_to_buffer( quick_diff_blob_to_str(a, NULL, b_content, 0, NULL);
a, b_content, strlen(b_content),
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
cl_assert_equal_i(1, expected.files); cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]); cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(0, expected.files_binary); cl_assert_equal_i(0, expected.files_binary);
...@@ -562,37 +667,25 @@ void test_diff_blob__can_compare_blob_to_buffer(void) ...@@ -562,37 +667,25 @@ void test_diff_blob__can_compare_blob_to_buffer(void)
cl_assert_equal_i(0, expected.line_dels); cl_assert_equal_i(0, expected.line_dels);
/* diff from blob a to content of a */ /* diff from blob a to content of a */
memset(&expected, 0, sizeof(expected)); opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
cl_git_pass(git_diff_blob_to_buffer( quick_diff_blob_to_str(a, NULL, a_content, 0, NULL);
a, a_content, strlen(a_content),
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_identical_blobs_comparison(&expected); assert_identical_blobs_comparison(&expected);
/* diff from NULL blob to content of a */ /* diff from NULL blob to content of a */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blob_to_buffer( quick_diff_blob_to_str(NULL, NULL, a_content, 0, NULL);
NULL, a_content, strlen(a_content),
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED); assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
/* diff from blob a to NULL buffer */ /* diff from blob a to NULL buffer */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blob_to_buffer( quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
a, NULL, 0,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED); assert_changed_single_one_line_file(&expected, GIT_DELTA_DELETED);
/* diff with reverse */ /* diff with reverse */
opts.flags ^= GIT_DIFF_REVERSE; opts.flags ^= GIT_DIFF_REVERSE;
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blob_to_buffer( quick_diff_blob_to_str(a, NULL, NULL, 0, NULL);
a, NULL, 0,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED); assert_changed_single_one_line_file(&expected, GIT_DELTA_ADDED);
git_blob_free(a); git_blob_free(a);
...@@ -613,7 +706,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) ...@@ -613,7 +706,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
/* diff from blob a to content of b */ /* diff from blob a to content of b */
cl_git_pass(git_diff_patch_from_blob_and_buffer( cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, a, b_content, strlen(b_content), &opts)); &p, a, NULL, b_content, strlen(b_content), NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status); cl_assert_equal_i(GIT_DELTA_MODIFIED, git_diff_patch_delta(p)->status);
...@@ -628,8 +721,9 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) ...@@ -628,8 +721,9 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
git_diff_patch_free(p); git_diff_patch_free(p);
/* diff from blob a to content of a */ /* diff from blob a to content of a */
opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
cl_git_pass(git_diff_patch_from_blob_and_buffer( cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, a, a_content, strlen(a_content), &opts)); &p, a, NULL, a_content, strlen(a_content), NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_UNMODIFIED, git_diff_patch_delta(p)->status); 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)); cl_assert_equal_i(0, (int)git_diff_patch_num_hunks(p));
...@@ -637,7 +731,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) ...@@ -637,7 +731,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
/* diff from NULL blob to content of a */ /* diff from NULL blob to content of a */
cl_git_pass(git_diff_patch_from_blob_and_buffer( cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, NULL, a_content, strlen(a_content), &opts)); &p, NULL, NULL, a_content, strlen(a_content), NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); 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, (int)git_diff_patch_num_hunks(p));
...@@ -646,7 +740,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) ...@@ -646,7 +740,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
/* diff from blob a to NULL buffer */ /* diff from blob a to NULL buffer */
cl_git_pass(git_diff_patch_from_blob_and_buffer( cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, a, NULL, 0, &opts)); &p, a, NULL, NULL, 0, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_DELETED, git_diff_patch_delta(p)->status); 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, (int)git_diff_patch_num_hunks(p));
...@@ -657,7 +751,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void) ...@@ -657,7 +751,7 @@ void test_diff_blob__can_compare_blob_to_buffer_with_patch(void)
opts.flags ^= GIT_DIFF_REVERSE; opts.flags ^= GIT_DIFF_REVERSE;
cl_git_pass(git_diff_patch_from_blob_and_buffer( cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, a, NULL, 0, &opts)); &p, a, NULL, NULL, 0, NULL, &opts));
cl_assert(p != NULL); cl_assert(p != NULL);
cl_assert_equal_i(GIT_DELTA_ADDED, git_diff_patch_delta(p)->status); 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, (int)git_diff_patch_num_hunks(p));
...@@ -684,6 +778,8 @@ void test_diff_blob__binary_data_comparisons(void) ...@@ -684,6 +778,8 @@ void test_diff_blob__binary_data_comparisons(void)
const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"; const char *bin_content = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
size_t bin_len = 33; size_t bin_len = 33;
opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8)); cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8));
cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4)); cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4));
...@@ -692,44 +788,32 @@ void test_diff_blob__binary_data_comparisons(void) ...@@ -692,44 +788,32 @@ void test_diff_blob__binary_data_comparisons(void)
/* non-binary to reference content */ /* non-binary to reference content */
memset(&expected, 0, sizeof(expected)); quick_diff_blob_to_str(nonbin, NULL, nonbin_content, nonbin_len, NULL);
cl_git_pass(git_diff_blob_to_buffer(
nonbin, nonbin_content, nonbin_len,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_identical_blobs_comparison(&expected); assert_identical_blobs_comparison(&expected);
cl_assert_equal_i(0, expected.files_binary); cl_assert_equal_i(0, expected.files_binary);
/* binary to reference content */ /* binary to reference content */
memset(&expected, 0, sizeof(expected)); quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
cl_git_pass(git_diff_blob_to_buffer(
bin, bin_content, bin_len,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_identical_blobs_comparison(&expected); assert_identical_blobs_comparison(&expected);
cl_assert_equal_i(1, expected.files_binary); cl_assert_equal_i(1, expected.files_binary);
/* non-binary to binary content */ /* non-binary to binary content */
memset(&expected, 0, sizeof(expected)); quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
cl_git_pass(git_diff_blob_to_buffer(
nonbin, bin_content, bin_len,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_binary_blobs_comparison(&expected); assert_binary_blobs_comparison(&expected);
/* binary to non-binary content */ /* binary to non-binary content */
memset(&expected, 0, sizeof(expected)); quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
cl_git_pass(git_diff_blob_to_buffer(
bin, nonbin_content, nonbin_len,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_binary_blobs_comparison(&expected); assert_binary_blobs_comparison(&expected);
/* non-binary to binary blob */ /* non-binary to binary blob */
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
bin, nonbin, &opts, bin, NULL, nonbin, NULL, &opts,
diff_file_cb, diff_hunk_cb, diff_line_cb, &expected)); diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_binary_blobs_comparison(&expected); assert_binary_blobs_comparison(&expected);
...@@ -739,27 +823,18 @@ void test_diff_blob__binary_data_comparisons(void) ...@@ -739,27 +823,18 @@ void test_diff_blob__binary_data_comparisons(void)
opts.flags |= GIT_DIFF_FORCE_TEXT; opts.flags |= GIT_DIFF_FORCE_TEXT;
memset(&expected, 0, sizeof(expected)); quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
cl_git_pass(git_diff_blob_to_buffer(
bin, bin_content, bin_len,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_identical_blobs_comparison(&expected); assert_identical_blobs_comparison(&expected);
memset(&expected, 0, sizeof(expected)); quick_diff_blob_to_str(nonbin, NULL, bin_content, bin_len, NULL);
cl_git_pass(git_diff_blob_to_buffer(
nonbin, bin_content, bin_len,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified_with_lines(&expected, 4); assert_one_modified_with_lines(&expected, 4);
memset(&expected, 0, sizeof(expected)); quick_diff_blob_to_str(bin, NULL, nonbin_content, nonbin_len, NULL);
cl_git_pass(git_diff_blob_to_buffer(
bin, nonbin_content, nonbin_len,
&opts, diff_file_cb, diff_hunk_cb, diff_line_cb, &expected));
assert_one_modified_with_lines(&expected, 4); assert_one_modified_with_lines(&expected, 4);
memset(&expected, 0, sizeof(expected)); memset(&expected, 0, sizeof(expected));
cl_git_pass(git_diff_blobs( cl_git_pass(git_diff_blobs(
bin, nonbin, &opts, bin, NULL, nonbin, 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_with_lines(&expected, 4); assert_one_modified_with_lines(&expected, 4);
...@@ -767,3 +842,227 @@ void test_diff_blob__binary_data_comparisons(void) ...@@ -767,3 +842,227 @@ void test_diff_blob__binary_data_comparisons(void)
git_blob_free(bin); git_blob_free(bin);
git_blob_free(nonbin); git_blob_free(nonbin);
} }
void test_diff_blob__using_path_and_attributes(void)
{
git_config *cfg;
git_blob *bin, *nonbin;
git_oid oid;
const char *nonbin_content = "Hello from the root\n";
const char *bin_content =
"0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n";
size_t bin_len = 33;
const char *changed;
git_diff_patch *p;
char *pout;
/* set up custom diff drivers and 'diff' attribute mappings for them */
cl_git_pass(git_repository_config(&cfg, g_repo));
cl_git_pass(git_config_set_bool(cfg, "diff.iam_binary.binary", 1));
cl_git_pass(git_config_set_bool(cfg, "diff.iam_text.binary", 0));
cl_git_pass(git_config_set_string(
cfg, "diff.iam_alphactx.xfuncname", "^[A-Za-z]"));
cl_git_pass(git_config_set_bool(cfg, "diff.iam_textalpha.binary", 0));
cl_git_pass(git_config_set_string(
cfg, "diff.iam_textalpha.xfuncname", "^[A-Za-z]"));
cl_git_pass(git_config_set_string(
cfg, "diff.iam_numctx.funcname", "^[0-9]"));
cl_git_pass(git_config_set_bool(cfg, "diff.iam_textnum.binary", 0));
cl_git_pass(git_config_set_string(
cfg, "diff.iam_textnum.funcname", "^[0-9]"));
git_config_free(cfg);
cl_git_append2file(
"attr/.gitattributes",
"\n\n# test_diff_blob__using_path_and_attributes extra\n\n"
"*.binary diff=iam_binary\n"
"*.textary diff=iam_text\n"
"*.alphary diff=iam_alphactx\n"
"*.textalphary diff=iam_textalpha\n"
"*.textnumary diff=iam_textnum\n"
"*.numary diff=iam_numctx\n\n");
opts.context_lines = 0;
opts.flags |= GIT_DIFF_INCLUDE_UNMODIFIED;
cl_git_pass(git_oid_fromstrn(&oid, "45141a79", 8));
cl_git_pass(git_blob_lookup_prefix(&nonbin, g_repo, &oid, 4));
/* 20b: "Hello from the root\n" */
cl_git_pass(git_oid_fromstrn(&oid, "b435cd56", 8));
cl_git_pass(git_blob_lookup_prefix(&bin, g_repo, &oid, 4));
/* 33b: "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\n0123456789\n" */
/* non-binary to reference content */
quick_diff_blob_to_str(nonbin, NULL, nonbin_content, 0, NULL);
assert_identical_blobs_comparison(&expected);
cl_assert_equal_i(0, expected.files_binary);
/* binary to reference content */
quick_diff_blob_to_str(bin, NULL, bin_content, bin_len, NULL);
assert_identical_blobs_comparison(&expected);
cl_assert_equal_i(1, expected.files_binary);
/* add some text */
changed = "Hello from the root\nMore lines\nAnd more\nGo here\n";
quick_diff_blob_to_str(nonbin, NULL, changed, 0, NULL);
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(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);
cl_assert_equal_i(1, expected.files);
cl_assert_equal_i(1, expected.file_status[GIT_DELTA_MODIFIED]);
cl_assert_equal_i(1, expected.files_binary);
cl_assert_equal_i(0, expected.hunks);
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);
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(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);
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(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_diff_patch_from_blob_and_buffer(
&p, nonbin, "zzz.normal", changed, strlen(changed), NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.normal b/zzz.normal\n"
"index 45141a7..75b0dbb 100644\n"
"--- a/zzz.normal\n"
"+++ b/zzz.normal\n"
"@@ -1,0 +2,3 @@ Hello from the root\n"
"+More lines\n"
"+And more\n"
"+Go here\n", pout);
git__free(pout);
git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, nonbin, "zzz.binary", changed, strlen(changed), NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.binary b/zzz.binary\n"
"index 45141a7..75b0dbb 100644\n"
"Binary files a/zzz.binary and b/zzz.binary differ\n", pout);
git__free(pout);
git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, nonbin, "zzz.alphary", changed, strlen(changed), NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.alphary b/zzz.alphary\n"
"index 45141a7..75b0dbb 100644\n"
"--- a/zzz.alphary\n"
"+++ b/zzz.alphary\n"
"@@ -1,0 +2,3 @@ Hello from the root\n"
"+More lines\n"
"+And more\n"
"+Go here\n", pout);
git__free(pout);
git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, nonbin, "zzz.numary", changed, strlen(changed), NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.numary b/zzz.numary\n"
"index 45141a7..75b0dbb 100644\n"
"--- a/zzz.numary\n"
"+++ b/zzz.numary\n"
"@@ -1,0 +2,3 @@\n"
"+More lines\n"
"+And more\n"
"+Go here\n", pout);
git__free(pout);
git_diff_patch_free(p);
/* "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\n0123456789\n"
* 33 bytes
*/
changed = "0123456789\n\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\nreplace a line\n";
cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, bin, "zzz.normal", changed, 37, NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.normal b/zzz.normal\n"
"index b435cd5..1604519 100644\n"
"Binary files a/zzz.normal and b/zzz.normal differ\n", pout);
git__free(pout);
git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, bin, "zzz.textary", changed, 37, NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.textary b/zzz.textary\n"
"index b435cd5..1604519 100644\n"
"--- a/zzz.textary\n"
"+++ b/zzz.textary\n"
"@@ -3 +3 @@\n"
"-0123456789\n"
"+replace a line\n", pout);
git__free(pout);
git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, bin, "zzz.textalphary", changed, 37, NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.textalphary b/zzz.textalphary\n"
"index b435cd5..1604519 100644\n"
"--- a/zzz.textalphary\n"
"+++ b/zzz.textalphary\n"
"@@ -3 +3 @@\n"
"-0123456789\n"
"+replace a line\n", pout);
git__free(pout);
git_diff_patch_free(p);
cl_git_pass(git_diff_patch_from_blob_and_buffer(
&p, bin, "zzz.textnumary", changed, 37, NULL, &opts));
cl_git_pass(git_diff_patch_to_str(&pout, p));
cl_assert_equal_s(
"diff --git a/zzz.textnumary b/zzz.textnumary\n"
"index b435cd5..1604519 100644\n"
"--- a/zzz.textnumary\n"
"+++ b/zzz.textnumary\n"
"@@ -3 +3 @@ 0123456789\n"
"-0123456789\n"
"+replace a line\n", pout);
git__free(pout);
git_diff_patch_free(p);
git_blob_free(nonbin);
git_blob_free(bin);
}
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