diff options
Diffstat (limited to 'diff.c')
-rw-r--r-- | diff.c | 110 |
1 files changed, 104 insertions, 6 deletions
@@ -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) |