vendor/libgit2/src/diff_print.c in rugged-0.25.0b5 vs vendor/libgit2/src/diff_print.c in rugged-0.25.0b6
- old
+ new
@@ -4,31 +4,32 @@
* 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_patch.h"
+#include "diff_file.h"
+#include "patch_generate.h"
#include "fileops.h"
#include "zstream.h"
#include "blob.h"
#include "delta.h"
#include "git2/sys/diff.h"
typedef struct {
- git_diff *diff;
git_diff_format_t format;
git_diff_line_cb print_cb;
void *payload;
+
git_buf *buf;
- uint32_t flags;
- int oid_strlen;
git_diff_line line;
- unsigned int
- content_loaded : 1,
- content_allocated : 1;
- git_diff_file_content *ofile;
- git_diff_file_content *nfile;
+
+ const char *old_prefix;
+ const char *new_prefix;
+ uint32_t flags;
+ int id_strlen;
+
+ int (*strcomp)(const char *, const char *);
} diff_print_info;
static int diff_print_info_init__common(
diff_print_info *pi,
git_buf *out,
@@ -40,22 +41,20 @@
pi->format = format;
pi->print_cb = cb;
pi->payload = payload;
pi->buf = out;
- if (!pi->oid_strlen) {
+ if (!pi->id_strlen) {
if (!repo)
- pi->oid_strlen = GIT_ABBREV_DEFAULT;
- else if (git_repository__cvar(&pi->oid_strlen, repo, GIT_CVAR_ABBREV) < 0)
+ pi->id_strlen = GIT_ABBREV_DEFAULT;
+ else if (git_repository__cvar(&pi->id_strlen, repo, GIT_CVAR_ABBREV) < 0)
return -1;
}
- pi->oid_strlen += 1; /* for NUL byte */
+ if (pi->id_strlen > GIT_OID_HEXSZ)
+ pi->id_strlen = GIT_OID_HEXSZ;
- if (pi->oid_strlen > GIT_OID_HEXSZ + 1)
- pi->oid_strlen = GIT_OID_HEXSZ + 1;
-
memset(&pi->line, 0, sizeof(pi->line));
pi->line.old_lineno = -1;
pi->line.new_lineno = -1;
pi->line.num_lines = 1;
@@ -72,15 +71,17 @@
{
git_repository *repo = diff ? diff->repo : NULL;
memset(pi, 0, sizeof(diff_print_info));
- pi->diff = diff;
-
if (diff) {
pi->flags = diff->opts.flags;
- pi->oid_strlen = diff->opts.id_abbrev;
+ pi->id_strlen = diff->opts.id_abbrev;
+ pi->old_prefix = diff->opts.old_prefix;
+ pi->new_prefix = diff->opts.new_prefix;
+
+ pi->strcomp = diff->strcomp;
}
return diff_print_info_init__common(pi, out, repo, format, cb, payload);
}
@@ -90,28 +91,20 @@
git_patch *patch,
git_diff_format_t format,
git_diff_line_cb cb,
void *payload)
{
- git_repository *repo;
-
assert(patch);
- repo = patch->diff ? patch->diff->repo : NULL;
-
memset(pi, 0, sizeof(diff_print_info));
- pi->diff = patch->diff;
-
pi->flags = patch->diff_opts.flags;
- pi->oid_strlen = patch->diff_opts.id_abbrev;
+ pi->id_strlen = patch->diff_opts.id_abbrev;
+ pi->old_prefix = patch->diff_opts.old_prefix;
+ pi->new_prefix = patch->diff_opts.new_prefix;
- pi->content_loaded = 1;
- pi->ofile = &patch->ofile;
- pi->nfile = &patch->nfile;
-
- return diff_print_info_init__common(pi, out, repo, format, cb, payload);
+ return diff_print_info_init__common(pi, out, patch->repo, format, cb, payload);
}
static char diff_pick_suffix(int mode)
{
if (S_ISDIR(mode))
@@ -171,12 +164,12 @@
const git_diff_delta *delta, float progress, void *data)
{
diff_print_info *pi = data;
git_buf *out = pi->buf;
char old_suffix, new_suffix, code = git_diff_status_char(delta->status);
- int (*strcomp)(const char *, const char *) =
- pi->diff ? pi->diff->strcomp : git__strcmp;
+ int(*strcomp)(const char *, const char *) = pi->strcomp ?
+ pi->strcomp : git__strcmp;
GIT_UNUSED(progress);
if ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
return 0;
@@ -211,25 +204,36 @@
static int diff_print_one_raw(
const git_diff_delta *delta, float progress, void *data)
{
diff_print_info *pi = data;
git_buf *out = pi->buf;
+ int id_abbrev;
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 ((pi->flags & GIT_DIFF_SHOW_UNMODIFIED) == 0 && code == ' ')
return 0;
git_buf_clear(out);
- git_oid_tostr(start_oid, pi->oid_strlen, &delta->old_file.id);
- git_oid_tostr(end_oid, pi->oid_strlen, &delta->new_file.id);
+ id_abbrev = delta->old_file.mode ? delta->old_file.id_abbrev :
+ delta->new_file.id_abbrev;
+ if (pi->id_strlen > id_abbrev) {
+ giterr_set(GITERR_PATCH,
+ "The patch input contains %d id characters (cannot print %d)",
+ id_abbrev, pi->id_strlen);
+ return -1;
+ }
+
+ git_oid_tostr(start_oid, pi->id_strlen + 1, &delta->old_file.id);
+ git_oid_tostr(end_oid, pi->id_strlen + 1, &delta->new_file.id);
+
git_buf_printf(
- out, (pi->oid_strlen <= GIT_OID_HEXSZ) ?
+ out, (pi->id_strlen <= GIT_OID_HEXSZ) ?
":%06o %06o %s... %s... %c" : ":%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(out, "%03u", delta->similarity);
@@ -250,85 +254,201 @@
pi->line.content_len = git_buf_len(out);
return pi->print_cb(delta, NULL, &pi->line, pi->payload);
}
+static int diff_print_modes(
+ git_buf *out, const git_diff_delta *delta)
+{
+ git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
+ git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
+
+ return git_buf_oom(out) ? -1 : 0;
+}
+
static int diff_print_oid_range(
- git_buf *out, const git_diff_delta *delta, int oid_strlen)
+ git_buf *out, const git_diff_delta *delta, int id_strlen)
{
char start_oid[GIT_OID_HEXSZ+1], end_oid[GIT_OID_HEXSZ+1];
- git_oid_tostr(start_oid, oid_strlen, &delta->old_file.id);
- git_oid_tostr(end_oid, oid_strlen, &delta->new_file.id);
+ if (delta->old_file.mode &&
+ id_strlen > delta->old_file.id_abbrev) {
+ giterr_set(GITERR_PATCH,
+ "The patch input contains %d id characters (cannot print %d)",
+ delta->old_file.id_abbrev, id_strlen);
+ return -1;
+ }
- /* TODO: Match git diff more closely */
+ if ((delta->new_file.mode &&
+ id_strlen > delta->new_file.id_abbrev)) {
+ giterr_set(GITERR_PATCH,
+ "The patch input contains %d id characters (cannot print %d)",
+ delta->new_file.id_abbrev, id_strlen);
+ return -1;
+ }
+
+ git_oid_tostr(start_oid, id_strlen + 1, &delta->old_file.id);
+ git_oid_tostr(end_oid, id_strlen + 1, &delta->new_file.id);
+
if (delta->old_file.mode == delta->new_file.mode) {
git_buf_printf(out, "index %s..%s %o\n",
start_oid, end_oid, delta->old_file.mode);
} else {
- if (delta->old_file.mode == 0) {
+ if (delta->old_file.mode == 0)
git_buf_printf(out, "new file mode %o\n", delta->new_file.mode);
- } else if (delta->new_file.mode == 0) {
+ else if (delta->new_file.mode == 0)
git_buf_printf(out, "deleted file mode %o\n", delta->old_file.mode);
- } else {
- git_buf_printf(out, "old mode %o\n", delta->old_file.mode);
- git_buf_printf(out, "new mode %o\n", delta->new_file.mode);
- }
+ else
+ diff_print_modes(out, delta);
+
git_buf_printf(out, "index %s..%s\n", start_oid, end_oid);
}
return git_buf_oom(out) ? -1 : 0;
}
+static int diff_delta_format_path(
+ git_buf *out, const char *prefix, const char *filename)
+{
+ if (git_buf_joinpath(out, prefix, filename) < 0)
+ return -1;
+
+ return git_buf_quote(out);
+}
+
static int diff_delta_format_with_paths(
git_buf *out,
const git_diff_delta *delta,
- const char *oldpfx,
- const char *newpfx,
- const char *template)
+ const char *template,
+ const char *oldpath,
+ const char *newpath)
{
- const char *oldpath = delta->old_file.path;
- const char *newpath = delta->new_file.path;
-
- if (git_oid_iszero(&delta->old_file.id)) {
- oldpfx = "";
+ if (git_oid_iszero(&delta->old_file.id))
oldpath = "/dev/null";
- }
- if (git_oid_iszero(&delta->new_file.id)) {
- newpfx = "";
+
+ if (git_oid_iszero(&delta->new_file.id))
newpath = "/dev/null";
+
+ return git_buf_printf(out, template, oldpath, newpath);
+}
+
+int diff_delta_format_similarity_header(
+ git_buf *out,
+ const git_diff_delta *delta)
+{
+ git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
+ const char *type;
+ int error = 0;
+
+ if (delta->similarity > 100) {
+ giterr_set(GITERR_PATCH, "invalid similarity %d", delta->similarity);
+ error = -1;
+ goto done;
}
- return git_buf_printf(out, template, oldpfx, oldpath, newpfx, newpath);
+ if (delta->status == GIT_DELTA_RENAMED)
+ type = "rename";
+ else if (delta->status == GIT_DELTA_COPIED)
+ type = "copy";
+ else
+ abort();
+
+ if ((error = git_buf_puts(&old_path, delta->old_file.path)) < 0 ||
+ (error = git_buf_puts(&new_path, delta->new_file.path)) < 0 ||
+ (error = git_buf_quote(&old_path)) < 0 ||
+ (error = git_buf_quote(&new_path)) < 0)
+ goto done;
+
+ git_buf_printf(out,
+ "similarity index %d%%\n"
+ "%s from %s\n"
+ "%s to %s\n",
+ delta->similarity,
+ type, old_path.ptr,
+ type, new_path.ptr);
+
+ if (git_buf_oom(out))
+ error = -1;
+
+done:
+ git_buf_free(&old_path);
+ git_buf_free(&new_path);
+
+ return error;
}
+static bool delta_is_unchanged(const git_diff_delta *delta)
+{
+ if (git_oid_iszero(&delta->old_file.id) &&
+ git_oid_iszero(&delta->new_file.id))
+ return true;
+
+ if (delta->old_file.mode == GIT_FILEMODE_COMMIT ||
+ delta->new_file.mode == GIT_FILEMODE_COMMIT)
+ return false;
+
+ if (git_oid_equal(&delta->old_file.id, &delta->new_file.id))
+ return true;
+
+ return false;
+}
+
int git_diff_delta__format_file_header(
git_buf *out,
const git_diff_delta *delta,
const char *oldpfx,
const char *newpfx,
- int oid_strlen)
+ int id_strlen)
{
+ git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
+ bool unchanged = delta_is_unchanged(delta);
+ int error = 0;
+
if (!oldpfx)
oldpfx = DIFF_OLD_PREFIX_DEFAULT;
if (!newpfx)
newpfx = DIFF_NEW_PREFIX_DEFAULT;
- if (!oid_strlen)
- oid_strlen = GIT_ABBREV_DEFAULT + 1;
+ if (!id_strlen)
+ id_strlen = GIT_ABBREV_DEFAULT;
+ if ((error = diff_delta_format_path(
+ &old_path, oldpfx, delta->old_file.path)) < 0 ||
+ (error = diff_delta_format_path(
+ &new_path, newpfx, delta->new_file.path)) < 0)
+ goto done;
+
git_buf_clear(out);
- git_buf_printf(out, "diff --git %s%s %s%s\n",
- oldpfx, delta->old_file.path, newpfx, delta->new_file.path);
+ git_buf_printf(out, "diff --git %s %s\n",
+ old_path.ptr, new_path.ptr);
- GITERR_CHECK_ERROR(diff_print_oid_range(out, delta, oid_strlen));
+ if (delta->status == GIT_DELTA_RENAMED ||
+ (delta->status == GIT_DELTA_COPIED && unchanged)) {
+ if ((error = diff_delta_format_similarity_header(out, delta)) < 0)
+ goto done;
+ }
- if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
- diff_delta_format_with_paths(
- out, delta, oldpfx, newpfx, "--- %s%s\n+++ %s%s\n");
+ if (!unchanged) {
+ if ((error = diff_print_oid_range(out, delta, id_strlen)) < 0)
+ goto done;
- return git_buf_oom(out) ? -1 : 0;
+ if ((delta->flags & GIT_DIFF_FLAG_BINARY) == 0)
+ diff_delta_format_with_paths(out, delta,
+ "--- %s\n+++ %s\n", old_path.ptr, new_path.ptr);
+ }
+
+ if (unchanged && delta->old_file.mode != delta->new_file.mode)
+ diff_print_modes(out, delta);
+
+ if (git_buf_oom(out))
+ error = -1;
+
+done:
+ git_buf_free(&old_path);
+ git_buf_free(&new_path);
+
+ return error;
}
static int format_binary(
diff_print_info *pi,
git_diff_binary_t type,
@@ -365,41 +485,34 @@
git_buf_putc(pi->buf, '\n');
return 0;
}
-static int diff_print_load_content(
- diff_print_info *pi,
- git_diff_delta *delta)
+static int diff_print_patch_file_binary_noshow(
+ diff_print_info *pi, git_diff_delta *delta,
+ const char *old_pfx, const char *new_pfx)
{
- git_diff_file_content *ofile, *nfile;
+ git_buf old_path = GIT_BUF_INIT, new_path = GIT_BUF_INIT;
int error;
- assert(pi->diff);
+ if ((error = diff_delta_format_path(
+ &old_path, old_pfx, delta->old_file.path)) < 0 ||
+ (error = diff_delta_format_path(
+ &new_path, new_pfx, delta->new_file.path)) < 0)
+ goto done;
- ofile = git__calloc(1, sizeof(git_diff_file_content));
- nfile = git__calloc(1, sizeof(git_diff_file_content));
- GITERR_CHECK_ALLOC(ofile);
- GITERR_CHECK_ALLOC(nfile);
+ pi->line.num_lines = 1;
+ error = diff_delta_format_with_paths(
+ pi->buf, delta, "Binary files %s and %s differ\n",
+ old_path.ptr, new_path.ptr);
- if ((error = git_diff_file_content__init_from_diff(
- ofile, pi->diff, delta, true)) < 0 ||
- (error = git_diff_file_content__init_from_diff(
- nfile, pi->diff, delta, true)) < 0) {
+done:
+ git_buf_free(&old_path);
+ git_buf_free(&new_path);
- git__free(ofile);
- git__free(nfile);
- return error;
- }
-
- pi->content_loaded = 1;
- pi->content_allocated = 1;
- pi->ofile = ofile;
- pi->nfile = nfile;
-
- return 0;
+ return error;
}
static int diff_print_patch_file_binary(
diff_print_info *pi, git_diff_delta *delta,
const char *old_pfx, const char *new_pfx,
@@ -407,15 +520,15 @@
{
size_t pre_binary_size;
int error;
if ((pi->flags & GIT_DIFF_SHOW_BINARY) == 0)
- goto noshow;
+ return diff_print_patch_file_binary_noshow(
+ pi, delta, old_pfx, new_pfx);
- if (!pi->content_loaded &&
- (error = diff_print_load_content(pi, delta)) < 0)
- return error;
+ if (binary->new_file.datalen == 0 && binary->old_file.datalen == 0)
+ return 0;
pre_binary_size = pi->buf->size;
git_buf_printf(pi->buf, "GIT binary patch\n");
pi->line.num_lines++;
@@ -425,39 +538,35 @@
binary->old_file.datalen, binary->old_file.inflatedlen)) < 0) {
if (error == GIT_EBUFS) {
giterr_clear();
git_buf_truncate(pi->buf, pre_binary_size);
- goto noshow;
+
+ return diff_print_patch_file_binary_noshow(
+ pi, delta, old_pfx, new_pfx);
}
}
pi->line.num_lines++;
return error;
-
-noshow:
- pi->line.num_lines = 1;
- return diff_delta_format_with_paths(
- pi->buf, delta, old_pfx, new_pfx,
- "Binary files %s%s and %s%s differ\n");
}
static int diff_print_patch_file(
const git_diff_delta *delta, float progress, void *data)
{
int error;
diff_print_info *pi = data;
const char *oldpfx =
- pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
+ pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
const char *newpfx =
- pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
+ pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
bool binary = (delta->flags & GIT_DIFF_FLAG_BINARY) ||
(pi->flags & GIT_DIFF_FORCE_BINARY);
bool show_binary = !!(pi->flags & GIT_DIFF_SHOW_BINARY);
- int oid_strlen = binary && show_binary ?
- GIT_OID_HEXSZ + 1 : pi->oid_strlen;
+ int id_strlen = binary && show_binary ?
+ GIT_OID_HEXSZ : pi->id_strlen;
GIT_UNUSED(progress);
if (S_ISDIR(delta->new_file.mode) ||
delta->status == GIT_DELTA_UNMODIFIED ||
@@ -466,11 +575,11 @@
(delta->status == GIT_DELTA_UNTRACKED &&
(pi->flags & GIT_DIFF_SHOW_UNTRACKED_CONTENT) == 0))
return 0;
if ((error = git_diff_delta__format_file_header(
- pi->buf, delta, oldpfx, newpfx, oid_strlen)) < 0)
+ pi->buf, delta, oldpfx, newpfx, id_strlen)) < 0)
return error;
pi->line.origin = GIT_DIFF_LINE_FILE_HDR;
pi->line.content = git_buf_cstr(pi->buf);
pi->line.content_len = git_buf_len(pi->buf);
@@ -483,13 +592,13 @@
const git_diff_binary *binary,
void *data)
{
diff_print_info *pi = data;
const char *old_pfx =
- pi->diff ? pi->diff->opts.old_prefix : DIFF_OLD_PREFIX_DEFAULT;
+ pi->old_prefix ? pi->old_prefix : DIFF_OLD_PREFIX_DEFAULT;
const char *new_pfx =
- pi->diff ? pi->diff->opts.new_prefix : DIFF_NEW_PREFIX_DEFAULT;
+ pi->new_prefix ? pi->new_prefix : DIFF_NEW_PREFIX_DEFAULT;
int error;
git_buf_clear(pi->buf);
if ((error = diff_print_patch_file_binary(
@@ -580,47 +689,15 @@
if (error) /* make sure error message is set */
giterr_set_after_callback_function(error, "git_diff_print");
}
- git__free(pi.nfile);
- git__free(pi.ofile);
-
git_buf_free(&buf);
return error;
}
-/* print a git_patch to an output callback */
-int git_patch_print(
- git_patch *patch,
- git_diff_line_cb print_cb,
- void *payload)
-{
- int error;
- git_buf temp = GIT_BUF_INIT;
- diff_print_info pi;
-
- assert(patch && print_cb);
-
- if (!(error = diff_print_info_init_frompatch(
- &pi, &temp, patch,
- GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
- {
- error = git_patch__invoke_callbacks(
- patch, diff_print_patch_file, diff_print_patch_binary,
- diff_print_patch_hunk, diff_print_patch_line, &pi);
-
- if (error) /* make sure error message is set */
- giterr_set_after_callback_function(error, "git_patch_print");
- }
-
- git_buf_free(&temp);
-
- return error;
-}
-
int git_diff_print_callback__to_buf(
const git_diff_delta *delta,
const git_diff_hunk *hunk,
const git_diff_line *line,
void *payload)
@@ -655,9 +732,49 @@
line->origin == GIT_DIFF_LINE_ADDITION ||
line->origin == GIT_DIFF_LINE_DELETION)
fputc(line->origin, fp);
fwrite(line->content, 1, line->content_len, fp);
return 0;
+}
+
+/* print a git_diff to a git_buf */
+int git_diff_to_buf(git_buf *out, git_diff *diff, git_diff_format_t format)
+{
+ assert(out && diff);
+ git_buf_sanitize(out);
+ return git_diff_print(
+ diff, format, git_diff_print_callback__to_buf, out);
+}
+
+/* print a git_patch to an output callback */
+int git_patch_print(
+ git_patch *patch,
+ git_diff_line_cb print_cb,
+ void *payload)
+{
+ int error;
+ git_buf temp = GIT_BUF_INIT;
+ diff_print_info pi;
+
+ assert(patch && print_cb);
+
+ if (!(error = diff_print_info_init_frompatch(
+ &pi, &temp, patch,
+ GIT_DIFF_FORMAT_PATCH, print_cb, payload)))
+ {
+ error = git_patch__invoke_callbacks(
+ patch,
+ diff_print_patch_file, diff_print_patch_binary,
+ diff_print_patch_hunk, diff_print_patch_line,
+ &pi);
+
+ if (error) /* make sure error message is set */
+ giterr_set_after_callback_function(error, "git_patch_print");
+ }
+
+ git_buf_free(&temp);
+
+ return error;
}
/* print a git_patch to a git_buf */
int git_patch_to_buf(git_buf *out, git_patch *patch)
{