diff --git a/cache.h b/cache.h index 958c96e14d..63823c3529 100644 --- a/cache.h +++ b/cache.h @@ -230,6 +230,7 @@ extern int get_sha1(const char *str, unsigned char *sha1); extern int get_sha1_hex(const char *hex, unsigned char *sha1); extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ extern int read_ref(const char *filename, unsigned char *sha1); +extern const char *resolve_ref(const char *path, unsigned char *sha1, int); /* General helper functions */ extern void usage(const char *err) NORETURN; diff --git a/refs.c b/refs.c index d4f3612487..6aa6aec82d 100644 --- a/refs.c +++ b/refs.c @@ -7,40 +7,75 @@ /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 -int read_ref(const char *filename, unsigned char *sha1) +const char *resolve_ref(const char *path, unsigned char *sha1, int reading) { - int depth = 0; - int ret = -1, fd; + int depth = MAXDEPTH, len; + char buffer[256]; - while ((fd = open(filename, O_RDONLY)) >= 0) { - char buffer[256]; - int len = read(fd, buffer, sizeof(buffer)-1); + for (;;) { + struct stat st; + char *buf; + int fd; - close(fd); - if (len < 0) - break; + if (--depth < 0) + return NULL; - buffer[len] = 0; - while (len && isspace(buffer[len-1])) - buffer[--len] = 0; + /* Special case: non-existing file. + * Not having the refs/heads/new-branch is OK + * if we are writing into it, so is .git/HEAD + * that points at refs/heads/master still to be + * born. It is NOT OK if we are resolving for + * reading. + */ + if (lstat(path, &st) < 0) { + if (reading || errno != ENOENT) + return NULL; + memset(sha1, 0, 20); + return path; + } - if (!strncmp(buffer, "ref:", 4)) { - char *buf; - if (depth > MAXDEPTH) - break; - depth++; - buf = buffer + 4; - len -= 4; - while (len && isspace(*buf)) - buf++, len--; - filename = git_path("%.*s", len, buf); - continue; + /* Follow "normalized" - ie "refs/.." symlinks by hand */ + if (S_ISLNK(st.st_mode)) { + len = readlink(path, buffer, sizeof(buffer)-1); + if (len >= 5 && !memcmp("refs/", buffer, 5)) { + path = git_path("%.*s", len, buffer); + continue; + } } - if (len >= 40) - ret = get_sha1_hex(buffer, sha1); - break; + + /* + * Anything else, just open it and try to use it as + * a ref + */ + fd = open(path, O_RDONLY); + if (fd < 0) + return NULL; + len = read(fd, buffer, sizeof(buffer)-1); + close(fd); + + /* + * Is it a symbolic ref? + */ + if (len < 4 || memcmp("ref:", buffer, 4)) + break; + buf = buffer + 4; + len -= 4; + while (len && isspace(*buf)) + buf++, len--; + while (len && isspace(buf[len-1])) + buf[--len] = 0; + path = git_path("%.*s", len, buf); } - return ret; + if (len < 40 || get_sha1_hex(buffer, sha1)) + return NULL; + return path; +} + +int read_ref(const char *filename, unsigned char *sha1) +{ + if (resolve_ref(filename, sha1, 1)) + return 0; + return -1; } static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1)) diff --git a/update-ref.c b/update-ref.c index 6919cead4b..4a1704c1a5 100644 --- a/update-ref.c +++ b/update-ref.c @@ -4,66 +4,6 @@ static const char git_update_ref_usage[] = "git-update-ref []"; -#define MAXDEPTH 5 - -static const char *resolve_ref(const char *path, unsigned char *sha1) -{ - int depth = MAXDEPTH, len; - char buffer[256]; - - for (;;) { - struct stat st; - char *buf; - int fd; - - if (--depth < 0) - return NULL; - - /* Special case: non-existing file */ - if (lstat(path, &st) < 0) { - if (errno != ENOENT) - return NULL; - memset(sha1, 0, 20); - return path; - } - - /* Follow "normalized" - ie "refs/.." symlinks by hand */ - if (S_ISLNK(st.st_mode)) { - len = readlink(path, buffer, sizeof(buffer)-1); - if (len >= 5 && !memcmp("refs/", buffer, 5)) { - path = git_path("%.*s", len, buffer); - continue; - } - } - - /* - * Anything else, just open it and try to use it as - * a ref - */ - fd = open(path, O_RDONLY); - if (fd < 0) - return NULL; - len = read(fd, buffer, sizeof(buffer)-1); - close(fd); - - /* - * Is it a symbolic ref? - */ - if (len < 4 || memcmp("ref:", buffer, 4)) - break; - buf = buffer + 4; - len -= 4; - while (len && isspace(*buf)) - buf++, len--; - while (len && isspace(buf[len-1])) - buf[--len] = 0; - path = git_path("%.*s", len, buf); - } - if (len < 40 || get_sha1_hex(buffer, sha1)) - return NULL; - return path; -} - static int re_verify(const char *path, unsigned char *oldsha1, unsigned char *currsha1) { char buf[40]; @@ -97,7 +37,7 @@ int main(int argc, char **argv) if (oldval && get_sha1(oldval, oldsha1) < 0) die("%s: not a valid old SHA1", oldval); - path = resolve_ref(git_path("%s", refname), currsha1); + path = resolve_ref(git_path("%s", refname), currsha1, !!oldval); if (!path) die("No such ref: %s", refname);