dacdd36394a30b95458221585f10f359924d89cb
   1/*
   2 * GIT - The information manager from hell
   3 *
   4 * Copyright (C) Linus Torvalds, 2005
   5 */
   6#include "cache.h"
   7
   8static char *diff_cmd = "diff -L '%s' -u -N  - '%s'";
   9
  10/* Help to copy the thing properly quoted for the shell safety.
  11 * any single quote is replaced with '\'', and the caller is
  12 * expected to enclose the result within a single quote pair.
  13 *
  14 * E.g.
  15 *  original     sq_expand     result
  16 *  name     ==> name      ==> 'name'
  17 *  a b      ==> a b       ==> 'a b'
  18 *  a'b      ==> a'\''b    ==> 'a'\''b'
  19 *
  20 * NOTE! The returned memory belongs to this function so
  21 * do not free it.
  22 */
  23static char *sq_expand(char *src)
  24{
  25        static char *buf = NULL;
  26        int cnt, c;
  27        char *cp;
  28
  29        /* count bytes needed to store the quoted string. */ 
  30        for (cnt = 1, cp = src; *cp; cnt++, cp++)
  31                if (*cp == '\'')
  32                        cnt += 3;
  33
  34        if (! (buf = malloc(cnt)))
  35            return buf;
  36        cp = buf;
  37        while ((c = *src++)) {
  38                if (c != '\'')
  39                        *cp++ = c;
  40                else {
  41                        cp = strcpy(cp, "'\\''");
  42                        cp += 4;
  43                }
  44        }
  45        *cp = 0;
  46        return buf;
  47}
  48
  49static void show_differences(char *name, void *old_contents,
  50                             unsigned long long old_size)
  51{
  52        FILE *f;
  53        char *name_sq = sq_expand(name);
  54        int cmd_size = strlen(name_sq) * 2 + strlen(diff_cmd);
  55        char *cmd = malloc(cmd_size);
  56
  57        snprintf(cmd, cmd_size, diff_cmd, name_sq, name_sq);
  58        f = popen(cmd, "w");
  59        if (old_size)
  60                fwrite(old_contents, old_size, 1, f);
  61        pclose(f);
  62        free(name_sq);
  63        free(cmd);
  64}
  65
  66static void show_diff_empty(struct cache_entry *ce)
  67{
  68        char *old;
  69        unsigned long int size;
  70        int lines=0;
  71        unsigned char type[20], *p, *end;
  72
  73        old = read_sha1_file(ce->sha1, type, &size);
  74        if (! old) {
  75                error("unable to read blob object for %s (%s)", ce->name,
  76                      sha1_to_hex(ce->sha1));
  77                return;
  78        }
  79        if (size > 0) {
  80                int startline = 1;
  81                int c = 0;
  82
  83                printf("--- %s\n", ce->name);
  84                printf("+++ /dev/null\n");
  85                p = old;
  86                end = old + size;
  87                while (p < end)
  88                        if (*p++ == '\n')
  89                                lines ++;
  90                printf("@@ -1,%d +0,0 @@\n", lines);
  91                p = old;
  92                while (p < end) {
  93                        c = *p++;
  94                        if (startline) {
  95                                putchar('-');
  96                                startline = 0;
  97                        }
  98                        putchar(c);
  99                        if (c == '\n')
 100                                startline = 1;
 101                }
 102                if (c!='\n')
 103                        printf("\n");
 104                fflush(stdout);
 105        }
 106}
 107
 108static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]";
 109
 110static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)
 111{
 112        int i;
 113        int namelen = ce_namelen(ce);
 114        for (i = 0; i < cnt; i++) {
 115                int speclen = strlen(spec[i]);
 116                if (! strncmp(spec[i], ce->name, speclen) &&
 117                    speclen <= namelen &&
 118                    (ce->name[speclen] == 0 ||
 119                     ce->name[speclen] == '/'))
 120                        return 1;
 121        }
 122        return 0;
 123}
 124
 125int main(int argc, char **argv)
 126{
 127        int silent = 0;
 128        int silent_on_nonexisting_files = 0;
 129        int machine_readable = 0;
 130        int entries = read_cache();
 131        int i;
 132
 133        while (1 < argc && argv[1][0] == '-') {
 134                if (!strcmp(argv[1], "-s"))
 135                        silent_on_nonexisting_files = silent = 1;
 136                else if (!strcmp(argv[1], "-q"))
 137                        silent_on_nonexisting_files = 1;
 138                else if (!strcmp(argv[1], "-z"))
 139                        machine_readable = 1;
 140                else
 141                        usage(show_diff_usage);
 142                argv++; argc--;
 143        }
 144
 145        /* At this point, if argc == 1, then we are doing everything.
 146         * Otherwise argv[1] .. argv[argc-1] have the explicit paths.
 147         */
 148        if (entries < 0) {
 149                perror("read_cache");
 150                exit(1);
 151        }
 152        for (i = 0; i < entries; i++) {
 153                struct stat st;
 154                struct cache_entry *ce = active_cache[i];
 155                int changed;
 156                unsigned long size;
 157                char type[20];
 158                void *old;
 159
 160                if (1 < argc &&
 161                    ! matches_pathspec(ce, argv+1, argc-1))
 162                        continue;
 163
 164                if (ce_stage(ce)) {
 165                        if (machine_readable)
 166                                printf("U %s%c", ce->name, 0);
 167                        else
 168                                printf("%s: Unmerged\n",
 169                                       ce->name);
 170                        while (i < entries &&
 171                               !strcmp(ce->name, active_cache[i]->name))
 172                                i++;
 173                        i--; /* compensate for loop control increments */
 174                        continue;
 175                }
 176 
 177                if (stat(ce->name, &st) < 0) {
 178                        if (errno == ENOENT && silent_on_nonexisting_files)
 179                                continue;
 180                        if (machine_readable)
 181                                printf("X %s%c", ce->name, 0);
 182                        else {
 183                                printf("%s: %s\n", ce->name, strerror(errno));
 184                                if (errno == ENOENT)
 185                                        show_diff_empty(ce);
 186                        }
 187                        continue;
 188                }
 189                changed = cache_match_stat(ce, &st);
 190                if (!changed)
 191                        continue;
 192                if (!machine_readable)
 193                        printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1));
 194                else {
 195                        printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0);
 196                        continue;
 197                }
 198                fflush(stdout);
 199                if (silent)
 200                        continue;
 201
 202                old = read_sha1_file(ce->sha1, type, &size);
 203                if (! old)
 204                        error("unable to read blob object for %s (%s)",
 205                              ce->name, sha1_to_hex(ce->sha1));
 206                else
 207                        show_differences(ce->name, old, size);
 208                free(old);
 209        }
 210        return 0;
 211}