X-Git-Url: https://git.octo.it/?a=blobdiff_plain;f=show-diff.c;h=2ff0bd4ea636db2ad54e9ff34e1d70ac7a9a98f0;hb=f5b913c9cb52cee019585375546817cfe5c75426;hp=f9e8c12f90e793bc039a6181a6b6424b3725f6dc;hpb=b8f80925e3cdf229f03245f2ebb02323ddd98118;p=git.git diff --git a/show-diff.c b/show-diff.c index f9e8c12f..2ff0bd4e 100644 --- a/show-diff.c +++ b/show-diff.c @@ -5,57 +5,104 @@ */ #include "cache.h" -static void show_differences(char *name, - void *old_contents, unsigned long long old_size) +static char *diff_cmd = "diff -L 'a/%s' -L 'b/%s' "; +static char *diff_opts = "-p -u"; +static char *diff_arg_forward = " - '%s'"; +static char *diff_arg_reverse = " '%s' -"; + +static void prepare_diff_cmd(void) +{ + /* + * 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_CMD="diff -L '%s' -L '%s'" + * GIT_DIFF_OPTS="-c"; + */ + diff_cmd = getenv("GIT_DIFF_CMD") ? : diff_cmd; + diff_opts = getenv("GIT_DIFF_OPTS") ? : diff_opts; +} + +/* Help to copy the thing properly quoted for the shell safety. + * any single quote is replaced with '\'', and the caller is + * expected to enclose the result within a single quote pair. + * + * E.g. + * original sq_expand result + * name ==> name ==> 'name' + * a b ==> a b ==> 'a b' + * a'b ==> a'\''b ==> 'a'\''b' + */ +static char *sq_expand(char *src) +{ + static char *buf = NULL; + int cnt, c; + char *cp; + + /* count bytes needed to store the quoted string. */ + for (cnt = 1, cp = src; *cp; cnt++, cp++) + if (*cp == '\'') + cnt += 3; + + if (! (buf = malloc(cnt))) + return buf; + cp = buf; + while ((c = *src++)) { + if (c != '\'') + *cp++ = c; + else { + cp = strcpy(cp, "'\\''"); + cp += 4; + } + } + *cp = 0; + return buf; +} + +static void show_differences(char *name, char *label, void *old_contents, + unsigned long long old_size, int reverse) { - static char cmd[1000]; FILE *f; + char *name_sq = sq_expand(name); + char *label_sq = (name != label) ? sq_expand(label) : name_sq; + char *diff_arg = reverse ? diff_arg_reverse : diff_arg_forward; + int cmd_size = strlen(name_sq) + strlen(label_sq) * 2 + + strlen(diff_cmd) + strlen(diff_opts) + strlen(diff_arg); + char *cmd = malloc(cmd_size); + int next_at; - snprintf(cmd, sizeof(cmd), "diff -L %s -u -N - %s", name, name); + fflush(stdout); + next_at = snprintf(cmd, cmd_size, diff_cmd, label_sq, label_sq); + 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, name_sq); f = popen(cmd, "w"); if (old_size) fwrite(old_contents, old_size, 1, f); pclose(f); + if (label_sq != name_sq) + free(label_sq); + free(name_sq); + free(cmd); } -static void show_diff_empty(struct cache_entry *ce) +static void show_diff_empty(struct cache_entry *ce, int reverse) { char *old; unsigned long int size; - int lines=0; - unsigned char type[20], *p, *end; + unsigned char type[20]; old = read_sha1_file(ce->sha1, type, &size); - if (size > 0) { - int startline = 1; - int c = 0; - - printf("--- %s\n", ce->name); - printf("+++ /dev/null\n"); - p = old; - end = old + size; - while (p < end) - if (*p++ == '\n') - lines ++; - printf("@@ -1,%d +0,0 @@\n", lines); - p = old; - while (p < end) { - c = *p++; - if (startline) { - putchar('-'); - startline = 0; - } - putchar(c); - if (c == '\n') - startline = 1; - } - if (c!='\n') - printf("\n"); - fflush(stdout); + if (! old) { + error("unable to read blob object for %s (%s)", ce->name, + sha1_to_hex(ce->sha1)); + return; } + show_differences("/dev/null", ce->name, old, size, reverse); } -static const char *show_diff_usage = "show-diff [-s] [-q] [paths...]"; +static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]"; static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt) { @@ -76,14 +123,20 @@ int main(int argc, char **argv) { int silent = 0; int silent_on_nonexisting_files = 0; + int machine_readable = 0; + int reverse = 0; int entries = read_cache(); int i; while (1 < argc && argv[1][0] == '-') { - if (!strcmp(argv[1], "-s")) + if (!strcmp(argv[1], "-R")) + reverse = 1; + else if (!strcmp(argv[1], "-s")) silent_on_nonexisting_files = silent = 1; else if (!strcmp(argv[1], "-q")) silent_on_nonexisting_files = 1; + else if (!strcmp(argv[1], "-z")) + machine_readable = 1; else usage(show_diff_usage); argv++; argc--; @@ -96,40 +149,64 @@ int main(int argc, char **argv) perror("read_cache"); exit(1); } + prepare_diff_cmd(); for (i = 0; i < entries; i++) { struct stat st; struct cache_entry *ce = active_cache[i]; - int n, changed; + int changed; unsigned long size; char type[20]; - void *new; + void *old; - if (1 name, 0); + else + printf("%s: Unmerged\n", + ce->name); + while (i < entries && + !strcmp(ce->name, active_cache[i]->name)) + i++; + i--; /* compensate for loop control increments */ + continue; + } + if (stat(ce->name, &st) < 0) { if (errno == ENOENT && silent_on_nonexisting_files) continue; - printf("%s: %s\n", ce->name, strerror(errno)); - if (errno == ENOENT) - show_diff_empty(ce); + if (machine_readable) + printf("X %s%c", ce->name, 0); + else { + printf("%s: %s\n", ce->name, strerror(errno)); + if (errno == ENOENT) + show_diff_empty(ce, reverse); + } continue; } changed = cache_match_stat(ce, &st); if (!changed) continue; - printf("%s: ", ce->name); - for (n = 0; n < 20; n++) - printf("%02x", ce->sha1[n]); - printf("\n"); - fflush(stdout); + if (!machine_readable) + printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1)); + else { + printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0); + continue; + } if (silent) continue; - new = read_sha1_file(ce->sha1, type, &size); - show_differences(ce->name, new, size); - free(new); + old = read_sha1_file(ce->sha1, type, &size); + if (! old) + error("unable to read blob object for %s (%s)", + ce->name, sha1_to_hex(ce->sha1)); + else + show_differences(ce->name, ce->name, old, size, + reverse); + free(old); } return 0; }