true built-in diff: run everything in-core.

This stops using temporary files when we are using the built-in
diff (including the complete rewrite).

Signed-off-by: Junio C Hamano <junkio@cox.net>
This commit is contained in:
Junio C Hamano 2006-03-25 23:12:17 -08:00
parent 3ce8f08944
commit cebff98dbe

254
diff.c
View File

@ -10,8 +10,6 @@
#include "diffcore.h" #include "diffcore.h"
#include "xdiff/xdiff.h" #include "xdiff/xdiff.h"
static const char *diff_opts = "-pu";
static int use_size_cache; static int use_size_cache;
int diff_rename_limit_default = -1; int diff_rename_limit_default = -1;
@ -70,25 +68,10 @@ static const char *external_diff(void)
{ {
static const char *external_diff_cmd = NULL; static const char *external_diff_cmd = NULL;
static int done_preparing = 0; static int done_preparing = 0;
const char *env_diff_opts;
if (done_preparing) if (done_preparing)
return external_diff_cmd; return external_diff_cmd;
/*
* Default values above are meant to match the
* Linux kernel development style. Examples of
* alternative styles you can specify via environment
* variables are:
*
* GIT_DIFF_OPTS="-c";
*/
external_diff_cmd = getenv("GIT_EXTERNAL_DIFF"); external_diff_cmd = getenv("GIT_EXTERNAL_DIFF");
/* In case external diff fails... */
env_diff_opts = getenv("GIT_DIFF_OPTS");
if (env_diff_opts) diff_opts = env_diff_opts;
done_preparing = 1; done_preparing = 1;
return external_diff_cmd; return external_diff_cmd;
} }
@ -102,13 +85,12 @@ static struct diff_tempfile {
char tmp_path[TEMPFILE_PATH_LEN]; char tmp_path[TEMPFILE_PATH_LEN];
} diff_temp[2]; } diff_temp[2];
static int count_lines(const char *filename) static int count_lines(const char *data, int size)
{ {
FILE *in;
int count, ch, completely_empty = 1, nl_just_seen = 0; int count, ch, completely_empty = 1, nl_just_seen = 0;
in = fopen(filename, "r");
count = 0; count = 0;
while ((ch = fgetc(in)) != EOF) while (0 < size--) {
ch = *data++;
if (ch == '\n') { if (ch == '\n') {
count++; count++;
nl_just_seen = 1; nl_just_seen = 1;
@ -118,7 +100,7 @@ static int count_lines(const char *filename)
nl_just_seen = 0; nl_just_seen = 0;
completely_empty = 0; completely_empty = 0;
} }
fclose(in); }
if (completely_empty) if (completely_empty)
return 0; return 0;
if (!nl_just_seen) if (!nl_just_seen)
@ -141,12 +123,11 @@ static void print_line_count(int count)
} }
} }
static void copy_file(int prefix, const char *filename) static void copy_file(int prefix, const char *data, int size)
{ {
FILE *in;
int ch, nl_just_seen = 1; int ch, nl_just_seen = 1;
in = fopen(filename, "r"); while (0 < size--) {
while ((ch = fgetc(in)) != EOF) { ch = *data++;
if (nl_just_seen) if (nl_just_seen)
putchar(prefix); putchar(prefix);
putchar(ch); putchar(ch);
@ -155,60 +136,41 @@ static void copy_file(int prefix, const char *filename)
else else
nl_just_seen = 0; nl_just_seen = 0;
} }
fclose(in);
if (!nl_just_seen) if (!nl_just_seen)
printf("\n\\ No newline at end of file\n"); printf("\n\\ No newline at end of file\n");
} }
static void emit_rewrite_diff(const char *name_a, static void emit_rewrite_diff(const char *name_a,
const char *name_b, const char *name_b,
struct diff_tempfile *temp) struct diff_filespec *one,
struct diff_filespec *two)
{ {
/* Use temp[i].name as input, name_a and name_b as labels */ /* Use temp[i].name as input, name_a and name_b as labels */
int lc_a, lc_b; int lc_a, lc_b;
lc_a = count_lines(temp[0].name); lc_a = count_lines(one->data, one->size);
lc_b = count_lines(temp[1].name); lc_b = count_lines(two->data, two->size);
printf("--- %s\n+++ %s\n@@ -", name_a, name_b); printf("--- %s\n+++ %s\n@@ -", name_a, name_b);
print_line_count(lc_a); print_line_count(lc_a);
printf(" +"); printf(" +");
print_line_count(lc_b); print_line_count(lc_b);
printf(" @@\n"); printf(" @@\n");
if (lc_a) if (lc_a)
copy_file('-', temp[0].name); copy_file('-', one->data, one->size);
if (lc_b) if (lc_b)
copy_file('+', temp[1].name); copy_file('+', two->data, two->size);
} }
static int fill_mmfile(mmfile_t *mf, const char *file) static int fill_mmfile(mmfile_t *mf, struct diff_filespec *one)
{ {
int fd = open(file, O_RDONLY); if (!DIFF_FILE_VALID(one)) {
struct stat st; mf->ptr = ""; /* does not matter */
char *buf; mf->size = 0;
unsigned long size;
mf->ptr = NULL;
mf->size = 0;
if (fd < 0)
return 0; return 0;
fstat(fd, &st);
size = st.st_size;
buf = xmalloc(size);
mf->ptr = buf;
mf->size = size;
while (size) {
int retval = read(fd, buf, size);
if (retval < 0) {
if (errno == EINTR || errno == EAGAIN)
continue;
break;
}
if (!retval)
break;
buf += retval;
size -= retval;
} }
mf->size -= size; else if (diff_populate_filespec(one, 0))
close(fd); return -1;
mf->ptr = one->data;
mf->size = one->size;
return 0; return 0;
} }
@ -243,69 +205,37 @@ static int mmfile_is_binary(mmfile_t *mf)
return 0; return 0;
} }
static const char *builtin_diff(const char *name_a, static void builtin_diff(const char *name_a,
const char *name_b, const char *name_b,
struct diff_tempfile *temp, struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg, const char *xfrm_msg,
int complete_rewrite, int complete_rewrite)
const char **args)
{ {
int i, next_at, cmd_size;
mmfile_t mf1, mf2; mmfile_t mf1, mf2;
const char *const diff_cmd = "diff -L%s -L%s"; const char *lbl[2];
const char *const diff_arg = "-- %s %s||:"; /* "||:" is to return 0 */ char *a_one, *b_two;
const char *input_name_sq[2];
const char *label_path[2];
char *cmd;
/* diff_cmd and diff_arg have 4 %s in total which makes a_one = quote_two("a/", name_a);
* the sum of these strings 8 bytes larger than required. b_two = quote_two("b/", name_b);
* we use 2 spaces around diff-opts, and we need to count lbl[0] = DIFF_FILE_VALID(one) ? a_one : "/dev/null";
* terminating NUL; we used to subtract 5 here, but we do not lbl[1] = DIFF_FILE_VALID(two) ? b_two : "/dev/null";
* care about small leaks in this subprocess that is about printf("diff --git %s %s\n", a_one, b_two);
* to exec "diff" anymore. if (lbl[0][0] == '/') {
*/ /* /dev/null */
cmd_size = (strlen(diff_cmd) + strlen(diff_opts) + strlen(diff_arg) printf("new file mode %06o\n", two->mode);
+ 128);
for (i = 0; i < 2; i++) {
input_name_sq[i] = sq_quote(temp[i].name);
if (!strcmp(temp[i].name, "/dev/null"))
label_path[i] = "/dev/null";
else if (!i)
label_path[i] = sq_quote(quote_two("a/", name_a));
else
label_path[i] = sq_quote(quote_two("b/", name_b));
cmd_size += (strlen(label_path[i]) + strlen(input_name_sq[i]));
}
cmd = xmalloc(cmd_size);
next_at = 0;
next_at += snprintf(cmd+next_at, cmd_size-next_at,
diff_cmd, label_path[0], label_path[1]);
next_at += snprintf(cmd+next_at, cmd_size-next_at,
" %s ", diff_opts);
next_at += snprintf(cmd+next_at, cmd_size-next_at,
diff_arg, input_name_sq[0], input_name_sq[1]);
printf("diff --git %s %s\n",
quote_two("a/", name_a), quote_two("b/", name_b));
if (label_path[0][0] == '/') {
/* dev/null */
printf("new file mode %s\n", temp[1].mode);
if (xfrm_msg && xfrm_msg[0]) if (xfrm_msg && xfrm_msg[0])
puts(xfrm_msg); puts(xfrm_msg);
} }
else if (label_path[1][0] == '/') { else if (lbl[1][0] == '/') {
printf("deleted file mode %s\n", temp[0].mode); printf("deleted file mode %06o\n", one->mode);
if (xfrm_msg && xfrm_msg[0]) if (xfrm_msg && xfrm_msg[0])
puts(xfrm_msg); puts(xfrm_msg);
} }
else { else {
if (strcmp(temp[0].mode, temp[1].mode)) { if (one->mode != two->mode) {
printf("old mode %s\n", temp[0].mode); printf("old mode %06o\n", one->mode);
printf("new mode %s\n", temp[1].mode); printf("new mode %06o\n", two->mode);
} }
if (xfrm_msg && xfrm_msg[0]) if (xfrm_msg && xfrm_msg[0])
puts(xfrm_msg); puts(xfrm_msg);
@ -313,27 +243,19 @@ static const char *builtin_diff(const char *name_a,
* we do not run diff between different kind * we do not run diff between different kind
* of objects. * of objects.
*/ */
if (strncmp(temp[0].mode, temp[1].mode, 3)) if ((one->mode ^ two->mode) & S_IFMT)
return NULL; goto free_ab_and_return;
if (complete_rewrite) { if (complete_rewrite) {
emit_rewrite_diff(name_a, name_b, temp); emit_rewrite_diff(name_a, name_b, one, two);
return NULL; goto free_ab_and_return;
} }
} }
/* Un-quote the paths */ if (fill_mmfile(&mf1, one) < 0 || fill_mmfile(&mf2, two) < 0)
if (label_path[0][0] != '/')
label_path[0] = quote_two("a/", name_a);
if (label_path[1][0] != '/')
label_path[1] = quote_two("b/", name_b);
if (fill_mmfile(&mf1, temp[0].name) < 0 ||
fill_mmfile(&mf2, temp[1].name) < 0)
die("unable to read files to diff"); die("unable to read files to diff");
if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2)) if (mmfile_is_binary(&mf1) || mmfile_is_binary(&mf2))
printf("Binary files %s and %s differ\n", printf("Binary files %s and %s differ\n", lbl[0], lbl[1]);
label_path[0], label_path[1]);
else { else {
/* Crazy xdl interfaces.. */ /* Crazy xdl interfaces.. */
const char *diffopts = getenv("GIT_DIFF_OPTS"); const char *diffopts = getenv("GIT_DIFF_OPTS");
@ -342,7 +264,7 @@ static const char *builtin_diff(const char *name_a,
xdemitcb_t ecb; xdemitcb_t ecb;
struct emit_callback ecbdata; struct emit_callback ecbdata;
ecbdata.label_path = label_path; ecbdata.label_path = lbl;
xpp.flags = XDF_NEED_MINIMAL; xpp.flags = XDF_NEED_MINIMAL;
xecfg.ctxlen = 3; xecfg.ctxlen = 3;
if (!diffopts) if (!diffopts)
@ -356,9 +278,10 @@ static const char *builtin_diff(const char *name_a,
xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb); xdl_diff(&mf1, &mf2, &xpp, &xecfg, &ecb);
} }
free(mf1.ptr); free_ab_and_return:
free(mf2.ptr); free(a_one);
return NULL; free(b_two);
return;
} }
struct diff_filespec *alloc_filespec(const char *path) struct diff_filespec *alloc_filespec(const char *path)
@ -716,6 +639,7 @@ static void run_external_diff(const char *pgm,
int retval; int retval;
static int atexit_asked = 0; static int atexit_asked = 0;
const char *othername; const char *othername;
const char **arg = &spawn_arg[0];
othername = (other? other : name); othername = (other? other : name);
if (one && two) { if (one && two) {
@ -730,36 +654,25 @@ static void run_external_diff(const char *pgm,
signal(SIGINT, remove_tempfile_on_signal); signal(SIGINT, remove_tempfile_on_signal);
} }
if (pgm) { if (one && two) {
const char **arg = &spawn_arg[0]; *arg++ = pgm;
if (one && two) { *arg++ = name;
*arg++ = pgm; *arg++ = temp[0].name;
*arg++ = name; *arg++ = temp[0].hex;
*arg++ = temp[0].name; *arg++ = temp[0].mode;
*arg++ = temp[0].hex; *arg++ = temp[1].name;
*arg++ = temp[0].mode; *arg++ = temp[1].hex;
*arg++ = temp[1].name; *arg++ = temp[1].mode;
*arg++ = temp[1].hex; if (other) {
*arg++ = temp[1].mode; *arg++ = other;
if (other) { *arg++ = xfrm_msg;
*arg++ = other;
*arg++ = xfrm_msg;
}
} else {
*arg++ = pgm;
*arg++ = name;
} }
*arg = NULL;
} else { } else {
if (one && two) { *arg++ = pgm;
pgm = builtin_diff(name, othername, temp, xfrm_msg, complete_rewrite, spawn_arg); *arg++ = name;
} else
printf("* Unmerged path %s\n", name);
} }
*arg = NULL;
retval = 0; retval = spawn_prog(pgm, spawn_arg);
if (pgm)
retval = spawn_prog(pgm, spawn_arg);
remove_tempfile(); remove_tempfile();
if (retval) { if (retval) {
fprintf(stderr, "external diff died, stopping at %s.\n", name); fprintf(stderr, "external diff died, stopping at %s.\n", name);
@ -767,6 +680,26 @@ static void run_external_diff(const char *pgm,
} }
} }
static void run_diff_cmd(const char *pgm,
const char *name,
const char *other,
struct diff_filespec *one,
struct diff_filespec *two,
const char *xfrm_msg,
int complete_rewrite)
{
if (pgm) {
run_external_diff(pgm, name, other, one, two, xfrm_msg,
complete_rewrite);
return;
}
if (one && two)
builtin_diff(name, other ? other : name,
one, two, xfrm_msg, complete_rewrite);
else
printf("* Unmerged path %s\n", name);
}
static void diff_fill_sha1_info(struct diff_filespec *one) static void diff_fill_sha1_info(struct diff_filespec *one)
{ {
if (DIFF_FILE_VALID(one)) { if (DIFF_FILE_VALID(one)) {
@ -796,8 +729,7 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
if (DIFF_PAIR_UNMERGED(p)) { if (DIFF_PAIR_UNMERGED(p)) {
/* unmerged */ /* unmerged */
run_external_diff(pgm, p->one->path, NULL, NULL, NULL, NULL, run_diff_cmd(pgm, p->one->path, NULL, NULL, NULL, NULL, 0);
0);
return; return;
} }
@ -869,15 +801,15 @@ static void run_diff(struct diff_filepair *p, struct diff_options *o)
* needs to be split into deletion and creation. * needs to be split into deletion and creation.
*/ */
struct diff_filespec *null = alloc_filespec(two->path); struct diff_filespec *null = alloc_filespec(two->path);
run_external_diff(NULL, name, other, one, null, xfrm_msg, 0); run_diff_cmd(NULL, name, other, one, null, xfrm_msg, 0);
free(null); free(null);
null = alloc_filespec(one->path); null = alloc_filespec(one->path);
run_external_diff(NULL, name, other, null, two, xfrm_msg, 0); run_diff_cmd(NULL, name, other, null, two, xfrm_msg, 0);
free(null); free(null);
} }
else else
run_external_diff(pgm, name, other, one, two, xfrm_msg, run_diff_cmd(pgm, name, other, one, two, xfrm_msg,
complete_rewrite); complete_rewrite);
free(name_munged); free(name_munged);
free(other_munged); free(other_munged);