summaryrefslogtreecommitdiff
path: root/src/diff.c
diff options
context:
space:
mode:
authorRussell Belfer <arrbee@arrbee.com>2012-01-27 11:29:25 -0800
committerRussell Belfer <arrbee@arrbee.com>2012-03-02 15:49:28 -0800
commitcd33323b7251e0bb15c5ee476e918859b661cc5f (patch)
treeca31375d8108a4186e664dc0f974ebf1f6a92fba /src/diff.c
parent8b75f7f3ea91b3efc4e58a7bf737aedfd19671e7 (diff)
downloadlibgit2-cd33323b7251e0bb15c5ee476e918859b661cc5f.tar.gz
Initial implementation of git_diff_blob
This gets the basic plumbing in place for git_diff_blob. There is a known issue where additional parameters like the number of lines of context to display on the diff are not working correctly (which leads one of the new unit tests to fail).
Diffstat (limited to 'src/diff.c')
-rw-r--r--src/diff.c104
1 files changed, 104 insertions, 0 deletions
diff --git a/src/diff.c b/src/diff.c
new file mode 100644
index 000000000..7128b7c76
--- /dev/null
+++ b/src/diff.c
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2009-2011 the libgit2 contributors
+ *
+ * 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 "git2/diff.h"
+#include "xdiff/xdiff.h"
+#include "blob.h"
+#include <ctype.h>
+
+static int read_next_int(const char **str, int *value)
+{
+ const char *scan = *str;
+ int v = 0, digits = 0;
+ /* find next digit */
+ for (scan = *str; *scan && !isdigit(*scan); scan++);
+ /* parse next number */
+ for (; isdigit(*scan); scan++, digits++)
+ v = (v * 10) + (*scan - '0');
+ *str = scan;
+ *value = v;
+ return (digits > 0) ? GIT_SUCCESS : GIT_ENOTFOUND;
+}
+
+static int diff_output_cb(void *priv, mmbuffer_t *bufs, int len)
+{
+ int err = GIT_SUCCESS;
+ git_diff_opts *opts = priv;
+
+ if (len == 1) {
+ int ostart = -1, olen = 0, nstart = -1, nlen = 0;
+ /* expect something of the form "@@ -%d[,%d] +%d[,%d] @@" */
+ if (opts->hunk_cb && bufs[0].ptr[0] == '@') {
+ const char *scan = bufs[0].ptr;
+ if (!(err = read_next_int(&scan, &ostart)) && *scan == ',')
+ err = read_next_int(&scan, &olen);
+ if (!err && !(err = read_next_int(&scan, &nstart)) && *scan == ',')
+ err = read_next_int(&scan, &nlen);
+ if (!err && ostart >= 0 && nstart >= 0)
+ err = opts->hunk_cb(
+ opts->cb_data, ostart, olen, nstart, nlen);
+ }
+ }
+ else if (len == 2 || len == 3) {
+ int origin;
+ /* expect " "/"-"/"+", then data, then maybe newline */
+ origin =
+ (*bufs[0].ptr == '+') ? GIT_DIFF_LINE_ADDITION :
+ (*bufs[0].ptr == '-') ? GIT_DIFF_LINE_DELETION :
+ GIT_DIFF_LINE_CONTEXT;
+
+ if (opts->line_cb)
+ err = opts->line_cb(
+ opts->cb_data, origin, bufs[1].ptr, bufs[1].size);
+ }
+
+ return err;
+}
+
+int git_diff_blobs(
+ git_repository *repo,
+ git_blob *old_blob,
+ git_blob *new_blob,
+ git_diff_opts *options)
+{
+ mmfile_t old, new;
+ xpparam_t params;
+ xdemitconf_t cfg;
+ xdemitcb_t callback;
+
+ assert(repo && old_blob && new_blob && options);
+
+ old.ptr = (char *)git_blob_rawcontent(old_blob);
+ old.size = git_blob_rawsize(old_blob);
+
+ new.ptr = (char *)git_blob_rawcontent(new_blob);
+ new.size = git_blob_rawsize(new_blob);
+
+ memset(&params, 0, sizeof(params));
+
+ memset(&cfg, 0, sizeof(cfg));
+ cfg.ctxlen = options->context_lines || 3;
+ cfg.interhunkctxlen = options->interhunk_lines || 3;
+ if (options->ignore_whitespace)
+ cfg.flags |= XDF_WHITESPACE_FLAGS;
+
+ memset(&callback, 0, sizeof(callback));
+ callback.outf = diff_output_cb;
+ callback.priv = options;
+
+ if (options->file_cb)
+ options->file_cb(
+ options->cb_data,
+ git_object_id((const git_object *)old_blob), NULL, 010644,
+ git_object_id((const git_object *)new_blob), NULL, 010644);
+
+ xdl_diff(&old, &new, &params, &cfg, &callback);
+
+ return GIT_SUCCESS;
+}
+