diff options
-rw-r--r-- | include/git2/diff.h | 24 | ||||
-rw-r--r-- | src/diff_output.c | 86 |
2 files changed, 109 insertions, 1 deletions
diff --git a/include/git2/diff.h b/include/git2/diff.h index 760de6fd1..f1c0cd969 100644 --- a/include/git2/diff.h +++ b/include/git2/diff.h @@ -824,6 +824,30 @@ GIT_EXTERN(int) git_diff_blobs( git_diff_data_cb line_cb, void *payload); +/** + * Directly run a text diff between a blob and a buffer. + * + * Compared to a file, a blob and a buffer lack some contextual information. As such, + * the `git_diff_file` parameters of the callbacks will be filled + * accordingly to the following: `mode` will be set to 0, `path` will be set + * to NULL. When dealing with a NULL blob, `oid` will be set to 0. + * + * When at least the blob or the buffer are binary, the + * `git_diff_delta` binary attribute will be set to 1 and no call to the + * hunk_cb nor line_cb will be made. + * + * @return 0 on success, GIT_EUSER on non-zero callback, or error code + */ +GIT_EXTERN(int) git_diff_blob_to_buffer( + git_blob *old_blob, + char *buffer, + size_t buffer_len, + const git_diff_options *options, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb data_cb, + void *payload); + GIT_END_DECL /** @} */ diff --git a/src/diff_output.c b/src/diff_output.c index b18255d58..eeb6f2d16 100644 --- a/src/diff_output.c +++ b/src/diff_output.c @@ -1231,7 +1231,6 @@ int git_diff_print_patch( return error; } - static void set_data_from_blob( git_blob *blob, git_map *map, git_diff_file *file) { @@ -1328,6 +1327,91 @@ cleanup: return error; } +static void set_data_from_buffer( + char *buffer, size_t buffer_len, git_map *map, git_diff_file *file) +{ + file->size = buffer_len; + file->mode = 0644; + + map->len = (size_t)file->size; + map->data = (char *)buffer; +} + +int git_diff_blob_to_buffer( + git_blob *old_blob, + char *buffer, + size_t buffer_len, + const git_diff_options *options, + git_diff_file_cb file_cb, + git_diff_hunk_cb hunk_cb, + git_diff_data_cb data_cb, + void *payload) +{ + int error; + git_repository *repo; + diff_context ctxt; + git_diff_delta delta; + git_diff_patch patch; + + GITERR_CHECK_VERSION(options, GIT_DIFF_OPTIONS_VERSION, "git_diff_options"); + + if (old_blob) + repo = git_object_owner((git_object *)old_blob); + else + repo = NULL; + + diff_context_init( + &ctxt, NULL, repo, options, + file_cb, hunk_cb, data_cb, payload); + + diff_patch_init(&ctxt, &patch); + + /* create a fake delta record and simulate diff_patch_load */ + + memset(&delta, 0, sizeof(delta)); + delta.binary = -1; + + if (options && (options->flags & GIT_DIFF_REVERSE)) { + set_data_from_blob(old_blob, &patch.new_data, &delta.new_file); + set_data_from_buffer(buffer, buffer_len, &patch.old_data, &delta.old_file); + } else { + set_data_from_blob(old_blob, &patch.old_data, &delta.old_file); + set_data_from_buffer(buffer, buffer_len, &patch.new_data, &delta.new_file); + } + + delta.status = buffer ? + (old_blob ? GIT_DELTA_MODIFIED : GIT_DELTA_ADDED) : + (old_blob ? GIT_DELTA_DELETED : GIT_DELTA_UNTRACKED); + + if (git_oid_cmp(&delta.new_file.oid, &delta.old_file.oid) == 0) + delta.status = GIT_DELTA_UNMODIFIED; + + patch.delta = δ + + if ((error = diff_delta_is_binary_by_content( + &ctxt, &delta, &delta.old_file, &patch.old_data)) < 0 || + (error = diff_delta_is_binary_by_content( + &ctxt, &delta, &delta.new_file, &patch.new_data)) < 0) + goto cleanup; + + patch.flags |= GIT_DIFF_PATCH_LOADED; + if (delta.binary != 1 && delta.status != GIT_DELTA_UNMODIFIED) + patch.flags |= GIT_DIFF_PATCH_DIFFABLE; + + /* do diffs */ + + if (!(error = diff_delta_file_callback(&ctxt, patch.delta, 1))) + error = diff_patch_generate(&ctxt, &patch); + +cleanup: + diff_patch_unload(&patch); + + if (error == GIT_EUSER) + giterr_clear(); + + return error; +} + size_t git_diff_num_deltas(git_diff_list *diff) { |