Teach 'diff' about 'diff' attribute.
[gitweb.git] / diff.c
diff --git a/diff.c b/diff.c
index 954ca83e0b0c95f55c0287d2deeb4cab76153fe0..e4efb657e8ad8d30d7f0d25b17350fe19840e449 100644 (file)
--- a/diff.c
+++ b/diff.c
@@ -8,6 +8,7 @@
 #include "delta.h"
 #include "xdiff-interface.h"
 #include "color.h"
+#include "attr.h"
 
 #ifdef NO_FAST_WORKING_DIRECTORY
 #define FAST_WORKING_DIRECTORY 0
@@ -811,7 +812,12 @@ static void show_stats(struct diffstat_t* data, struct diff_options *options)
 
                if (data->files[i]->is_binary) {
                        show_name(prefix, name, len, reset, set);
-                       printf("  Bin\n");
+                       printf("  Bin ");
+                       printf("%s%d%s", del_c, deleted, reset);
+                       printf(" -> ");
+                       printf("%s%d%s", add_c, added, reset);
+                       printf(" bytes");
+                       printf("\n");
                        goto free_diffstat_file;
                }
                else if (data->files[i]->is_unmerged) {
@@ -1046,13 +1052,34 @@ static void emit_binary_diff(mmfile_t *one, mmfile_t *two)
        emit_binary_diff_body(two, one);
 }
 
+static void setup_diff_attr_check(struct git_attr_check *check)
+{
+       static struct git_attr *attr_diff;
+
+       if (!attr_diff)
+               attr_diff = git_attr("diff", 4);
+       check->attr = attr_diff;
+}
+
 #define FIRST_FEW_BYTES 8000
-static int mmfile_is_binary(mmfile_t *mf)
+static int file_is_binary(struct diff_filespec *one)
 {
-       long sz = mf->size;
+       unsigned long sz;
+       struct git_attr_check attr_diff_check;
+
+       setup_diff_attr_check(&attr_diff_check);
+       if (!git_checkattr(one->path, 1, &attr_diff_check) &&
+           (0 == attr_diff_check.isset))
+               return 1;
+       if (!one->data) {
+               if (!DIFF_FILE_VALID(one))
+                       return 0;
+               diff_populate_filespec(one, 0);
+       }
+       sz = one->size;
        if (FIRST_FEW_BYTES < sz)
                sz = FIRST_FEW_BYTES;
-       return !!memchr(mf->ptr, 0, sz);
+       return !!memchr(one->data, 0, sz);
 }
 
 static void builtin_diff(const char *name_a,
@@ -1109,7 +1136,7 @@ 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 (!o->text && (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))) {
+       if (!o->text && (file_is_binary(one) || file_is_binary(two))) {
                /* Quite common confusing case */
                if (mf1.size == mf2.size &&
                    !memcmp(mf1.ptr, mf2.ptr, mf1.size))
@@ -1185,9 +1212,11 @@ static void builtin_diffstat(const char *name_a, const char *name_b,
        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))
+       if (file_is_binary(one) || file_is_binary(two)) {
                data->is_binary = 1;
-       else {
+               data->added = mf2.size;
+               data->deleted = mf1.size;
+       } else {
                /* Crazy xdl interfaces.. */
                xpparam_t xpp;
                xdemitconf_t xecfg;
@@ -1221,7 +1250,7 @@ static void builtin_checkdiff(const char *name_a, const char *name_b,
        if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
                die("unable to read files to diff");
 
-       if (mmfile_is_binary(&mf2))
+       if (file_is_binary(two))
                return;
        else {
                /* Crazy xdl interfaces.. */
@@ -1798,8 +1827,8 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
 
                if (o->binary) {
                        mmfile_t mf;
-                       if ((!fill_mmfile(&mf, one) && mmfile_is_binary(&mf)) ||
-                           (!fill_mmfile(&mf, two) && mmfile_is_binary(&mf)))
+                       if ((!fill_mmfile(&mf, one) && file_is_binary(one)) ||
+                           (!fill_mmfile(&mf, two) && file_is_binary(two)))
                                abbrev = 40;
                }
                len += snprintf(msg + len, sizeof(msg) - len,
@@ -1958,6 +1987,23 @@ int diff_setup_done(struct diff_options *options)
        if (options->abbrev <= 0 || 40 < options->abbrev)
                options->abbrev = 40; /* full */
 
+       /*
+        * It does not make sense to show the first hit we happened
+        * to have found.  It does not make sense not to return with
+        * exit code in such a case either.
+        */
+       if (options->quiet) {
+               options->output_format = DIFF_FORMAT_NO_OUTPUT;
+               options->exit_with_status = 1;
+       }
+
+       /*
+        * If we postprocess in diffcore, we cannot simply return
+        * upon the first hit.  We need to run diff as usual.
+        */
+       if (options->pickaxe || options->filter)
+               options->quiet = 0;
+
        return 0;
 }
 
@@ -2134,6 +2180,10 @@ int diff_opt_parse(struct diff_options *options, const char **av, int ac)
                options->color_diff = options->color_diff_words = 1;
        else if (!strcmp(arg, "--no-renames"))
                options->detect_rename = 0;
+       else if (!strcmp(arg, "--exit-code"))
+               options->exit_with_status = 1;
+       else if (!strcmp(arg, "--quiet"))
+               options->quiet = 1;
        else
                return 0;
        return 1;
@@ -2673,7 +2723,7 @@ static int diff_get_patch_id(struct diff_options *options, unsigned char *sha1)
                        return error("unable to read files to diff");
 
                /* Maybe hash p->two? into the patch id? */
-               if (mmfile_is_binary(&mf2))
+               if (file_is_binary(p->two))
                        continue;
 
                len1 = remove_space(p->one->path, strlen(p->one->path));
@@ -2898,6 +2948,8 @@ static void diffcore_apply_filter(const char *filter)
 
 void diffcore_std(struct diff_options *options)
 {
+       if (options->quiet)
+               return;
        if (options->break_opt != -1)
                diffcore_break(options->break_opt);
        if (options->detect_rename)
@@ -2910,18 +2962,11 @@ void diffcore_std(struct diff_options *options)
                diffcore_order(options->orderfile);
        diff_resolve_rename_copy();
        diffcore_apply_filter(options->filter);
-}
-
 
-void diffcore_std_no_resolve(struct diff_options *options)
-{
-       if (options->pickaxe)
-               diffcore_pickaxe(options->pickaxe, options->pickaxe_opts);
-       if (options->orderfile)
-               diffcore_order(options->orderfile);
-       diffcore_apply_filter(options->filter);
+       options->has_changes = !!diff_queued_diff.nr;
 }
 
+
 void diff_addremove(struct diff_options *options,
                    int addremove, unsigned mode,
                    const unsigned char *sha1,
@@ -2957,6 +3002,7 @@ void diff_addremove(struct diff_options *options,
                fill_filespec(two, sha1, mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_change(struct diff_options *options,
@@ -2982,6 +3028,7 @@ void diff_change(struct diff_options *options,
        fill_filespec(two, new_sha1, new_mode);
 
        diff_queue(&diff_queued_diff, one, two);
+       options->has_changes = 1;
 }
 
 void diff_unmerge(struct diff_options *options,