diff-tree.con commit [PATCH] rev-tree support for "in X but not in Y". (727ff27)
   1#include "cache.h"
   2
   3static int recursive = 0;
   4
   5static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base);
   6
   7static void update_tree_entry(void **bufp, unsigned long *sizep)
   8{
   9        void *buf = *bufp;
  10        unsigned long size = *sizep;
  11        int len = strlen(buf) + 1 + 20;
  12
  13        if (size < len)
  14                usage("corrupt tree file");
  15        *bufp = buf + len;
  16        *sizep = size - len;
  17}
  18
  19static const unsigned char *extract(void *tree, unsigned long size, const char **pathp, unsigned int *modep)
  20{
  21        int len = strlen(tree)+1;
  22        const unsigned char *sha1 = tree + len;
  23        const char *path = strchr(tree, ' ');
  24
  25        if (!path || size < len + 20 || sscanf(tree, "%o", modep) != 1)
  26                usage("corrupt tree file");
  27        *pathp = path+1;
  28        return sha1;
  29}
  30
  31static char *malloc_base(const char *base, const char *path, int pathlen)
  32{
  33        int baselen = strlen(base);
  34        char *newbase = malloc(baselen + pathlen + 2);
  35        memcpy(newbase, base, baselen);
  36        memcpy(newbase + baselen, path, pathlen);
  37        memcpy(newbase + baselen + pathlen, "/", 2);
  38        return newbase;
  39}
  40
  41static void show_file(const char *prefix, void *tree, unsigned long size, const char *base);
  42
  43/* A whole sub-tree went away or appeared */
  44static void show_tree(const char *prefix, void *tree, unsigned long size, const char *base)
  45{
  46        while (size) {
  47                show_file(prefix, tree, size, base);
  48                update_tree_entry(&tree, &size);
  49        }
  50}
  51
  52/* A file entry went away or appeared */
  53static void show_file(const char *prefix, void *tree, unsigned long size, const char *base)
  54{
  55        unsigned mode;
  56        const char *path;
  57        const unsigned char *sha1 = extract(tree, size, &path, &mode);
  58
  59        if (recursive && S_ISDIR(mode)) {
  60                char type[20];
  61                unsigned long size;
  62                char *newbase = malloc_base(base, path, strlen(path));
  63                void *tree;
  64
  65                tree = read_sha1_file(sha1, type, &size);
  66                if (!tree || strcmp(type, "tree"))
  67                        usage("corrupt tree sha %s", sha1_to_hex(sha1));
  68
  69                show_tree(prefix, tree, size, newbase);
  70                
  71                free(tree);
  72                free(newbase);
  73                return;
  74        }
  75
  76        printf("%s%o %s %s%s%c", prefix, mode, sha1_to_hex(sha1), base, path, 0);
  77}
  78
  79static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base)
  80{
  81        unsigned mode1, mode2;
  82        const char *path1, *path2;
  83        const unsigned char *sha1, *sha2;
  84        int cmp, pathlen1, pathlen2;
  85        char old_sha1_hex[50];
  86
  87        sha1 = extract(tree1, size1, &path1, &mode1);
  88        sha2 = extract(tree2, size2, &path2, &mode2);
  89
  90        pathlen1 = strlen(path1);
  91        pathlen2 = strlen(path2);
  92        cmp = cache_name_compare(path1, pathlen1, path2, pathlen2);
  93        if (cmp < 0) {
  94                show_file("-", tree1, size1, base);
  95                return -1;
  96        }
  97        if (cmp > 0) {
  98                show_file("+", tree2, size2, base);
  99                return 1;
 100        }
 101        if (!memcmp(sha1, sha2, 20) && mode1 == mode2)
 102                return 0;
 103
 104        /*
 105         * If the filemode has changed to/from a directory from/to a regular
 106         * file, we need to consider it a remove and an add. 
 107         */
 108        if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
 109                show_file("-", tree1, size1, base);
 110                show_file("+", tree2, size2, base);
 111                return 0;
 112        }
 113
 114        if (recursive && S_ISDIR(mode1)) {
 115                int retval;
 116                char *newbase = malloc_base(base, path1, pathlen1);
 117                retval = diff_tree_sha1(sha1, sha2, newbase);
 118                free(newbase);
 119                return retval;
 120        }
 121
 122        strcpy(old_sha1_hex, sha1_to_hex(sha1));
 123        printf("*%o->%o %s->%s %s%s%c", mode1, mode2, old_sha1_hex, sha1_to_hex(sha2), base, path1, 0);
 124        return 0;
 125}
 126
 127static int diff_tree(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *base)
 128{
 129        while (size1 | size2) {
 130                if (!size1) {
 131                        show_file("+", tree2, size2, base);
 132                        update_tree_entry(&tree2, &size2);
 133                        continue;
 134                }
 135                if (!size2) {
 136                        show_file("-", tree1, size1, base);
 137                        update_tree_entry(&tree1, &size1);
 138                        continue;
 139                }
 140                switch (compare_tree_entry(tree1, size1, tree2, size2, base)) {
 141                case -1:
 142                        update_tree_entry(&tree1, &size1);
 143                        continue;
 144                case 0:
 145                        update_tree_entry(&tree1, &size1);
 146                        /* Fallthrough */
 147                case 1:
 148                        update_tree_entry(&tree2, &size2);
 149                        continue;
 150                }
 151                usage("diff-tree: internal error");
 152        }
 153        return 0;
 154}
 155
 156static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base)
 157{
 158        void *tree1, *tree2;
 159        unsigned long size1, size2;
 160        char type[20];
 161        int retval;
 162
 163        tree1 = read_sha1_file(old, type, &size1);
 164        if (!tree1 || strcmp(type, "tree"))
 165                usage("unable to read source tree");
 166        tree2 = read_sha1_file(new, type, &size2);
 167        if (!tree2 || strcmp(type, "tree"))
 168                usage("unable to read destination tree");
 169        retval = diff_tree(tree1, size1, tree2, size2, base);
 170        free(tree1);
 171        free(tree2);
 172        return retval;
 173}
 174
 175int main(int argc, char **argv)
 176{
 177        unsigned char old[20], new[20];
 178
 179        while (argc > 3) {
 180                char *arg = argv[1];
 181                argv++;
 182                argc--;
 183                if (!strcmp(arg, "-r")) {
 184                        recursive = 1;
 185                        continue;
 186                }
 187                usage("diff-tree [-R] <tree sha1> <tree sha1>");
 188        }
 189
 190        if (argc != 3 || get_sha1_hex(argv[1], old) || get_sha1_hex(argv[2], new))
 191                usage("diff-tree <tree sha1> <tree sha1>");
 192        return diff_tree_sha1(old, new, "");
 193}