aboutsummaryrefslogtreecommitdiff
path: root/diff.c
diff options
context:
space:
mode:
Diffstat (limited to 'diff.c')
-rw-r--r--diff.c110
1 files changed, 104 insertions, 6 deletions
diff --git a/diff.c b/diff.c
index c73a40b9a..4440465b4 100644
--- a/diff.c
+++ b/diff.c
@@ -8,6 +8,7 @@
#include "quote.h"
#include "diff.h"
#include "diffcore.h"
+#include "xdiff/xdiff.h"
static const char *diff_opts = "-pu";
@@ -178,6 +179,70 @@ static void emit_rewrite_diff(const char *name_a,
copy_file('+', temp[1].name);
}
+static int fill_mmfile(mmfile_t *mf, const char *file)
+{
+ int fd = open(file, O_RDONLY);
+ struct stat st;
+ char *buf;
+ unsigned long size;
+
+ mf->ptr = NULL;
+ mf->size = 0;
+ if (fd < 0)
+ return 0;
+ fstat(fd, &st);
+ size = st.st_size;
+ buf = xmalloc(size);
+ mf->ptr = buf;
+ mf->size = size;
+ while (size) {
+ int retval = read(fd, buf, size);
+ if (retval < 0) {
+ if (errno == EINTR || errno == EAGAIN)
+ continue;
+ break;
+ }
+ if (!retval)
+ break;
+ buf += retval;
+ size -= retval;
+ }
+ mf->size -= size;
+ close(fd);
+ return 0;
+}
+
+struct emit_callback {
+ const char **label_path;
+};
+
+static int fn_out(void *priv, mmbuffer_t *mb, int nbuf)
+{
+ int i;
+ struct emit_callback *ecbdata = priv;
+
+ if (ecbdata->label_path[0]) {
+ printf("--- %s\n", ecbdata->label_path[0]);
+ printf("+++ %s\n", ecbdata->label_path[1]);
+ ecbdata->label_path[0] = ecbdata->label_path[1] = NULL;
+ }
+ for (i = 0; i < nbuf; i++)
+ if (!fwrite(mb[i].ptr, mb[i].size, 1, stdout))
+ return -1;
+ return 0;
+}
+
+#define FIRST_FEW_BYTES 8000
+static int mmfile_is_binary(mmfile_t *mf)
+{
+ long sz = mf->size;
+ if (FIRST_FEW_BYTES < sz)
+ sz = FIRST_FEW_BYTES;
+ if (memchr(mf->ptr, 0, sz))
+ return 1;
+ return 0;
+}
+
static const char *builtin_diff(const char *name_a,
const char *name_b,
struct diff_tempfile *temp,
@@ -186,6 +251,7 @@ static const char *builtin_diff(const char *name_a,
const char **args)
{
int i, next_at, cmd_size;
+ mmfile_t mf1, mf2;
const char *const diff_cmd = "diff -L%s -L%s";
const char *const diff_arg = "-- %s %s||:"; /* "||:" is to return 0 */
const char *input_name_sq[2];
@@ -255,12 +321,44 @@ static const char *builtin_diff(const char *name_a,
}
}
- /* This is disgusting */
- *args++ = "sh";
- *args++ = "-c";
- *args++ = cmd;
- *args = NULL;
- return "/bin/sh";
+ /* Un-quote the paths */
+ if (label_path[0][0] != '/')
+ label_path[0] = quote_two("a/", name_a);
+ if (label_path[1][0] != '/')
+ label_path[1] = quote_two("b/", name_b);
+
+ if (fill_mmfile(&mf1, temp[0].name) < 0 ||
+ fill_mmfile(&mf2, temp[1].name) < 0)
+ die("unable to read files to diff");
+
+ if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
+ printf("Binary files %s and %s differ\n",
+ label_path[0], label_path[1]);
+ else {
+ /* Crazy xdl interfaces.. */
+ const char *diffopts = getenv("GIT_DIFF_OPTS");
+ xpparam_t xpp;
+ xdemitconf_t xecfg;
+ xdemitcb_t ecb;
+ struct emit_callback ecbdata;
+
+ ecbdata.label_path = label_path;
+ xpp.flags = XDF_NEED_MINIMAL;
+ xecfg.ctxlen = 3;
+ if (!diffopts)
+ ;
+ else if (!strncmp(diffopts, "--unified=", 10))
+ xecfg.ctxlen = strtoul(diffopts + 10, NULL, 10);
+ else if (!strncmp(diffopts, "-u", 2))
+ xecfg.ctxlen = strtoul(diffopts + 2, NULL, 10);
+ ecb.outf = fn_out;
+ ecb.priv = &ecbdata;
+ xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
+ }
+
+ free(mf1.ptr);
+ free(mf2.ptr);
+ return NULL;
}
struct diff_filespec *alloc_filespec(const char *path)