[PATCH] Store peeled refs in packed-refs (take 2).

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



This fixes the previous implementation which failed to optimize
repositories with tons of lightweight tags.  The updated
packed-refs format begins with "# packed-refs with:" line that
lists the kind of extended data the file records.  Currently,
there is only one such extension defined, "peeled".  This stores
the "peeled tag" on a line that immediately follows a line for a
tag object itself in the format "^<sha-1>".

The header line itself and any extended data are ignored by
older implementation, so packed-refs file generated with this
version can still be used by older git.  packed-refs made by
older git can of course be used with this version.

Signed-off-by: Junio C Hamano <junkio@xxxxxxx>
---
 builtin-pack-refs.c |    8 +++-
 builtin-show-ref.c  |    6 ++-
 refs.c              |  111 +++++++++++++++++++++++++++++---------------------
 refs.h              |    7 +--
 4 files changed, 77 insertions(+), 55 deletions(-)

diff --git a/builtin-pack-refs.c b/builtin-pack-refs.c
index ee5a556..8dc5b9e 100644
--- a/builtin-pack-refs.c
+++ b/builtin-pack-refs.c
@@ -46,8 +46,8 @@ static int handle_one_ref(const char *pa
 		if (o->type == OBJ_TAG) {
 			o = deref_tag(o, path, 0);
 			if (o)
-				fprintf(cb->refs_file, "%s  %s^{}\n",
-					sha1_to_hex(o->sha1), path);
+				fprintf(cb->refs_file, "^%s\n",
+					sha1_to_hex(o->sha1));
 		}
 	}
 
@@ -111,6 +111,10 @@ int cmd_pack_refs(int argc, const char *
 	if (!cbdata.refs_file)
 		die("unable to create ref-pack file structure (%s)",
 		    strerror(errno));
+
+	/* perhaps other traits later as well */
+	fprintf(cbdata.refs_file, "# pack-refs with: peeled \n");
+
 	for_each_ref(handle_one_ref, &cbdata);
 	fflush(cbdata.refs_file);
 	fsync(fd);
diff --git a/builtin-show-ref.c b/builtin-show-ref.c
index 9ae3d08..0739798 100644
--- a/builtin-show-ref.c
+++ b/builtin-show-ref.c
@@ -67,8 +67,10 @@ match:
 		return 0;
 
 	if ((flag & REF_ISPACKED) && !peel_ref(refname, peeled)) {
-		hex = find_unique_abbrev(peeled, abbrev);
-		printf("%s %s^{}\n", hex, refname);
+		if (!is_null_sha1(peeled)) {
+			hex = find_unique_abbrev(peeled, abbrev);
+			printf("%s %s^{}\n", hex, refname);
+		}
 	}
 	else {
 		obj = parse_object(sha1);
diff --git a/refs.c b/refs.c
index 75cbc0e..96ea8b6 100644
--- a/refs.c
+++ b/refs.c
@@ -5,14 +5,18 @@
 
 #include <errno.h>
 
+/* ISSYMREF=01 and ISPACKED=02 are public interfaces */
+#define REF_KNOWS_PEELED 04
+
 struct ref_list {
 	struct ref_list *next;
-	unsigned char flag; /* ISSYMREF? ISPACKED? ISPEELED? */
+	unsigned char flag; /* ISSYMREF? ISPACKED? */
 	unsigned char sha1[20];
+	unsigned char peeled[20];
 	char name[FLEX_ARRAY];
 };
 
-static const char *parse_ref_line(char *line, unsigned char *sha1, int *flag)
+static const char *parse_ref_line(char *line, unsigned char *sha1)
 {
 	/*
 	 * 42: the answer to everything.
@@ -23,7 +27,6 @@ static const char *parse_ref_line(char *
 	 *  +1 (newline at the end of the line)
 	 */
 	int len = strlen(line) - 42;
-	int peeled = 0;
 
 	if (len <= 0)
 		return NULL;
@@ -32,29 +35,18 @@ static const char *parse_ref_line(char *
 	if (!isspace(line[40]))
 		return NULL;
 	line += 41;
-
-	if (isspace(*line)) {
-		/* "SHA-1 SP SP refs/tags/tagname^{} LF"? */
-		line++;
-		len--;
-		peeled = 1;
-	}
+	if (isspace(*line))
+		return NULL;
 	if (line[len] != '\n')
 		return NULL;
 	line[len] = 0;
 
-	if (peeled && (len < 3 || strcmp(line + len - 3, "^{}")))
-		return NULL;
-
-	if (!peeled)
-		*flag &= ~REF_ISPEELED;
-	else
-		*flag |= REF_ISPEELED;
 	return line;
 }
 
 static struct ref_list *add_ref(const char *name, const unsigned char *sha1,
-				int flag, struct ref_list *list)
+				int flag, struct ref_list *list,
+				struct ref_list **new_entry)
 {
 	int len;
 	struct ref_list **p = &list, *entry;
@@ -66,8 +58,11 @@ static struct ref_list *add_ref(const ch
 			break;
 
 		/* Same as existing entry? */
-		if (!cmp)
+		if (!cmp) {
+			if (new_entry)
+				*new_entry = entry;
 			return list;
+		}
 		p = &entry->next;
 	}
 
@@ -75,10 +70,13 @@ static struct ref_list *add_ref(const ch
 	len = strlen(name) + 1;
 	entry = xmalloc(sizeof(struct ref_list) + len);
 	hashcpy(entry->sha1, sha1);
+	hashclr(entry->peeled);
 	memcpy(entry->name, name, len);
 	entry->flag = flag;
 	entry->next = *p;
 	*p = entry;
+	if (new_entry)
+		*new_entry = entry;
 	return list;
 }
 
@@ -114,27 +112,50 @@ static void invalidate_cached_refs(void)
 	ca->did_loose = ca->did_packed = 0;
 }
 
+static void read_packed_refs(FILE *f, struct cached_refs *cached_refs)
+{
+	struct ref_list *list = NULL;
+	struct ref_list *last = NULL;
+	char refline[PATH_MAX];
+	int flag = REF_ISPACKED;
+
+	while (fgets(refline, sizeof(refline), f)) {
+		unsigned char sha1[20];
+		const char *name;
+		static const char header[] = "# pack-refs with:";
+
+		if (!strncmp(refline, header, sizeof(header)-1)) {
+			const char *traits = refline + sizeof(header) - 1;
+			if (strstr(traits, " peeled "))
+				flag |= REF_KNOWS_PEELED;
+			/* perhaps other traits later as well */
+			continue;
+		}
+
+		name = parse_ref_line(refline, sha1);
+		if (name) {
+			list = add_ref(name, sha1, flag, list, &last);
+			continue;
+		}
+		if (last &&
+		    refline[0] == '^' &&
+		    strlen(refline) == 42 &&
+		    refline[41] == '\n' &&
+		    !get_sha1_hex(refline + 1, sha1))
+			hashcpy(last->peeled, sha1);
+	}
+	cached_refs->packed = list;
+}
+
 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");
+		cached_refs.packed = NULL;
 		if (f) {
-			struct ref_list *list = NULL;
-			char refline[PATH_MAX];
-			while (fgets(refline, sizeof(refline), f)) {
-				unsigned char sha1[20];
-				int flag = REF_ISPACKED;
-				const char *name =
-					parse_ref_line(refline, sha1, &flag);
-				if (!name)
-					continue;
-				list = add_ref(name, sha1, flag, list);
-			}
+			read_packed_refs(f, &cached_refs);
 			fclose(f);
-			refs = list;
 		}
-		cached_refs.packed = refs;
 		cached_refs.did_packed = 1;
 	}
 	return cached_refs.packed;
@@ -177,7 +198,7 @@ static struct ref_list *get_ref_dir(cons
 				error("%s points nowhere!", ref);
 				continue;
 			}
-			list = add_ref(ref, sha1, flag, list);
+			list = add_ref(ref, sha1, flag, list, NULL);
 		}
 		free(ref);
 		closedir(dir);
