show-diff.con commit [PATCH] Switch implementations of merge-base, port to parsing (b5039db)
   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        static int buf_size = -1;
  27        int cnt, c;
  28        char *cp;
  29
  30        /* count single quote characters */ 
  31        for (cnt = 0, cp = src; *cp; cnt++, cp++)
  32                if (*cp == '\'')
  33                        cnt += 3;
  34
  35        if (buf_size < cnt) {
  36                free(buf);
  37                buf_size = cnt;
  38                buf = malloc(cnt);
  39        }
  40
  41        cp = buf;
  42        while ((c = *src++)) {
  43                if (c != '\'')
  44                        *cp++ = c;
  45                else {
  46                        cp = strcpy(cp, "'\\''");
  47                        cp += 4;
  48                }
  49        }
  50        *cp = 0;
  51        return buf;
  52}
  53
  54static void show_differences(char *name, void *old_contents,
  55                             unsigned long long old_size)
  56{
  57        FILE *f;
  58        static char *cmd = NULL;
  59        static int cmd_size = -1;
  60
  61        char *name_sq = sq_expand(name);
  62        int cmd_required_length = strlen(name_sq) * 2 + strlen(diff_cmd);
  63
  64        if (cmd_size < cmd_required_length) {
  65                free(cmd);
  66                cmd_size = cmd_required_length;
  67                cmd = malloc(cmd_required_length);
  68        }
  69        snprintf(cmd, cmd_size, diff_cmd, name_sq, name_sq);
  70        f = popen(cmd, "w");
  71        if (old_size)
  72                fwrite(old_contents, old_size, 1, f);
  73        pclose(f);
  74}
  75
  76static void show_diff_empty(struct cache_entry *ce)
  77{
  78        char *old;
  79        unsigned long int size;
  80        int lines=0;
  81        unsigned char type[20], *p, *end;
  82
  83        old = read_sha1_file(ce->sha1, type, &size);
  84        if (size > 0) {
  85                int startline = 1;
  86                int c = 0;
  87
  88                printf("--- %s\n", ce->name);
  89                printf("+++ /dev/null\n");
  90                p = old;
  91                end = old + size;
  92                while (p < end)
  93                        if (*p++ == '\n')
  94                                lines ++;
  95                printf("@@ -1,%d +0,0 @@\n", lines);
  96                p = old;
  97                while (p < end) {
  98                        c = *p++;
  99                        if (startline) {
 100                                putchar('-');
 101                                startline = 0;
 102                        }
 103                        putchar(c);
 104                        if (c == '\n')
 105                                startline = 1;
 106                }
 107                if (c!='\n')
 108                        printf("\n");
 109                fflush(stdout);
 110        }
 111}
 112
 113static const char *show_diff_usage = "show-diff [-q] [-s] [-z] [paths...]";
 114
 115static int matches_pathspec(struct cache_entry *ce, char **spec, int cnt)
 116{
 117        int i;
 118        int namelen = ce_namelen(ce);
 119        for (i = 0; i < cnt; i++) {
 120                int speclen = strlen(spec[i]);
 121                if (! strncmp(spec[i], ce->name, speclen) &&
 122                    speclen <= namelen &&
 123                    (ce->name[speclen] == 0 ||
 124                     ce->name[speclen] == '/'))
 125                        return 1;
 126        }
 127        return 0;
 128}
 129
 130int main(int argc, char **argv)
 131{
 132        int silent = 0;
 133        int silent_on_nonexisting_files = 0;
 134        int machine_readable = 0;
 135        int entries = read_cache();
 136        int i;
 137
 138        while (1 < argc && argv[1][0] == '-') {
 139                if (!strcmp(argv[1], "-s"))
 140                        silent_on_nonexisting_files = silent = 1;
 141                else if (!strcmp(argv[1], "-q"))
 142                        silent_on_nonexisting_files = 1;
 143                else if (!strcmp(argv[1], "-z"))
 144                        machine_readable = 1;
 145                else
 146                        usage(show_diff_usage);
 147                argv++; argc--;
 148        }
 149
 150        /* At this point, if argc == 1, then we are doing everything.
 151         * Otherwise argv[1] .. argv[argc-1] have the explicit paths.
 152         */
 153        if (entries < 0) {
 154                perror("read_cache");
 155                exit(1);
 156        }
 157        for (i = 0; i < entries; i++) {
 158                struct stat st;
 159                struct cache_entry *ce = active_cache[i];
 160                int changed;
 161                unsigned long size;
 162                char type[20];
 163                void *old;
 164
 165                if (1 < argc &&
 166                    ! matches_pathspec(ce, argv+1, argc-1))
 167                        continue;
 168
 169                if (ce_stage(ce)) {
 170                        if (machine_readable)
 171                                printf("U %s%c", ce->name, 0);
 172                        else
 173                                printf("%s: Unmerged\n",
 174                                       ce->name);
 175                        while (i < entries &&
 176                               !strcmp(ce->name, active_cache[i]->name))
 177                                i++;
 178                        i--; /* compensate for loop control increments */
 179                        continue;
 180                }
 181 
 182                if (stat(ce->name, &st) < 0) {
 183                        if (errno == ENOENT && silent_on_nonexisting_files)
 184                                continue;
 185                        if (machine_readable)
 186                                printf("X %s%c", ce->name, 0);
 187                        else {
 188                                printf("%s: %s\n", ce->name, strerror(errno));
 189                                if (errno == ENOENT)
 190                                        show_diff_empty(ce);
 191                        }
 192                        continue;
 193                }
 194                changed = cache_match_stat(ce, &st);
 195                if (!changed)
 196                        continue;
 197                if (!machine_readable)
 198                        printf("%s: %s\n", ce->name, sha1_to_hex(ce->sha1));
 199                else {
 200                        printf("%s %s%c", sha1_to_hex(ce->sha1), ce->name, 0);
 201                        continue;
 202                }
 203                fflush(stdout);
 204                if (silent)
 205                        continue;
 206
 207                old = read_sha1_file(ce->sha1, type, &size);
 208                show_differences(ce->name, old, size);
 209                free(old);
 210        }
 211        return 0;
 212}