207617c8fa247821474948cde4e34aefe4d6b5f2
   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 *src, const char *dst);
   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 void show_file(const char *prefix, void *tree, unsigned long size, const char *base)
  32{
  33        unsigned mode;
  34        const char *path;
  35        const unsigned char *sha1 = extract(tree, size, &path, &mode);
  36        printf("%s%o %s %s%s%c", prefix, mode, sha1_to_hex(sha1), base, path, 0);
  37}
  38
  39static int compare_tree_entry(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *src, const char *dst)
  40{
  41        unsigned mode1, mode2;
  42        const char *path1, *path2;
  43        const unsigned char *sha1, *sha2;
  44        int cmp, pathlen1, pathlen2;
  45
  46        sha1 = extract(tree1, size1, &path1, &mode1);
  47        sha2 = extract(tree2, size2, &path2, &mode2);
  48
  49        pathlen1 = strlen(path1);
  50        pathlen2 = strlen(path2);
  51        cmp = cache_name_compare(path1, pathlen1, path2, pathlen2);
  52        if (cmp < 0) {
  53                show_file("-", tree1, size1, src);
  54                return -1;
  55        }
  56        if (cmp > 0) {
  57                show_file("+", tree2, size2, dst);
  58                return 1;
  59        }
  60        if (!memcmp(sha1, sha2, 20) && mode1 == mode2)
  61                return 0;
  62        if (recursive && S_ISDIR(mode1) && S_ISDIR(mode2)) {
  63                int srclen = strlen(src);
  64                int dstlen = strlen(dst);
  65                char *srcbase = malloc(srclen + pathlen1 + 2);
  66                char *dstbase = malloc(srclen + pathlen1 + 2);
  67                memcpy(srcbase, src, srclen);
  68                memcpy(srcbase + srclen, path1, pathlen1);
  69                memcpy(srcbase + srclen + pathlen1, "/", 2);
  70                memcpy(dstbase, dst, dstlen);
  71                memcpy(dstbase + dstlen, path2, pathlen2);
  72                memcpy(dstbase + dstlen + pathlen2, "/", 2);
  73                return diff_tree_sha1(sha1, sha2, srcbase, dstbase);
  74        }
  75
  76        show_file("<", tree1, size1, src);
  77        show_file(">", tree2, size2, dst);
  78        return 0;
  79}
  80
  81static int diff_tree(void *tree1, unsigned long size1, void *tree2, unsigned long size2, const char *src, const char *dst)
  82{
  83        while (size1 | size2) {
  84                if (!size1) {
  85                        show_file("+", tree2, size2, dst);
  86                        update_tree_entry(&tree2, &size2);
  87                        continue;
  88                }
  89                if (!size2) {
  90                        show_file("-", tree1, size1, src);
  91                        update_tree_entry(&tree1, &size1);
  92                        continue;
  93                }
  94                switch (compare_tree_entry(tree1, size1, tree2, size2, src, dst)) {
  95                case -1:
  96                        update_tree_entry(&tree1, &size1);
  97                        continue;
  98                case 0:
  99                        update_tree_entry(&tree1, &size1);
 100                        /* Fallthrough */
 101                case 1:
 102                        update_tree_entry(&tree2, &size2);
 103                        continue;
 104                }
 105                usage("diff-tree: internal error");
 106        }
 107        return 0;
 108}
 109
 110static int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *src, const char *dst)
 111{
 112        void *tree1, *tree2;
 113        unsigned long size1, size2;
 114        char type[20];
 115        int retval;
 116
 117        tree1 = read_sha1_file(old, type, &size1);
 118        if (!tree1 || strcmp(type, "tree"))
 119                usage("unable to read source tree");
 120        tree2 = read_sha1_file(new, type, &size2);
 121        if (!tree2 || strcmp(type, "tree"))
 122                usage("unable to read destination tree");
 123        retval = diff_tree(tree1, size1, tree2, size2, src, dst);
 124        free(tree1);
 125        free(tree2);
 126        return retval;
 127}
 128
 129int main(int argc, char **argv)
 130{
 131        unsigned char old[20], new[20];
 132
 133        while (argc > 3) {
 134                char *arg = argv[1];
 135                argv++;
 136                argc--;
 137                if (!strcmp(arg, "-R")) {
 138                        recursive = 1;
 139                        continue;
 140                }
 141                usage("diff-tree [-R] <tree sha1> <tree sha1>");
 142        }
 143
 144        if (argc != 3 || get_sha1_hex(argv[1], old) || get_sha1_hex(argv[2], new))
 145                usage("diff-tree <tree sha1> <tree sha1>");
 146        return diff_tree_sha1(old, new, "", "");
 147}