tree-diff.con commit git-clone: exit early if repo isn't specified (ef5b4ea)
   1/*
   2 * Helper functions for tree diff generation
   3 */
   4#include "cache.h"
   5#include "diff.h"
   6
   7// What paths are we interested in?
   8static int nr_paths = 0;
   9static const char **paths = NULL;
  10static int *pathlens = NULL;
  11
  12static char *malloc_base(const char *base, const char *path, int pathlen)
  13{
  14        int baselen = strlen(base);
  15        char *newbase = xmalloc(baselen + pathlen + 2);
  16        memcpy(newbase, base, baselen);
  17        memcpy(newbase + baselen, path, pathlen);
  18        memcpy(newbase + baselen + pathlen, "/", 2);
  19        return newbase;
  20}
  21
  22static int show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base);
  23
  24static int compare_tree_entry(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
  25{
  26        unsigned mode1, mode2;
  27        const char *path1, *path2;
  28        const unsigned char *sha1, *sha2;
  29        int cmp, pathlen1, pathlen2;
  30
  31        sha1 = tree_entry_extract(t1, &path1, &mode1);
  32        sha2 = tree_entry_extract(t2, &path2, &mode2);
  33
  34        pathlen1 = strlen(path1);
  35        pathlen2 = strlen(path2);
  36        cmp = base_name_compare(path1, pathlen1, mode1, path2, pathlen2, mode2);
  37        if (cmp < 0) {
  38                show_entry(opt, "-", t1, base);
  39                return -1;
  40        }
  41        if (cmp > 0) {
  42                show_entry(opt, "+", t2, base);
  43                return 1;
  44        }
  45        if (!opt->find_copies_harder &&
  46            !memcmp(sha1, sha2, 20) && mode1 == mode2)
  47                return 0;
  48
  49        /*
  50         * If the filemode has changed to/from a directory from/to a regular
  51         * file, we need to consider it a remove and an add.
  52         */
  53        if (S_ISDIR(mode1) != S_ISDIR(mode2)) {
  54                show_entry(opt, "-", t1, base);
  55                show_entry(opt, "+", t2, base);
  56                return 0;
  57        }
  58
  59        if (opt->recursive && S_ISDIR(mode1)) {
  60                int retval;
  61                char *newbase = malloc_base(base, path1, pathlen1);
  62                if (opt->tree_in_recursive)
  63                        opt->change(opt, mode1, mode2,
  64                                    sha1, sha2, base, path1);
  65                retval = diff_tree_sha1(sha1, sha2, newbase, opt);
  66                free(newbase);
  67                return retval;
  68        }
  69
  70        opt->change(opt, mode1, mode2, sha1, sha2, base, path1);
  71        return 0;
  72}
  73
  74static int interesting(struct tree_desc *desc, const char *base)
  75{
  76        const char *path;
  77        unsigned mode;
  78        int i;
  79        int baselen, pathlen;
  80
  81        if (!nr_paths)
  82                return 1;
  83
  84        (void)tree_entry_extract(desc, &path, &mode);
  85
  86        pathlen = strlen(path);
  87        baselen = strlen(base);
  88
  89        for (i=0; i < nr_paths; i++) {
  90                const char *match = paths[i];
  91                int matchlen = pathlens[i];
  92
  93                if (baselen >= matchlen) {
  94                        /* If it doesn't match, move along... */
  95                        if (strncmp(base, match, matchlen))
  96                                continue;
  97
  98                        /* The base is a subdirectory of a path which was specified. */
  99                        return 1;
 100                }
 101
 102                /* Does the base match? */
 103                if (strncmp(base, match, baselen))
 104                        continue;
 105
 106                match += baselen;
 107                matchlen -= baselen;
 108
 109                if (pathlen > matchlen)
 110                        continue;
 111
 112                if (matchlen > pathlen) {
 113                        if (match[pathlen] != '/')
 114                                continue;
 115                        if (!S_ISDIR(mode))
 116                                continue;
 117                }
 118
 119                if (strncmp(path, match, pathlen))
 120                        continue;
 121
 122                return 1;
 123        }
 124        return 0; /* No matches */
 125}
 126
 127/* A whole sub-tree went away or appeared */
 128static void show_tree(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)
 129{
 130        while (desc->size) {
 131                if (interesting(desc, base))
 132                        show_entry(opt, prefix, desc, base);
 133                update_tree_entry(desc);
 134        }
 135}
 136
 137/* A file entry went away or appeared */
 138static int show_entry(struct diff_options *opt, const char *prefix, struct tree_desc *desc, const char *base)
 139{
 140        unsigned mode;
 141        const char *path;
 142        const unsigned char *sha1 = tree_entry_extract(desc, &path, &mode);
 143
 144        if (opt->recursive && S_ISDIR(mode)) {
 145                char type[20];
 146                char *newbase = malloc_base(base, path, strlen(path));
 147                struct tree_desc inner;
 148                void *tree;
 149
 150                tree = read_sha1_file(sha1, type, &inner.size);
 151                if (!tree || strcmp(type, "tree"))
 152                        die("corrupt tree sha %s", sha1_to_hex(sha1));
 153
 154                inner.buf = tree;
 155                show_tree(opt, prefix, &inner, newbase);
 156
 157                free(tree);
 158                free(newbase);
 159                return 0;
 160        }
 161
 162        opt->add_remove(opt, prefix[0], mode, sha1, base, path);
 163        return 0;
 164}
 165
 166int diff_tree(struct tree_desc *t1, struct tree_desc *t2, const char *base, struct diff_options *opt)
 167{
 168        while (t1->size | t2->size) {
 169                if (nr_paths && t1->size && !interesting(t1, base)) {
 170                        update_tree_entry(t1);
 171                        continue;
 172                }
 173                if (nr_paths && t2->size && !interesting(t2, base)) {
 174                        update_tree_entry(t2);
 175                        continue;
 176                }
 177                if (!t1->size) {
 178                        show_entry(opt, "+", t2, base);
 179                        update_tree_entry(t2);
 180                        continue;
 181                }
 182                if (!t2->size) {
 183                        show_entry(opt, "-", t1, base);
 184                        update_tree_entry(t1);
 185                        continue;
 186                }
 187                switch (compare_tree_entry(t1, t2, base, opt)) {
 188                case -1:
 189                        update_tree_entry(t1);
 190                        continue;
 191                case 0:
 192                        update_tree_entry(t1);
 193                        /* Fallthrough */
 194                case 1:
 195                        update_tree_entry(t2);
 196                        continue;
 197                }
 198                die("git-diff-tree: internal error");
 199        }
 200        return 0;
 201}
 202
 203int diff_tree_sha1(const unsigned char *old, const unsigned char *new, const char *base, struct diff_options *opt)
 204{
 205        void *tree1, *tree2;
 206        struct tree_desc t1, t2;
 207        int retval;
 208
 209        tree1 = read_object_with_reference(old, "tree", &t1.size, NULL);
 210        if (!tree1)
 211                die("unable to read source tree (%s)", sha1_to_hex(old));
 212        tree2 = read_object_with_reference(new, "tree", &t2.size, NULL);
 213        if (!tree2)
 214                die("unable to read destination tree (%s)", sha1_to_hex(new));
 215        t1.buf = tree1;
 216        t2.buf = tree2;
 217        retval = diff_tree(&t1, &t2, base, opt);
 218        free(tree1);
 219        free(tree2);
 220        return retval;
 221}
 222
 223static int count_paths(const char **paths)
 224{
 225        int i = 0;
 226        while (*paths++)
 227                i++;
 228        return i;
 229}
 230
 231void diff_tree_setup_paths(const char **p)
 232{
 233        if (p) {
 234                int i;
 235
 236                paths = p;
 237                nr_paths = count_paths(paths);
 238                if (nr_paths == 0) {
 239                        pathlens = NULL;
 240                        return;
 241                }
 242                pathlens = xmalloc(nr_paths * sizeof(int));
 243                for (i=0; i<nr_paths; i++)
 244                        pathlens[i] = strlen(paths[i]);
 245        }
 246}