#include "cache.h" #include "commit.h" static int find_short_object_filename(int len, const char *name, unsigned char *sha1) { static char dirname[PATH_MAX]; char hex[40]; DIR *dir; int found; snprintf(dirname, sizeof(dirname), "%s/%.2s", get_object_directory(), name); dir = opendir(dirname); sprintf(hex, "%.2s", name); found = 0; if (dir) { struct dirent *de; while ((de = readdir(dir)) != NULL) { if (strlen(de->d_name) != 38) continue; if (memcmp(de->d_name, name + 2, len-2)) continue; memcpy(hex + 2, de->d_name, 38); if (++found > 1) break; } closedir(dir); } if (found == 1) return get_sha1_hex(hex, sha1) == 0; return 0; } static int match_sha(unsigned len, const unsigned char *a, const unsigned char *b) { do { if (*a != *b) return 0; a++; b++; len -= 2; } while (len > 1); if (len) if ((*a ^ *b) & 0xf0) return 0; return 1; } static int find_short_packed_object(int len, const unsigned char *match, unsigned char *sha1) { struct packed_git *p; prepare_packed_git(); for (p = packed_git; p; p = p->next) { unsigned num = num_packed_objects(p); unsigned first = 0, last = num; while (first < last) { unsigned mid = (first + last) / 2; unsigned char now[20]; int cmp; nth_packed_object_sha1(p, mid, now); cmp = memcmp(match, now, 20); if (!cmp) { first = mid; break; } if (cmp > 0) { first = mid+1; continue; } last = mid; } if (first < num) { unsigned char now[20], next[20]; nth_packed_object_sha1(p, first, now); if (match_sha(len, match, now)) { if (nth_packed_object_sha1(p, first+1, next) || !match_sha(len, match, next)) { memcpy(sha1, now, 20); return 1; } } } } return 0; } static int get_short_sha1(const char *name, unsigned char *sha1) { int i; char canonical[40]; unsigned char res[20]; memset(res, 0, 20); memset(canonical, 'x', 40); for (i = 0;;i++) { unsigned char c = name[i]; unsigned char val; if (!c || i > 40) break; if (c >= '0' && c <= '9') val = c - '0'; else if (c >= 'a' && c <= 'f') val = c - 'a' + 10; else if (c >= 'A' && c <='F') { val = c - 'A' + 10; c -= 'A' - 'a'; } else return -1; canonical[i] = c; if (!(i & 1)) val <<= 4; res[i >> 1] |= val; } if (i < 4) return -1; if (find_short_object_filename(i, canonical, sha1)) return 0; if (find_short_packed_object(i, res, sha1)) return 0; return -1; } static int get_sha1_file(const char *path, unsigned char *result) { char buffer[60]; int fd = open(path, O_RDONLY); int len; if (fd < 0) return -1; len = read(fd, buffer, sizeof(buffer)); close(fd); if (len < 40) return -1; return get_sha1_hex(buffer, result); } static int get_sha1_basic(const char *str, int len, unsigned char *sha1) { static const char *prefix[] = { "", "refs", "refs/tags", "refs/heads", NULL }; const char **p; if (len == 40 && !get_sha1_hex(str, sha1)) return 0; for (p = prefix; *p; p++) { char *pathname = git_path("%s/%.*s", *p, len, str); if (!get_sha1_file(pathname, sha1)) return 0; } return -1; } static int get_sha1_1(const char *name, int len, unsigned char *sha1); static int get_parent(const char *name, int len, unsigned char *result, int idx) { unsigned char sha1[20]; int ret = get_sha1_1(name, len, sha1); struct commit *commit; struct commit_list *p; if (ret) return ret; commit = lookup_commit_reference(sha1); if (!commit) return -1; if (parse_commit(commit)) return -1; if (!idx) { memcpy(result, commit->object.sha1, 20); return 0; } p = commit->parents; while (p) { if (!--idx) { memcpy(result, p->item->object.sha1, 20); return 0; } p = p->next; } return -1; } static int get_nth_ancestor(const char *name, int len, unsigned char *result, int generation) { unsigned char sha1[20]; int ret = get_sha1_1(name, len, sha1); if (ret) return ret; while (generation--) { struct commit *commit = lookup_commit_reference(sha1); if (!commit || parse_commit(commit) || !commit->parents) return -1; memcpy(sha1, commit->parents->item->object.sha1, 20); } memcpy(result, sha1, 20); return 0; } static int get_sha1_1(const char *name, int len, unsigned char *sha1) { int parent, ret; const char *cp; /* foo^[0-9] or foo^ (== foo^1); we do not do more than 9 parents. */ if (len > 2 && name[len-2] == '^' && name[len-1] >= '0' && name[len-1] <= '9') { parent = name[len-1] - '0'; len -= 2; } else if (len > 1 && name[len-1] == '^') { parent = 1; len--; } else parent = -1; if (parent >= 0) return get_parent(name, len, sha1, parent); /* "name~3" is "name^^^", * "name~12" is "name^^^^^^^^^^^^", and * "name~" and "name~0" are name -- not "name^0"! */ parent = 0; for (cp = name + len - 1; name <= cp; cp--) { int ch = *cp; if ('0' <= ch && ch <= '9') continue; if (ch != '~') parent = -1; break; } if (!parent && *cp == '~') { int len1 = cp - name; cp++; while (cp < name + len) parent = parent * 10 + *cp++ - '0'; return get_nth_ancestor(name, len1, sha1, parent); } ret = get_sha1_basic(name, len, sha1); if (!ret) return 0; return get_short_sha1(name, sha1); } /* * This is like "get_sha1_basic()", except it allows "sha1 expressions", * notably "xyz^" for "parent of xyz" */ int get_sha1(const char *name, unsigned char *sha1) { return get_sha1_1(name, strlen(name), sha1); }