diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/buffer.c | 36 | ||||
-rw-r--r-- | src/buffer.h | 3 | ||||
-rw-r--r-- | src/diff_print.c | 131 |
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 = δ + 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); } |