summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/buffer.c36
-rw-r--r--src/buffer.h3
-rw-r--r--src/diff_print.c131
3 files changed, 164 insertions, 6 deletions
diff --git a/src/buffer.c b/src/buffer.c
index f6e34a445..5169c3e09 100644
--- a/src/buffer.c
+++ b/src/buffer.c
@@ -212,6 +212,42 @@ int git_buf_put_base64(git_buf *buf, const char *data, size_t len)
return 0;
}
+static const char b85str[] =
+ "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz!#$%&()*+-;<=>?@^_`{|}~";
+
+int git_buf_put_base85(git_buf *buf, const char *data, size_t len)
+{
+ ENSURE_SIZE(buf, buf->size + (5 * ((len / 4) + !!(len % 4))) + 1);
+
+ while (len) {
+ uint32_t acc = 0;
+ char b85[5];
+ int i;
+
+ for (i = 24; i >= 0; i -= 8) {
+ uint8_t ch = *data++;
+ acc |= ch << i;
+
+ if (--len == 0)
+ break;
+ }
+
+ for (i = 4; i >= 0; i--) {
+ int val = acc % 85;
+ acc /= 85;
+
+ b85[i] = b85str[val];
+ }
+
+ for (i = 0; i < 5; i++)
+ buf->ptr[buf->size++] = b85[i];
+ }
+
+ buf->ptr[buf->size] = '\0';
+
+ return 0;
+}
+
int git_buf_vprintf(git_buf *buf, const char *format, va_list ap)
{
int len;
diff --git a/src/buffer.h b/src/buffer.h
index 398aec9b7..70d6d73b3 100644
--- a/src/buffer.h
+++ b/src/buffer.h
@@ -158,6 +158,9 @@ int git_buf_cmp(const git_buf *a, const git_buf *b);
/* Write data as base64 encoded in buffer */
int git_buf_put_base64(git_buf *buf, const char *data, size_t len);
+/* Write data as "base85" encoded in buffer */
+int git_buf_put_base85(git_buf *buf, const char *data, size_t len);
+
/*
* Insert, remove or replace a portion of the buffer.
*
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);
}