summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorRussell Belfer <rb@github.com>2013-06-04 10:32:59 -0700
committerRussell Belfer <rb@github.com>2013-06-05 12:13:54 -0700
commit7000f3fa7bad25ec07355d6afb640ea272201dff (patch)
treed5e98c96f0d36a5d4b432342ce7e5e33b5b4cce2 /src
parentaad6967be1025b1819c3ba25163d7f69fc814130 (diff)
downloadlibgit2-7000f3fa7bad25ec07355d6afb640ea272201dff.tar.gz
Move some diff helpers into separate file
Diffstat (limited to 'src')
-rw-r--r--src/diff_output.c424
-rw-r--r--src/diff_print.c431
2 files changed, 433 insertions, 422 deletions
diff --git a/src/diff_output.c b/src/diff_output.c
index bcd39f093..9a5be2a10 100644
--- a/src/diff_output.c
+++ b/src/diff_output.c
@@ -207,6 +207,8 @@ static long diff_context_find(
const char *scan;
bool found_paren = false;
+ GIT_UNUSED(ctxt);
+
if (line_len > 0 && line[line_len - 1] == '\n')
line_len--;
if (line_len > 0 && line[line_len - 1] == '\r')
@@ -1064,359 +1066,6 @@ int git_diff_foreach(
return error;
}
-
-typedef struct {
- git_diff_list *diff;
- git_diff_data_cb print_cb;
- void *payload;
- git_buf *buf;
- int oid_strlen;
-} diff_print_info;
-
-static int diff_print_info_init(
- diff_print_info *pi,
- git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
-{
- assert(diff && diff->repo);
-
- pi->diff = diff;
- pi->print_cb = cb;
- pi->payload = payload;
- pi->buf = out;
-
- if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
- return -1;
-
- pi->oid_strlen += 1; /* for NUL byte */
-
- if (pi->oid_strlen < 2)
- pi->oid_strlen = 2;
- else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
- pi->oid_strlen = GIT_OID_HEXSZ + 1;
-
- return 0;
-}
-
-static char pick_suffix(int mode)
-{
- if (S_ISDIR(mode))
- return '/';
- else if (mode & 0100) //-V536
- /* in git, modes are very regular, so we must have 0100755 mode */
- return '*';
- else
- return ' ';
-}
-
-char git_diff_status_char(git_delta_t status)
-{
- char code;
-
- switch (status) {
- case GIT_DELTA_ADDED: code = 'A'; break;
- case GIT_DELTA_DELETED: code = 'D'; break;
- case GIT_DELTA_MODIFIED: code = 'M'; break;
- case GIT_DELTA_RENAMED: code = 'R'; break;
- case GIT_DELTA_COPIED: code = 'C'; break;
- case GIT_DELTA_IGNORED: code = 'I'; break;
- case GIT_DELTA_UNTRACKED: code = '?'; break;
- default: code = ' '; break;
- }
-
- return code;
-}
-
-static int callback_error(void)
-{
- giterr_clear();
- return GIT_EUSER;
-}
-
-static int print_compact(
- const git_diff_delta *delta, float progress, void *data)
-{
- diff_print_info *pi = data;
- char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
-
- GIT_UNUSED(progress);
-
- if (code == ' ')
- return 0;
-
- old_suffix = pick_suffix(delta->old_file.mode);
- new_suffix = pick_suffix(delta->new_file.mode);
-
- git_buf_clear(pi->buf);
-
- if (delta->old_file.path != delta->new_file.path &&
- pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
- git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
- delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
- else if (delta->old_file.mode != delta->new_file.mode &&
- delta->old_file.mode != 0 && delta->new_file.mode != 0)
- git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
- delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
- else if (old_suffix != ' ')
- git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
- else
- git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
- return callback_error();
-
- return 0;
-}
-
-int git_diff_print_compact(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
-
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
- error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi);
-
- git_buf_free(&buf);
-
- return error;
-}
-
-static int print_raw(
- const git_diff_delta *delta, float progress, void *data)
-{
- diff_print_info *pi = data;
- char code = git_diff_status_char(delta->status);
- char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
-
- GIT_UNUSED(progress);
-
- if (code == ' ')
- return 0;
-
- git_buf_clear(pi->buf);
-
- git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
- git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
-
- git_buf_printf(
- pi->buf, ":%06o %06o %s... %s... %c",
- delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
-
- if (delta->similarity > 0)
- git_buf_printf(pi->buf, "%03u", delta->similarity);
-
- if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
- git_buf_printf(
- pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
- else
- git_buf_printf(
- pi->buf, "\t%s\n", delta->old_file.path ?
- delta->old_file.path : delta->new_file.path);
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
- return callback_error();
-
- return 0;
-}
-
-int git_diff_print_raw(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
-
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
- error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi);
-
- git_buf_free(&buf);
-
- return error;
-}
-
-static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
-{
- char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
-
- git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
- git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
-
- /* TODO: Match git diff more closely */
- if (delta->old_file.mode == delta->new_file.mode) {
- git_buf_printf(pi->buf, "index %s..%s %o\n",
- start_oid, end_oid, delta->old_file.mode);
- } else {
- if (delta->old_file.mode == 0) {
- git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
- } else if (delta->new_file.mode == 0) {
- git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
- } else {
- git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
- git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
- }
- git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
- }
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- return 0;
-}
-
-static int print_patch_file(
- const git_diff_delta *delta, float progress, void *data)
-{
- diff_print_info *pi = data;
- const char *oldpfx = pi->diff->opts.old_prefix;
- const char *oldpath = delta->old_file.path;
- const char *newpfx = pi->diff->opts.new_prefix;
- const char *newpath = delta->new_file.path;
-
- GIT_UNUSED(progress);
-
- if (S_ISDIR(delta->new_file.mode) ||
- delta->status == GIT_DELTA_UNMODIFIED ||
- delta->status == GIT_DELTA_IGNORED ||
- (delta->status == GIT_DELTA_UNTRACKED &&
- (pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
- return 0;
-
- if (!oldpfx)
- oldpfx = DIFF_OLD_PREFIX_DEFAULT;
-
- if (!newpfx)
- newpfx = DIFF_NEW_PREFIX_DEFAULT;
-
- git_buf_clear(pi->buf);
- git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
-
- if (print_oid_range(pi, delta) < 0)
- return -1;
-
- if (git_oid_iszero(&delta->old_file.oid)) {
- oldpfx = "";
- oldpath = "/dev/null";
- }
- if (git_oid_iszero(&delta->new_file.oid)) {
- newpfx = "";
- newpath = "/dev/null";
- }
-
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
- git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
- git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
- }
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
- return callback_error();
-
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
- return 0;
-
- git_buf_clear(pi->buf);
- git_buf_printf(
- pi->buf, "Binary files %s%s and %s%s differ\n",
- oldpfx, oldpath, newpfx, newpath);
- if (git_buf_oom(pi->buf))
- return -1;
-
- if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
- return callback_error();
-
- return 0;
-}
-
-static int print_patch_hunk(
- const git_diff_delta *d,
- const git_diff_range *r,
- const char *header,
- size_t header_len,
- void *data)
-{
- diff_print_info *pi = data;
-
- if (S_ISDIR(d->new_file.mode))
- return 0;
-
- git_buf_clear(pi->buf);
- if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
- return -1;
-
- if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
- return callback_error();
-
- return 0;
-}
-
-static int print_patch_line(
- const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin, /* GIT_DIFF_LINE value from above */
- const char *content,
- size_t content_len,
- void *data)
-{
- diff_print_info *pi = data;
-
- if (S_ISDIR(delta->new_file.mode))
- return 0;
-
- git_buf_clear(pi->buf);
-
- if (line_origin == GIT_DIFF_LINE_ADDITION ||
- line_origin == GIT_DIFF_LINE_DELETION ||
- line_origin == GIT_DIFF_LINE_CONTEXT)
- git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
- else if (content_len > 0)
- git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
-
- if (git_buf_oom(pi->buf))
- return -1;
-
- if (pi->print_cb(delta, range, line_origin,
- git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
- return callback_error();
-
- return 0;
-}
-
-int git_diff_print_patch(
- git_diff_list *diff,
- git_diff_data_cb print_cb,
- void *payload)
-{
- int error;
- git_buf buf = GIT_BUF_INIT;
- diff_print_info pi;
-
- if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
- error = git_diff_foreach(
- diff, print_patch_file, print_patch_hunk, print_patch_line, &pi);
-
- git_buf_free(&buf);
-
- return error;
-}
-
static void set_data_from_blob(
const git_blob *blob, git_map *map, git_diff_file *file)
{
@@ -1826,75 +1475,6 @@ notfound:
return diff_error_outofrange(thing);
}
-static int print_to_buffer_cb(
- const git_diff_delta *delta,
- const git_diff_range *range,
- char line_origin,
- const char *content,
- size_t content_len,
- void *payload)
-{
- git_buf *output = payload;
- GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
- return git_buf_put(output, content, content_len);
-}
-
-int git_diff_patch_print(
- git_diff_patch *patch,
- git_diff_data_cb print_cb,
- void *payload)
-{
- int error;
- git_buf temp = GIT_BUF_INIT;
- diff_print_info pi;
- size_t h, l;
-
- assert(patch && print_cb);
-
- if (!(error = diff_print_info_init(
- &pi, &temp, patch->diff, print_cb, payload)))
- error = print_patch_file(patch->delta, 0, &pi);
-
- for (h = 0; h < patch->hunks_size && !error; ++h) {
- diff_patch_hunk *hunk = &patch->hunks[h];
-
- error = print_patch_hunk(
- patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi);
-
- for (l = 0; l < hunk->line_count && !error; ++l) {
- diff_patch_line *line = &patch->lines[hunk->line_start + l];
-
- error = print_patch_line(
- patch->delta, &hunk->range,
- line->origin, line->ptr, line->len, &pi);
- }
- }
-
- git_buf_free(&temp);
-
- return error;
-}
-
-int git_diff_patch_to_str(
- char **string,
- git_diff_patch *patch)
-{
- int error;
- git_buf output = GIT_BUF_INIT;
-
- error = git_diff_patch_print(patch, print_to_buffer_cb, &output);
-
- /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
- * meaning a memory allocation failure, so just map to -1...
- */
- if (error == GIT_EUSER)
- error = -1;
-
- *string = git_buf_detach(&output);
-
- return error;
-}
-
int git_diff__paired_foreach(
git_diff_list *idx2head,
git_diff_list *wd2idx,
diff --git a/src/diff_print.c b/src/diff_print.c
new file mode 100644
index 000000000..b6fbec829
--- /dev/null
+++ b/src/diff_print.c
@@ -0,0 +1,431 @@
+/*
+ * Copyright (C) the libgit2 contributors. All rights reserved.
+ *
+ * This file is part of libgit2, distributed under the GNU GPL v2 with
+ * a Linking Exception. For full terms see the included COPYING file.
+ */
+#include "common.h"
+#include "diff.h"
+#include "diff_output.h"
+
+typedef struct {
+ git_diff_list *diff;
+ git_diff_data_cb print_cb;
+ void *payload;
+ git_buf *buf;
+ int oid_strlen;
+} diff_print_info;
+
+static int diff_print_info_init(
+ diff_print_info *pi,
+ git_buf *out, git_diff_list *diff, git_diff_data_cb cb, void *payload)
+{
+ assert(diff && diff->repo);
+
+ pi->diff = diff;
+ pi->print_cb = cb;
+ pi->payload = payload;
+ pi->buf = out;
+
+ if (git_repository__cvar(&pi->oid_strlen, diff->repo, GIT_CVAR_ABBREV) < 0)
+ return -1;
+
+ pi->oid_strlen += 1; /* for NUL byte */
+
+ if (pi->oid_strlen < 2)
+ pi->oid_strlen = 2;
+ else if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
+ pi->oid_strlen = GIT_OID_HEXSZ + 1;
+
+ return 0;
+}
+
+static char pick_suffix(int mode)
+{
+ if (S_ISDIR(mode))
+ return '/';
+ else if (mode & 0100) //-V536
+ /* in git, modes are very regular, so we must have 0100755 mode */
+ return '*';
+ else
+ return ' ';
+}
+
+char git_diff_status_char(git_delta_t status)
+{
+ char code;
+
+ switch (status) {
+ case GIT_DELTA_ADDED: code = 'A'; break;
+ case GIT_DELTA_DELETED: code = 'D'; break;
+ case GIT_DELTA_MODIFIED: code = 'M'; break;
+ case GIT_DELTA_RENAMED: code = 'R'; break;
+ case GIT_DELTA_COPIED: code = 'C'; break;
+ case GIT_DELTA_IGNORED: code = 'I'; break;
+ case GIT_DELTA_UNTRACKED: code = '?'; break;
+ default: code = ' '; break;
+ }
+
+ return code;
+}
+
+static int callback_error(void)
+{
+ giterr_clear();
+ return GIT_EUSER;
+}
+
+static int print_compact(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
+
+ GIT_UNUSED(progress);
+
+ if (code == ' ')
+ return 0;
+
+ old_suffix = pick_suffix(delta->old_file.mode);
+ new_suffix = pick_suffix(delta->new_file.mode);
+
+ git_buf_clear(pi->buf);
+
+ if (delta->old_file.path != delta->new_file.path &&
+ pi->diff->strcomp(delta->old_file.path,delta->new_file.path) != 0)
+ git_buf_printf(pi->buf, "%c\t%s%c -> %s%c\n", code,
+ delta->old_file.path, old_suffix, delta->new_file.path, new_suffix);
+ else if (delta->old_file.mode != delta->new_file.mode &&
+ delta->old_file.mode != 0 && delta->new_file.mode != 0)
+ git_buf_printf(pi->buf, "%c\t%s%c (%o -> %o)\n", code,
+ delta->old_file.path, new_suffix, delta->old_file.mode, delta->new_file.mode);
+ else if (old_suffix != ' ')
+ git_buf_printf(pi->buf, "%c\t%s%c\n", code, delta->old_file.path, old_suffix);
+ else
+ git_buf_printf(pi->buf, "%c\t%s\n", code, delta->old_file.path);
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
+ git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ return callback_error();
+
+ return 0;
+}
+
+int git_diff_print_compact(
+ git_diff_list *diff,
+ git_diff_data_cb print_cb,
+ void *payload)
+{
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
+ error = git_diff_foreach(diff, print_compact, NULL, NULL, &pi);
+
+ git_buf_free(&buf);
+
+ return error;
+}
+
+static int print_raw(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ char code = git_diff_status_char(delta->status);
+ char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
+
+ GIT_UNUSED(progress);
+
+ if (code == ' ')
+ return 0;
+
+ git_buf_clear(pi->buf);
+
+ git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
+ git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
+
+ git_buf_printf(
+ pi->buf, ":%06o %06o %s... %s... %c",
+ delta->old_file.mode, delta->new_file.mode, start_oid, end_oid, code);
+
+ if (delta->similarity > 0)
+ git_buf_printf(pi->buf, "%03u", delta->similarity);
+
+ if (delta->status == GIT_DELTA_RENAMED || delta->status == GIT_DELTA_COPIED)
+ git_buf_printf(
+ pi->buf, "\t%s %s\n", delta->old_file.path, delta->new_file.path);
+ else
+ git_buf_printf(
+ pi->buf, "\t%s\n", delta->old_file.path ?
+ delta->old_file.path : delta->new_file.path);
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
+ git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ return callback_error();
+
+ return 0;
+}
+
+int git_diff_print_raw(
+ git_diff_list *diff,
+ git_diff_data_cb print_cb,
+ void *payload)
+{
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
+ error = git_diff_foreach(diff, print_raw, NULL, NULL, &pi);
+
+ git_buf_free(&buf);
+
+ return error;
+}
+
+static int print_oid_range(diff_print_info *pi, const git_diff_delta *delta)
+{
+ char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
+
+ git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.oid);
+ git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.oid);
+
+ /* TODO: Match git diff more closely */
+ if (delta->old_file.mode == delta->new_file.mode) {
+ git_buf_printf(pi->buf, "index %s..%s %o\n",
+ start_oid, end_oid, delta->old_file.mode);
+ } else {
+ if (delta->old_file.mode == 0) {
+ git_buf_printf(pi->buf, "new file mode %o\n", delta->new_file.mode);
+ } else if (delta->new_file.mode == 0) {
+ git_buf_printf(pi->buf, "deleted file mode %o\n", delta->old_file.mode);
+ } else {
+ git_buf_printf(pi->buf, "old mode %o\n", delta->old_file.mode);
+ git_buf_printf(pi->buf, "new mode %o\n", delta->new_file.mode);
+ }
+ git_buf_printf(pi->buf, "index %s..%s\n", start_oid, end_oid);
+ }
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ return 0;
+}
+
+static int print_patch_file(
+ const git_diff_delta *delta, float progress, void *data)
+{
+ diff_print_info *pi = data;
+ const char *oldpfx = pi->diff->opts.old_prefix;
+ const char *oldpath = delta->old_file.path;
+ const char *newpfx = pi->diff->opts.new_prefix;
+ const char *newpath = delta->new_file.path;
+
+ GIT_UNUSED(progress);
+
+ if (S_ISDIR(delta->new_file.mode) ||
+ delta->status == GIT_DELTA_UNMODIFIED ||
+ delta->status == GIT_DELTA_IGNORED ||
+ (delta->status == GIT_DELTA_UNTRACKED &&
+ (pi->diff->opts.flags & GIT_DIFF_INCLUDE_UNTRACKED_CONTENT) == 0))
+ return 0;
+
+ if (!oldpfx)
+ oldpfx = DIFF_OLD_PREFIX_DEFAULT;
+
+ if (!newpfx)
+ newpfx = DIFF_NEW_PREFIX_DEFAULT;
+
+ git_buf_clear(pi->buf);
+ git_buf_printf(pi->buf, "diff --git %s%s %s%s\n", oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
+
+ if (print_oid_range(pi, delta) < 0)
+ return -1;
+
+ if (git_oid_iszero(&delta->old_file.oid)) {
+ oldpfx = "";
+ oldpath = "/dev/null";
+ }
+ if (git_oid_iszero(&delta->new_file.oid)) {
+ newpfx = "";
+ newpath = "/dev/null";
+ }
+
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0) {
+ git_buf_printf(pi->buf, "--- %s%s\n", oldpfx, oldpath);
+ git_buf_printf(pi->buf, "+++ %s%s\n", newpfx, newpath);
+ }
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_FILE_HDR,
+ git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ return callback_error();
+
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+ return 0;
+
+ git_buf_clear(pi->buf);
+ git_buf_printf(
+ pi->buf, "Binary files %s%s and %s%s differ\n",
+ oldpfx, oldpath, newpfx, newpath);
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ if (pi->print_cb(delta, NULL, GIT_DIFF_LINE_BINARY,
+ git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ return callback_error();
+
+ return 0;
+}
+
+static int print_patch_hunk(
+ const git_diff_delta *d,
+ const git_diff_range *r,
+ const char *header,
+ size_t header_len,
+ void *data)
+{
+ diff_print_info *pi = data;
+
+ if (S_ISDIR(d->new_file.mode))
+ return 0;
+
+ git_buf_clear(pi->buf);
+ if (git_buf_printf(pi->buf, "%.*s", (int)header_len, header) < 0)
+ return -1;
+
+ if (pi->print_cb(d, r, GIT_DIFF_LINE_HUNK_HDR,
+ git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ return callback_error();
+
+ return 0;
+}
+
+static int print_patch_line(
+ const git_diff_delta *delta,
+ const git_diff_range *range,
+ char line_origin, /* GIT_DIFF_LINE value from above */
+ const char *content,
+ size_t content_len,
+ void *data)
+{
+ diff_print_info *pi = data;
+
+ if (S_ISDIR(delta->new_file.mode))
+ return 0;
+
+ git_buf_clear(pi->buf);
+
+ if (line_origin == GIT_DIFF_LINE_ADDITION ||
+ line_origin == GIT_DIFF_LINE_DELETION ||
+ line_origin == GIT_DIFF_LINE_CONTEXT)
+ git_buf_printf(pi->buf, "%c%.*s", line_origin, (int)content_len, content);
+ else if (content_len > 0)
+ git_buf_printf(pi->buf, "%.*s", (int)content_len, content);
+
+ if (git_buf_oom(pi->buf))
+ return -1;
+
+ if (pi->print_cb(delta, range, line_origin,
+ git_buf_cstr(pi->buf), git_buf_len(pi->buf), pi->payload))
+ return callback_error();
+
+ return 0;
+}
+
+int git_diff_print_patch(
+ git_diff_list *diff,
+ git_diff_data_cb print_cb,
+ void *payload)
+{
+ int error;
+ git_buf buf = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ if (!(error = diff_print_info_init(&pi, &buf, diff, print_cb, payload)))
+ error = git_diff_foreach(
+ diff, print_patch_file, print_patch_hunk, print_patch_line, &pi);
+
+ git_buf_free(&buf);
+
+ return error;
+}
+
+
+static int print_to_buffer_cb(
+ const git_diff_delta *delta,
+ const git_diff_range *range,
+ char line_origin,
+ const char *content,
+ size_t content_len,
+ void *payload)
+{
+ git_buf *output = payload;
+ GIT_UNUSED(delta); GIT_UNUSED(range); GIT_UNUSED(line_origin);
+ return git_buf_put(output, content, content_len);
+}
+
+int git_diff_patch_print(
+ git_diff_patch *patch,
+ git_diff_data_cb print_cb,
+ void *payload)
+{
+ int error;
+ git_buf temp = GIT_BUF_INIT;
+ diff_print_info pi;
+ size_t h, l;
+
+ assert(patch && print_cb);
+
+ if (!(error = diff_print_info_init(
+ &pi, &temp, patch->diff, print_cb, payload)))
+ error = print_patch_file(patch->delta, 0, &pi);
+
+ for (h = 0; h < patch->hunks_size && !error; ++h) {
+ diff_patch_hunk *hunk = &patch->hunks[h];
+
+ error = print_patch_hunk(
+ patch->delta, &hunk->range, hunk->header, hunk->header_len, &pi);
+
+ for (l = 0; l < hunk->line_count && !error; ++l) {
+ diff_patch_line *line = &patch->lines[hunk->line_start + l];
+
+ error = print_patch_line(
+ patch->delta, &hunk->range,
+ line->origin, line->ptr, line->len, &pi);
+ }
+ }
+
+ git_buf_free(&temp);
+
+ return error;
+}
+
+int git_diff_patch_to_str(
+ char **string,
+ git_diff_patch *patch)
+{
+ int error;
+ git_buf output = GIT_BUF_INIT;
+
+ error = git_diff_patch_print(patch, print_to_buffer_cb, &output);
+
+ /* GIT_EUSER means git_buf_put in print_to_buffer_cb returned -1,
+ * meaning a memory allocation failure, so just map to -1...
+ */
+ if (error == GIT_EUSER)
+ error = -1;
+
+ *string = git_buf_detach(&output);
+
+ return error;
+}