Merge branch 'lt/oneway'
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 5315270601eeb6ab54a12d97f69c5f29679c4cf7..1bdaba2f776efa1303c8fe69247b381e1a874935 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -8,6 +8,7 @@
 #include "quote.h"
 #include "diff.h"
 #include "diffcore.h"
+#include "delta.h"
 #include "xdiff-interface.h"
 
 static int use_size_cache;
@@ -231,11 +232,16 @@ static char *pprint_rename(const char *a, const char *b)
         * name-a => name-b
         */
        if (pfx_length + sfx_length) {
+               int a_midlen = len_a - pfx_length - sfx_length;
+               int b_midlen = len_b - pfx_length - sfx_length;
+               if (a_midlen < 0) a_midlen = 0;
+               if (b_midlen < 0) b_midlen = 0;
+
                name = xmalloc(len_a + len_b - pfx_length - sfx_length + 7);
                sprintf(name, "%.*s{%.*s => %.*s}%s",
                        pfx_length, a,
-                       len_a - pfx_length - sfx_length, a + pfx_length,
-                       len_b - pfx_length - sfx_length, b + pfx_length,
+                       a_midlen, a + pfx_length,
+                       b_midlen, b + pfx_length,
                        a + len_a - sfx_length);
        }
        else {
@@ -391,6 +397,90 @@ static void show_stats(struct diffstat_t* data)
                        total_files, adds, dels);
 }
 
