X-Git-Url: https://git.verplant.org/?a=blobdiff_plain;ds=sidebyside;f=show-diff.c;h=32d2fceaff2a3fb8e3e52d4708946e205ab41d76;hb=5a6ad74623571a1479af7914ec16783a9566e4b2;hp=3c6d4f98b48634d63554821b8d23592e359f8c91;hpb=19b2860cba5742ab31fd682b80fefefac19be141;p=git.git diff --git a/show-diff.c b/show-diff.c index 3c6d4f98..32d2fcea 100644 --- a/show-diff.c +++ b/show-diff.c @@ -5,53 +5,124 @@ */ #include "cache.h" -#define MTIME_CHANGED 0x0001 -#define CTIME_CHANGED 0x0002 -#define OWNER_CHANGED 0x0004 -#define MODE_CHANGED 0x0008 -#define INODE_CHANGED 0x0010 -#define DATA_CHANGED 0x0020 - -static int match_stat(struct cache_entry *ce, struct stat *st) +static char *diff_cmd = "diff -L 'a/%s' -L 'b/%s' -p -u - '%s'"; + +/* 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' + * + * NOTE! The returned memory belongs to this function so + * do not free it. + */ +static char *sq_expand(char *src) { - unsigned int changed = 0; - - if (ce->mtime.sec != (unsigned int)st->st_mtim.tv_sec || - ce->mtime.nsec != (unsigned int)st->st_mtim.tv_nsec) - changed |= MTIME_CHANGED; - if (ce->ctime.sec != (unsigned int)st->st_ctim.tv_sec || - ce->ctime.nsec != (unsigned int)st->st_ctim.tv_nsec) - changed |= CTIME_CHANGED; - if (ce->st_uid != (unsigned int)st->st_uid || - ce->st_gid != (unsigned int)st->st_gid) - changed |= OWNER_CHANGED; - if (ce->st_mode != (unsigned int)st->st_mode) - changed |= MODE_CHANGED; - if (ce->st_dev != (unsigned int)st->st_dev || - ce->st_ino != (unsigned int)st->st_ino) - changed |= INODE_CHANGED; - if (ce->st_size != (unsigned int)st->st_size) - changed |= DATA_CHANGED; - return changed; + 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(struct cache_entry *ce, struct stat *cur, - void *old_contents, unsigned long long old_size) +static void show_differences(char *name, char *label, void *old_contents, + unsigned long long old_size) { - static char cmd[1000]; FILE *f; + char *name_sq = sq_expand(name); + char *label_sq = (name != label) ? sq_expand(label) : name_sq; + int cmd_size = strlen(name_sq) + + strlen(label_sq) * 2 + strlen(diff_cmd); + char *cmd = malloc(cmd_size); - snprintf(cmd, sizeof(cmd), "diff -u - %s", ce->name); + fflush(stdout); + snprintf(cmd, cmd_size, diff_cmd, label_sq, label_sq, name_sq); f = popen(cmd, "w"); - fwrite(old_contents, old_size, 1, f); + 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) +{ + char *old; + unsigned long int size; + unsigned char type[20]; + + 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)); + return; + } + show_differences("/dev/null", ce->name, old, size); +} + +static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]"; + +static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt) +{ + int i; + int namelen = ce_namelen(ce); + for (i = 0; i < cnt; i++) { + int speclen = strlen(spec[i]); + if (! strncmp(spec[i], ce->name, speclen) && + speclen <= namelen && + (ce->name[speclen] == 0 || + ce->name[speclen] == '/')) + return 1; + } + return 0; } int main(int argc, char **argv) { + int silent = 0; + int silent_on_nonexisting_files = 0; + int machine_readable = 0; int entries = read_cache(); int i; + while (1 < argc && argv[1][0] == '-') { + 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--; + } + + /* At this point, if argc == 1, then we are doing everything. + * Otherwise argv[1] .. argv[argc-1] have the explicit paths. + */ if (entries < 0) { perror("read_cache"); exit(1); @@ -59,27 +130,59 @@ int main(int argc, char **argv) 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 < argc && + ! matches_pathspec(ce, argv+1, argc-1)) + continue; + + if (ce_stage(ce)) { + if (machine_readable) + printf("U %s%c", ce->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) { - printf("%s: %s\n", ce->name, strerror(errno)); + if (errno == ENOENT && silent_on_nonexisting_files) + continue; + 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); + } continue; } - changed = match_stat(ce, &st); - if (!changed) { - printf("%s: ok\n", ce->name); + changed = cache_match_stat(ce, &st); + if (!changed) + continue; + 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; } - printf("%.*s: ", ce->namelen, ce->name); - for (n = 0; n < 20; n++) - printf("%02x", ce->sha1[n]); - printf("\n"); - new = read_sha1_file(ce->sha1, type, &size); - show_differences(ce, &st, new, size); - free(new); + if (silent) + continue; + + 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); + free(old); } return 0; }