Introduce git_diff_blob_to_buffer
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139
diff --git a/include/git2/diff.h b/include/git2/diff.h
index 760de6f..f1c0cd9 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 b18255d..eeb6f2d 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)
{