+static unsigned char *deflate_it(char *data,
+                                unsigned long size,
+                                unsigned long *result_size)
+{
+       int bound;
+       unsigned char *deflated;
+       z_stream stream;
+
+       memset(&stream, 0, sizeof(stream));
+       deflateInit(&stream, Z_BEST_COMPRESSION);
+       bound = deflateBound(&stream, size);
+       deflated = xmalloc(bound);
+       stream.next_out = deflated;
+       stream.avail_out = bound;
+
+       stream.next_in = (unsigned char *)data;
+       stream.avail_in = size;
+       while (deflate(&stream, Z_FINISH) == Z_OK)
+               ; /* nothing */
+       deflateEnd(&stream);
+       *result_size = stream.total_out;
+       return deflated;
+}
+
+static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
+{
+       void *cp;
+       void *delta;
+       void *deflated;
+       void *data;
+       unsigned long orig_size;
+       unsigned long delta_size;
+       unsigned long deflate_size;
+       unsigned long data_size;
+
+       printf("GIT binary patch\n");
+       /* We could do deflated delta, or we could do just deflated two,
+        * whichever is smaller.
+        */
+       delta = NULL;
+       deflated = deflate_it(two->ptr, two->size, &deflate_size);
+       if (one->size && two->size) {
+               delta = diff_delta(one->ptr, one->size,
+                                  two->ptr, two->size,
+                                  &delta_size, deflate_size);
+               if (delta) {
+                       void *to_free = delta;
+                       orig_size = delta_size;
+                       delta = deflate_it(delta, delta_size, &delta_size);
+                       free(to_free);
+               }
+       }
+
+       if (delta && delta_size < deflate_size) {
+               printf("delta %lu\n", orig_size);
+               free(deflated);
+               data = delta;
+               data_size = delta_size;
+       }
+       else {
+               printf("literal %lu\n", two->size);
+               free(delta);
+               data = deflated;
+               data_size = deflate_size;
+       }
+
+       /* emit data encoded in base85 */
+       cp = data;
+       while (data_size) {
+               int bytes = (52 < data_size) ? 52 : data_size;
+               char line[70];
+               data_size -= bytes;
+               if (bytes <= 26)
+                       line[0] = bytes + 'A' - 1;
+               else
+                       line[0] = bytes - 26 + 'a' - 1;
+               encode_85(line + 1, cp, bytes);
+               cp += bytes;
+               puts(line);
+       }
+       printf("\n");
+       free(data);
+}
+
 #define FIRST_FEW_BYTES 8000
 static int mmfile_is_binary(mmfile_t *mf)
 {
@@ -407,6 +497,7 @@ static void builtin_diff(const char *name_a,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
+                        struct diff_options *o,
                         int complete_rewrite)
 {
        mmfile_t mf1, mf2;
@@ -451,8 +542,17 @@ static void builtin_diff(const char *name_a,
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 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", lbl[0], lbl[1]);
+       if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) {
+               /* Quite common confusing case */
+               if (mf1.size == mf2.size &&
+                   !memcmp(mf1.ptr, mf2.ptr, mf1.size))
+                       goto free_ab_and_return;
+               if (o->binary)
+                       emit_binary_diff(&mf1, &mf2);
+               else
+                       printf("Binary files %s and %s differ\n",
+                              lbl[0], lbl[1]);
+       }
        else {
                /* Crazy xdl interfaces.. */
                const char *diffopts = getenv("GIT_DIFF_OPTS");
@@ -463,7 +563,7 @@ static void builtin_diff(const char *name_a,
 
                ecbdata.label_path = lbl;
                xpp.flags = XDF_NEED_MINIMAL;
-               xecfg.ctxlen = 3;
+               xecfg.ctxlen = o->context;
                xecfg.flags = XDL_EMIT_FUNCNAMES;
                if (!diffopts)
                        ;
@@ -928,6 +1028,7 @@ static void run_diff_cmd(const char *pgm,
                         struct diff_filespec *one,
                         struct diff_filespec *two,
                         const char *xfrm_msg,
+                        struct diff_options *o,
                         int complete_rewrite)
 {
        if (pgm) {
@@ -937,7 +1038,7 @@ static void run_diff_cmd(const char *pgm,
        }
        if (one && two)
                builtin_diff(name, other ? other : name,
-                            one, two, xfrm_msg, complete_rewrite);
+                            one, two, xfrm_msg, o, complete_rewrite);
        else
                printf("* Unmerged path %s\n", name);
 }
@@ -971,7 +1072,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 
        if (DIFF_PAIR_UNMERGED(p)) {
                /* unmerged */
-               run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
+               run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, o, 0);
                return;
        }
 
@@ -1041,14 +1142,14 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
                 * needs to be split into deletion and creation.
                 */
                struct diff_filespec *null = alloc_filespec(two->path);
-               run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
+               run_diff_cmd(NULL, name, other, one, null, xfrm_msg, o, 0);
                free(null);
                null = alloc_filespec(one->path);
-               run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
+               run_diff_cmd(NULL, name, other, null, two, xfrm_msg, o, 0);
                free(null);
        }
        else
-               run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
+               run_diff_cmd(pgm, name, other, one, two, xfrm_msg, o,
                             complete_rewrite);
 
        free(name_munged);
@@ -1086,6 +1187,7 @@ void diff_setup(struct diff_options *options)
        options->line_termination = '\n';
        options->break_opt = -1;
        options->rename_limit = -1;
+       options->context = 3;
 
        options->change = diff_change;
        options->add_remove = diff_addremove;
@@ -1126,11 +1228,60 @@ int diff_setup_done(struct diff_options *options)
        return 0;
 }
 
+int opt_arg(const char *arg, int arg_short, const char *arg_long, int *val)
+{
+       char c, *eq;
+       int len;
+
+       if (*arg != '-')
+               return 0;
+       c = *++arg;
+       if (!c)
+               return 0;
+       if (c == arg_short) {
+               c = *++arg;
+               if (!c)
+                       return 1;
+               if (val && isdigit(c)) {
+                       char *end;
+                       int n = strtoul(arg, &end, 10);
+                       if (*end)
+                               return 0;
+                       *val = n;
+                       return 1;
+               }
+               return 0;
+       }
+       if (c != '-')
+               return 0;
+       arg++;
+       eq = strchr(arg, '=');
+       if (eq)
+               len = eq - arg;
+       else
+               len = strlen(arg);
+       if (!len || strncmp(arg, arg_long, len))
+               return 0;
+       if (eq) {
+               int n;
+               char *end;
+               if (!isdigit(*++eq))
+                       return 0;
+               n = strtoul(eq, &end, 10);
+               if (*end)
+                       return 0;
+               *val = n;
+       }
+       return 1;
+}
+
 int diff_opt_parse(struct diff_options *options, const char **av, int ac)
 {
        const char *arg = av[0];
        if (!strcmp(arg, "-p") || !strcmp(arg, "-u"))
                options->output_format = DIFF_FORMAT_PATCH;
+       else if (opt_arg(arg, 'U', "unified", &options->context))
+               options->output_format = DIFF_FORMAT_PATCH;
        else if (!strcmp(arg, "--patch-with-raw")) {
                options->output_format = DIFF_FORMAT_PATCH;
                options->with_raw = 1;
@@ -1147,6 +1298,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->rename_limit = strtoul(arg+2, NULL, 10);
        else if (!strcmp(arg, "--full-index"))
                options->full_index = 1;
+       else if (!strcmp(arg, "--binary")) {
+               options->output_format = DIFF_FORMAT_PATCH;
+               options->full_index = options->binary = 1;
+       }
        else if (!strcmp(arg, "--name-only"))
                options->output_format = DIFF_FORMAT_NAME;
        else if (!strcmp(arg, "--name-status"))