Skip to content

Commit

Permalink
diff: add filter for converting binary to text
Browse files Browse the repository at this point in the history
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
Jeff King authored and Junio C Hamano committed Oct 18, 2008
1 parent 122aa6f commit 9cb92c3
Show file tree
Hide file tree
Showing 3 changed files with 50 additions and 2 deletions.
49 changes: 47 additions & 2 deletions diff.c
Original file line number Diff line number Diff line change
Expand Up @@ -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"))
Expand Down Expand Up @@ -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;
}

Expand Down Expand Up @@ -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);
}
2 changes: 2 additions & 0 deletions userdiff.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand Down
1 change: 1 addition & 0 deletions userdiff.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down

0 comments on commit 9cb92c3

Please sign in to comment.