Permalink
Browse files

diff: add filter for converting binary to text

When diffing binary files, it is sometimes nice to see the
differences of a canonical text form rather than either a
binary patch or simply "binary files differ."

Until now, the only option for doing this was to define an
external diff command to perform the diff. This was a lot of
work, since the external command needed to take care of
doing the diff itself (including mode changes), and lost the
benefit of git's colorization and other options.

This patch adds a text conversion option, which converts a
file to its canonical format before performing the diff.
This is less flexible than an arbitrary external diff, but
is much less work to set up. For example:

  $ echo '*.jpg diff=exif' >>.gitattributes
  $ git config diff.exif.textconv exiftool
  $ git config diff.exif.binary false

allows one to see jpg diffs represented by the text output
of exiftool.

Signed-off-by: Jeff King <peff@peff.net>
Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
  • Loading branch information...
1 parent 122aa6f commit 9cb92c390cefd3bf3f71bbda12eb04893c861361 @peff peff committed with gitster Oct 5, 2008
Showing with 50 additions and 2 deletions.
  1. +47 −2 diff.c
  2. +2 −0 userdiff.c
  3. +1 −0 userdiff.h
View
49 diff.c
@@ -38,6 +38,9 @@ static char diff_colors[][COLOR_MAXLEN] = {
"\033[41m", /* WHITESPACE (red background) */
};
+static void diff_filespec_load_driver(struct diff_filespec *one);
+static char *run_textconv(const char *, struct diff_filespec *, size_t *);
+
static int parse_diff_color_slot(const char *var, int ofs)
{
if (!strcasecmp(var+ofs, "plain"))
@@ -290,8 +293,19 @@ static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
}
else if (diff_populate_filespec(one, 0))
return -1;
- mf->ptr = one->data;
- mf->size = one->size;
+
+ diff_filespec_load_driver(one);
+ if (one->driver->textconv) {
+ size_t size;
+ mf->ptr = run_textconv(one->driver->textconv, one, &size);
+ if (!mf->ptr)
+ return -1;
+ mf->size = size;
+ }
+ else {
+ mf->ptr = one->data;
+ mf->size = one->size;
+ }
return 0;
}
@@ -3373,3 +3387,34 @@ void diff_unmerge(struct diff_options *options,
fill_filespec(one, sha1, mode);
diff_queue(&diff_queued_diff, one, two)->is_unmerged = 1;
}
+
+static char *run_textconv(const char *pgm, struct diff_filespec *spec,
+ size_t *outsize)
+{
+ struct diff_tempfile temp;
+ const char *argv[3];
+ const char **arg = argv;
+ struct child_process child;
+ struct strbuf buf = STRBUF_INIT;
+
+ prepare_temp_file(spec->path, &temp, spec);
+ *arg++ = pgm;
+ *arg++ = temp.name;
+ *arg = NULL;
+
+ memset(&child, 0, sizeof(child));
+ child.argv = argv;
+ child.out = -1;
+ if (start_command(&child) != 0 ||
+ strbuf_read(&buf, child.out, 0) < 0 ||
+ finish_command(&child) != 0) {
+ if (temp.name == temp.tmp_path)
+ unlink(temp.name);
+ error("error running textconv command '%s'", pgm);
+ return NULL;
+ }
+ if (temp.name == temp.tmp_path)
+ unlink(temp.name);
+
+ return strbuf_detach(&buf, outsize);
+}
View
@@ -140,6 +140,8 @@ int userdiff_config_porcelain(const char *k, const char *v)
if ((drv = parse_driver(k, v, "command")))
return parse_string(&drv->external, k, v);
+ if ((drv = parse_driver(k, v, "textconv")))
+ return parse_string(&drv->textconv, k, v);
return 0;
}
View
@@ -11,6 +11,7 @@ struct userdiff_driver {
const char *external;
int binary;
struct userdiff_funcname funcname;
+ const char *textconv;
};
int userdiff_config_basic(const char *k, const char *v);

0 comments on commit 9cb92c3

Please sign in to comment.