summaryrefslogtreecommitdiff
path: root/src/diff_print.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/diff_print.c')
-rw-r--r--src/diff_print.c131
1 files changed, 125 insertions, 6 deletions
diff --git a/src/diff_print.c b/src/diff_print.c
index a7f7b6fe8..e54155458 100644
--- a/src/diff_print.c
+++ b/src/diff_print.c
@@ -8,6 +8,9 @@
#include "diff.h"
#include "diff_patch.h"
#include "fileops.h"
+#include "zstream.h"
+#include "blob.h"
+#include "delta.h"
#include "git2/sys/diff.h"
typedef struct {
@@ -37,6 +40,8 @@ static int diff_print_info_init(
if (diff)
pi->flags = diff->opts.flags;
+ else
+ pi->flags = 0;
if (diff && diff->opts.id_abbrev != 0)
pi->oid_strlen = diff->opts.id_abbrev;
@@ -276,6 +281,118 @@ int git_diff_delta__format_file_header(
return git_buf_oom(out) ? -1 : 0;
}
+static int print_binary_hunk(diff_print_info *pi, git_blob *old, git_blob *new)
+{
+ git_buf deflate = GIT_BUF_INIT, delta = GIT_BUF_INIT, *out = NULL;
+ const void *old_data, *new_data;
+ size_t old_data_len, new_data_len, delta_data_len, inflated_len, remain;
+ const char *out_type = "literal";
+ char *ptr;
+ int error;
+
+ old_data = old ? git_blob_rawcontent(old) : NULL;
+ new_data = new ? git_blob_rawcontent(new) : NULL;
+
+ old_data_len = old ? (size_t)git_blob_rawsize(old) : 0;
+ new_data_len = new ? (size_t)git_blob_rawsize(new) : 0;
+
+ out = &deflate;
+ inflated_len = new_data_len;
+
+ if ((error = git_zstream_deflatebuf(
+ &deflate, new_data, new_data_len)) < 0)
+ goto done;
+
+ if (old && new) {
+ void *delta_data;
+
+ delta_data = git_delta(old_data, old_data_len, new_data,
+ new_data_len, &delta_data_len, deflate.size);
+
+ if (delta_data) {
+ error = git_zstream_deflatebuf(&delta, delta_data, delta_data_len);
+ free(delta_data);
+
+ if (error < 0)
+ goto done;
+
+ if (delta.size < deflate.size) {
+ out = &delta;
+ out_type = "delta";
+ inflated_len = delta_data_len;
+ }
+ }
+ }
+
+ git_buf_printf(pi->buf, "%s %" PRIuZ "\n", out_type, inflated_len);
+ pi->line.num_lines++;
+
+ for (ptr = out->ptr, remain = out->size; remain > 0; ) {
+ size_t chunk_len = (52 < remain) ? 52 : remain;
+
+ if (chunk_len <= 26)
+ git_buf_putc(pi->buf, chunk_len + 'A' - 1);
+ else
+ git_buf_putc(pi->buf, chunk_len - 26 + 'a' - 1);
+
+ git_buf_put_base85(pi->buf, ptr, chunk_len);
+ git_buf_putc(pi->buf, '\n');
+
+ if (git_buf_oom(pi->buf)) {
+ error = -1;
+ goto done;
+ }
+
+ ptr += chunk_len;
+ remain -= chunk_len;
+ pi->line.num_lines++;
+ }
+
+done:
+ git_buf_free(&deflate);
+ git_buf_free(&delta);
+
+ return error;
+}
+
+/* git diff --binary 8d7523f~2 8d7523f~1 */
+static int diff_print_patch_file_binary(
+ diff_print_info *pi, const git_diff_delta *delta,
+ const char *oldpfx, const char *newpfx)
+{
+ git_blob *old = NULL, *new = NULL;
+ const git_oid *old_id, *new_id;
+ int error;
+
+ if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0) {
+ pi->line.num_lines = 1;
+ return diff_delta_format_with_paths(
+ pi->buf, delta, oldpfx, newpfx,
+ "Binary files %s%s and %s%s differ\n");
+ }
+
+ git_buf_printf(pi->buf, "GIT binary patch\n");
+ pi->line.num_lines++;
+
+ old_id = (delta->status != GIT_DELTA_ADDED) ? &delta->old_file.id : NULL;
+ new_id = (delta->status != GIT_DELTA_DELETED) ? &delta->new_file.id : NULL;
+
+ if ((old_id && (error = git_blob_lookup(&old, pi->diff->repo, old_id)) < 0) ||
+ (new_id && (error = git_blob_lookup(&new, pi->diff->repo,new_id)) < 0) ||
+ (error = print_binary_hunk(pi, old, new)) < 0 ||
+ (error = git_buf_putc(pi->buf, '\n')) < 0 ||
+ (error = print_binary_hunk(pi, new, old)) < 0)
+ goto done;
+
+ pi->line.num_lines++;
+
+done:
+ git_blob_free(old);
+ git_blob_free(new);
+
+ return error;
+}
+
static int diff_print_patch_file(
const git_diff_delta *delta, float progress, void *data)
{
@@ -286,6 +403,11 @@ static int diff_print_patch_file(
const char *newpfx =
pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
+ bool binary = !!(delta->flags & GIT_DIFF_FLAG_BINARY);
+ bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
+ int oid_strlen = binary && show_binary ?
+ GIT_OID_HEXSZ + 1 : pi->oid_strlen;
+
GIT_UNUSED(progress);
if (S_ISDIR(delta->new_file.mode) ||
@@ -296,7 +418,7 @@ static int diff_print_patch_file(
return 0;
if ((error = git_diff_delta__format_file_header(
- pi->buf, delta, oldpfx, newpfx, pi->oid_strlen)) < 0)
+ pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0)
return error;
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
@@ -306,20 +428,17 @@ static int diff_print_patch_file(
if ((error = pi->print_cb(delta, NULL, &pi->line, pi->payload)) != 0)
return error;
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+ if (!binary)
return 0;
git_buf_clear(pi->buf);
- if ((error = diff_delta_format_with_paths(
- pi->buf, delta, oldpfx, newpfx,
- "Binary files %s%s and %s%s differ\n")) < 0)
+ if ((error = diff_print_patch_file_binary(pi, delta, oldpfx, newpfx)) < 0)
return error;
pi->line.origin = GIT_DIFF_LINE_BINARY;
pi->line.content = git_buf_cstr(pi->buf);
pi->line.content_len = git_buf_len(pi->buf);
- pi->line.num_lines = 1;
return pi->print_cb(delta, NULL, &pi->line, pi->payload);
}