summaryrefslogtreecommitdiff
path: root/refs.c
diff options
context:
space:
mode:
authorLinus Torvalds <torvalds@osdl.org>2006-09-11 23:37:32 (GMT)
committerJunio C Hamano <junkio@cox.net>2006-09-18 02:09:11 (GMT)
commite1e22e37f47e3f4d741d28920e1d27e3775c31ad (patch)
treebfecb4750f1bd33a27dbf663001b17597e73cdd2 /refs.c
parent808239a7db1b4c04c5a9edcf9ee33ff33217fad2 (diff)
downloadgit-e1e22e37f47e3f4d741d28920e1d27e3775c31ad.zip
git-e1e22e37f47e3f4d741d28920e1d27e3775c31ad.tar.gz
git-e1e22e37f47e3f4d741d28920e1d27e3775c31ad.tar.bz2
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 <torvalds@osdl.org> Signed-off-by: Junio C Hamano <junkio@cox.net>
Diffstat (limited to 'refs.c')
-rw-r--r--refs.c226
1 files changed, 173 insertions, 53 deletions
diff --git a/refs.c b/refs.c
index 5e65314..5f80a68 100644
--- a/refs.c
+++ b/refs.c
@@ -3,6 +3,145 @@
#include <errno.h>
+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)