combine-diff: handle binary files as binary

The combined diff code path is totally different from the regular diff code path, and didn't handle binary files at all. The results of a combined diff on a binary file could range from annoying (since we spewed binary garbage, possibly upsetting the user's terminal), to wrong (embedded NULs caused us to show incorrect diffs, with lines truncated at the NUL character), to potential security problems (embedded NULs could interfere with "-z" output, possibly defeating policy hooks which parse diff output). Instead, we consider a combined diff to be binary if any of the input blobs is binary. To show a binary combined diff, we indicate "Binary blobs differ"; the "index" meta line will show which parents had which blob. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed May 23, 2011 at 16:27 UTC 4d5f34719970274990cded2201957eb2262bca10
2 files changed +148 -2
combine-diff.c
+35 -2
@@ -7,6 +7,7 @@
7 #include "xdiff-interface.h"
8 #include "log-tree.h"
9 #include "refs.h"
10 +#include "userdiff.h"
11
12 static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
13 {
@@ -685,7 +686,8 @@ static void show_combined_header(struct combine_diff_path *elem,
686 int num_parent,
687 int dense,
688 struct rev_info *rev,
688 - int mode_differs)
689 + int mode_differs,
690 + int show_file_header)
691 {
692 struct diff_options *opt = &rev->diffopt;
693 int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
@@ -739,6 +741,9 @@ static void show_combined_header(struct combine_diff_path *elem,
741 printf("%s\n", c_reset);
742 }
743
744 + if (!show_file_header)
745 + return;
746 +
747 if (added)
748 dump_quoted_path("--- ", "", "/dev/null",
749 c_meta, c_reset);
@@ -765,8 +770,13 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
770 int i, show_hunks;
771 int working_tree_file = is_null_sha1(elem->sha1);
772 mmfile_t result_file;
773 + struct userdiff_driver *userdiff;
774 + int is_binary;
775
776 context = opt->context;
777 + userdiff = userdiff_find_by_path(elem->path);
778 + if (!userdiff)
779 + userdiff = userdiff_find_by_name("default");
780
781 /* Read the result of merge first */
782 if (!working_tree_file)
@@ -852,6 +862,29 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
862 }
863 }
864
865 + if (userdiff->binary != -1)
866 + is_binary = userdiff->binary;
867 + else {
868 + is_binary = buffer_is_binary(result, result_size);
869 + for (i = 0; !is_binary && i < num_parent; i++) {
870 + char *buf;
871 + unsigned long size;
872 + buf = grab_blob(elem->parent[i].sha1,
873 + elem->parent[i].mode,
874 + &size);
875 + if (buffer_is_binary(buf, size))
876 + is_binary = 1;
877 + free(buf);
878 + }
879 + }
880 + if (is_binary) {
881 + show_combined_header(elem, num_parent, dense, rev,
882 + mode_differs, 0);
883 + printf("Binary files differ\n");
884 + free(result);
885 + return;
886 + }
887 +
888 for (cnt = 0, cp = result; cp < result + result_size; cp++) {
889 if (*cp == '\n')
890 cnt++;
@@ -906,7 +939,7 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
939
940 if (show_hunks || mode_differs || working_tree_file) {
941 show_combined_header(elem, num_parent, dense, rev,
909 - mode_differs);
942 + mode_differs, 1);
943 dump_sline(sline, cnt, num_parent,
944 DIFF_OPT_TST(opt, COLOR_DIFF), result_deleted);
945 }
t/t4048-diff-combined-binary.sh new
+113
@@ -0,0 +1,113 @@
1 +#!/bin/sh
2 +
3 +test_description='combined and merge diff handle binary files and textconv'
4 +. ./test-lib.sh
5 +
6 +test_expect_success 'setup binary merge conflict' '
7 + echo oneQ1 | q_to_nul >binary &&
8 + git add binary &&
9 + git commit -m one &&
10 + echo twoQ2 | q_to_nul >binary &&
11 + git commit -a -m two &&
12 + git checkout -b branch-binary HEAD^ &&
13 + echo threeQ3 | q_to_nul >binary &&
14 + git commit -a -m three &&
15 + test_must_fail git merge master &&
16 + echo resolvedQhooray | q_to_nul >binary &&
17 + git commit -a -m resolved
18 +'
19 +
20 +cat >expect <<'EOF'
21 +resolved
22 +
23 +diff --git a/binary b/binary
24 +index 7ea6ded..9563691 100644
25 +Binary files a/binary and b/binary differ
26 +resolved
27 +
28 +diff --git a/binary b/binary
29 +index 6197570..9563691 100644
30 +Binary files a/binary and b/binary differ
31 +EOF
32 +test_expect_success 'diff -m indicates binary-ness' '
33 + git show --format=%s -m >actual &&
34 + test_cmp expect actual
35 +'
36 +
37 +cat >expect <<'EOF'
38 +resolved
39 +
40 +diff --combined binary
41 +index 7ea6ded,6197570..9563691
42 +Binary files differ
43 +EOF
44 +test_expect_success 'diff -c indicates binary-ness' '
45 + git show --format=%s -c >actual &&
46 + test_cmp expect actual
47 +'
48 +
49 +cat >expect <<'EOF'
50 +resolved
51 +
52 +diff --cc binary
53 +index 7ea6ded,6197570..9563691
54 +Binary files differ
55 +EOF
56 +test_expect_success 'diff --cc indicates binary-ness' '
57 + git show --format=%s --cc >actual &&
58 + test_cmp expect actual
59 +'
60 +
61 +test_expect_success 'setup non-binary with binary attribute' '
62 + git checkout master &&
63 + test_commit one text &&
64 + test_commit two text &&
65 + git checkout -b branch-text HEAD^ &&
66 + test_commit three text &&
67 + test_must_fail git merge master &&
68 + test_commit resolved text &&
69 + echo text -diff >.gitattributes
70 +'
71 +
72 +cat >expect <<'EOF'
73 +resolved
74 +
75 +diff --git a/text b/text
76 +index 2bdf67a..2ab19ae 100644
77 +Binary files a/text and b/text differ
78 +resolved
79 +
80 +diff --git a/text b/text
81 +index f719efd..2ab19ae 100644
82 +Binary files a/text and b/text differ
83 +EOF
84 +test_expect_success 'diff -m respects binary attribute' '
85 + git show --format=%s -m >actual &&
86 + test_cmp expect actual
87 +'
88 +
89 +cat >expect <<'EOF'
90 +resolved
91 +
92 +diff --combined text
93 +index 2bdf67a,f719efd..2ab19ae
94 +Binary files differ
95 +EOF
96 +test_expect_success 'diff -c respects binary attribute' '
97 + git show --format=%s -c >actual &&
98 + test_cmp expect actual
99 +'
100 +
101 +cat >expect <<'EOF'
102 +resolved
103 +
104 +diff --cc text
105 +index 2bdf67a,f719efd..2ab19ae
106 +Binary files differ
107 +EOF
108 +test_expect_success 'diff --cc respects binary attribute' '
109 + git show --format=%s --cc >actual &&
110 + test_cmp expect actual
111 +'
112 +
113 +test_done