tree-walk.con commit Teach git diff-tree --stdin to diff trees (140b378)
   1#include "cache.h"
   2#include "tree-walk.h"
   3#include "tree.h"
   4
   5static const char *get_mode(const char *str, unsigned int *modep)
   6{
   7        unsigned char c;
   8        unsigned int mode = 0;
   9
  10        if (*str == ' ')
  11                return NULL;
  12
  13        while ((c = *str++) != ' ') {
  14                if (c < '0' || c > '7')
  15                        return NULL;
  16                mode = (mode << 3) + (c - '0');
  17        }
  18        *modep = mode;
  19        return str;
  20}
  21
  22static void decode_tree_entry(struct tree_desc *desc, const char *buf, unsigned long size)
  23{
  24        const char *path;
  25        unsigned int mode, len;
  26
  27        if (size < 24 || buf[size - 21])
  28                die("corrupt tree file");
  29
  30        path = get_mode(buf, &mode);
  31        if (!path || !*path)
  32                die("corrupt tree file");
  33        len = strlen(path) + 1;
  34
  35        /* Initialize the descriptor entry */
  36        desc->entry.path = path;
  37        desc->entry.mode = mode;
  38        desc->entry.sha1 = (const unsigned char *)(path + len);
  39}
  40
  41void init_tree_desc(struct tree_desc *desc, const void *buffer, unsigned long size)
  42{
  43        desc->buffer = buffer;
  44        desc->size = size;
  45        if (size)
  46                decode_tree_entry(desc, buffer, size);
  47}
  48
  49void *fill_tree_descriptor(struct tree_desc *desc, const unsigned char *sha1)
  50{
  51        unsigned long size = 0;
  52        void *buf = NULL;
  53
  54        if (sha1) {
  55                buf = read_object_with_reference(sha1, tree_type, &size, NULL);
  56                if (!buf)
  57                        die("unable to read tree %s", sha1_to_hex(sha1));
  58        }
  59        init_tree_desc(desc, buf, size);
  60        return buf;
  61}
  62
  63static int entry_compare(struct name_entry *a, struct name_entry *b)
  64{
  65        return df_name_compare(
  66                        a->path, tree_entry_len(a->path, a->sha1), a->mode,
  67                        b->path, tree_entry_len(b->path, b->sha1), b->mode);
  68}
  69
  70static void entry_clear(struct name_entry *a)
  71{
  72        memset(a, 0, sizeof(*a));
  73}
  74
  75static void entry_extract(struct tree_desc *t, struct name_entry *a)
  76{
  77        *a = t->entry;
  78}
  79
  80void update_tree_entry(struct tree_desc *desc)
  81{
  82        const void *buf = desc->buffer;
  83        const unsigned char *end = desc->entry.sha1 + 20;
  84        unsigned long size = desc->size;
  85        unsigned long len = end - (const unsigned char *)buf;
  86
  87        if (size < len)
  88                die("corrupt tree file");
  89        buf = end;
  90        size -= len;
  91        desc->buffer = buf;
  92        desc->size = size;
  93        if (size)
  94                decode_tree_entry(desc, buf, size);
  95}
  96
  97int tree_entry(struct tree_desc *desc, struct name_entry *entry)
  98{
  99        if (!desc->size)
 100                return 0;
 101
 102        *entry = desc->entry;
 103        update_tree_entry(desc);
 104        return 1;
 105}
 106
 107void setup_traverse_info(struct traverse_info *info, const char *base)
 108{
 109        int pathlen = strlen(base);
 110        static struct traverse_info dummy;
 111
 112        memset(info, 0, sizeof(*info));
 113        if (pathlen && base[pathlen-1] == '/')
 114                pathlen--;
 115        info->pathlen = pathlen ? pathlen + 1 : 0;
 116        info->name.path = base;
 117        info->name.sha1 = (void *)(base + pathlen + 1);
 118        if (pathlen)
 119                info->prev = &dummy;
 120}
 121
 122char *make_traverse_path(char *path, const struct traverse_info *info, const struct name_entry *n)
 123{
 124        int len = tree_entry_len(n->path, n->sha1);
 125        int pathlen = info->pathlen;
 126
 127        path[pathlen + len] = 0;
 128        for (;;) {
 129                memcpy(path + pathlen, n->path, len);
 130                if (!pathlen)
 131                        break;
 132                path[--pathlen] = '/';
 133                n = &info->name;
 134                len = tree_entry_len(n->path, n->sha1);
 135                info = info->prev;
 136                pathlen -= len;
 137        }
 138        return path;
 139}
 140
 141int traverse_trees(int n, struct tree_desc *t, struct traverse_info *info)
 142{
 143        int ret = 0;
 144        struct name_entry *entry = xmalloc(n*sizeof(*entry));
 145
 146        for (;;) {
 147                unsigned long mask = 0;
 148                unsigned long dirmask = 0;
 149                int i, last;
 150
 151                last = -1;
 152                for (i = 0; i < n; i++) {
 153                        if (!t[i].size)
 154                                continue;
 155                        entry_extract(t+i, entry+i);
 156                        if (last >= 0) {
 157                                int cmp = entry_compare(entry+i, entry+last);
 158
 159                                /*
 160                                 * Is the new name bigger than the old one?
 161                                 * Ignore it
 162                                 */
 163                                if (cmp > 0)
 164                                        continue;
 165                                /*
 166                                 * Is the new name smaller than the old one?
 167                                 * Ignore all old ones
 168                                 */
 169                                if (cmp < 0)
 170                                        mask = 0;
 171                        }
 172                        mask |= 1ul << i;
 173                        if (S_ISDIR(entry[i].mode))
 174                                dirmask |= 1ul << i;
 175                        last = i;
 176                }
 177                if (!mask)
 178                        break;
 179                dirmask &= mask;
 180
 181                /*
 182                 * Clear all the unused name-entries.
 183                 */
 184                for (i = 0; i < n; i++) {
 185                        if (mask & (1ul << i))
 186                                continue;
 187                        entry_clear(entry + i);
 188                }
 189                ret = info->fn(n, mask, dirmask, entry, info);
 190                if (ret < 0)
 191                        break;
 192                if (ret)
 193                        mask &= ret;
 194                ret = 0;
 195                for (i = 0; i < n; i++) {
 196                        if (mask & (1ul << i))
 197                                update_tree_entry(t + i);
 198                }
 199        }
 200        free(entry);
 201        return ret;
 202}
 203
 204static int find_tree_entry(struct tree_desc *t, const char *name, unsigned char *result, unsigned *mode)
 205{
 206        int namelen = strlen(name);
 207        while (t->size) {
 208                const char *entry;
 209                const unsigned char *sha1;
 210                int entrylen, cmp;
 211
 212                sha1 = tree_entry_extract(t, &entry, mode);
 213                update_tree_entry(t);
 214                entrylen = tree_entry_len(entry, sha1);
 215                if (entrylen > namelen)
 216                        continue;
 217                cmp = memcmp(name, entry, entrylen);
 218                if (cmp > 0)
 219                        continue;
 220                if (cmp < 0)
 221                        break;
 222                if (entrylen == namelen) {
 223                        hashcpy(result, sha1);
 224                        return 0;
 225                }
 226                if (name[entrylen] != '/')
 227                        continue;
 228                if (!S_ISDIR(*mode))
 229                        break;
 230                if (++entrylen == namelen) {
 231                        hashcpy(result, sha1);
 232                        return 0;
 233                }
 234                return get_tree_entry(sha1, name + entrylen, result, mode);
 235        }
 236        return -1;
 237}
 238
 239int get_tree_entry(const unsigned char *tree_sha1, const char *name, unsigned char *sha1, unsigned *mode)
 240{
 241        int retval;
 242        void *tree;
 243        unsigned long size;
 244        struct tree_desc t;
 245        unsigned char root[20];
 246
 247        tree = read_object_with_reference(tree_sha1, tree_type, &size, root);
 248        if (!tree)
 249                return -1;
 250
 251        if (name[0] == '\0') {
 252                hashcpy(sha1, root);
 253                return 0;
 254        }
 255
 256        init_tree_desc(&t, tree, size);
 257        retval = find_tree_entry(&t, name, sha1, mode);
 258        free(tree);
 259        return retval;
 260}