@@ -225,8 +246,7 @@ const char *resolve_ref(const char *ref,
 		if (lstat(path, &st) < 0) {
 			struct ref_list *list = get_packed_refs();
 			while (list) {
-				if (!(list->flag & REF_ISPEELED) &&
-				    !strcmp(ref, list->name)) {
+				if (!strcmp(ref, list->name)) {
 					hashcpy(sha1, list->sha1);
 					if (flag)
 						*flag |= REF_ISPACKED;
@@ -348,8 +368,6 @@ static int do_one_ref(const char *base,
 		return 0;
 	if (is_null_sha1(entry->sha1))
 		return 0;
-	if (entry->flag & REF_ISPEELED)
-		return 0;
 	if (!has_sha1_file(entry->sha1)) {
 		error("%s does not point to a valid object!", entry->name);
 		return 0;
@@ -368,22 +386,21 @@ int peel_ref(const char *ref, unsigned c
 
 	if ((flag & REF_ISPACKED)) {
 		struct ref_list *list = get_packed_refs();
-		int len = strlen(ref);
 
 		while (list) {
-			if ((list->flag & REF_ISPEELED) &&
-			    !strncmp(list->name, ref, len) &&
-			    strlen(list->name) == len + 3 &&
-			    !strcmp(list->name + len, "^{}")) {
-				hashcpy(sha1, list->sha1);
-				return 0;
+			if (!strcmp(list->name, ref)) {
+				if (list->flag & REF_KNOWS_PEELED) {
+					hashcpy(sha1, list->peeled);
+					return 0;
+				}
+				/* older pack-refs did not leave peeled ones */
+				break;
 			}
 			list = list->next;
 		}
-		/* older pack-refs did not leave peeled ones in */
 	}
 
-	/* otherwise ... */
+	/* fallback - callers should not call this for unpacked refs */
 	o = parse_object(base);
 	if (o->type == OBJ_TAG) {
 		o = deref_tag(o, ref, 0);
diff --git a/refs.h b/refs.h
index 40048a6..cd1e1d6 100644
--- a/refs.h
+++ b/refs.h
@@ -10,14 +10,13 @@ struct ref_lock {
 	int force_write;
 };
 
+#define REF_ISSYMREF 01
+#define REF_ISPACKED 02
+
 /*
  * Calls the specified function for each ref file until it returns nonzero,
  * and returns the value
  */
-#define REF_ISSYMREF 01
-#define REF_ISPACKED 02
-#define REF_ISPEELED 04 /* internal use */
-
 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 *);
-- 
1.4.4.g26805


-
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]