sha1_name.con commit [PATCH] Add a new extended SHA1 syntax <name>~<num> (4f7599a)
   1#include "cache.h"
   2#include "commit.h"
   3
   4static int find_short_object_filename(int len, const char *name, unsigned char *sha1)
   5{
   6        static char dirname[PATH_MAX];
   7        char hex[40];
   8        DIR *dir;
   9        int found;
  10
  11        snprintf(dirname, sizeof(dirname), "%s/%.2s", get_object_directory(), name);
  12        dir = opendir(dirname);
  13        sprintf(hex, "%.2s", name);
  14        found = 0;
  15        if (dir) {
  16                struct dirent *de;
  17                while ((de = readdir(dir)) != NULL) {
  18                        if (strlen(de->d_name) != 38)
  19                                continue;
  20                        if (memcmp(de->d_name, name + 2, len-2))
  21                                continue;
  22                        memcpy(hex + 2, de->d_name, 38);
  23                        if (++found > 1)
  24                                break;
  25                }
  26                closedir(dir);
  27        }
  28        if (found == 1)
  29                return get_sha1_hex(hex, sha1) == 0;
  30        return 0;
  31}
  32
  33static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b)
  34{
  35        do {
  36                if (*a != *b)
  37                        return 0;
  38                a++;
  39                b++;
  40                len -= 2;
  41        } while (len > 1);
  42        if (len)
  43                if ((*a ^ *b) & 0xf0)
  44                        return 0;
  45        return 1;
  46}
  47
  48static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1)
  49{
  50        struct packed_git *p;
  51
  52        prepare_packed_git();
  53        for (p = packed_git; p; p = p->next) {
  54                unsigned num = num_packed_objects(p);
  55                unsigned first = 0, last = num;
  56                while (first < last) {
  57                        unsigned mid = (first + last) / 2;
  58                        unsigned char now[20];
  59                        int cmp;
  60
  61                        nth_packed_object_sha1(p, mid, now);
  62                        cmp = memcmp(match, now, 20);
  63                        if (!cmp) {
  64                                first = mid;
  65                                break;
  66                        }
  67                        if (cmp > 0) {
  68                                first = mid+1;
  69                                continue;
  70                        }
  71                        last = mid;
  72                }
  73                if (first < num) {
  74                        unsigned char now[20], next[20];
  75                        nth_packed_object_sha1(p, first, now);
  76                        if (match_sha(len, match, now)) {
  77                                if (nth_packed_object_sha1(p, first+1, next) || !match_sha(len, match, next)) {
  78                                        memcpy(sha1, now, 20);
  79                                        return 1;
  80                                }
  81                        }
  82                }
  83        }
  84        return 0;
  85}
  86
  87static int get_short_sha1(const char *name, unsigned char *sha1)
  88{
  89        int i;
  90        char canonical[40];
  91        unsigned char res[20];
  92
  93        memset(res, 0, 20);
  94        memset(canonical, 'x', 40);
  95        for (i = 0;;i++) {
  96                unsigned char c = name[i];
  97                unsigned char val;
  98                if (!c || i > 40)
  99                        break;
 100                if (c >= '0' && c <= '9')
 101                        val = c - '0';
 102                else if (c >= 'a' && c <= 'f')
 103                        val = c - 'a' + 10;
 104                else if (c >= 'A' && c <='F') {
 105                        val = c - 'A' + 10;
 106                        c -= 'A' - 'a';
 107                }
 108                else
 109                        return -1;
 110                canonical[i] = c;
 111                if (!(i & 1))
 112                        val <<= 4;
 113                res[i >> 1] |= val;
 114        }
 115        if (i < 4)
 116                return -1;
 117        if (find_short_object_filename(i, canonical, sha1))
 118                return 0;
 119        if (find_short_packed_object(i, res, sha1))
 120                return 0;
 121        return -1;
 122}
 123
 124static int get_sha1_file(const char *path, unsigned char *result)
 125{
 126        char buffer[60];
 127        int fd = open(path, O_RDONLY);
 128        int len;
 129
 130        if (fd < 0)
 131                return -1;
 132        len = read(fd, buffer, sizeof(buffer));
 133        close(fd);
 134        if (len < 40)
 135                return -1;
 136        return get_sha1_hex(buffer, result);
 137}
 138
 139static int get_sha1_basic(const char *str, int len, unsigned char *sha1)
 140{
 141        static const char *prefix[] = {
 142                "",
 143                "refs",
 144                "refs/tags",
 145                "refs/heads",
 146                NULL
 147        };
 148        const char **p;
 149
 150        if (len == 40 && !get_sha1_hex(str, sha1))
 151                return 0;
 152
 153        for (p = prefix; *p; p++) {
 154                char *pathname = git_path("%s/%.*s", *p, len, str);
 155                if (!get_sha1_file(pathname, sha1))
 156                        return 0;
 157        }
 158
 159        return -1;
 160}
 161
 162static int get_sha1_1(const char *name, int len, unsigned char *sha1);
 163
 164static int get_parent(const char *name, int len,
 165                      unsigned char *result, int idx)
 166{
 167        unsigned char sha1[20];
 168        int ret = get_sha1_1(name, len, sha1);
 169        struct commit *commit;
 170        struct commit_list *p;
 171
 172        if (ret)
 173                return ret;
 174        commit = lookup_commit_reference(sha1);
 175        if (!commit)
 176                return -1;
 177        if (parse_commit(commit))
 178                return -1;
 179        if (!idx) {
 180                memcpy(result, commit->object.sha1, 20);
 181                return 0;
 182        }
 183        p = commit->parents;
 184        while (p) {
 185                if (!--idx) {
 186                        memcpy(result, p->item->object.sha1, 20);
 187                        return 0;
 188                }
 189                p = p->next;
 190        }
 191        return -1;
 192}
 193
 194static int get_nth_ancestor(const char *name, int len,
 195                            unsigned char *result, int generation)
 196{
 197        unsigned char sha1[20];
 198        int ret = get_sha1_1(name, len, sha1);
 199        if (ret)
 200                return ret;
 201
 202        while (generation--) {
 203                struct commit *commit = lookup_commit_reference(sha1);
 204
 205                if (!commit || parse_commit(commit) || !commit->parents)
 206                        return -1;
 207                memcpy(sha1, commit->parents->item->object.sha1, 20);
 208        }
 209        memcpy(result, sha1, 20);
 210        return 0;
 211}
 212
 213static int get_sha1_1(const char *name, int len, unsigned char *sha1)
 214{
 215        int parent, ret;
 216        const char *cp;
 217
 218        /* foo^[0-9] or foo^ (== foo^1); we do not do more than 9 parents. */
 219        if (len > 2 && name[len-2] == '^' &&
 220            name[len-1] >= '0' && name[len-1] <= '9') {
 221                parent = name[len-1] - '0';
 222                len -= 2;
 223        }
 224        else if (len > 1 && name[len-1] == '^') {
 225                parent = 1;
 226                len--;
 227        } else
 228                parent = -1;
 229
 230        if (parent >= 0)
 231                return get_parent(name, len, sha1, parent);
 232
 233        /* "name~3" is "name^^^",
 234         * "name~12" is "name^^^^^^^^^^^^", and
 235         * "name~" and "name~0" are name -- not "name^0"!
 236         */
 237        parent = 0;
 238        for (cp = name + len - 1; name <= cp; cp--) {
 239                int ch = *cp;
 240                if ('0' <= ch && ch <= '9')
 241                        continue;
 242                if (ch != '~')
 243                        parent = -1;
 244                break;
 245        }
 246        if (!parent && *cp == '~') {
 247                int len1 = cp - name;
 248                cp++;
 249                while (cp < name + len)
 250                        parent = parent * 10 + *cp++ - '0';
 251                return get_nth_ancestor(name, len1, sha1, parent);
 252        }
 253
 254        ret = get_sha1_basic(name, len, sha1);
 255        if (!ret)
 256                return 0;
 257        return get_short_sha1(name, sha1);
 258}
 259
 260/*
 261 * This is like "get_sha1_basic()", except it allows "sha1 expressions",
 262 * notably "xyz^" for "parent of xyz"
 263 */
 264int get_sha1(const char *name, unsigned char *sha1)
 265{
 266        return get_sha1_1(name, strlen(name), sha1);
 267}