diff-cache.con commit [PATCH] simplify Makefile (6ca25ed)
   1#include "cache.h"
   2
   3static int cached_only = 0;
   4static int recursive = 0;
   5static int line_termination = '\n';
   6
   7static int diff_cache(void *tree, unsigned long size, struct cache_entry **ac, int entries, const char *base);
   8
   9static void update_tree_entry(void **bufp, unsigned long *sizep)
  10{
  11        void *buf = *bufp;
  12        unsigned long size = *sizep;
  13        int len = strlen(buf) + 1 + 20;
  14
  15        if (size < len)
  16                die("corrupt tree file 1 (%s)", size);
  17        *bufp = buf + len;
  18        *sizep = size - len;
  19}
  20
  21static const unsigned char *extract(void *tree, unsigned long size, const char **pathp, unsigned int *modep)
  22{
  23        int len = strlen(tree)+1;
  24        const unsigned char *sha1 = tree + len;
  25        const char *path = strchr(tree, ' ');
  26
  27        if (!path || size < len + 20 || sscanf(tree, "%o", modep) != 1)
  28                die("corrupt tree file 2 (%d)", size);
  29        *pathp = path+1;
  30        return sha1;
  31}
  32
  33static char *malloc_base(const char *base, const char *path, int pathlen)
  34{
  35        int baselen = strlen(base);
  36        char *newbase = malloc(baselen + pathlen + 2);
  37        memcpy(newbase, base, baselen);
  38        memcpy(newbase + baselen, path, pathlen);
  39        memcpy(newbase + baselen + pathlen, "/", 2);
  40        return newbase;
  41}
  42
  43static void show_file(const char *prefix, const char *path, unsigned int mode, const unsigned char *sha1, const char *base);
  44
  45/* A whole sub-tree went away or appeared */
  46static void show_tree(const char *prefix, void *tree, unsigned long size, const char *base)
  47{
  48        while (size) {
  49                const char *path;
  50                unsigned int mode;
  51                const unsigned char *sha1 = extract(tree, size, &path, &mode);
  52                
  53                show_file(prefix, path, mode, sha1, base);
  54                update_tree_entry(&tree, &size);
  55        }
  56}
  57
  58/* A file entry went away or appeared */
  59static void show_file(const char *prefix, const char *path, unsigned int mode, const unsigned char *sha1, const char *base)
  60{
  61        if (recursive && S_ISDIR(mode)) {
  62                char type[20];
  63                unsigned long size;
  64                char *newbase = malloc_base(base, path, strlen(path));
  65                void *tree;
  66
  67                tree = read_sha1_file(sha1, type, &size);
  68                if (!tree || strcmp(type, "tree"))
  69                        die("corrupt tree sha %s", sha1_to_hex(sha1));
  70
  71                show_tree(prefix, tree, size, newbase);
  72                
  73                free(tree);
  74                free(newbase);
  75                return;
  76        }
  77
  78        printf("%s%o\t%s\t%s\t%s%s%c", prefix, mode,
  79               S_ISDIR(mode) ? "tree" : "blob",
  80               sha1_to_hex(sha1), base, path,
  81               line_termination);
  82}
  83
  84static int compare_tree_entry(const char *path1, unsigned int mode1, const unsigned char *sha1,
  85                              struct cache_entry **ac, int *entries, const char *base)
  86{
  87        int baselen = strlen(base);
  88        struct cache_entry *ce = *ac;
  89        const char *path2 = ce->name + baselen;
  90        unsigned int mode2 = ntohl(ce->ce_mode);
  91        const unsigned char *sha2 = ce->sha1;
  92        int cmp, pathlen1, pathlen2;
  93        char old_sha1_hex[50];
  94
  95        pathlen1 = strlen(path1);
  96        pathlen2 = strlen(path2);
  97        cmp = cache_name_compare(path1, pathlen1, path2, pathlen2);
  98        if (cmp < 0) {
  99                if (S_ISDIR(mode1)) {
 100                        char type[20];
 101                        unsigned long size;
 102                        void *tree = read_sha1_file(sha1, type, &size);
 103                        char *newbase = malloc(baselen + 2 + pathlen1);
 104
 105                        memcpy(newbase, base, baselen);
 106                        memcpy(newbase + baselen, path1, pathlen1);
 107                        memcpy(newbase + baselen + pathlen1, "/", 2);
 108                        if (!tree || strcmp(type, "tree"))
 109                                die("unable to read tree object %s", sha1_to_hex(sha1));
 110                        *entries = diff_cache(tree, size, ac, *entries, newbase);
 111                        free(newbase);
 112                        free(tree);
 113                        return -1;
 114                }
 115                show_file("-", path1, mode1, sha1, base);
 116                return -1;
 117        }
 118
 119        if (!cached_only) {
 120                static unsigned char no_sha1[20];
 121                int fd, changed;
 122                struct stat st;
 123                fd = open(ce->name, O_RDONLY);
 124                if (fd < 0 || fstat(fd, &st) < 0) {
 125                        show_file("-", path1, mode1, sha1, base);
 126                        return -1;
 127                }
 128                changed = cache_match_stat(ce, &st);
 129                close(fd);
 130                if (changed) {
 131                        mode2 = st.st_mode;
 132                        sha2 = no_sha1;
 133                }
 134        }
 135
 136        if (cmp > 0) {
 137                show_file("+", path2, mode2, sha2, base);
 138                return 1;
 139        }
 140        if (!memcmp(sha1, sha2, 20) && mode1 == mode2)
 141                return 0;
 142
 143        /*
 144         * If the filemode has changed to/from a directory from/to a regular
 145         * file, we need to consider it a remove and an add.
 146         */
 147        if (S_ISDIR(mode1) || S_ISDIR(mode2)) {
 148                show_file("-", path1, mode1, sha1, base);
 149                show_file("+", path2, mode2, sha2, base);
 150                return 0;
 151        }
 152
 153        strcpy(old_sha1_hex, sha1_to_hex(sha1));
 154        printf("*%o->%o\t%s\t%s->%s\t%s%s%c", mode1, mode2,
 155               S_ISDIR(mode1) ? "tree" : "blob",
 156               old_sha1_hex, sha1_to_hex(sha2), base, path1,
 157               line_termination);
 158        return 0;
 159}
 160
 161static int diff_cache(void *tree, unsigned long size, struct cache_entry **ac, int entries, const char *base)
 162{
 163        int baselen = strlen(base);
 164
 165        for (;;) {
 166                struct cache_entry *ce;
 167                unsigned int mode;
 168                const char *path;
 169                const unsigned char *sha1;
 170                int left;
 171
 172                /*
 173                 * No entries in the cache (with this base)?
 174                 * Output the tree contents.
 175                 */
 176                if (!entries || ce_namelen(ce = *ac) < baselen || memcmp(ce->name, base, baselen)) {
 177                        if (!size)
 178                                return entries;
 179                        sha1 = extract(tree, size, &path, &mode);
 180                        show_file("-", path, mode, sha1, base);
 181                        update_tree_entry(&tree, &size);
 182                        continue;
 183                }
 184
 185                /*
 186                 * No entries in the tree? Output the cache contents
 187                 */
 188                if (!size) {
 189                        show_file("+", ce->name, ntohl(ce->ce_mode), ce->sha1, "");
 190                        ac++;
 191                        entries--;
 192                        continue;
 193                }
 194
 195                sha1 = extract(tree, size, &path, &mode);
 196                left = entries;
 197                switch (compare_tree_entry(path, mode, sha1, ac, &left, base)) {
 198                case -1:
 199                        update_tree_entry(&tree, &size);
 200                        if (left < entries) {
 201                                ac += (entries - left);
 202                                entries = left;
 203                        }
 204                        continue;
 205                case 0:
 206                        update_tree_entry(&tree, &size);
 207                        /* Fallthrough */
 208                case 1:
 209                        ac++;
 210                        entries--;
 211                        continue;
 212                }
 213                die("diff-cache: internal error");
 214        }
 215        return 0;
 216}
 217
 218int main(int argc, char **argv)
 219{
 220        unsigned char tree_sha1[20];
 221        void *tree;
 222        unsigned long size;
 223        char type[20];
 224
 225        read_cache();
 226        while (argc > 2) {
 227                char *arg = argv[1];
 228                argv++;
 229                argc--;
 230                if (!strcmp(arg, "-r")) {
 231                        recursive = 1;
 232                        continue;
 233                }
 234                if (!strcmp(arg, "-z")) {
 235                        line_termination = '\0';
 236                        continue;
 237                }
 238                if (!strcmp(arg, "--cached")) {
 239                        cached_only = 1;
 240                        continue;
 241                }
 242                usage("diff-cache [-r] [-z] <tree sha1>");
 243        }
 244
 245        if (argc != 2 || get_sha1_hex(argv[1], tree_sha1))
 246                usage("diff-cache [-r] [-z] <tree sha1>");
 247
 248        tree = read_sha1_file(tree_sha1, type, &size);
 249        if (!tree)
 250                die("bad tree object %s", argv[1]);
 251
 252        /* We allow people to feed us a commit object, just because we're nice */
 253        if (!strcmp(type, "commit")) {
 254                /* tree sha1 is always at offset 5 ("tree ") */
 255                if (get_sha1_hex(tree + 5, tree_sha1))
 256                        die("bad commit object %s", argv[1]);
 257                free(tree);
 258                tree = read_sha1_file(tree_sha1, type, &size);       
 259                if (!tree)
 260                        die("unable to read tree object %s", sha1_to_hex(tree_sha1));
 261        }
 262
 263        if (strcmp(type, "tree"))
 264                die("bad tree object %s (%s)", sha1_to_hex(tree_sha1), type);
 265
 266        return diff_cache(tree, size, active_cache, active_nr, "");
 267}