summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--combine-diff.c214
-rw-r--r--diff.c14
-rwxr-xr-xt/t4048-diff-combined-binary.sh212
-rw-r--r--userdiff.c17
-rw-r--r--userdiff.h2
5 files changed, 376 insertions, 83 deletions
diff --git a/combine-diff.c b/combine-diff.c
index 655fa89d8a..be67cfcd45 100644
--- a/combine-diff.c
+++ b/combine-diff.c
@@ -7,6 +7,7 @@
#include "xdiff-interface.h"
#include "log-tree.h"
#include "refs.h"
+#include "userdiff.h"
static struct combine_diff_path *intersect_paths(struct combine_diff_path *curr, int n, int num_parent)
{
@@ -92,7 +93,9 @@ struct sline {
unsigned long *p_lno;
};
-static char *grab_blob(const unsigned char *sha1, unsigned int mode, unsigned long *size)
+static char *grab_blob(const unsigned char *sha1, unsigned int mode,
+ unsigned long *size, struct userdiff_driver *textconv,
+ const char *path)
{
char *blob;
enum object_type type;
@@ -105,6 +108,11 @@ static char *grab_blob(const unsigned char *sha1, unsigned int mode, unsigned lo
/* deleted blob */
*size = 0;
return xcalloc(1, 1);
+ } else if (textconv) {
+ struct diff_filespec *df = alloc_filespec(path);
+ fill_filespec(df, sha1, mode);
+ *size = fill_textconv(textconv, df, &blob);
+ free_filespec(df);
} else {
blob = read_sha1_file(sha1, &type, size);
if (type != OBJ_BLOB)
@@ -204,7 +212,9 @@ static void consume_line(void *state_, char *line, unsigned long len)
static void combine_diff(const unsigned char *parent, unsigned int mode,
mmfile_t *result_file,
struct sline *sline, unsigned int cnt, int n,
- int num_parent, int result_deleted)
+ int num_parent, int result_deleted,
+ struct userdiff_driver *textconv,
+ const char *path)
{
unsigned int p_lno, lno;
unsigned long nmask = (1UL << n);
@@ -217,7 +227,7 @@ static void combine_diff(const unsigned char *parent, unsigned int mode,
if (result_deleted)
return; /* result deleted */
- parent_file.ptr = grab_blob(parent, mode, &sz);
+ parent_file.ptr = grab_blob(parent, mode, &sz, textconv, path);
parent_file.size = sz;
memset(&xpp, 0, sizeof(xpp));
xpp.flags = 0;
@@ -681,6 +691,82 @@ static void dump_quoted_path(const char *head,
puts(buf.buf);
}
+static void show_combined_header(struct combine_diff_path *elem,
+ int num_parent,
+ int dense,
+ struct rev_info *rev,
+ int mode_differs,
+ int show_file_header)
+{
+ struct diff_options *opt = &rev->diffopt;
+ int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
+ const char *a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
+ const char *b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+ int use_color = DIFF_OPT_TST(opt, COLOR_DIFF);
+ const char *c_meta = diff_get_color(use_color, DIFF_METAINFO);
+ const char *c_reset = diff_get_color(use_color, DIFF_RESET);
+ const char *abb;
+ int added = 0;
+ int deleted = 0;
+ int i;
+
+ if (rev->loginfo && !rev->no_commit_id)
+ show_log(rev);
+
+ dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
+ "", elem->path, c_meta, c_reset);
+ printf("%sindex ", c_meta);
+ for (i = 0; i < num_parent; i++) {
+ abb = find_unique_abbrev(elem->parent[i].sha1,
+ abbrev);
+ printf("%s%s", i ? "," : "", abb);
+ }
+ abb = find_unique_abbrev(elem->sha1, abbrev);
+ printf("..%s%s\n", abb, c_reset);
+
+ if (mode_differs) {
+ deleted = !elem->mode;
+
+ /* We say it was added if nobody had it */
+ added = !deleted;
+ for (i = 0; added && i < num_parent; i++)
+ if (elem->parent[i].status !=
+ DIFF_STATUS_ADDED)
+ added = 0;
+ if (added)
+ printf("%snew file mode %06o",
+ c_meta, elem->mode);
+ else {
+ if (deleted)
+ printf("%sdeleted file ", c_meta);
+ printf("mode ");
+ for (i = 0; i < num_parent; i++) {
+ printf("%s%06o", i ? "," : "",
+ elem->parent[i].mode);
+ }
+ if (elem->mode)
+ printf("..%06o", elem->mode);
+ }
+ printf("%s\n", c_reset);
+ }
+
+ if (!show_file_header)
+ return;
+
+ if (added)
+ dump_quoted_path("--- ", "", "/dev/null",
+ c_meta, c_reset);
+ else
+ dump_quoted_path("--- ", a_prefix, elem->path,
+ c_meta, c_reset);
+ if (deleted)
+ dump_quoted_path("+++ ", "", "/dev/null",
+ c_meta, c_reset);
+ else
+ dump_quoted_path("+++ ", b_prefix, elem->path,
+ c_meta, c_reset);
+}
+
static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
int dense, struct rev_info *rev)
{
@@ -692,17 +778,22 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
int mode_differs = 0;
int i, show_hunks;
int working_tree_file = is_null_sha1(elem->sha1);
- int abbrev = DIFF_OPT_TST(opt, FULL_INDEX) ? 40 : DEFAULT_ABBREV;
- const char *a_prefix, *b_prefix;
mmfile_t result_file;
+ struct userdiff_driver *userdiff;
+ struct userdiff_driver *textconv = NULL;
+ int is_binary;
context = opt->context;
- a_prefix = opt->a_prefix ? opt->a_prefix : "a/";
- b_prefix = opt->b_prefix ? opt->b_prefix : "b/";
+ userdiff = userdiff_find_by_path(elem->path);
+ if (!userdiff)
+ userdiff = userdiff_find_by_name("default");
+ if (DIFF_OPT_TST(opt, ALLOW_TEXTCONV))
+ textconv = userdiff_get_textconv(userdiff);
/* Read the result of merge first */
if (!working_tree_file)
- result = grab_blob(elem->sha1, elem->mode, &result_size);
+ result = grab_blob(elem->sha1, elem->mode, &result_size,
+ textconv, elem->path);
else {
/* Used by diff-tree to read from the working tree */
struct stat st;
@@ -725,9 +816,16 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
} else if (S_ISDIR(st.st_mode)) {
unsigned char sha1[20];
if (resolve_gitlink_ref(elem->path, "HEAD", sha1) < 0)
- result = grab_blob(elem->sha1, elem->mode, &result_size);
+ result = grab_blob(elem->sha1, elem->mode,
+ &result_size, NULL, NULL);
else
- result = grab_blob(sha1, elem->mode, &result_size);
+ result = grab_blob(sha1, elem->mode,
+ &result_size, NULL, NULL);
+ } else if (textconv) {
+ struct diff_filespec *df = alloc_filespec(elem->path);
+ fill_filespec(df, null_sha1, st.st_mode);
+ result_size = fill_textconv(textconv, df, &result);
+ free_filespec(df);
} else if (0 <= (fd = open(elem->path, O_RDONLY))) {
size_t len = xsize_t(st.st_size);
ssize_t done;
@@ -777,6 +875,38 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
close(fd);
}
+ for (i = 0; i < num_parent; i++) {
+ if (elem->parent[i].mode != elem->mode) {
+ mode_differs = 1;
+ break;
+ }
+ }
+
+ if (textconv)
+ is_binary = 0;
+ else if (userdiff->binary != -1)
+ is_binary = userdiff->binary;
+ else {
+ is_binary = buffer_is_binary(result, result_size);
+ for (i = 0; !is_binary && i < num_parent; i++) {
+ char *buf;
+ unsigned long size;
+ buf = grab_blob(elem->parent[i].sha1,
+ elem->parent[i].mode,
+ &size, NULL, NULL);
+ if (buffer_is_binary(buf, size))
+ is_binary = 1;
+ free(buf);
+ }
+ }
+ if (is_binary) {
+ show_combined_header(elem, num_parent, dense, rev,
+ mode_differs, 0);
+ printf("Binary files differ\n");
+ free(result);
+ return;
+ }
+
for (cnt = 0, cp = result; cp < result + result_size; cp++) {
if (*cp == '\n')
cnt++;
@@ -824,71 +954,15 @@ static void show_patch_diff(struct combine_diff_path *elem, int num_parent,
combine_diff(elem->parent[i].sha1,
elem->parent[i].mode,
&result_file, sline,
- cnt, i, num_parent, result_deleted);
- if (elem->parent[i].mode != elem->mode)
- mode_differs = 1;
+ cnt, i, num_parent, result_deleted,
+ textconv, elem->path);
}
show_hunks = make_hunks(sline, cnt, num_parent, dense);
if (show_hunks || mode_differs || working_tree_file) {
- const char *abb;
- int use_color = DIFF_OPT_TST(opt, COLOR_DIFF);
- const char *c_meta = diff_get_color(use_color, DIFF_METAINFO);
- const char *c_reset = diff_get_color(use_color, DIFF_RESET);
- int added = 0;
- int deleted = 0;
-
- if (rev->loginfo && !rev->no_commit_id)
- show_log(rev);
- dump_quoted_path(dense ? "diff --cc " : "diff --combined ",
- "", elem->path, c_meta, c_reset);
- printf("%sindex ", c_meta);
- for (i = 0; i < num_parent; i++) {
- abb = find_unique_abbrev(elem->parent[i].sha1,
- abbrev);
- printf("%s%s", i ? "," : "", abb);
- }
- abb = find_unique_abbrev(elem->sha1, abbrev);
- printf("..%s%s\n", abb, c_reset);
-
- if (mode_differs) {
- deleted = !elem->mode;
-
- /* We say it was added if nobody had it */
- added = !deleted;
- for (i = 0; added && i < num_parent; i++)
- if (elem->parent[i].status !=
- DIFF_STATUS_ADDED)
- added = 0;
- if (added)
- printf("%snew file mode %06o",
- c_meta, elem->mode);
- else {
- if (deleted)
- printf("%sdeleted file ", c_meta);
- printf("mode ");
- for (i = 0; i < num_parent; i++) {
- printf("%s%06o", i ? "," : "",
- elem->parent[i].mode);
- }
- if (elem->mode)
- printf("..%06o", elem->mode);
- }
- printf("%s\n", c_reset);
- }
- if (added)
- dump_quoted_path("--- ", "", "/dev/null",
- c_meta, c_reset);
- else
- dump_quoted_path("--- ", a_prefix, elem->path,
- c_meta, c_reset);
- if (deleted)
- dump_quoted_path("+++ ", "", "/dev/null",
- c_meta, c_reset);
- else
- dump_quoted_path("+++ ", b_prefix, elem->path,
- c_meta, c_reset);
+ show_combined_header(elem, num_parent, dense, rev,
+ mode_differs, 1);
dump_sline(sline, cnt, num_parent,
DIFF_OPT_TST(opt, COLOR_DIFF), result_deleted);
}
diff --git a/diff.c b/diff.c
index 61bedaed57..5dd9049c7d 100644
--- a/diff.c
+++ b/diff.c
@@ -1984,19 +1984,7 @@ struct userdiff_driver *get_textconv(struct diff_filespec *one)
return NULL;
diff_filespec_load_driver(one);
- if (!one->driver->textconv)
- return NULL;
-
- if (one->driver->textconv_want_cache && !one->driver->textconv_cache) {
- struct notes_cache *c = xmalloc(sizeof(*c));
- struct strbuf name = STRBUF_INIT;
-
- strbuf_addf(&name, "textconv/%s", one->driver->name);
- notes_cache_init(c, name.buf, one->driver->textconv);
- one->driver->textconv_cache = c;
- }
-
- return one->driver;
+ return userdiff_get_textconv(one->driver);
}
static void builtin_diff(const char *name_a,
diff --git a/t/t4048-diff-combined-binary.sh b/t/t4048-diff-combined-binary.sh
new file mode 100755
index 0000000000..87a8949500
--- /dev/null
+++ b/t/t4048-diff-combined-binary.sh
@@ -0,0 +1,212 @@
+#!/bin/sh
+
+test_description='combined and merge diff handle binary files and textconv'
+. ./test-lib.sh
+
+test_expect_success 'setup binary merge conflict' '
+ echo oneQ1 | q_to_nul >binary &&
+ git add binary &&
+ git commit -m one &&
+ echo twoQ2 | q_to_nul >binary &&
+ git commit -a -m two &&
+ git checkout -b branch-binary HEAD^ &&
+ echo threeQ3 | q_to_nul >binary &&
+ git commit -a -m three &&
+ test_must_fail git merge master &&
+ echo resolvedQhooray | q_to_nul >binary &&
+ git commit -a -m resolved
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/binary b/binary
+index 7ea6ded..9563691 100644
+Binary files a/binary and b/binary differ
+resolved
+
+diff --git a/binary b/binary
+index 6197570..9563691 100644
+Binary files a/binary and b/binary differ
+EOF
+test_expect_success 'diff -m indicates binary-ness' '
+ git show --format=%s -m >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff -c indicates binary-ness' '
+ git show --format=%s -c >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc binary
+index 7ea6ded,6197570..9563691
+Binary files differ
+EOF
+test_expect_success 'diff --cc indicates binary-ness' '
+ git show --format=%s --cc >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup non-binary with binary attribute' '
+ git checkout master &&
+ test_commit one text &&
+ test_commit two text &&
+ git checkout -b branch-text HEAD^ &&
+ test_commit three text &&
+ test_must_fail git merge master &&
+ test_commit resolved text &&
+ echo text -diff >.gitattributes
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+Binary files a/text and b/text differ
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+Binary files a/text and b/text differ
+EOF
+test_expect_success 'diff -m respects binary attribute' '
+ git show --format=%s -m >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff -c respects binary attribute' '
+ git show --format=%s -c >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+Binary files differ
+EOF
+test_expect_success 'diff --cc respects binary attribute' '
+ git show --format=%s --cc >actual &&
+ test_cmp expect actual
+'
+
+test_expect_success 'setup textconv attribute' '
+ echo "text diff=upcase" >.gitattributes &&
+ git config diff.upcase.textconv "tr a-z A-Z <"
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --git a/text b/text
+index 2bdf67a..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-THREE
++RESOLVED
+resolved
+
+diff --git a/text b/text
+index f719efd..2ab19ae 100644
+--- a/text
++++ b/text
+@@ -1 +1 @@
+-TWO
++RESOLVED
+EOF
+test_expect_success 'diff -m respects textconv attribute' '
+ git show --format=%s -m >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff -c respects textconv attribute' '
+ git show --format=%s -c >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+resolved
+
+diff --cc text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- THREE
+ -TWO
+++RESOLVED
+EOF
+test_expect_success 'diff --cc respects textconv attribute' '
+ git show --format=%s --cc >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --combined text
+index 2bdf67a,f719efd..2ab19ae
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,1 @@@
+- three
+ -two
+++resolved
+EOF
+test_expect_success 'diff-tree plumbing does not respect textconv' '
+ git diff-tree HEAD -c -p >full &&
+ tail -n +2 full >actual &&
+ test_cmp expect actual
+'
+
+cat >expect <<'EOF'
+diff --cc text
+index 2bdf67a,f719efd..0000000
+--- a/text
++++ b/text
+@@@ -1,1 -1,1 +1,5 @@@
+++<<<<<<< HEAD
+ +THREE
+++=======
++ TWO
+++>>>>>>> MASTER
+EOF
+test_expect_success 'diff --cc respects textconv on worktree file' '
+ git reset --hard HEAD^ &&
+ test_must_fail git merge master &&
+ git diff >actual &&
+ test_cmp expect actual
+'
+
+test_done
diff --git a/userdiff.c b/userdiff.c
index e55310cd02..01d3a8b81e 100644
--- a/userdiff.c
+++ b/userdiff.c
@@ -281,3 +281,20 @@ struct userdiff_driver *userdiff_find_by_path(const char *path)
return NULL;
return userdiff_find_by_name(check.value);
}
+
+struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver)
+{
+ if (!driver->textconv)
+ return NULL;
+
+ if (driver->textconv_want_cache && !driver->textconv_cache) {
+ struct notes_cache *c = xmalloc(sizeof(*c));
+ struct strbuf name = STRBUF_INIT;
+
+ strbuf_addf(&name, "textconv/%s", driver->name);
+ notes_cache_init(c, name.buf, driver->textconv);
+ driver->textconv_cache = c;
+ }
+
+ return driver;
+}
diff --git a/userdiff.h b/userdiff.h
index 942d594950..4a7e78ffbc 100644
--- a/userdiff.h
+++ b/userdiff.h
@@ -23,4 +23,6 @@ int userdiff_config(const char *k, const char *v);
struct userdiff_driver *userdiff_find_by_name(const char *name);
struct userdiff_driver *userdiff_find_by_path(const char *path);
+struct userdiff_driver *userdiff_get_textconv(struct userdiff_driver *driver);
+
#endif /* USERDIFF */