From e1e22e37f47e3f4d741d28920e1d27e3775c31ad Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 16:37:32 -0700 Subject: Start handling references internally as a sorted in-memory list This also adds some very rudimentary support for the notion of packed refs. HOWEVER! At this point it isn't used to actually look up a ref yet, only for listing them (ie "for_each_ref()" and friends see the packed refs, but none of the other single-ref lookup routines). Note how we keep two separate lists: one for the loose refs, and one for the packed refs we read. That's so that we can easily keep the two apart, and read only one set or the other (and still always make sure that the loose refs take precedence). [ From this, it's not actually obvious why we'd keep the two separate lists, but it's important to have the packed refs on their own list later on, when I add support for looking up a single loose one. For that case, we will want to read _just_ the packed refs in case the single-ref lookup fails, yet we may end up needing the other list at some point in the future, so keeping them separated is important ] Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- refs.c | 226 +++++++++++++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 173 insertions(+), 53 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 5e653141ce..5f80a68922 100644 --- a/refs.c +++ b/refs.c @@ -3,6 +3,145 @@ #include +struct ref_list { + struct ref_list *next; + unsigned char sha1[20]; + char name[FLEX_ARRAY]; +}; + +static const char *parse_ref_line(char *line, unsigned char *sha1) +{ + /* + * 42: the answer to everything. + * + * In this case, it happens to be the answer to + * 40 (length of sha1 hex representation) + * +1 (space in between hex and name) + * +1 (newline at the end of the line) + */ + int len = strlen(line) - 42; + + if (len <= 0) + return NULL; + if (get_sha1_hex(line, sha1) < 0) + return NULL; + if (!isspace(line[40])) + return NULL; + line += 41; + if (line[len] != '\n') + return NULL; + line[len] = 0; + return line; +} + +static struct ref_list *add_ref(const char *name, const unsigned char *sha1, struct ref_list *list) +{ + int len; + struct ref_list **p = &list, *entry; + + /* Find the place to insert the ref into.. */ + while ((entry = *p) != NULL) { + int cmp = strcmp(entry->name, name); + if (cmp > 0) + break; + + /* Same as existing entry? */ + if (!cmp) + return list; + p = &entry->next; + } + + /* Allocate it and add it in.. */ + len = strlen(name) + 1; + entry = xmalloc(sizeof(struct ref_list) + len); + hashcpy(entry->sha1, sha1); + memcpy(entry->name, name, len); + entry->next = *p; + *p = entry; + return list; +} + +static struct ref_list *get_packed_refs(void) +{ + static int did_refs = 0; + static struct ref_list *refs = NULL; + + if (!did_refs) { + FILE *f = fopen(git_path("packed-refs"), "r"); + if (f) { + struct ref_list *list = NULL; + char refline[PATH_MAX]; + while (fgets(refline, sizeof(refline), f)) { + unsigned char sha1[20]; + const char *name = parse_ref_line(refline, sha1); + if (!name) + continue; + list = add_ref(name, sha1, list); + } + fclose(f); + refs = list; + } + did_refs = 1; + } + return refs; +} + +static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) +{ + DIR *dir = opendir(git_path("%s", base)); + + if (dir) { + struct dirent *de; + int baselen = strlen(base); + char *path = xmalloc(baselen + 257); + + memcpy(path, base, baselen); + if (baselen && base[baselen-1] != '/') + path[baselen++] = '/'; + + while ((de = readdir(dir)) != NULL) { + unsigned char sha1[20]; + struct stat st; + int namelen; + + if (de->d_name[0] == '.') + continue; + namelen = strlen(de->d_name); + if (namelen > 255) + continue; + if (has_extension(de->d_name, ".lock")) + continue; + memcpy(path + baselen, de->d_name, namelen+1); + if (stat(git_path("%s", path), &st) < 0) + continue; + if (S_ISDIR(st.st_mode)) { + list = get_ref_dir(path, list); + continue; + } + if (read_ref(git_path("%s", path), sha1) < 0) { + error("%s points nowhere!", path); + continue; + } + list = add_ref(path, sha1, list); + } + free(path); + closedir(dir); + } + return list; +} + +static struct ref_list *get_loose_refs(void) +{ + static int did_refs = 0; + static struct ref_list *refs = NULL; + + if (!did_refs) { + refs = get_ref_dir("refs", NULL); + did_refs = 1; + } + return refs; +} + /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 @@ -121,60 +260,41 @@ int read_ref(const char *filename, unsigned char *sha1) static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim) { - int retval = 0; - DIR *dir = opendir(git_path("%s", base)); - - if (dir) { - struct dirent *de; - int baselen = strlen(base); - char *path = xmalloc(baselen + 257); - - if (!strncmp(base, "./", 2)) { - base += 2; - baselen -= 2; + int retval; + struct ref_list *packed = get_packed_refs(); + struct ref_list *loose = get_loose_refs(); + + while (packed && loose) { + struct ref_list *entry; + int cmp = strcmp(packed->name, loose->name); + if (!cmp) { + packed = packed->next; + continue; } - memcpy(path, base, baselen); - if (baselen && base[baselen-1] != '/') - path[baselen++] = '/'; - - while ((de = readdir(dir)) != NULL) { - unsigned char sha1[20]; - struct stat st; - int namelen; + if (cmp > 0) { + entry = loose; + loose = loose->next; + } else { + entry = packed; + packed = packed->next; + } + if (strncmp(base, entry->name, trim)) + continue; + retval = fn(entry->name + trim, entry->sha1); + if (retval) + return retval; + } - if (de->d_name[0] == '.') - continue; - namelen = strlen(de->d_name); - if (namelen > 255) - continue; - if (has_extension(de->d_name, ".lock")) - continue; - memcpy(path + baselen, de->d_name, namelen+1); - if (stat(git_path("%s", path), &st) < 0) - continue; - if (S_ISDIR(st.st_mode)) { - retval = do_for_each_ref(path, fn, trim); - if (retval) - break; - continue; - } - if (read_ref(git_path("%s", path), sha1) < 0) { - error("%s points nowhere!", path); - continue; - } - if (!has_sha1_file(sha1)) { - error("%s does not point to a valid " - "commit object!", path); - continue; - } - retval = fn(path + trim, sha1); + packed = packed ? packed : loose; + while (packed) { + if (!strncmp(base, packed->name, trim)) { + retval = fn(packed->name + trim, packed->sha1); if (retval) - break; + return retval; } - free(path); - closedir(dir); + packed = packed->next; } - return retval; + return 0; } int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) @@ -187,22 +307,22 @@ int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs", fn, 0); + return do_for_each_ref("refs/", fn, 0); } int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/tags", fn, 10); + return do_for_each_ref("refs/tags/", fn, 10); } int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/heads", fn, 11); + return do_for_each_ref("refs/heads/", fn, 11); } int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)) { - return do_for_each_ref("refs/remotes", fn, 13); + return do_for_each_ref("refs/remotes/", fn, 13); } int get_ref_sha1(const char *ref, unsigned char *sha1) -- cgit v1.3 From b37a562a1097af7403c649a5f903a93acaf279e8 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 20:10:15 -0700 Subject: Add support for negative refs You can remove a ref that is packed two different ways: either simply repack all the refs without that one, or create a loose ref that has the magic all-zero SHA1. This also adds back the test that a ref actually has the object it points to. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- refs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 5f80a68922..72e22834fa 100644 --- a/refs.c +++ b/refs.c @@ -280,6 +280,12 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u } if (strncmp(base, entry->name, trim)) continue; + if (is_null_sha1(entry->sha1)) + continue; + if (!has_sha1_file(entry->sha1)) { + error("%s does not point to a valid object!", entry->name); + continue; + } retval = fn(entry->name + trim, entry->sha1); if (retval) return retval; -- cgit v1.3 From ed378ec7e85fd2c5cfcc7bd64b454236357fdd97 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Mon, 11 Sep 2006 20:17:35 -0700 Subject: Make ref resolution saner The old code used to totally mix up the notion of a ref-name and the path that that ref was associated with. That was not only horribly ugly (a number of users got the path, and then wanted to try to turn it back into a ref-name again), but it fundamnetally doesn't work at all once we do any setup where a ref doesn't have a 1:1 relationship with a particular pathname. This fixes things up so that we use the ref-name throughout, and only turn it into a pathname once we actually look it up in the filesystem. That makes a lot of things much clearer and more straightforward. Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- builtin-fmt-merge-msg.c | 7 ++--- builtin-init-db.c | 4 +-- builtin-show-branch.c | 46 ++++++++++++++------------------- builtin-symbolic-ref.c | 14 ++++------ cache.h | 4 +-- refs.c | 69 ++++++++++++++++++++++++++----------------------- sha1_name.c | 14 +++++----- 7 files changed, 74 insertions(+), 84 deletions(-) (limited to 'refs.c') diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index c407c033e7..b93c17c2f0 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -249,7 +249,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) FILE *in = stdin; const char *sep = ""; unsigned char head_sha1[20]; - const char *head, *current_branch; + const char *current_branch; git_config(fmt_merge_msg_config); @@ -277,10 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) usage(fmt_merge_msg_usage); /* get current branch */ - head = xstrdup(git_path("HEAD")); - current_branch = resolve_ref(head, head_sha1, 1); - current_branch += strlen(head) - 4; - free((char *)head); + current_branch = resolve_ref("HEAD", head_sha1, 1); if (!strncmp(current_branch, "refs/heads/", 11)) current_branch += 11; diff --git a/builtin-init-db.c b/builtin-init-db.c index 5085018e46..23b7714f89 100644 --- a/builtin-init-db.c +++ b/builtin-init-db.c @@ -218,8 +218,8 @@ static void create_default_files(const char *git_dir, const char *template_path) * branch, if it does not exist yet. */ strcpy(path + len, "HEAD"); - if (read_ref(path, sha1) < 0) { - if (create_symref(path, "refs/heads/master") < 0) + if (read_ref("HEAD", sha1) < 0) { + if (create_symref("HEAD", "refs/heads/master") < 0) exit(1); } diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 578c9fafd0..4d8db0c102 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -437,21 +437,13 @@ static void snarf_refs(int head, int tag) } } -static int rev_is_head(char *head_path, int headlen, char *name, +static int rev_is_head(char *head, int headlen, char *name, unsigned char *head_sha1, unsigned char *sha1) { - int namelen; - if ((!head_path[0]) || + if ((!head[0]) || (head_sha1 && sha1 && hashcmp(head_sha1, sha1))) return 0; - namelen = strlen(name); - if ((headlen < namelen) || - memcmp(head_path + headlen - namelen, name, namelen)) - return 0; - if (headlen == namelen || - head_path[headlen - namelen - 1] == '/') - return 1; - return 0; + return !strcmp(head, name); } static int show_merge_base(struct commit_list *seen, int num_rev) @@ -559,9 +551,9 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) int all_heads = 0, all_tags = 0; int all_mask, all_revs; int lifo = 1; - char head_path[128]; - const char *head_path_p; - int head_path_len; + char head[128]; + const char *head_p; + int head_len; unsigned char head_sha1[20]; int merge_base = 0; int independent = 0; @@ -638,31 +630,31 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) ac--; av++; } - head_path_p = resolve_ref(git_path("HEAD"), head_sha1, 1); - if (head_path_p) { - head_path_len = strlen(head_path_p); - memcpy(head_path, head_path_p, head_path_len + 1); + head_p = resolve_ref("HEAD", head_sha1, 1); + if (head_p) { + head_len = strlen(head_p); + memcpy(head, head_p, head_len + 1); } else { - head_path_len = 0; - head_path[0] = 0; + head_len = 0; + head[0] = 0; } - if (with_current_branch && head_path_p) { + if (with_current_branch && head_p) { int has_head = 0; for (i = 0; !has_head && i < ref_name_cnt; i++) { /* We are only interested in adding the branch * HEAD points at. */ - if (rev_is_head(head_path, - head_path_len, + if (rev_is_head(head, + head_len, ref_name[i], head_sha1, NULL)) has_head++; } if (!has_head) { - int pfxlen = strlen(git_path("refs/heads/")); - append_one_rev(head_path + pfxlen); + int pfxlen = strlen("refs/heads/"); + append_one_rev(head + pfxlen); } } @@ -713,8 +705,8 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) if (1 < num_rev || extra < 0) { for (i = 0; i < num_rev; i++) { int j; - int is_head = rev_is_head(head_path, - head_path_len, + int is_head = rev_is_head(head, + head_len, ref_name[i], head_sha1, rev[i]->object.sha1); diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 1d3a5e229a..6f18db870a 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -7,15 +7,11 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *git_HEAD = xstrdup(git_path("%s", HEAD)); - const char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0); - if (git_refs_heads_master) { - /* we want to strip the .git/ part */ - int pfxlen = strlen(git_HEAD) - strlen(HEAD); - puts(git_refs_heads_master + pfxlen); - } - else + const char *refs_heads_master = resolve_ref("HEAD", sha1, 0); + + if (!refs_heads_master) die("No such ref: %s", HEAD); + puts(refs_heads_master); } int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) @@ -26,7 +22,7 @@ int cmd_symbolic_ref(int argc, const char **argv, const char *prefix) check_symref(argv[1]); break; case 3: - create_symref(xstrdup(git_path("%s", argv[1])), argv[2]); + create_symref(argv[1], argv[2]); break; default: usage(git_symbolic_ref_usage); diff --git a/cache.h b/cache.h index 57db7c9b20..282eed6f07 100644 --- a/cache.h +++ b/cache.h @@ -287,8 +287,8 @@ 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); -extern int create_symref(const char *git_HEAD, const char *refs_heads_master); -extern int validate_symref(const char *git_HEAD); +extern int create_symref(const char *ref, const char *refs_heads_master); +extern int validate_symref(const char *ref); extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2); extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2); diff --git a/refs.c b/refs.c index 72e22834fa..50c25d3d28 100644 --- a/refs.c +++ b/refs.c @@ -93,11 +93,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) if (dir) { struct dirent *de; int baselen = strlen(base); - char *path = xmalloc(baselen + 257); + char *ref = xmalloc(baselen + 257); - memcpy(path, base, baselen); + memcpy(ref, base, baselen); if (baselen && base[baselen-1] != '/') - path[baselen++] = '/'; + ref[baselen++] = '/'; while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; @@ -111,20 +111,20 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) continue; if (has_extension(de->d_name, ".lock")) continue; - memcpy(path + baselen, de->d_name, namelen+1); - if (stat(git_path("%s", path), &st) < 0) + memcpy(ref + baselen, de->d_name, namelen+1); + if (stat(git_path("%s", ref), &st) < 0) continue; if (S_ISDIR(st.st_mode)) { - list = get_ref_dir(path, list); + list = get_ref_dir(ref, list); continue; } - if (read_ref(git_path("%s", path), sha1) < 0) { - error("%s points nowhere!", path); + if (read_ref(ref, sha1) < 0) { + error("%s points nowhere!", ref); continue; } - list = add_ref(path, sha1, list); + list = add_ref(ref, sha1, list); } - free(path); + free(ref); closedir(dir); } return list; @@ -145,12 +145,14 @@ static struct ref_list *get_loose_refs(void) /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 -const char *resolve_ref(const char *path, unsigned char *sha1, int reading) +const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) { int depth = MAXDEPTH, len; char buffer[256]; + static char ref_buffer[256]; for (;;) { + const char *path = git_path("%s", ref); struct stat st; char *buf; int fd; @@ -169,14 +171,16 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) if (reading || errno != ENOENT) return NULL; hashclr(sha1); - return path; + return ref; } /* 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); + buffer[len] = 0; + strcpy(ref_buffer, buffer); + ref = ref_buffer; continue; } } @@ -201,19 +205,22 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) while (len && isspace(*buf)) buf++, len--; while (len && isspace(buf[len-1])) - buf[--len] = 0; - path = git_path("%.*s", len, buf); + len--; + buf[len] = 0; + memcpy(ref_buffer, buf, len + 1); + ref = ref_buffer; } if (len < 40 || get_sha1_hex(buffer, sha1)) return NULL; - return path; + return ref; } -int create_symref(const char *git_HEAD, const char *refs_heads_master) +int create_symref(const char *ref_target, const char *refs_heads_master) { const char *lockpath; char ref[1000]; int fd, len, written; + const char *git_HEAD = git_path("%s", ref_target); #ifndef NO_SYMLINK_HEAD if (prefer_symlink_refs) { @@ -251,9 +258,9 @@ int create_symref(const char *git_HEAD, const char *refs_heads_master) return 0; } -int read_ref(const char *filename, unsigned char *sha1) +int read_ref(const char *ref, unsigned char *sha1) { - if (resolve_ref(filename, sha1, 1)) + if (resolve_ref(ref, sha1, 1)) return 0; return -1; } @@ -306,7 +313,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) { unsigned char sha1[20]; - if (!read_ref(git_path("HEAD"), sha1)) + if (!read_ref("HEAD", sha1)) return fn("HEAD", sha1); return 0; } @@ -335,7 +342,7 @@ int get_ref_sha1(const char *ref, unsigned char *sha1) { if (check_ref_format(ref)) return -1; - return read_ref(git_path("refs/%s", ref), sha1); + return read_ref(mkpath("refs/%s", ref), sha1); } /* @@ -416,31 +423,30 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } -static struct ref_lock *lock_ref_sha1_basic(const char *path, +static struct ref_lock *lock_ref_sha1_basic(const char *ref, int plen, const unsigned char *old_sha1, int mustexist) { - const char *orig_path = path; + const char *orig_ref = ref; struct ref_lock *lock; struct stat st; lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - plen = strlen(path) - plen; - path = resolve_ref(path, lock->old_sha1, mustexist); - if (!path) { + ref = resolve_ref(ref, lock->old_sha1, mustexist); + if (!ref) { int last_errno = errno; error("unable to resolve reference %s: %s", - orig_path, strerror(errno)); + orig_ref, strerror(errno)); unlock_ref(lock); errno = last_errno; return NULL; } lock->lk = xcalloc(1, sizeof(struct lock_file)); - lock->ref_file = xstrdup(path); - lock->log_file = xstrdup(git_path("logs/%s", lock->ref_file + plen)); + lock->ref_file = xstrdup(git_path("%s", ref)); + lock->log_file = xstrdup(git_path("logs/%s", ref)); lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; if (safe_create_leading_directories(lock->ref_file)) @@ -455,15 +461,14 @@ struct ref_lock *lock_ref_sha1(const char *ref, { if (check_ref_format(ref)) return NULL; - return lock_ref_sha1_basic(git_path("refs/%s", ref), + return lock_ref_sha1_basic(mkpath("refs/%s", ref), 5 + strlen(ref), old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist) { - return lock_ref_sha1_basic(git_path("%s", ref), - strlen(ref), old_sha1, mustexist); + return lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist); } void unlock_ref(struct ref_lock *lock) diff --git a/sha1_name.c b/sha1_name.c index 1fbc443805..b4975289d5 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -247,8 +247,8 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) NULL }; static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; - const char **p, *pathname; - char *real_path = NULL; + const char **p, *ref; + char *real_ref = NULL; int refs_found = 0, am; unsigned long at_time = (unsigned long)-1; unsigned char *this_result; @@ -276,10 +276,10 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) for (p = fmt; *p; p++) { this_result = refs_found ? sha1_from_ref : sha1; - pathname = resolve_ref(git_path(*p, len, str), this_result, 1); - if (pathname) { + ref = resolve_ref(mkpath(*p, len, str), this_result, 1); + if (ref) { if (!refs_found++) - real_path = xstrdup(pathname); + real_ref = xstrdup(ref); if (!warn_ambiguous_refs) break; } @@ -293,12 +293,12 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (at_time != (unsigned long)-1) { read_ref_at( - real_path + strlen(git_path(".")) - 1, + real_ref, at_time, sha1); } - free(real_path); + free(real_ref); return 0; } -- cgit v1.3 From 434cd0cd30d17bc703397a76480fdd129cecc064 Mon Sep 17 00:00:00 2001 From: Linus Torvalds Date: Thu, 14 Sep 2006 10:14:47 -0700 Subject: Enable the packed refs file format This actually "turns on" the packed ref file format, now that the infrastructure to do so sanely exists (ie notably the change to make the reference reading logic take refnames rather than pathnames to the loose objects that no longer necessarily even exist). In particular, when the ref lookup hits a refname that has no loose file associated with it, it falls back on the packed-ref information. Also, the ref-locking code, while still using a loose file for the locking itself (and _creating_ a loose file for the new ref) no longer requires that the old ref be in such an unpacked state. Finally, this does a minimal hack to git-checkout.sh to rather than check the ref-file directly, do a "git-rev-parse" on the "heads/$refname". That's not really wonderful - we should rather really have a special routine to verify the names as proper branch head names, but it is a workable solution for now. With this, I can literally do something like git pack-refs find .git/refs -type f -print0 | xargs -0 rm -f -- and the end result is a largely working repository (ie I've done two commits - which creates _one_ unpacked ref file - done things like run "gitk" and "git log" etc, and it all looks ok). There are probably things missing, but I'm hoping that the missing things are now of the "small and obvious" kind, and that somebody else might want to start looking at this too. Hint hint ;) Signed-off-by: Linus Torvalds Signed-off-by: Junio C Hamano --- git-checkout.sh | 4 ++-- refs.c | 41 ++++++++++++++++++++++------------------- refs.h | 2 +- 3 files changed, 25 insertions(+), 22 deletions(-) (limited to 'refs.c') diff --git a/git-checkout.sh b/git-checkout.sh index 580a9e8a23..c60e029b6d 100755 --- a/git-checkout.sh +++ b/git-checkout.sh @@ -22,7 +22,7 @@ while [ "$#" != "0" ]; do shift [ -z "$newbranch" ] && die "git checkout: -b needs a branch name" - [ -e "$GIT_DIR/refs/heads/$newbranch" ] && + git-rev-parse --symbolic "heads/$newbranch" >&/dev/null && die "git checkout: branch $newbranch already exists" git-check-ref-format "heads/$newbranch" || die "git checkout: we do not like '$newbranch' as a branch name." @@ -51,7 +51,7 @@ while [ "$#" != "0" ]; do fi new="$rev" new_name="$arg^0" - if [ -f "$GIT_DIR/refs/heads/$arg" ]; then + if git-rev-parse "heads/$arg^0" >&/dev/null; then branch="$arg" fi elif rev=$(git-rev-parse --verify "$arg^{tree}" 2>/dev/null) diff --git a/refs.c b/refs.c index 50c25d3d28..134c0fc015 100644 --- a/refs.c +++ b/refs.c @@ -28,6 +28,8 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) if (!isspace(line[40])) return NULL; line += 41; + if (isspace(*line)) + return NULL; if (line[len] != '\n') return NULL; line[len] = 0; @@ -168,6 +170,14 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) * reading. */ if (lstat(path, &st) < 0) { + struct ref_list *list = get_packed_refs(); + while (list) { + if (!strcmp(ref, list->name)) { + hashcpy(sha1, list->sha1); + return ref; + } + list = list->next; + } if (reading || errno != ENOENT) return NULL; hashclr(sha1); @@ -400,22 +410,13 @@ int check_ref_format(const char *ref) static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { - char buf[40]; - int nr, fd = open(lock->ref_file, O_RDONLY); - if (fd < 0 && (mustexist || errno != ENOENT)) { - error("Can't verify ref %s", lock->ref_file); - unlock_ref(lock); - return NULL; - } - nr = read(fd, buf, 40); - close(fd); - if (nr != 40 || get_sha1_hex(buf, lock->old_sha1) < 0) { - error("Can't verify ref %s", lock->ref_file); + if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist)) { + error("Can't verify ref %s", lock->ref_name); unlock_ref(lock); return NULL; } if (hashcmp(lock->old_sha1, old_sha1)) { - error("Ref %s is at %s but expected %s", lock->ref_file, + error("Ref %s is at %s but expected %s", lock->ref_name, sha1_to_hex(lock->old_sha1), sha1_to_hex(old_sha1)); unlock_ref(lock); return NULL; @@ -427,6 +428,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, int plen, const unsigned char *old_sha1, int mustexist) { + char *ref_file; const char *orig_ref = ref; struct ref_lock *lock; struct stat st; @@ -445,13 +447,14 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, } lock->lk = xcalloc(1, sizeof(struct lock_file)); - lock->ref_file = xstrdup(git_path("%s", ref)); + lock->ref_name = xstrdup(ref); lock->log_file = xstrdup(git_path("logs/%s", ref)); - lock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT; + ref_file = git_path(ref); + lock->force_write = lstat(ref_file, &st) && errno == ENOENT; - if (safe_create_leading_directories(lock->ref_file)) - die("unable to create directory for %s", lock->ref_file); - lock->lock_fd = hold_lock_file_for_update(lock->lk, lock->ref_file, 1); + if (safe_create_leading_directories(ref_file)) + die("unable to create directory for %s", ref_file); + lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; } @@ -479,7 +482,7 @@ void unlock_ref(struct ref_lock *lock) if (lock->lk) rollback_lock_file(lock->lk); } - free(lock->ref_file); + free(lock->ref_name); free(lock->log_file); free(lock); } @@ -556,7 +559,7 @@ int write_ref_sha1(struct ref_lock *lock, return -1; } if (commit_lock_file(lock->lk)) { - error("Couldn't set %s", lock->ref_file); + error("Couldn't set %s", lock->ref_name); unlock_ref(lock); return -1; } diff --git a/refs.h b/refs.h index 553155c04a..af347e6b19 100644 --- a/refs.h +++ b/refs.h @@ -2,7 +2,7 @@ #define REFS_H struct ref_lock { - char *ref_file; + char *ref_name; char *log_file; struct lock_file *lk; unsigned char old_sha1[20]; -- cgit v1.3 From 53cce84c05d3cba237ab45540b1e882be9c97215 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Tue, 19 Sep 2006 22:58:23 +0200 Subject: Fix broken sha1 locking Current git#next is totally broken wrt. cloning over HTTP, generating refs at random directories. Of course it's caused by the static get_pathname() buffer. lock_ref_sha1() stores return value of mkpath()'s get_pathname() call, then calls lock_ref_sha1_basic() which calls git_path(ref) which calls get_pathname() at that point returning pointer to the same buffer. So now you are sprintf()ing a format string into itself, wow! The resulting pathnames are really cute. (If you've been paying attention, yes, the mere fact that a format string _could_ write over itself is very wrong and probably exploitable here. See the other mail I've just sent.) I've never liked how we use return values of those functions so liberally, the "allow some random number of get_pathname() return values to work concurrently" is absolutely horrible pit and we've already fallen in this before IIRC. I consider it an awful coding practice, you add a call somewhere and at some other point some distant caller of that breaks since it reuses the same return values. Not to mention this takes quite some time to debug. My gut feeling tells me that there might be more of this. I don't have time to review the rest of the users of the refs.c functions though. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- refs.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 134c0fc015..7bd36e4f63 100644 --- a/refs.c +++ b/refs.c @@ -462,10 +462,12 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist) { + char refpath[PATH_MAX]; if (check_ref_format(ref)) return NULL; - return lock_ref_sha1_basic(mkpath("refs/%s", ref), - 5 + strlen(ref), old_sha1, mustexist); + strcpy(refpath, mkpath("refs/%s", ref)); + return lock_ref_sha1_basic(refpath, strlen(refpath), + old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, -- cgit v1.3 From cb5d709ff8a4bae19d57a470ba2b137c25938a44 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 21:47:42 -0700 Subject: Add callback data to for_each_ref() family. This is a long overdue fix to the API for for_each_ref() family of functions. It allows the callers to specify a callback data pointer, so that the caller does not have to use static variables to communicate with the callback funciton. The updated for_each_ref() family takes a function of type int (*fn)(const char *, const unsigned char *, void *) and a void pointer as parameters, and calls the function with the name of the ref and its SHA-1 with the caller-supplied void pointer as parameters. The commit updates two callers, builtin-name-rev.c and builtin-pack-refs.c as an example. Signed-off-by: Junio C Hamano --- builtin-name-rev.c | 8 ++++---- builtin-pack-refs.c | 8 +++++--- builtin-prune.c | 4 ++-- builtin-push.c | 4 ++-- builtin-rev-parse.c | 10 +++++----- builtin-show-branch.c | 26 +++++++++++++------------- describe.c | 4 ++-- fetch-pack.c | 8 ++++---- fetch.c | 4 ++-- fsck-objects.c | 4 ++-- http-push.c | 4 ++-- receive-pack.c | 6 +++--- refs.c | 31 ++++++++++++++++++------------- refs.h | 11 ++++++----- revision.c | 4 ++-- send-pack.c | 4 ++-- server-info.c | 4 ++-- upload-pack.c | 6 +++--- 18 files changed, 79 insertions(+), 71 deletions(-) (limited to 'refs.c') diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 52886b69b0..9e3e537f38 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -75,11 +75,10 @@ copy_data: } } -static int tags_only; - -static int name_ref(const char *path, const unsigned char *sha1) +static int name_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = parse_object(sha1); + int tags_only = *(int*)cb_data; int deref = 0; if (tags_only && strncmp(path, "refs/tags/", 10)) @@ -131,6 +130,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) { struct object_array revs = { 0, 0, NULL }; int as_is = 0, all = 0, transform_stdin = 0; + int tags_only = 0; git_config(git_default_config); @@ -186,7 +186,7 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix) add_object_array((struct object *)commit, *argv, &revs); } - for_each_ref(name_ref); + for_each_ref(name_ref, &tags_only); if (transform_stdin) { char buffer[2048]; diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index 0f5d827c25..b3d5470cc0 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -1,7 +1,6 @@ #include "cache.h" #include "refs.h" -static FILE *refs_file; static const char *result_path, *lock_path; static void remove_lock_file(void) @@ -10,8 +9,10 @@ static void remove_lock_file(void) unlink(lock_path); } -static int handle_one_ref(const char *path, const unsigned char *sha1) +static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) { + FILE *refs_file = cb_data; + fprintf(refs_file, "%s %s\n", sha1_to_hex(sha1), path); return 0; } @@ -19,6 +20,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1) int cmd_pack_refs(int argc, const char **argv, const char *prefix) { int fd; + FILE *refs_file; result_path = xstrdup(git_path("packed-refs")); lock_path = xstrdup(mkpath("%s.lock", result_path)); @@ -31,7 +33,7 @@ int cmd_pack_refs(int argc, const char **argv, const char *prefix) refs_file = fdopen(fd, "w"); if (!refs_file) die("unable to create ref-pack file structure (%s)", strerror(errno)); - for_each_ref(handle_one_ref); + for_each_ref(handle_one_ref, refs_file); fsync(fd); fclose(refs_file); if (rename(lock_path, result_path) < 0) diff --git a/builtin-prune.c b/builtin-prune.c index 6228c7907b..e21c29baec 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs) } } -static int add_one_ref(const char *path, const unsigned char *sha1) +static int add_one_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *object = parse_object(sha1); if (!object) @@ -240,7 +240,7 @@ int cmd_prune(int argc, const char **argv, const char *prefix) revs.tree_objects = 1; /* Add all external refs */ - for_each_ref(add_one_ref); + for_each_ref(add_one_ref, NULL); /* Add all refs from the index file */ add_cache_refs(); diff --git a/builtin-push.c b/builtin-push.c index c43f2566d5..88fc8e2a3b 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -27,7 +27,7 @@ static void add_refspec(const char *ref) refspec_nr = nr; } -static int expand_one_ref(const char *ref, const unsigned char *sha1) +static int expand_one_ref(const char *ref, const unsigned char *sha1, void *cb_data) { /* Ignore the "refs/" at the beginning of the refname */ ref += 5; @@ -51,7 +51,7 @@ static void expand_refspecs(void) } if (!tags) return; - for_each_ref(expand_one_ref); + for_each_ref(expand_one_ref, NULL); } static void set_refspecs(const char **refs, int nr) diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index fd3ccc8546..c7712748bc 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -137,7 +137,7 @@ static void show_default(void) } } -static int show_reference(const char *refname, const unsigned char *sha1) +static int show_reference(const char *refname, const unsigned char *sha1, void *cb_data) { show_rev(NORMAL, sha1, refname); return 0; @@ -299,19 +299,19 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix) continue; } if (!strcmp(arg, "--all")) { - for_each_ref(show_reference); + for_each_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--branches")) { - for_each_branch_ref(show_reference); + for_each_branch_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--tags")) { - for_each_tag_ref(show_reference); + for_each_tag_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--remotes")) { - for_each_remote_ref(show_reference); + for_each_remote_ref(show_reference, NULL); continue; } if (!strcmp(arg, "--show-prefix")) { diff --git a/builtin-show-branch.c b/builtin-show-branch.c index 4d8db0c102..b3548ae36f 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top) compare_ref_name); } -static int append_ref(const char *refname, const unsigned char *sha1) +static int append_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); int i; @@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1) return 0; } -static int append_head_ref(const char *refname, const unsigned char *sha1) +static int append_head_ref(const char *refname, const unsigned char *sha1, void *cb_data) { unsigned char tmp[20]; int ofs = 11; @@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1) */ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) ofs = 5; - return append_ref(refname + ofs, sha1); + return append_ref(refname + ofs, sha1, cb_data); } -static int append_tag_ref(const char *refname, const unsigned char *sha1) +static int append_tag_ref(const char *refname, const unsigned char *sha1, void *cb_data) { if (strncmp(refname, "refs/tags/", 10)) return 0; - return append_ref(refname + 5, sha1); + return append_ref(refname + 5, sha1, cb_data); } static const char *match_ref_pattern = NULL; @@ -401,7 +401,7 @@ static int count_slash(const char *s) return cnt; } -static int append_matching_ref(const char *refname, const unsigned char *sha1) +static int append_matching_ref(const char *refname, const unsigned char *sha1, void *cb_data) { /* we want to allow pattern hold/ to show all * branches under refs/heads/hold/, and v0.99.9? to show @@ -417,22 +417,22 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1) if (fnmatch(match_ref_pattern, tail, 0)) return 0; if (!strncmp("refs/heads/", refname, 11)) - return append_head_ref(refname, sha1); + return append_head_ref(refname, sha1, cb_data); if (!strncmp("refs/tags/", refname, 10)) - return append_tag_ref(refname, sha1); - return append_ref(refname, sha1); + return append_tag_ref(refname, sha1, cb_data); + return append_ref(refname, sha1, cb_data); } static void snarf_refs(int head, int tag) { if (head) { int orig_cnt = ref_name_cnt; - for_each_ref(append_head_ref); + for_each_ref(append_head_ref, NULL); sort_ref_range(orig_cnt, ref_name_cnt); } if (tag) { int orig_cnt = ref_name_cnt; - for_each_ref(append_tag_ref); + for_each_ref(append_tag_ref, NULL); sort_ref_range(orig_cnt, ref_name_cnt); } } @@ -487,7 +487,7 @@ static void append_one_rev(const char *av) { unsigned char revkey[20]; if (!get_sha1(av, revkey)) { - append_ref(av, revkey); + append_ref(av, revkey, NULL); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { @@ -495,7 +495,7 @@ static void append_one_rev(const char *av) int saved_matches = ref_name_cnt; match_ref_pattern = av; match_ref_slash = count_slash(av); - for_each_ref(append_matching_ref); + for_each_ref(append_matching_ref, NULL); if (saved_matches == ref_name_cnt && ref_name_cnt < MAX_REVS) error("no matching refs with %s", av); diff --git a/describe.c b/describe.c index ab192f83ae..ea0f2ce55b 100644 --- a/describe.c +++ b/describe.c @@ -53,7 +53,7 @@ static void add_to_known_names(const char *path, names = ++idx; } -static int get_name(const char *path, const unsigned char *sha1) +static int get_name(const char *path, const unsigned char *sha1, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); struct object *object; @@ -113,7 +113,7 @@ static void describe(const char *arg, int last_one) if (!initialized) { initialized = 1; - for_each_ref(get_name); + for_each_ref(get_name, NULL); qsort(name_array, names, sizeof(*name_array), compare_names); } diff --git a/fetch-pack.c b/fetch-pack.c index e8708aa802..6264ea1af9 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark) } } -static int rev_list_insert_ref(const char *path, const unsigned char *sha1) +static int rev_list_insert_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = deref_tag(parse_object(sha1), path, 0); @@ -143,7 +143,7 @@ static int find_common(int fd[2], unsigned char *result_sha1, unsigned in_vain = 0; int got_continue = 0; - for_each_ref(rev_list_insert_ref); + for_each_ref(rev_list_insert_ref, NULL); fetching = 0; for ( ; refs ; refs = refs->next) { @@ -253,7 +253,7 @@ done: static struct commit_list *complete; -static int mark_complete(const char *path, const unsigned char *sha1) +static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = parse_object(sha1); @@ -365,7 +365,7 @@ static int everything_local(struct ref **refs, int nr_match, char **match) } } - for_each_ref(mark_complete); + for_each_ref(mark_complete, NULL); if (cutoff) mark_recent_complete_commits(cutoff); diff --git a/fetch.c b/fetch.c index 34df8d37d7..36d1e7668e 100644 --- a/fetch.c +++ b/fetch.c @@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1) return -1; } -static int mark_complete(const char *path, const unsigned char *sha1) +static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); if (commit) { @@ -274,7 +274,7 @@ int pull(int targets, char **target, const char **write_ref, } if (!get_recover) - for_each_ref(mark_complete); + for_each_ref(mark_complete, NULL); for (i = 0; i < targets; i++) { if (interpret_target(target[i], &sha1[20 * i])) { diff --git a/fsck-objects.c b/fsck-objects.c index 456c17e2f6..bb0c94e9d3 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path) static int default_refs; -static int fsck_handle_ref(const char *refname, const unsigned char *sha1) +static int fsck_handle_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct object *obj; @@ -424,7 +424,7 @@ static int fsck_handle_ref(const char *refname, const unsigned char *sha1) static void get_default_heads(void) { - for_each_ref(fsck_handle_ref); + for_each_ref(fsck_handle_ref, NULL); /* * Not having any default heads isn't really fatal, but diff --git a/http-push.c b/http-push.c index 670ff007be..460c9bec10 100644 --- a/http-push.c +++ b/http-push.c @@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1) +static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; @@ -1913,7 +1913,7 @@ static void one_remote_ref(char *refname) static void get_local_heads(void) { local_tail = &local_refs; - for_each_ref(one_local_ref); + for_each_ref(one_local_ref, NULL); } static void get_dav_remote_heads(void) diff --git a/receive-pack.c b/receive-pack.c index 78f75da5ca..7abc9210c5 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -12,7 +12,7 @@ static int report_status; static char capabilities[] = "report-status"; static int capabilities_sent; -static int show_ref(const char *path, const unsigned char *sha1) +static int show_ref(const char *path, const unsigned char *sha1, void *cb_data) { if (capabilities_sent) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); @@ -25,9 +25,9 @@ static int show_ref(const char *path, const unsigned char *sha1) static void write_head_info(void) { - for_each_ref(show_ref); + for_each_ref(show_ref, NULL); if (!capabilities_sent) - show_ref("capabilities^{}", null_sha1); + show_ref("capabilities^{}", null_sha1, NULL); } diff --git a/refs.c b/refs.c index 7bd36e4f63..85564f0dc7 100644 --- a/refs.c +++ b/refs.c @@ -275,7 +275,7 @@ int read_ref(const char *ref, unsigned char *sha1) return -1; } -static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim) +static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data) { int retval; struct ref_list *packed = get_packed_refs(); @@ -303,7 +303,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u error("%s does not point to a valid object!", entry->name); continue; } - retval = fn(entry->name + trim, entry->sha1); + retval = fn(entry->name + trim, entry->sha1, cb_data); if (retval) return retval; } @@ -311,7 +311,7 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u packed = packed ? packed : loose; while (packed) { if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1); + retval = fn(packed->name + trim, packed->sha1, cb_data); if (retval) return retval; } @@ -320,34 +320,39 @@ static int do_for_each_ref(const char *base, int (*fn)(const char *path, const u return 0; } -int head_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int head_ref(each_ref_fn fn, void *cb_data) { unsigned char sha1[20]; if (!read_ref("HEAD", sha1)) - return fn("HEAD", sha1); + return fn("HEAD", sha1, cb_data); return 0; } -int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/", fn, 0); + return do_for_each_ref("refs/", fn, 0, cb_data); } -int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_tag_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/tags/", fn, 10); + return do_for_each_ref("refs/tags/", fn, 10, cb_data); } -int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_branch_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/heads/", fn, 11); + return do_for_each_ref("refs/heads/", fn, 11, cb_data); } -int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)) +int for_each_remote_ref(each_ref_fn fn, void *cb_data) { - return do_for_each_ref("refs/remotes/", fn, 13); + return do_for_each_ref("refs/remotes/", fn, 13, cb_data); } +/* NEEDSWORK: This is only used by ssh-upload and it should go; the + * caller should do resolve_ref or read_ref like everybody else. Or + * maybe everybody else should use get_ref_sha1() instead of doing + * read_ref(). + */ int get_ref_sha1(const char *ref, unsigned char *sha1) { if (check_ref_format(ref)) diff --git a/refs.h b/refs.h index af347e6b19..886c857105 100644 --- a/refs.h +++ b/refs.h @@ -14,11 +14,12 @@ struct ref_lock { * Calls the specified function for each ref file until it returns nonzero, * and returns the value */ -extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1)); -extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1)); +typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data); +extern int head_ref(each_ref_fn, void *); +extern int for_each_ref(each_ref_fn, void *); +extern int for_each_tag_ref(each_ref_fn, void *); +extern int for_each_branch_ref(each_ref_fn, void *); +extern int for_each_remote_ref(each_ref_fn, void *); /** Reads the refs file specified into sha1 **/ extern int get_ref_sha1(const char *ref, unsigned char *sha1); diff --git a/revision.c b/revision.c index 6a2539b623..0e84b8a0fb 100644 --- a/revision.c +++ b/revision.c @@ -466,7 +466,7 @@ static void limit_list(struct rev_info *revs) static int all_flags; static struct rev_info *all_revs; -static int handle_one_ref(const char *path, const unsigned char *sha1) +static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *object = get_reference(all_revs, path, sha1, all_flags); add_pending_object(all_revs, object, ""); @@ -477,7 +477,7 @@ static void handle_all(struct rev_info *revs, unsigned flags) { all_revs = revs; all_flags = flags; - for_each_ref(handle_one_ref); + for_each_ref(handle_one_ref, NULL); } static int add_parents_only(struct rev_info *revs, const char *arg, int flags) diff --git a/send-pack.c b/send-pack.c index 5bb123a376..ee1309313f 100644 --- a/send-pack.c +++ b/send-pack.c @@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1, static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1) +static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; @@ -230,7 +230,7 @@ static int one_local_ref(const char *refname, const unsigned char *sha1) static void get_local_heads(void) { local_tail = &local_refs; - for_each_ref(one_local_ref); + for_each_ref(one_local_ref, NULL); } static int receive_status(int in) diff --git a/server-info.c b/server-info.c index 2fb8f57103..7667b41257 100644 --- a/server-info.c +++ b/server-info.c @@ -7,7 +7,7 @@ /* refs */ static FILE *info_ref_fp; -static int add_info_ref(const char *path, const unsigned char *sha1) +static int add_info_ref(const char *path, const unsigned char *sha1, void *cb_data) { struct object *o = parse_object(sha1); @@ -34,7 +34,7 @@ static int update_info_refs(int force) info_ref_fp = fopen(path1, "w"); if (!info_ref_fp) return error("unable to update %s", path0); - for_each_ref(add_info_ref); + for_each_ref(add_info_ref, NULL); fclose(info_ref_fp); rename(path1, path0); free(path0); diff --git a/upload-pack.c b/upload-pack.c index 189b239cc0..10237ebe54 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -416,7 +416,7 @@ static void receive_needs(void) } } -static int send_ref(const char *refname, const unsigned char *sha1) +static int send_ref(const char *refname, const unsigned char *sha1, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band side-band-64k"; struct object *o = parse_object(sha1); @@ -444,8 +444,8 @@ static int send_ref(const char *refname, const unsigned char *sha1) static void upload_pack(void) { reset_timeout(); - head_ref(send_ref); - for_each_ref(send_ref); + head_ref(send_ref, NULL); + for_each_ref(send_ref, NULL); packet_flush(1); receive_needs(); if (want_obj.nr) { -- cgit v1.3 From 8da197755450d4f16018bd4b5486dc8ed88b0f2a Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 20 Sep 2006 22:02:01 -0700 Subject: Tell between packed, unpacked and symbolic refs. This adds a "int *flag" parameter to resolve_ref() and makes for_each_ref() family to call callback function with an extra "int flag" parameter. They are used to give two bits of information (REF_ISSYMREF and REF_ISPACKED) about the ref. Signed-off-by: Junio C Hamano --- builtin-fmt-merge-msg.c | 2 +- builtin-name-rev.c | 2 +- builtin-pack-refs.c | 3 ++- builtin-prune.c | 2 +- builtin-push.c | 2 +- builtin-rev-parse.c | 2 +- builtin-show-branch.c | 22 +++++++++++----------- builtin-symbolic-ref.c | 6 +++++- cache.h | 2 +- describe.c | 2 +- fetch-pack.c | 4 ++-- fetch.c | 2 +- fsck-objects.c | 7 ++++--- http-push.c | 2 +- receive-pack.c | 4 ++-- refs.c | 44 +++++++++++++++++++++++++++++++------------- refs.h | 4 +++- revision.c | 2 +- send-pack.c | 2 +- server-info.c | 2 +- sha1_name.c | 2 +- upload-pack.c | 2 +- wt-status.c | 2 +- 23 files changed, 75 insertions(+), 49 deletions(-) (limited to 'refs.c') diff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c index b93c17c2f0..3d3097d299 100644 --- a/builtin-fmt-merge-msg.c +++ b/builtin-fmt-merge-msg.c @@ -277,7 +277,7 @@ int cmd_fmt_merge_msg(int argc, const char **argv, const char *prefix) usage(fmt_merge_msg_usage); /* get current branch */ - current_branch = resolve_ref("HEAD", head_sha1, 1); + current_branch = resolve_ref("HEAD", head_sha1, 1, NULL); if (!strncmp(current_branch, "refs/heads/", 11)) current_branch += 11; diff --git a/builtin-name-rev.c b/builtin-name-rev.c index 9e3e537f38..618aa314d2 100644 --- a/builtin-name-rev.c +++ b/builtin-name-rev.c @@ -75,7 +75,7 @@ copy_data: } } -static int name_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data) { struct object *o = parse_object(sha1); int tags_only = *(int*)cb_data; diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index b3d5470cc0..98710893b0 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -9,7 +9,8 @@ static void remove_lock_file(void) unlink(lock_path); } -static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int handle_one_ref(const char *path, const unsigned char *sha1, + int flags, void *cb_data) { FILE *refs_file = cb_data; diff --git a/builtin-prune.c b/builtin-prune.c index e21c29baec..e79b515c76 100644 --- a/builtin-prune.c +++ b/builtin-prune.c @@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_info *revs) } } -static int add_one_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *object = parse_object(sha1); if (!object) diff --git a/builtin-push.c b/builtin-push.c index 88fc8e2a3b..581c44ba8e 100644 --- a/builtin-push.c +++ b/builtin-push.c @@ -27,7 +27,7 @@ static void add_refspec(const char *ref) refspec_nr = nr; } -static int expand_one_ref(const char *ref, const unsigned char *sha1, void *cb_data) +static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data) { /* Ignore the "refs/" at the beginning of the refname */ ref += 5; diff --git a/builtin-rev-parse.c b/builtin-rev-parse.c index c7712748bc..3b716fba13 100644 --- a/builtin-rev-parse.c +++ b/builtin-rev-parse.c @@ -137,7 +137,7 @@ static void show_default(void) } } -static int show_reference(const char *refname, const unsigned char *sha1, void *cb_data) +static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { show_rev(NORMAL, sha1, refname); return 0; diff --git a/builtin-show-branch.c b/builtin-show-branch.c index b3548ae36f..5d6ce56836 100644 --- a/builtin-show-branch.c +++ b/builtin-show-branch.c @@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, int top) compare_ref_name); } -static int append_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); int i; @@ -369,7 +369,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, void *cb_d return 0; } -static int append_head_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { unsigned char tmp[20]; int ofs = 11; @@ -380,14 +380,14 @@ static int append_head_ref(const char *refname, const unsigned char *sha1, void */ if (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1)) ofs = 5; - return append_ref(refname + ofs, sha1, cb_data); + return append_ref(refname + ofs, sha1, flag, cb_data); } -static int append_tag_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { if (strncmp(refname, "refs/tags/", 10)) return 0; - return append_ref(refname + 5, sha1, cb_data); + return append_ref(refname + 5, sha1, flag, cb_data); } static const char *match_ref_pattern = NULL; @@ -401,7 +401,7 @@ static int count_slash(const char *s) return cnt; } -static int append_matching_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { /* we want to allow pattern hold/ to show all * branches under refs/heads/hold/, and v0.99.9? to show @@ -417,10 +417,10 @@ static int append_matching_ref(const char *refname, const unsigned char *sha1, v if (fnmatch(match_ref_pattern, tail, 0)) return 0; if (!strncmp("refs/heads/", refname, 11)) - return append_head_ref(refname, sha1, cb_data); + return append_head_ref(refname, sha1, flag, cb_data); if (!strncmp("refs/tags/", refname, 10)) - return append_tag_ref(refname, sha1, cb_data); - return append_ref(refname, sha1, cb_data); + return append_tag_ref(refname, sha1, flag, cb_data); + return append_ref(refname, sha1, flag, cb_data); } static void snarf_refs(int head, int tag) @@ -487,7 +487,7 @@ static void append_one_rev(const char *av) { unsigned char revkey[20]; if (!get_sha1(av, revkey)) { - append_ref(av, revkey, NULL); + append_ref(av, revkey, 0, NULL); return; } if (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) { @@ -630,7 +630,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix) ac--; av++; } - head_p = resolve_ref("HEAD", head_sha1, 1); + head_p = resolve_ref("HEAD", head_sha1, 1, NULL); if (head_p) { head_len = strlen(head_p); memcpy(head, head_p, head_len + 1); diff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c index 13163baa88..d8be0527f4 100644 --- a/builtin-symbolic-ref.c +++ b/builtin-symbolic-ref.c @@ -1,5 +1,6 @@ #include "builtin.h" #include "cache.h" +#include "refs.h" static const char git_symbolic_ref_usage[] = "git-symbolic-ref name [ref]"; @@ -7,10 +8,13 @@ static const char git_symbolic_ref_usage[] = static void check_symref(const char *HEAD) { unsigned char sha1[20]; - const char *refs_heads_master = resolve_ref(HEAD, sha1, 0); + int flag; + const char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag); if (!refs_heads_master) die("No such ref: %s", HEAD); + else if (!(flag & REF_ISSYMREF)) + die("ref %s is not a symbolic ref", HEAD); puts(refs_heads_master); } diff --git a/cache.h b/cache.h index 282eed6f07..6def155162 100644 --- a/cache.h +++ b/cache.h @@ -286,7 +286,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); +extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); extern int create_symref(const char *ref, const char *refs_heads_master); extern int validate_symref(const char *ref); diff --git a/describe.c b/describe.c index ea0f2ce55b..f4029ee74e 100644 --- a/describe.c +++ b/describe.c @@ -53,7 +53,7 @@ static void add_to_known_names(const char *path, names = ++idx; } -static int get_name(const char *path, const unsigned char *sha1, void *cb_data) +static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); struct object *object; diff --git a/fetch-pack.c b/fetch-pack.c index 6264ea1af9..99ac08b2c2 100644 --- a/fetch-pack.c +++ b/fetch-pack.c @@ -42,7 +42,7 @@ static void rev_list_push(struct commit *commit, int mark) } } -static int rev_list_insert_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = deref_tag(parse_object(sha1), path, 0); @@ -253,7 +253,7 @@ done: static struct commit_list *complete; -static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) +static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = parse_object(sha1); diff --git a/fetch.c b/fetch.c index 36d1e7668e..a2cbdfba82 100644 --- a/fetch.c +++ b/fetch.c @@ -201,7 +201,7 @@ static int interpret_target(char *target, unsigned char *sha1) return -1; } -static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data) +static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct commit *commit = lookup_commit_reference_gently(sha1, 1); if (commit) { diff --git a/fsck-objects.c b/fsck-objects.c index bb0c94e9d3..46b628cb94 100644 --- a/fsck-objects.c +++ b/fsck-objects.c @@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path) static int default_refs; -static int fsck_handle_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct object *obj; @@ -458,9 +458,10 @@ static void fsck_object_dir(const char *path) static int fsck_head_link(void) { unsigned char sha1[20]; - const char *head_points_at = resolve_ref("HEAD", sha1, 1); + int flag; + const char *head_points_at = resolve_ref("HEAD", sha1, 1, &flag); - if (!head_points_at) + if (!head_points_at || !(flag & REF_ISSYMREF)) return error("HEAD is not a symbolic ref"); if (strncmp(head_points_at, "refs/heads/", 11)) return error("HEAD points to something strange (%s)", diff --git a/http-push.c b/http-push.c index 460c9bec10..ecefdfd4f8 100644 --- a/http-push.c +++ b/http-push.c @@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *sha1, struct remote_lock *lock) static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; diff --git a/receive-pack.c b/receive-pack.c index 7abc9210c5..abbcb6af0b 100644 --- a/receive-pack.c +++ b/receive-pack.c @@ -12,7 +12,7 @@ static int report_status; static char capabilities[] = "report-status"; static int capabilities_sent; -static int show_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { if (capabilities_sent) packet_write(1, "%s %s\n", sha1_to_hex(sha1), path); @@ -27,7 +27,7 @@ static void write_head_info(void) { for_each_ref(show_ref, NULL); if (!capabilities_sent) - show_ref("capabilities^{}", null_sha1, NULL); + show_ref("capabilities^{}", null_sha1, 0, NULL); } diff --git a/refs.c b/refs.c index 85564f0dc7..40f16af185 100644 --- a/refs.c +++ b/refs.c @@ -5,6 +5,7 @@ struct ref_list { struct ref_list *next; + unsigned char flag; /* ISSYMREF? ISPACKED? */ unsigned char sha1[20]; char name[FLEX_ARRAY]; }; @@ -36,7 +37,8 @@ static const char *parse_ref_line(char *line, unsigned char *sha1) return line; } -static struct ref_list *add_ref(const char *name, const unsigned char *sha1, struct ref_list *list) +static struct ref_list *add_ref(const char *name, const unsigned char *sha1, + int flag, struct ref_list *list) { int len; struct ref_list **p = &list, *entry; @@ -58,6 +60,7 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1, str entry = xmalloc(sizeof(struct ref_list) + len); hashcpy(entry->sha1, sha1); memcpy(entry->name, name, len); + entry->flag = flag; entry->next = *p; *p = entry; return list; @@ -78,7 +81,7 @@ static struct ref_list *get_packed_refs(void) const char *name = parse_ref_line(refline, sha1); if (!name) continue; - list = add_ref(name, sha1, list); + list = add_ref(name, sha1, REF_ISPACKED, list); } fclose(f); refs = list; @@ -104,6 +107,7 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) while ((de = readdir(dir)) != NULL) { unsigned char sha1[20]; struct stat st; + int flag; int namelen; if (de->d_name[0] == '.') @@ -120,11 +124,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) list = get_ref_dir(ref, list); continue; } - if (read_ref(ref, sha1) < 0) { + if (!resolve_ref(ref, sha1, 1, &flag)) { error("%s points nowhere!", ref); continue; } - list = add_ref(ref, sha1, list); + list = add_ref(ref, sha1, flag, list); } free(ref); closedir(dir); @@ -147,12 +151,15 @@ static struct ref_list *get_loose_refs(void) /* We allow "recursive" symbolic refs. Only within reason, though */ #define MAXDEPTH 5 -const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) +const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag) { int depth = MAXDEPTH, len; char buffer[256]; static char ref_buffer[256]; + if (flag) + *flag = 0; + for (;;) { const char *path = git_path("%s", ref); struct stat st; @@ -174,6 +181,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) while (list) { if (!strcmp(ref, list->name)) { hashcpy(sha1, list->sha1); + if (flag) + *flag |= REF_ISPACKED; return ref; } list = list->next; @@ -191,6 +200,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) buffer[len] = 0; strcpy(ref_buffer, buffer); ref = ref_buffer; + if (flag) + *flag |= REF_ISSYMREF; continue; } } @@ -219,6 +230,8 @@ const char *resolve_ref(const char *ref, unsigned char *sha1, int reading) buf[len] = 0; memcpy(ref_buffer, buf, len + 1); ref = ref_buffer; + if (flag) + *flag |= REF_ISSYMREF; } if (len < 40 || get_sha1_hex(buffer, sha1)) return NULL; @@ -270,12 +283,13 @@ int create_symref(const char *ref_target, const char *refs_heads_master) int read_ref(const char *ref, unsigned char *sha1) { - if (resolve_ref(ref, sha1, 1)) + if (resolve_ref(ref, sha1, 1, NULL)) return 0; return -1; } -static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data) +static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, + void *cb_data) { int retval; struct ref_list *packed = get_packed_refs(); @@ -303,7 +317,8 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ error("%s does not point to a valid object!", entry->name); continue; } - retval = fn(entry->name + trim, entry->sha1, cb_data); + retval = fn(entry->name + trim, entry->sha1, + entry->flag, cb_data); if (retval) return retval; } @@ -311,7 +326,8 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ packed = packed ? packed : loose; while (packed) { if (!strncmp(base, packed->name, trim)) { - retval = fn(packed->name + trim, packed->sha1, cb_data); + retval = fn(packed->name + trim, packed->sha1, + packed->flag, cb_data); if (retval) return retval; } @@ -323,8 +339,10 @@ static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_ int head_ref(each_ref_fn fn, void *cb_data) { unsigned char sha1[20]; - if (!read_ref("HEAD", sha1)) - return fn("HEAD", sha1, cb_data); + int flag; + + if (resolve_ref("HEAD", sha1, 1, &flag)) + return fn("HEAD", sha1, flag, cb_data); return 0; } @@ -415,7 +433,7 @@ int check_ref_format(const char *ref) static struct ref_lock *verify_lock(struct ref_lock *lock, const unsigned char *old_sha1, int mustexist) { - if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist)) { + if (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) { error("Can't verify ref %s", lock->ref_name); unlock_ref(lock); return NULL; @@ -441,7 +459,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - ref = resolve_ref(ref, lock->old_sha1, mustexist); + ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL); if (!ref) { int last_errno = errno; error("unable to resolve reference %s: %s", diff --git a/refs.h b/refs.h index 886c857105..305d408690 100644 --- a/refs.h +++ b/refs.h @@ -14,7 +14,9 @@ struct ref_lock { * Calls the specified function for each ref file until it returns nonzero, * and returns the value */ -typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data); +#define REF_ISSYMREF 01 +#define REF_ISPACKED 02 +typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data); extern int head_ref(each_ref_fn, void *); extern int for_each_ref(each_ref_fn, void *); extern int for_each_tag_ref(each_ref_fn, void *); diff --git a/revision.c b/revision.c index 0e84b8a0fb..cb13b90776 100644 --- a/revision.c +++ b/revision.c @@ -466,7 +466,7 @@ static void limit_list(struct rev_info *revs) static int all_flags; static struct rev_info *all_revs; -static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *object = get_reference(all_revs, path, sha1, all_flags); add_pending_object(all_revs, object, ""); diff --git a/send-pack.c b/send-pack.c index ee1309313f..fbd792ccf4 100644 --- a/send-pack.c +++ b/send-pack.c @@ -215,7 +215,7 @@ static int ref_newer(const unsigned char *new_sha1, static struct ref *local_refs, **local_tail; static struct ref *remote_refs, **remote_tail; -static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { struct ref *ref; int len = strlen(refname) + 1; diff --git a/server-info.c b/server-info.c index 7667b41257..6cd38be329 100644 --- a/server-info.c +++ b/server-info.c @@ -7,7 +7,7 @@ /* refs */ static FILE *info_ref_fp; -static int add_info_ref(const char *path, const unsigned char *sha1, void *cb_data) +static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data) { struct object *o = parse_object(sha1); diff --git a/sha1_name.c b/sha1_name.c index b4975289d5..84d24c6abf 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -276,7 +276,7 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) for (p = fmt; *p; p++) { this_result = refs_found ? sha1_from_ref : sha1; - ref = resolve_ref(mkpath(*p, len, str), this_result, 1); + ref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL); if (ref) { if (!refs_found++) real_ref = xstrdup(ref); diff --git a/upload-pack.c b/upload-pack.c index 10237ebe54..9412a9b260 100644 --- a/upload-pack.c +++ b/upload-pack.c @@ -416,7 +416,7 @@ static void receive_needs(void) } } -static int send_ref(const char *refname, const unsigned char *sha1, void *cb_data) +static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) { static const char *capabilities = "multi_ack thin-pack side-band side-band-64k"; struct object *o = parse_object(sha1); diff --git a/wt-status.c b/wt-status.c index 050922df54..d8e284c311 100644 --- a/wt-status.c +++ b/wt-status.c @@ -41,7 +41,7 @@ void wt_status_prepare(struct wt_status *s) s->is_initial = get_sha1("HEAD", sha1) ? 1 : 0; - head = resolve_ref("HEAD", sha1, 0); + head = resolve_ref("HEAD", sha1, 0, NULL); s->branch = head ? xstrdup(head) : NULL; s->reference = "HEAD"; -- cgit v1.3 From 7c1a278d99a18445e12bd55e308cd69080963198 Mon Sep 17 00:00:00 2001 From: Petr Baudis Date: Sat, 23 Sep 2006 01:08:45 +0200 Subject: Fix buggy ref recording There is a format string vulnerability introduced with the packed refs file format. Signed-off-by: Petr Baudis Signed-off-by: Junio C Hamano --- refs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 40f16af185..5fdf9c4139 100644 --- a/refs.c +++ b/refs.c @@ -472,7 +472,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, lock->ref_name = xstrdup(ref); lock->log_file = xstrdup(git_path("logs/%s", ref)); - ref_file = git_path(ref); + ref_file = git_path("%s", ref); lock->force_write = lstat(ref_file, &st) && errno == ENOENT; if (safe_create_leading_directories(ref_file)) -- cgit v1.3 From 5fd6f5cffc7b8bc679c922b8ff0b1cd35bb1a30c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Fri, 22 Sep 2006 21:41:49 -0700 Subject: lock_ref_sha1_basic: remove unused parameter "plen". Signed-off-by: Junio C Hamano --- refs.c | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 5fdf9c4139..2cef2b4f0e 100644 --- a/refs.c +++ b/refs.c @@ -447,9 +447,7 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } -static struct ref_lock *lock_ref_sha1_basic(const char *ref, - int plen, - const unsigned char *old_sha1, int mustexist) +static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int mustexist) { char *ref_file; const char *orig_ref = ref; @@ -489,14 +487,13 @@ struct ref_lock *lock_ref_sha1(const char *ref, if (check_ref_format(ref)) return NULL; strcpy(refpath, mkpath("refs/%s", ref)); - return lock_ref_sha1_basic(refpath, strlen(refpath), - old_sha1, mustexist); + return lock_ref_sha1_basic(refpath, old_sha1, mustexist); } struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist) { - return lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist); + return lock_ref_sha1_basic(ref, old_sha1, mustexist); } void unlock_ref(struct ref_lock *lock) -- cgit v1.3 From 4431fcc4b134ae501e3e57dc568ae4f031e57898 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 01:09:18 -0700 Subject: Clean-up lock-ref implementation This drops "mustexist" parameter lock_ref_sha1() and lock_any_ref_forupdate() functions take. Signed-off-by: Junio C Hamano --- builtin-pack-refs.c | 2 +- builtin-update-ref.c | 2 +- fetch.c | 2 +- refs.c | 13 ++++++------- refs.h | 4 ++-- 5 files changed, 11 insertions(+), 12 deletions(-) (limited to 'refs.c') diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c index db57fee72d..4093973a05 100644 --- a/builtin-pack-refs.c +++ b/builtin-pack-refs.c @@ -53,7 +53,7 @@ static int handle_one_ref(const char *path, const unsigned char *sha1, /* make sure nobody touched the ref, and unlink */ static void prune_ref(struct ref_to_prune *r) { - struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1); + struct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1); if (lock) { unlink(git_path("%s", r->name)); diff --git a/builtin-update-ref.c b/builtin-update-ref.c index 90a3da53ad..ab528337aa 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -48,7 +48,7 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (oldval && get_sha1(oldval, oldsha1)) die("%s: not a valid old SHA1", oldval); - lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL, 0); + lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL); if (!lock) return 1; if (write_ref_sha1(lock, sha1, msg) < 0) diff --git a/fetch.c b/fetch.c index a2cbdfba82..c426c04997 100644 --- a/fetch.c +++ b/fetch.c @@ -266,7 +266,7 @@ int pull(int targets, char **target, const char **write_ref, if (!write_ref || !write_ref[i]) continue; - lock[i] = lock_ref_sha1(write_ref[i], NULL, 0); + lock[i] = lock_ref_sha1(write_ref[i], NULL); if (!lock[i]) { error("Can't lock ref %s", write_ref[i]); goto unlock_and_fail; diff --git a/refs.c b/refs.c index 2cef2b4f0e..9a1bc0db59 100644 --- a/refs.c +++ b/refs.c @@ -447,12 +447,13 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } -static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int mustexist) +static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1) { char *ref_file; const char *orig_ref = ref; struct ref_lock *lock; struct stat st; + int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; @@ -480,20 +481,18 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; } -struct ref_lock *lock_ref_sha1(const char *ref, - const unsigned char *old_sha1, int mustexist) +struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) { char refpath[PATH_MAX]; if (check_ref_format(ref)) return NULL; strcpy(refpath, mkpath("refs/%s", ref)); - return lock_ref_sha1_basic(refpath, old_sha1, mustexist); + return lock_ref_sha1_basic(refpath, old_sha1); } -struct ref_lock *lock_any_ref_for_update(const char *ref, - const unsigned char *old_sha1, int mustexist) +struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1) { - return lock_ref_sha1_basic(ref, old_sha1, mustexist); + return lock_ref_sha1_basic(ref, old_sha1); } void unlock_ref(struct ref_lock *lock) diff --git a/refs.h b/refs.h index 305d408690..0d4d79e03f 100644 --- a/refs.h +++ b/refs.h @@ -27,10 +27,10 @@ extern int for_each_remote_ref(each_ref_fn, void *); extern int get_ref_sha1(const char *ref, unsigned char *sha1); /** Locks a "refs/" ref returning the lock on success and NULL on failure. **/ -extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1, int mustexist); +extern struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1); /** Locks any ref (for 'HEAD' type refs). */ -extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int mustexist); +extern struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1); /** Release any lock taken but not written. **/ extern void unlock_ref(struct ref_lock *lock); -- cgit v1.3 From ac5409e420e5fdd7c4a381f873ffcedfb83d7117 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Wed, 27 Sep 2006 01:58:57 -0700 Subject: update-ref: -d flag and ref creation safety. This adds -d flag to update-ref to allow safe deletion of ref. Before deleting it, the command checks if the given still matches the value the caller thought the ref contained. Similarly, it also accepts 0{40} or an empty string as to allow safe creation of a new ref. Signed-off-by: Junio C Hamano --- Documentation/git-update-ref.txt | 10 ++++++++-- builtin-update-ref.c | 18 +++++++++++++++--- cache.h | 1 + refs.c | 26 ++++++++++++++++++++++++++ 4 files changed, 50 insertions(+), 5 deletions(-) (limited to 'refs.c') diff --git a/Documentation/git-update-ref.txt b/Documentation/git-update-ref.txt index e062030e91..71bcb7954f 100644 --- a/Documentation/git-update-ref.txt +++ b/Documentation/git-update-ref.txt @@ -7,7 +7,7 @@ git-update-ref - update the object name stored in a ref safely SYNOPSIS -------- -'git-update-ref' [-m ] [] +'git-update-ref' [-m ] (-d | []) DESCRIPTION ----------- @@ -20,7 +20,9 @@ possibly dereferencing the symbolic refs, after verifying that the current value of the matches . E.g. `git-update-ref refs/heads/master ` updates the master branch head to only if its current -value is . +value is . You can specify 40 "0" or an empty string +as to make sure that the ref you are creating does +not exist. It also allows a "ref" file to be a symbolic pointer to another ref file by starting with the four-byte header sequence of @@ -49,6 +51,10 @@ for reading but not for writing (so we'll never write through a ref symlink to some other tree, if you have copied a whole archive by creating a symlink tree). +With `-d` flag, it deletes the named after verifying it +still contains . + + Logging Updates --------------- If config parameter "core.logAllRefUpdates" is true or the file diff --git a/builtin-update-ref.c b/builtin-update-ref.c index ab528337aa..b34e5987dd 100644 --- a/builtin-update-ref.c +++ b/builtin-update-ref.c @@ -3,15 +3,16 @@ #include "builtin.h" static const char git_update_ref_usage[] = -"git-update-ref [] [-m ]"; +"git-update-ref [-m ] (-d | [])"; int cmd_update_ref(int argc, const char **argv, const char *prefix) { const char *refname=NULL, *value=NULL, *oldval=NULL, *msg=NULL; struct ref_lock *lock; unsigned char sha1[20], oldsha1[20]; - int i; + int i, delete; + delete = 0; setup_ident(); git_config(git_default_config); @@ -26,6 +27,10 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) die("Refusing to perform update with \\n in message."); continue; } + if (!strcmp("-d", argv[i])) { + delete = 1; + continue; + } if (!refname) { refname = argv[i]; continue; @@ -44,8 +49,15 @@ int cmd_update_ref(int argc, const char **argv, const char *prefix) if (get_sha1(value, sha1)) die("%s: not a valid SHA1", value); + + if (delete) { + if (oldval) + usage(git_update_ref_usage); + return delete_ref(refname, sha1); + } + hashclr(oldsha1); - if (oldval && get_sha1(oldval, oldsha1)) + if (oldval && *oldval && get_sha1(oldval, oldsha1)) die("%s: not a valid old SHA1", oldval); lock = lock_any_ref_for_update(refname, oldval ? oldsha1 : NULL); diff --git a/cache.h b/cache.h index 6def155162..6e004505be 100644 --- a/cache.h +++ b/cache.h @@ -179,6 +179,7 @@ struct lock_file { extern int hold_lock_file_for_update(struct lock_file *, const char *path, int); extern int commit_lock_file(struct lock_file *); extern void rollback_lock_file(struct lock_file *); +extern int delete_ref(const char *, unsigned char *sha1); /* Environment bits from configuration mechanism */ extern int use_legacy_headers; diff --git a/refs.c b/refs.c index 9a1bc0db59..3d4cdd1eb9 100644 --- a/refs.c +++ b/refs.c @@ -378,6 +378,32 @@ int get_ref_sha1(const char *ref, unsigned char *sha1) return read_ref(mkpath("refs/%s", ref), sha1); } +int delete_ref(const char *refname, unsigned char *sha1) +{ + struct ref_lock *lock; + int err, i, ret = 0; + + lock = lock_any_ref_for_update(refname, sha1); + if (!lock) + return 1; + i = strlen(lock->lk->filename) - 5; /* .lock */ + lock->lk->filename[i] = 0; + err = unlink(lock->lk->filename); + if (err) { + ret = 1; + error("unlink(%s) failed: %s", + lock->lk->filename, strerror(errno)); + } + lock->lk->filename[i] = '.'; + + err = unlink(lock->log_file); + if (err && errno != ENOENT) + fprintf(stderr, "warning: unlink(%s) failed: %s", + lock->log_file, strerror(errno)); + + return ret; +} + /* * Make sure "ref" is something reasonable to have under ".git/refs/"; * We do not like it if: -- cgit v1.3 From bc7127ef0f81d996e9691c6c898b926867aae89f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 02:25:30 -0700 Subject: ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore. It is normal to have .git/refs/heads/foo directory which is empty after the last branch whose name starts with foo/ is removed. Make sure we notice this case and allow creation of branch foo by removing the empty directory. Signed-off-by: Junio C Hamano --- refs.c | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 3d4cdd1eb9..b433c0c2d0 100644 --- a/refs.c +++ b/refs.c @@ -473,6 +473,59 @@ static struct ref_lock *verify_lock(struct ref_lock *lock, return lock; } +static int remove_empty_dir_recursive(char *path, int len) +{ + DIR *dir = opendir(path); + struct dirent *e; + int ret = 0; + + if (!dir) + return -1; + if (path[len-1] != '/') + path[len++] = '/'; + while ((e = readdir(dir)) != NULL) { + struct stat st; + int namlen; + if ((e->d_name[0] == '.') && + ((e->d_name[1] == 0) || + ((e->d_name[1] == '.') && e->d_name[2] == 0))) + continue; /* "." and ".." */ + + namlen = strlen(e->d_name); + if ((len + namlen < PATH_MAX) && + strcpy(path + len, e->d_name) && + !lstat(path, &st) && + S_ISDIR(st.st_mode) && + remove_empty_dir_recursive(path, len + namlen)) + continue; /* happy */ + + /* path too long, stat fails, or non-directory still exists */ + ret = -1; + break; + } + closedir(dir); + if (!ret) { + path[len] = 0; + ret = rmdir(path); + } + return ret; +} + +static int remove_empty_directories(char *file) +{ + /* we want to create a file but there is a directory there; + * if that is an empty directory (or a directory that contains + * only empty directories), remove them. + */ + char path[PATH_MAX]; + int len = strlen(file); + + if (len >= PATH_MAX) /* path too long ;-) */ + return -1; + strcpy(path, file); + return remove_empty_dir_recursive(path, len); +} + static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1) { char *ref_file; @@ -485,6 +538,17 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock->lock_fd = -1; ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL); + if (!ref && errno == EISDIR) { + /* we are trying to lock foo but we used to + * have foo/bar which now does not exist; + * it is normal for the empty directory 'foo' + * to remain. + */ + ref_file = git_path("%s", orig_ref); + if (remove_empty_directories(ref_file)) + die("there are still refs under '%s'", orig_ref); + ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL); + } if (!ref) { int last_errno = errno; error("unable to resolve reference %s: %s", -- cgit v1.3 From 5e290ff75a0e6996f297dc438aceb8e1f29a20a5 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 12:37:37 -0700 Subject: refs: minor restructuring of cached refs data. Once we read packed and loose refs, for_each_ref() and friends kept using them even after write_ref_sha1() and delete_ref() changed the refs. This adds invalidate_cached_refs() as a way to flush the cached information. Signed-off-by: Junio C Hamano --- refs.c | 56 +++++++++++++++++++++++++++++++++++++++++++------------- 1 file changed, 43 insertions(+), 13 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index b433c0c2d0..6ee5f96943 100644 --- a/refs.c +++ b/refs.c @@ -66,12 +66,42 @@ static struct ref_list *add_ref(const char *name, const unsigned char *sha1, return list; } -static struct ref_list *get_packed_refs(void) +/* + * Future: need to be in "struct repository" + * when doing a full libification. + */ +struct cached_refs { + char did_loose; + char did_packed; + struct ref_list *loose; + struct ref_list *packed; +} cached_refs; + +static void free_ref_list(struct ref_list *list) +{ + struct ref_list *next; + for ( ; list; list = next) { + next = list->next; + free(list); + } +} + +static void invalidate_cached_refs(void) { - static int did_refs = 0; - static struct ref_list *refs = NULL; + struct cached_refs *ca = &cached_refs; + + if (ca->did_loose && ca->loose) + free_ref_list(ca->loose); + if (ca->did_packed && ca->packed) + free_ref_list(ca->packed); + ca->loose = ca->packed = NULL; + ca->did_loose = ca->did_packed = 0; +} - if (!did_refs) { +static struct ref_list *get_packed_refs(void) +{ + if (!cached_refs.did_packed) { + struct ref_list *refs = NULL; FILE *f = fopen(git_path("packed-refs"), "r"); if (f) { struct ref_list *list = NULL; @@ -86,9 +116,10 @@ static struct ref_list *get_packed_refs(void) fclose(f); refs = list; } - did_refs = 1; + cached_refs.packed = refs; + cached_refs.did_packed = 1; } - return refs; + return cached_refs.packed; } static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) @@ -138,14 +169,11 @@ static struct ref_list *get_ref_dir(const char *base, struct ref_list *list) static struct ref_list *get_loose_refs(void) { - static int did_refs = 0; - static struct ref_list *refs = NULL; - - if (!did_refs) { - refs = get_ref_dir("refs", NULL); - did_refs = 1; + if (!cached_refs.did_loose) { + cached_refs.loose = get_ref_dir("refs", NULL); + cached_refs.did_loose = 1; } - return refs; + return cached_refs.loose; } /* We allow "recursive" symbolic refs. Only within reason, though */ @@ -401,6 +429,7 @@ int delete_ref(const char *refname, unsigned char *sha1) fprintf(stderr, "warning: unlink(%s) failed: %s", lock->log_file, strerror(errno)); + invalidate_cached_refs(); return ret; } @@ -665,6 +694,7 @@ int write_ref_sha1(struct ref_lock *lock, unlock_ref(lock); return -1; } + invalidate_cached_refs(); if (log_ref_write(lock, sha1, logmsg) < 0) { unlock_ref(lock); return -1; -- cgit v1.3 From 5cc3cef997503e7543d927dbe23daca891131168 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 14:14:31 -0700 Subject: lock_ref_sha1(): do not sometimes error() and sometimes die(). This cleans up the error path in the function so it does not die() itself sometimes while signalling an error with NULL some other times which was inconsistent and confusing. Signed-off-by: Junio C Hamano --- refs.c | 26 ++++++++++++++++++-------- 1 file changed, 18 insertions(+), 8 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 6ee5f96943..157de432ce 100644 --- a/refs.c +++ b/refs.c @@ -561,6 +561,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char const char *orig_ref = ref; struct ref_lock *lock; struct stat st; + int last_errno = 0; int mustexist = (old_sha1 && !is_null_sha1(old_sha1)); lock = xcalloc(1, sizeof(struct ref_lock)); @@ -574,17 +575,18 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char * to remain. */ ref_file = git_path("%s", orig_ref); - if (remove_empty_directories(ref_file)) - die("there are still refs under '%s'", orig_ref); + if (remove_empty_directories(ref_file)) { + last_errno = errno; + error("there are still refs under '%s'", orig_ref); + goto error_return; + } ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL); } if (!ref) { - int last_errno = errno; + last_errno = errno; error("unable to resolve reference %s: %s", orig_ref, strerror(errno)); - unlock_ref(lock); - errno = last_errno; - return NULL; + goto error_return; } lock->lk = xcalloc(1, sizeof(struct lock_file)); @@ -593,11 +595,19 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char ref_file = git_path("%s", ref); lock->force_write = lstat(ref_file, &st) && errno == ENOENT; - if (safe_create_leading_directories(ref_file)) - die("unable to create directory for %s", ref_file); + if (safe_create_leading_directories(ref_file)) { + last_errno = errno; + error("unable to create directory for %s", ref_file); + goto error_return; + } lock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1); return old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock; + + error_return: + unlock_ref(lock); + errno = last_errno; + return NULL; } struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) -- cgit v1.3 From 22a3844ebada55c207e2b85fb68509a7c9e2b5f0 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 14:19:25 -0700 Subject: lock_ref_sha1(): check D/F conflict with packed ref when creating. This makes the ref locking codepath to notice if an existing ref overlaps with the ref we are creating. Signed-off-by: Junio C Hamano --- refs.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 157de432ce..2bfa92a8a3 100644 --- a/refs.c +++ b/refs.c @@ -588,6 +588,30 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char orig_ref, strerror(errno)); goto error_return; } + if (is_null_sha1(lock->old_sha1)) { + /* The ref did not exist and we are creating it. + * Make sure there is no existing ref that is packed + * whose name begins with our refname, nor a ref whose + * name is a proper prefix of our refname. + */ + int namlen = strlen(ref); /* e.g. 'foo/bar' */ + struct ref_list *list = get_packed_refs(); + while (list) { + /* list->name could be 'foo' or 'foo/bar/baz' */ + int len = strlen(list->name); + int cmplen = (namlen < len) ? namlen : len; + const char *lead = (namlen < len) ? list->name : ref; + + if (!strncmp(ref, list->name, cmplen) && + lead[cmplen] == '/') { + error("'%s' exists; cannot create '%s'", + list->name, ref); + goto error_return; + } + list = list->next; + } + } + lock->lk = xcalloc(1, sizeof(struct lock_file)); lock->ref_name = xstrdup(ref); -- cgit v1.3 From c0277d15effb9efcaaaa0cd84decf71a327ac07b Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sat, 30 Sep 2006 15:02:00 -0700 Subject: delete_ref(): delete packed ref This implements deletion of a packed ref. Since it is a very rare event to delete a ref compared to looking up, creating and updating, this opts to remove the ref from the packed-ref file instead of doing any of the filesystem based "negative ref" trick to optimize the deletion path. Signed-off-by: Junio C Hamano --- refs.c | 109 ++++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 77 insertions(+), 32 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 2bfa92a8a3..858c53445c 100644 --- a/refs.c +++ b/refs.c @@ -406,33 +406,6 @@ int get_ref_sha1(const char *ref, unsigned char *sha1) return read_ref(mkpath("refs/%s", ref), sha1); } -int delete_ref(const char *refname, unsigned char *sha1) -{ - struct ref_lock *lock; - int err, i, ret = 0; - - lock = lock_any_ref_for_update(refname, sha1); - if (!lock) - return 1; - i = strlen(lock->lk->filename) - 5; /* .lock */ - lock->lk->filename[i] = 0; - err = unlink(lock->lk->filename); - if (err) { - ret = 1; - error("unlink(%s) failed: %s", - lock->lk->filename, strerror(errno)); - } - lock->lk->filename[i] = '.'; - - err = unlink(lock->log_file); - if (err && errno != ENOENT) - fprintf(stderr, "warning: unlink(%s) failed: %s", - lock->log_file, strerror(errno)); - - invalidate_cached_refs(); - return ret; -} - /* * Make sure "ref" is something reasonable to have under ".git/refs/"; * We do not like it if: @@ -555,7 +528,7 @@ static int remove_empty_directories(char *file) return remove_empty_dir_recursive(path, len); } -static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1) +static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag) { char *ref_file; const char *orig_ref = ref; @@ -567,7 +540,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char lock = xcalloc(1, sizeof(struct ref_lock)); lock->lock_fd = -1; - ref = resolve_ref(ref, lock->old_sha1, mustexist, NULL); + ref = resolve_ref(ref, lock->old_sha1, mustexist, flag); if (!ref && errno == EISDIR) { /* we are trying to lock foo but we used to * have foo/bar which now does not exist; @@ -580,7 +553,7 @@ static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char error("there are still refs under '%s'", orig_ref); goto error_return; } - ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL); + ref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag); } if (!ref) { last_errno = errno; @@ -640,12 +613,84 @@ struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) if (check_ref_format(ref)) return NULL; strcpy(refpath, mkpath("refs/%s", ref)); - return lock_ref_sha1_basic(refpath, old_sha1); + return lock_ref_sha1_basic(refpath, old_sha1, NULL); } struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1) { - return lock_ref_sha1_basic(ref, old_sha1); + return lock_ref_sha1_basic(ref, old_sha1, NULL); +} + +static int repack_without_ref(const char *refname) +{ + struct ref_list *list, *packed_ref_list; + int fd; + int found = 0; + struct lock_file packlock; + + packed_ref_list = get_packed_refs(); + for (list = packed_ref_list; list; list = list->next) { + if (!strcmp(refname, list->name)) { + found = 1; + break; + } + } + if (!found) + return 0; + memset(&packlock, 0, sizeof(packlock)); + fd = hold_lock_file_for_update(&packlock, git_path("packed-refs"), 0); + if (fd < 0) + return error("cannot delete '%s' from packed refs", refname); + + for (list = packed_ref_list; list; list = list->next) { + char line[PATH_MAX + 100]; + int len; + + if (!strcmp(refname, list->name)) + continue; + len = snprintf(line, sizeof(line), "%s %s\n", + sha1_to_hex(list->sha1), list->name); + /* this should not happen but just being defensive */ + if (len > sizeof(line)) + die("too long a refname '%s'", list->name); + write_or_die(fd, line, len); + } + return commit_lock_file(&packlock); +} + +int delete_ref(const char *refname, unsigned char *sha1) +{ + struct ref_lock *lock; + int err, i, ret = 0, flag = 0; + + lock = lock_ref_sha1_basic(refname, sha1, &flag); + if (!lock) + return 1; + if (!(flag & REF_ISPACKED)) { + /* loose */ + i = strlen(lock->lk->filename) - 5; /* .lock */ + lock->lk->filename[i] = 0; + err = unlink(lock->lk->filename); + if (err) { + ret = 1; + error("unlink(%s) failed: %s", + lock->lk->filename, strerror(errno)); + } + lock->lk->filename[i] = '.'; + } + /* removing the loose one could have resurrected an earlier + * packed one. Also, if it was not loose we need to repack + * without it. + */ + ret |= repack_without_ref(refname); + + err = unlink(lock->log_file); + if (err && errno != ENOENT) + fprintf(stderr, "warning: unlink(%s) failed: %s", + lock->log_file, strerror(errno)); + invalidate_cached_refs(); + unlock_ref(lock); + return ret; } void unlock_ref(struct ref_lock *lock) -- cgit v1.3 From 28bed6ea2198f6589ad43e48666906a879839442 Mon Sep 17 00:00:00 2001 From: Christian Couder Date: Sun, 1 Oct 2006 14:36:49 +0200 Subject: Fix a remove_empty_dir_recursive problem. Signed-off-by: Christian Couder Signed-off-by: Junio C Hamano --- refs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 858c53445c..221eb3896e 100644 --- a/refs.c +++ b/refs.c @@ -498,7 +498,7 @@ static int remove_empty_dir_recursive(char *path, int len) strcpy(path + len, e->d_name) && !lstat(path, &st) && S_ISDIR(st.st_mode) && - remove_empty_dir_recursive(path, len + namlen)) + !remove_empty_dir_recursive(path, len + namlen)) continue; /* happy */ /* path too long, stat fails, or non-directory still exists */ -- cgit v1.3 From 26a063a10bca57f65d8fed6c4550a70d44a70b81 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 1 Oct 2006 11:41:00 -0700 Subject: Fix refs.c;:repack_without_ref() clean-up path The function repack_without_ref() passes a lock-file structure on the stack to hold_lock_file_for_update(), which in turn registers it to be cleaned up via atexit(). This is a big no-no. This is the same bug James Bottomley fixed with commit 31f584c242e7af28018ff920b6c8d1952beadbd4. Signed-off-by: Junio C Hamano --- refs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 221eb3896e..aa4c4e0b94 100644 --- a/refs.c +++ b/refs.c @@ -621,12 +621,13 @@ struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *o return lock_ref_sha1_basic(ref, old_sha1, NULL); } +static struct lock_file packlock; + static int repack_without_ref(const char *refname) { struct ref_list *list, *packed_ref_list; int fd; int found = 0; - struct lock_file packlock; packed_ref_list = get_packed_refs(); for (list = packed_ref_list; list; list = list->next) { -- cgit v1.3 From 7a21632fa346df58d94d32f09625025931ef13ec Mon Sep 17 00:00:00 2001 From: Dennis Stosberg Date: Mon, 2 Oct 2006 19:23:53 +0200 Subject: lock_ref_sha1_basic does not remove empty directories on BSD lock_ref_sha1_basic relies on errno beeing set to EISDIR by the call to read() in resolve_ref() to detect directories. But calling read() on a directory under NetBSD returns EPERM, and even succeeds for local filesystems on FreeBSD. Signed-off-by: Dennis Stosberg Signed-off-by: Junio C Hamano --- refs.c | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 5e653141ce..98327d7983 100644 --- a/refs.c +++ b/refs.c @@ -42,6 +42,12 @@ const char *resolve_ref(const char *path, unsigned char *sha1, int reading) } } + /* Is it a directory? */ + if (S_ISDIR(st.st_mode)) { + errno = EISDIR; + return NULL; + } + /* * Anything else, just open it and try to use it as * a ref -- cgit v1.3 From ab2a1a32ffa5a39aaf4204bd717562bce49e0a36 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 5 Oct 2006 23:16:15 -0700 Subject: ref-log: allow ref@{count} syntax. Often I find myself wanting to say 'tip of "next" before I merged the last three topics'. Now I can say that with: git log next@{3}..next Since small integers alone are invalid input strings to approxidate, there is no fear of confusion. Signed-off-by: Junio C Hamano --- refs.c | 6 ++++-- refs.h | 2 +- sha1_name.c | 44 +++++++++++++++++++++++++++----------------- 3 files changed, 32 insertions(+), 20 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 305c1a92cc..d7f4aa5d87 100644 --- a/refs.c +++ b/refs.c @@ -795,7 +795,7 @@ int write_ref_sha1(struct ref_lock *lock, return 0; } -int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) +int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1) { const char *logfile, *logdata, *logend, *rec, *lastgt, *lastrec; char *tz_c; @@ -828,7 +828,7 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) if (!lastgt) die("Log %s is corrupt.", logfile); date = strtoul(lastgt + 1, &tz_c, 10); - if (date <= at_time) { + if (date <= at_time || cnt == 0) { if (lastrec) { if (get_sha1_hex(lastrec, logged_sha1)) die("Log %s is corrupt.", logfile); @@ -859,6 +859,8 @@ int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1) return 0; } lastrec = rec; + if (cnt > 0) + cnt--; } rec = logdata; diff --git a/refs.h b/refs.h index 0d4d79e03f..a57d43726a 100644 --- a/refs.h +++ b/refs.h @@ -39,7 +39,7 @@ extern void unlock_ref(struct ref_lock *lock); extern int write_ref_sha1(struct ref_lock *lock, const unsigned char *sha1, const char *msg); /** Reads log for the value of ref during at_time. **/ -extern int read_ref_at(const char *ref, unsigned long at_time, unsigned char *sha1); +extern int read_ref_at(const char *ref, unsigned long at_time, int cnt, unsigned char *sha1); /** Returns 0 if target has the right format for a ref. **/ extern int check_ref_format(const char *target); diff --git a/sha1_name.c b/sha1_name.c index ed711f2079..e5170336ff 100644 --- a/sha1_name.c +++ b/sha1_name.c @@ -249,24 +249,23 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) static const char *warning = "warning: refname '%.*s' is ambiguous.\n"; const char **p, *ref; char *real_ref = NULL; - int refs_found = 0, am; - unsigned long at_time = (unsigned long)-1; + int refs_found = 0; + int at, reflog_len; unsigned char *this_result; unsigned char sha1_from_ref[20]; if (len == 40 && !get_sha1_hex(str, sha1)) return 0; - /* At a given period of time? "@{2 hours ago}" */ - for (am = 1; am < len - 1; am++) { - if (str[am] == '@' && str[am+1] == '{' && str[len-1] == '}') { - int date_len = len - am - 3; - char *date_spec = xmalloc(date_len + 1); - strlcpy(date_spec, str + am + 2, date_len + 1); - at_time = approxidate(date_spec); - free(date_spec); - len = am; - break; + /* basic@{time or number} format to query ref-log */ + reflog_len = 0; + if (str[len-1] == '}') { + for (at = 1; at < len - 1; at++) { + if (str[at] == '@' && str[at+1] == '{') { + reflog_len = (len-1) - (at+2); + len = at; + break; + } } } @@ -291,11 +290,22 @@ static int get_sha1_basic(const char *str, int len, unsigned char *sha1) if (warn_ambiguous_refs && refs_found > 1) fprintf(stderr, warning, len, str); - if (at_time != (unsigned long)-1) { - read_ref_at( - real_ref, - at_time, - sha1); + if (reflog_len) { + /* Is it asking for N-th entry, or approxidate? */ + int nth, i; + unsigned long at_time; + for (i = nth = 0; 0 <= nth && i < reflog_len; i++) { + char ch = str[at+2+i]; + if ('0' <= ch && ch <= '9') + nth = nth * 10 + ch - '0'; + else + nth = -1; + } + if (0 <= nth) + at_time = 0; + else + at_time = approxidate(str + at + 2); + read_ref_at(real_ref, at_time, nth, sha1); } free(real_ref); -- cgit v1.3 From 4057deb5de110176ac19519177654108607b685c Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Sun, 8 Oct 2006 01:35:18 -0700 Subject: core.logallrefupdates create new log file only for branch heads. It used to mean "create log file for any ref that is updated", but now it creates new log files only for branch heads. The old behaviour made this configuration less useful than otherwise it would be; automatically creating log file for tags is almost always not useful. Signed-off-by: Junio C Hamano --- Documentation/config.txt | 16 ++++++++++------ refs.c | 3 ++- 2 files changed, 12 insertions(+), 7 deletions(-) (limited to 'refs.c') diff --git a/Documentation/config.txt b/Documentation/config.txt index 84e38911ee..232e2a9732 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -71,12 +71,16 @@ core.preferSymlinkRefs:: expect HEAD to be a symbolic link. core.logAllRefUpdates:: - If true, `git-update-ref` will append a line to - "$GIT_DIR/logs/" listing the new SHA1 and the date/time - of the update. If the file does not exist it will be - created automatically. This information can be used to - determine what commit was the tip of a branch "2 days ago". - This value is false by default (no logging). + Updates to a ref is logged to the file + "$GIT_DIR/logs/", by appending the new and old + SHA1, the date/time and the reason of the update, but + only when the file exists. If this configuration + variable is set to true, missing "$GIT_DIR/logs/" + file is automatically created for branch heads. + + This information can be used to determine what commit + was the tip of a branch "2 days ago". This value is + false by default (no automated creation of log files). core.repositoryFormatVersion:: Internal variable identifying the repository format and layout diff --git a/refs.c b/refs.c index 305c1a92cc..75a0d7b064 100644 --- a/refs.c +++ b/refs.c @@ -721,7 +721,8 @@ static int log_ref_write(struct ref_lock *lock, char *logrec; const char *committer; - if (log_all_ref_updates) { + if (log_all_ref_updates && + !strncmp(lock->ref_name, "refs/heads/", 11)) { if (safe_create_leading_directories(lock->log_file) < 0) return error("unable to create directory for %s", lock->log_file); -- cgit v1.3 From 1974bf620b436b014bfe86179ff76485610a4887 Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Mon, 9 Oct 2006 21:15:59 -0700 Subject: core.logallrefupdates thinko-fix --- refs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 75a0d7b064..3d100df85c 100644 --- a/refs.c +++ b/refs.c @@ -731,7 +731,7 @@ static int log_ref_write(struct ref_lock *lock, logfd = open(lock->log_file, oflags, 0666); if (logfd < 0) { - if (!log_all_ref_updates && errno == ENOENT) + if (!(oflags & O_CREAT) && errno == ENOENT) return 0; return error("Unable to append to %s: %s", lock->log_file, strerror(errno)); -- cgit v1.3 From 3b463c3f02f83ef0bce2d5daa193459418e5258f Mon Sep 17 00:00:00 2001 From: Junio C Hamano Date: Thu, 19 Oct 2006 01:28:47 -0700 Subject: ref-log: fix D/F conflict coming from deleted refs. After deleting a branch l/k, you should be able to create a branch l. Earlier we added remove_empty_directories() on the ref creation side to remove leftover .git/refs/l directory but we also need a matching code to remove .git/logs/refs/l directory. Signed-off-by: Junio C Hamano --- refs.c | 14 ++++++++++++-- t/t3210-pack-refs.sh | 2 ++ 2 files changed, 14 insertions(+), 2 deletions(-) (limited to 'refs.c') diff --git a/refs.c b/refs.c index 3d100df85c..ed2e3b16a7 100644 --- a/refs.c +++ b/refs.c @@ -733,8 +733,18 @@ static int log_ref_write(struct ref_lock *lock, if (logfd < 0) { if (!(oflags & O_CREAT) && errno == ENOENT) return 0; - return error("Unable to append to %s: %s", - lock->log_file, strerror(errno)); + + if ((oflags & O_CREAT) && errno == EISDIR) { + if (remove_empty_directories(lock->log_file)) { + return error("There are still logs under '%s'", + lock->log_file); + } + logfd = open(lock->log_file, oflags, 0666); + } + + if (logfd < 0) + return error("Unable to append to %s: %s", + lock->log_file, strerror(errno)); } committer = git_committer_info(1); diff --git a/t/t3210-pack-refs.sh b/t/t3210-pack-refs.sh index a4fbfda467..b1e9f2eed2 100755 --- a/t/t3210-pack-refs.sh +++ b/t/t3210-pack-refs.sh @@ -11,6 +11,8 @@ semantic is still the same. ' . ./test-lib.sh +echo '[core] logallrefupdates = true' >>.git/config + test_expect_success \ 'prepare a trivial repository' \ 'echo Hello > A && -- cgit v1.3