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>
This commit is contained in:
Jeff King 2008-10-05 17:43:45 -04:00 коммит произвёл Junio C Hamano
Родитель 122aa6f9c0
Коммит 9cb92c390c
3 изменённых файлов: 50 добавлений и 2 удалений

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);
}

Просмотреть файл

@ -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;
}

Просмотреть файл

@ -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);