{"thread":{"id":"5537","subject":"Allow multiple \"git_path()\" uses","startedAt":"2006-09-11T19:03:15Z","lastAt":"2006-09-22T04:57:54Z","messageCount":21,"participants":["Linus Torvalds","Junio C Hamano","Chris Wedgwood","Jeff King"],"isPatch":false,"patchVersion":null,"patchTotal":null},"messages":[{"id":"26741","messageId":"Pine.LNX.4.64.0609111158390.3960@g5.osdl.org","threadId":"5537","inReplyTo":null,"subject":"Allow multiple \"git_path()\" uses","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-11T19:03:15Z","receivedAt":"2006-09-11T19:03:15Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis allows you to maintain a few filesystem pathnames concurrently, by \nsimply replacing the single static \"pathname\" buffer with a LRU of four \nbuffers.\n\nWe did exactly the same thing with sha1_to_hex(), for pretty much exactly \nthe same reason. Sometimes you want to use two pathnames, and while it's \neasy enough to xstrdup() them, why not just do the LU buffer thing.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\n[ This actually came up when I was playing with git_path(\"refs-packed\") \n  when following refs, so while it doesn't hit us right now, it's for some \n  future work.. ]\n\ndiff --git a/path.c b/path.c\nindex db8905f..bb89fb0 100644\n--- a/path.c\n+++ b/path.c\n@@ -13,9 +13,15 @@\n #include \"cache.h\"\n #include <pwd.h>\n \n-static char pathname[PATH_MAX];\n static char bad_path[] = \"/bad-path/\";\n \n+static char *get_pathname(void)\n+{\n+\tstatic char pathname_array[4][PATH_MAX];\n+\tstatic int index;\n+\treturn pathname_array[3 & ++index];\n+}\n+\n static char *cleanup_path(char *path)\n {\n \t/* Clean it up */\n@@ -31,6 +37,7 @@ char *mkpath(const char *fmt, ...)\n {\n \tva_list args;\n \tunsigned len;\n+\tchar *pathname = get_pathname();\n \n \tva_start(args, fmt);\n \tlen = vsnprintf(pathname, PATH_MAX, fmt, args);\n@@ -43,6 +50,7 @@ char *mkpath(const char *fmt, ...)\n char *git_path(const char *fmt, ...)\n {\n \tconst char *git_dir = get_git_dir();\n+\tchar *pathname = get_pathname();\n \tva_list args;\n \tunsigned len;\n \n"},{"id":"26750","messageId":"Pine.LNX.4.64.0609111632050.27779@g5.osdl.org","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609111158390.3960@g5.osdl.org","subject":"Start handling references internally as a sorted in-memory list","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-11T23:37:32Z","receivedAt":"2006-09-11T23:37:32Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThis also adds some very rudimentary support for the notion of packed\nrefs.  HOWEVER! At this point it isn't used to actually look up a ref\nyet, only for listing them (ie \"for_each_ref()\" and friends see the\npacked refs, but none of the other single-ref lookup routines).\n\nNote how we keep two separate lists: one for the loose refs, and one for \nthe packed refs we read. That's so that we can easily keep the two apart, \nand read only one set or the other (and still always make sure that the \nloose refs take precedence).\n\n[ From this, it's not actually obvious why we'd keep the two separate \n  lists, but it's important to have the packed refs on their own list \n  later on, when I add support for looking up a single loose one.\n\n  For that case, we will want to read _just_ the packed refs in case the \n  single-ref lookup fails, yet we may end up needing the other list at \n  some point in the future, so keeping them separated is important ]\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n----\n\nThis passes all the tests, but largely because it doesn't yet actually use \nand test the ref-packing code. The code exists, but it doesn't remove old \nrefs yet. It will be easy enough to do with this background code, though: \nI think this is all set up to be reasonably efficient.\n\nAnd yeah, I know that the \"sorting\" code is O(n**2) thanks to doing an \ninsertion sort into a simple linked list. Tough. I didn't care enough to \ndo it well. With \"n\" usually being a few hundred at most, we really don't \ncare, and if we ever do, we _can_ fix it later on to use a heap or \nsomething.\n\ndiff --git a/Makefile b/Makefile\nindex 7b3114f..5a2e946 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -295,7 +295,8 @@ BUILTIN_OBJS = \\\n \tbuiltin-upload-tar.o \\\n \tbuiltin-verify-pack.o \\\n \tbuiltin-write-tree.o \\\n-\tbuiltin-zip-tree.o\n+\tbuiltin-zip-tree.o \\\n+\tbuiltin-pack-refs.o\n \n GITLIBS = $(LIB_FILE) $(XDIFF_LIB)\n LIBS = $(GITLIBS) -lz\ndiff --git a/builtin-pack-refs.c b/builtin-pack-refs.c\nnew file mode 100644\nindex 0000000..0f5d827\n--- /dev/null\n+++ b/builtin-pack-refs.c\n@@ -0,0 +1,41 @@\n+#include \"cache.h\"\n+#include \"refs.h\"\n+\n+static FILE *refs_file;\n+static const char *result_path, *lock_path;\n+\n+static void remove_lock_file(void)\n+{\n+\tif (lock_path)\n+\t\tunlink(lock_path);\n+}\n+\n+static int handle_one_ref(const char *path, const unsigned char *sha1)\n+{\n+\tfprintf(refs_file, \"%s %s\\n\", sha1_to_hex(sha1), path);\n+\treturn 0;\n+}\n+\n+int cmd_pack_refs(int argc, const char **argv, const char *prefix)\n+{\n+\tint fd;\n+\n+\tresult_path = xstrdup(git_path(\"packed-refs\"));\n+\tlock_path = xstrdup(mkpath(\"%s.lock\", result_path));\n+\n+\tfd = open(lock_path, O_CREAT | O_EXCL | O_WRONLY, 0666);\n+\tif (fd < 0)\n+\t\tdie(\"unable to create new ref-pack file (%s)\", strerror(errno));\n+\tatexit(remove_lock_file);\n+\n+\trefs_file = fdopen(fd, \"w\");\n+\tif (!refs_file)\n+\t\tdie(\"unable to create ref-pack file structure (%s)\", strerror(errno));\n+\tfor_each_ref(handle_one_ref);\n+\tfsync(fd);\n+\tfclose(refs_file);\n+\tif (rename(lock_path, result_path) < 0)\n+\t\tdie(\"unable to overwrite old ref-pack file (%s)\", strerror(errno));\n+\tlock_path = NULL;\n+\treturn 0;\n+}\ndiff --git a/builtin.h b/builtin.h\nindex 25431d7..34ed7b9 100644\n--- a/builtin.h\n+++ b/builtin.h\n@@ -61,5 +61,6 @@ extern int cmd_version(int argc, const c\n extern int cmd_whatchanged(int argc, const char **argv, const char *prefix);\n extern int cmd_write_tree(int argc, const char **argv, const char *prefix);\n extern int cmd_verify_pack(int argc, const char **argv, const char *prefix);\n+extern int cmd_pack_refs(int argc, const char **argv, const char *prefix);\n \n #endif\ndiff --git a/git.c b/git.c\nindex 335f405..63d1ba3 100644\n--- a/git.c\n+++ b/git.c\n@@ -266,6 +266,7 @@ static void handle_internal_command(int \n \t\t{ \"whatchanged\", cmd_whatchanged, RUN_SETUP | USE_PAGER },\n \t\t{ \"write-tree\", cmd_write_tree, RUN_SETUP },\n \t\t{ \"verify-pack\", cmd_verify_pack },\n+\t\t{ \"pack-refs\", cmd_pack_refs, RUN_SETUP },\n \t};\n \tint i;\n \ndiff --git a/refs.c b/refs.c\nindex 5e65314..5f80a68 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -3,6 +3,145 @@ #include \"cache.h\"\n \n #include <errno.h>\n \n+struct ref_list {\n+\tstruct ref_list *next;\n+\tunsigned char sha1[20];\n+\tchar name[FLEX_ARRAY];\n+};\n+\n+static const char *parse_ref_line(char *line, unsigned char *sha1)\n+{\n+\t/*\n+\t * 42: the answer to everything.\n+\t *\n+\t * In this case, it happens to be the answer to\n+\t *  40 (length of sha1 hex representation)\n+\t *  +1 (space in between hex and name)\n+\t *  +1 (newline at the end of the line)\n+\t */\n+\tint len = strlen(line) - 42;\n+\n+\tif (len <= 0)\n+\t\treturn NULL;\n+\tif (get_sha1_hex(line, sha1) < 0)\n+\t\treturn NULL;\n+\tif (!isspace(line[40]))\n+\t\treturn NULL;\n+\tline += 41;\n+\tif (line[len] != '\\n')\n+\t\treturn NULL;\n+\tline[len] = 0;\n+\treturn line;\n+}\n+\n+static struct ref_list *add_ref(const char *name, const unsigned char *sha1, struct ref_list *list)\n+{\n+\tint len;\n+\tstruct ref_list **p = &list, *entry;\n+\n+\t/* Find the place to insert the ref into.. */\n+\twhile ((entry = *p) != NULL) {\n+\t\tint cmp = strcmp(entry->name, name);\n+\t\tif (cmp > 0)\n+\t\t\tbreak;\n+\n+\t\t/* Same as existing entry? */\n+\t\tif (!cmp)\n+\t\t\treturn list;\n+\t\tp = &entry->next;\n+\t}\n+\n+\t/* Allocate it and add it in.. */\n+\tlen = strlen(name) + 1;\n+\tentry = xmalloc(sizeof(struct ref_list) + len);\n+\thashcpy(entry->sha1, sha1);\n+\tmemcpy(entry->name, name, len);\n+\tentry->next = *p;\n+\t*p = entry;\n+\treturn list;\n+}\n+\n+static struct ref_list *get_packed_refs(void)\n+{\n+\tstatic int did_refs = 0;\n+\tstatic struct ref_list *refs = NULL;\n+\n+\tif (!did_refs) {\n+\t\tFILE *f = fopen(git_path(\"packed-refs\"), \"r\");\n+\t\tif (f) {\n+\t\t\tstruct ref_list *list = NULL;\n+\t\t\tchar refline[PATH_MAX];\n+\t\t\twhile (fgets(refline, sizeof(refline), f)) {\n+\t\t\t\tunsigned char sha1[20];\n+\t\t\t\tconst char *name = parse_ref_line(refline, sha1);\n+\t\t\t\tif (!name)\n+\t\t\t\t\tcontinue;\n+\t\t\t\tlist = add_ref(name, sha1, list);\n+\t\t\t}\n+\t\t\tfclose(f);\n+\t\t\trefs = list;\n+\t\t}\n+\t\tdid_refs = 1;\n+\t}\n+\treturn refs;\n+}\n+\n+static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)\n+{\n+\tDIR *dir = opendir(git_path(\"%s\", base));\n+\n+\tif (dir) {\n+\t\tstruct dirent *de;\n+\t\tint baselen = strlen(base);\n+\t\tchar *path = xmalloc(baselen + 257);\n+\n+\t\tmemcpy(path, base, baselen);\n+\t\tif (baselen && base[baselen-1] != '/')\n+\t\t\tpath[baselen++] = '/';\n+\n+\t\twhile ((de = readdir(dir)) != NULL) {\n+\t\t\tunsigned char sha1[20];\n+\t\t\tstruct stat st;\n+\t\t\tint namelen;\n+\n+\t\t\tif (de->d_name[0] == '.')\n+\t\t\t\tcontinue;\n+\t\t\tnamelen = strlen(de->d_name);\n+\t\t\tif (namelen > 255)\n+\t\t\t\tcontinue;\n+\t\t\tif (has_extension(de->d_name, \".lock\"))\n+\t\t\t\tcontinue;\n+\t\t\tmemcpy(path + baselen, de->d_name, namelen+1);\n+\t\t\tif (stat(git_path(\"%s\", path), &st) < 0)\n+\t\t\t\tcontinue;\n+\t\t\tif (S_ISDIR(st.st_mode)) {\n+\t\t\t\tlist = get_ref_dir(path, list);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tif (read_ref(git_path(\"%s\", path), sha1) < 0) {\n+\t\t\t\terror(\"%s points nowhere!\", path);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\t\t\tlist = add_ref(path, sha1, list);\n+\t\t}\n+\t\tfree(path);\n+\t\tclosedir(dir);\n+\t}\n+\treturn list;\n+}\n+\n+static struct ref_list *get_loose_refs(void)\n+{\n+\tstatic int did_refs = 0;\n+\tstatic struct ref_list *refs = NULL;\n+\n+\tif (!did_refs) {\n+\t\trefs = get_ref_dir(\"refs\", NULL);\n+\t\tdid_refs = 1;\n+\t}\n+\treturn refs;\n+}\n+\n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define MAXDEPTH 5\n \n@@ -121,60 +260,41 @@ int read_ref(const char *filename, unsig\n \n static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim)\n {\n-\tint retval = 0;\n-\tDIR *dir = opendir(git_path(\"%s\", base));\n-\n-\tif (dir) {\n-\t\tstruct dirent *de;\n-\t\tint baselen = strlen(base);\n-\t\tchar *path = xmalloc(baselen + 257);\n-\n-\t\tif (!strncmp(base, \"./\", 2)) {\n-\t\t\tbase += 2;\n-\t\t\tbaselen -= 2;\n+\tint retval;\n+\tstruct ref_list *packed = get_packed_refs();\n+\tstruct ref_list *loose = get_loose_refs();\n+\n+\twhile (packed && loose) {\n+\t\tstruct ref_list *entry;\n+\t\tint cmp = strcmp(packed->name, loose->name);\n+\t\tif (!cmp) {\n+\t\t\tpacked = packed->next;\n+\t\t\tcontinue;\n \t\t}\n-\t\tmemcpy(path, base, baselen);\n-\t\tif (baselen && base[baselen-1] != '/')\n-\t\t\tpath[baselen++] = '/';\n-\n-\t\twhile ((de = readdir(dir)) != NULL) {\n-\t\t\tunsigned char sha1[20];\n-\t\t\tstruct stat st;\n-\t\t\tint namelen;\n+\t\tif (cmp > 0) {\n+\t\t\tentry = loose;\n+\t\t\tloose = loose->next;\n+\t\t} else {\n+\t\t\tentry = packed;\n+\t\t\tpacked = packed->next;\n+\t\t}\n+\t\tif (strncmp(base, entry->name, trim))\n+\t\t\tcontinue;\n+\t\tretval = fn(entry->name + trim, entry->sha1);\n+\t\tif (retval)\n+\t\t\treturn retval;\n+\t}\n \n-\t\t\tif (de->d_name[0] == '.')\n-\t\t\t\tcontinue;\n-\t\t\tnamelen = strlen(de->d_name);\n-\t\t\tif (namelen > 255)\n-\t\t\t\tcontinue;\n-\t\t\tif (has_extension(de->d_name, \".lock\"))\n-\t\t\t\tcontinue;\n-\t\t\tmemcpy(path + baselen, de->d_name, namelen+1);\n-\t\t\tif (stat(git_path(\"%s\", path), &st) < 0)\n-\t\t\t\tcontinue;\n-\t\t\tif (S_ISDIR(st.st_mode)) {\n-\t\t\t\tretval = do_for_each_ref(path, fn, trim);\n-\t\t\t\tif (retval)\n-\t\t\t\t\tbreak;\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (read_ref(git_path(\"%s\", path), sha1) < 0) {\n-\t\t\t\terror(\"%s points nowhere!\", path);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tif (!has_sha1_file(sha1)) {\n-\t\t\t\terror(\"%s does not point to a valid \"\n-\t\t\t\t      \"commit object!\", path);\n-\t\t\t\tcontinue;\n-\t\t\t}\n-\t\t\tretval = fn(path + trim, sha1);\n+\tpacked = packed ? packed : loose;\n+\twhile (packed) {\n+\t\tif (!strncmp(base, packed->name, trim)) {\n+\t\t\tretval = fn(packed->name + trim, packed->sha1);\n \t\t\tif (retval)\n-\t\t\t\tbreak;\n+\t\t\t\treturn retval;\n \t\t}\n-\t\tfree(path);\n-\t\tclosedir(dir);\n+\t\tpacked = packed->next;\n \t}\n-\treturn retval;\n+\treturn 0;\n }\n \n int head_ref(int (*fn)(const char *path, const unsigned char *sha1))\n@@ -187,22 +307,22 @@ int head_ref(int (*fn)(const char *path,\n \n int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))\n {\n-\treturn do_for_each_ref(\"refs\", fn, 0);\n+\treturn do_for_each_ref(\"refs/\", fn, 0);\n }\n \n int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1))\n {\n-\treturn do_for_each_ref(\"refs/tags\", fn, 10);\n+\treturn do_for_each_ref(\"refs/tags/\", fn, 10);\n }\n \n int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1))\n {\n-\treturn do_for_each_ref(\"refs/heads\", fn, 11);\n+\treturn do_for_each_ref(\"refs/heads/\", fn, 11);\n }\n \n int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))\n {\n-\treturn do_for_each_ref(\"refs/remotes\", fn, 13);\n+\treturn do_for_each_ref(\"refs/remotes/\", fn, 13);\n }\n \n int get_ref_sha1(const char *ref, unsigned char *sha1)\n"},{"id":"26753","messageId":"Pine.LNX.4.64.0609111647000.27779@g5.osdl.org","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609111632050.27779@g5.osdl.org","subject":"Re: Start handling references internally as a sorted in-memory list","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-11T23:50:14Z","receivedAt":"2006-09-11T23:50:14Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 11 Sep 2006, Linus Torvalds wrote:\n> \n> And yeah, I know that the \"sorting\" code is O(n**2) thanks to doing an \n> insertion sort into a simple linked list. Tough. I didn't care enough to \n> do it well. With \"n\" usually being a few hundred at most, we really don't \n> care, and if we ever do, we _can_ fix it later on to use a heap or \n> something.\n\nBtw, to expand on that - one of the nice things from this whole re-org of \nhow we handle refs is that when you do\n\n\tgit-rev-parse --all\n\nit now sorts them in a reliable order - by name. Before, they came out in \nsome random order that totally depended on the filesystem that the \nreferences were on.\n\nUse the \"--symbolic --all\" to see this in a more obvious way, before and \nafter.\n\n\t\tLinus\n"},{"id":"26754","messageId":"7vsliyez2m.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609111632050.27779@g5.osdl.org","subject":"Re: Start handling references internally as a sorted in-memory list","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-11T23:57:37Z","receivedAt":"2006-09-11T23:57:37Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> This also adds some very rudimentary support for the notion of packed\n> And yeah, I know that the \"sorting\" code is O(n**2) thanks to doing an \n> insertion sort into a simple linked list. Tough. I didn't care enough to \n> do it well. With \"n\" usually being a few hundred at most, we really don't \n> care, and if we ever do, we _can_ fix it later on to use a heap or \n> something.\n\nI thought what triggered the restructuring of this part was N\nactually being large enough to cause pain to some people, so I\nsuspect later might need to be reasonably soon ;-).\n\n> +static const char *parse_ref_line(char *line, unsigned char *sha1)\n> +{\n> +\t/*\n> +\t * 42: the answer to everything.\n> +\t *\n> +\t * In this case, it happens to be the answer to\n>...\n\nRof,l.\n\nI've been wondering what happens if you pack \"refs/heads/foo\",\ndelete it, and create \"refs/heads/foo/bar\" with your proposal.\nI'll find the answer (which is _not_ \"42\") in the updated\ndo_for_each_ref(), I guess.\n"},{"id":"26758","messageId":"Pine.LNX.4.64.0609111800590.27779@g5.osdl.org","threadId":"5537","inReplyTo":"7vsliyez2m.fsf@assigned-by-dhcp.cox.net","subject":"Re: Start handling references internally as a sorted in-memory list","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-12T01:05:09Z","receivedAt":"2006-09-12T01:05:09Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 11 Sep 2006, Junio C Hamano wrote:\n> \n> I thought what triggered the restructuring of this part was N\n> actually being large enough to cause pain to some people, so I\n> suspect later might need to be reasonably soon ;-).\n\nWell, the current problems is actually related to the _IO_ load of O(n) \n(and to some degree the disk usage), and that is indeed fairly high. The \nnew O(n**2) is all for a (very) simple CPU loop, so I suspect \"n\" has to \nbe in the millions before it even approaches the IO load problem ;)\n\n> I've been wondering what happens if you pack \"refs/heads/foo\",\n> delete it, and create \"refs/heads/foo/bar\" with your proposal.\n> I'll find the answer (which is _not_ \"42\") in the updated\n> do_for_each_ref(), I guess.\n\nRight now, the code in question only contains some preliminary support for \nrefs at all, and it's technically incorrect. If you ever have a \n\"packed-refs\" file that contains _anything_ outside of the actual loose \nrefs, it won't do the right thing.\n\nBut I'd rather have this as a base, than send one huge patch that does \neverything.\n\nFor example, right now the code doesn't support negative refs (even the \nsimple kind) at all, so you don't need to even have the \"file turned into \na directory\" case for it to \"not work\".\n\nSo right now it's basically just scaffolding. It's hopefully _good_ \nscaffolding, so that when I send the next few patches, it actually starts \nworking.. ;)\n\n\t\tLinus\n"},{"id":"26759","messageId":"20060912010930.GA18639@tuatara.stupidest.org","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609111632050.27779@g5.osdl.org","subject":"Re: Start handling references internally as a sorted in-memory list","fromName":"Chris Wedgwood","fromEmail":"cw@f00f.org","sentAt":"2006-09-12T01:09:30Z","receivedAt":"2006-09-12T01:09:30Z","isPatch":false,"sender":{"key":"cw@f00f.org","avatar":null},"body":"On Mon, Sep 11, 2006 at 04:37:32PM -0700, Linus Torvalds wrote:\n\n> This also adds some very rudimentary support for the notion of packed\n> refs.\n\nNeat.  Although git makes some repos nice and small the refs take a\nlot of space when people use a lot of them:\n\n3.5M  glibc/glibc-cvs.git/.git/refs/\n11M   gcc-git/gcc.git/.git/refs/\n"},{"id":"26762","messageId":"Pine.LNX.4.64.0609112008500.27779@g5.osdl.org","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609111632050.27779@g5.osdl.org","subject":"Add support for negative refs","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-12T03:10:15Z","receivedAt":"2006-09-12T03:10:15Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nYou can remove a ref that is packed two different ways: either simply\nrepack all the refs without that one, or create a loose ref that has the\nmagic all-zero SHA1.\n\nThis also adds back the test that a ref actually has the object it\npoints to.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nThis one is trivial, and obviously depends on the previous series.\n\nI'm sending it just because I have a much bigger cleanup that I'd _really_ \nlike to go in, and I did this trivial one before that much more \ninteresting one.\n\ndiff --git a/refs.c b/refs.c\nindex 5f80a68..72e2283 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -280,6 +280,12 @@ static int do_for_each_ref(const char *b\n \t\t}\n \t\tif (strncmp(base, entry->name, trim))\n \t\t\tcontinue;\n+\t\tif (is_null_sha1(entry->sha1))\n+\t\t\tcontinue;\n+\t\tif (!has_sha1_file(entry->sha1)) {\n+\t\t\terror(\"%s does not point to a valid object!\", entry->name);\n+\t\t\tcontinue;\n+\t\t}\n \t\tretval = fn(entry->name + trim, entry->sha1);\n \t\tif (retval)\n \t\t\treturn retval;\n"},{"id":"26763","messageId":"Pine.LNX.4.64.0609112010340.27779@g5.osdl.org","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609112008500.27779@g5.osdl.org","subject":"Make ref resolution saner","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-12T03:17:35Z","receivedAt":"2006-09-12T03:17:35Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\nThe old code used to totally mix up the notion of a ref-name and the path \nthat that ref was associated with.  That was not only horribly ugly (a \nnumber of users got the path, and then wanted to try to turn it back into \na ref-name again), but it fundamnetally doesn't work at all once we do any \nsetup where a ref doesn't have a 1:1 relationship with a particular \npathname.\n\nThis fixes things up so that we use the ref-name throughout, and only\nturn it into a pathname once we actually look it up in the filesystem.\nThat makes a lot of things much clearer and more straightforward.\n\nSigned-off-by: Linus Torvalds <torvalds@osdl.org>\n---\n\nThis patch is a bit scary, but look at the first two hunks to \nbuiltin-fmt-merge-msg.c to get an idea of what kind of *crap* it gets rid \nof.\n\nThere were several places where we used \"resolve_ref()\" to figure out what \nthe branch it was on, but then to actually figure out what the branch-name \nof that branch was (rather than the cwd-relative _filename_ is), it then \ndid strange things with the string length of the original ref compared \nwith the string length of the _filename_ of the original ref etc etc.\n\nMaking all the ref-related functions just use the ref-name itself \nthroughout just gets rid of the crap, but more importantly, it's \nabsolutely required for just about _any_ ref-packing scheme.\n\nThat said, it _is_ a scary patch. It removes more lines that it adds:\n\n builtin-fmt-merge-msg.c |    7 +----\n builtin-init-db.c       |    4 +--\n builtin-show-branch.c   |   46 +++++++++++++------------------\n builtin-symbolic-ref.c  |   14 +++-------\n cache.h                 |    4 +--\n refs.c                  |   69 +++++++++++++++++++++++++----------------------\n sha1_name.c             |   14 +++++-----\n 7 files changed, 74 insertions(+), 84 deletions(-)\n\nbut if I missed some place where we used a ref as a pathname, or added one \n\"git_path()\" translation too many, it results in problems.\n\nThe good news is that our test-suite for this seems to be reasonably \ncomplete. It sure caught a lot of places I had missed the first time \naround. So this should be ok.\n\nI'd _really_ like for this to go in, even regardless of the other patches \nI have sent (although I think it depends on them in mostly fairly trivial \nways, since the previous patches changed how we do the recursive \nreaddir() on the refs/ directory).\n\nIt's really needed.\n\n\ndiff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c\nindex c407c03..b93c17c 100644\n--- a/builtin-fmt-merge-msg.c\n+++ b/builtin-fmt-merge-msg.c\n@@ -249,7 +249,7 @@ int cmd_fmt_merge_msg(int argc, const ch\n \tFILE *in = stdin;\n \tconst char *sep = \"\";\n \tunsigned char head_sha1[20];\n-\tconst char *head, *current_branch;\n+\tconst char *current_branch;\n \n \tgit_config(fmt_merge_msg_config);\n \n@@ -277,10 +277,7 @@ int cmd_fmt_merge_msg(int argc, const ch\n \t\tusage(fmt_merge_msg_usage);\n \n \t/* get current branch */\n-\thead = xstrdup(git_path(\"HEAD\"));\n-\tcurrent_branch = resolve_ref(head, head_sha1, 1);\n-\tcurrent_branch += strlen(head) - 4;\n-\tfree((char *)head);\n+\tcurrent_branch = resolve_ref(\"HEAD\", head_sha1, 1);\n \tif (!strncmp(current_branch, \"refs/heads/\", 11))\n \t\tcurrent_branch += 11;\n \ndiff --git a/builtin-init-db.c b/builtin-init-db.c\nindex 5085018..23b7714 100644\n--- a/builtin-init-db.c\n+++ b/builtin-init-db.c\n@@ -218,8 +218,8 @@ static void create_default_files(const c\n \t * branch, if it does not exist yet.\n \t */\n \tstrcpy(path + len, \"HEAD\");\n-\tif (read_ref(path, sha1) < 0) {\n-\t\tif (create_symref(path, \"refs/heads/master\") < 0)\n+\tif (read_ref(\"HEAD\", sha1) < 0) {\n+\t\tif (create_symref(\"HEAD\", \"refs/heads/master\") < 0)\n \t\t\texit(1);\n \t}\n \ndiff --git a/builtin-show-branch.c b/builtin-show-branch.c\nindex 578c9fa..4d8db0c 100644\n--- a/builtin-show-branch.c\n+++ b/builtin-show-branch.c\n@@ -437,21 +437,13 @@ static void snarf_refs(int head, int tag\n \t}\n }\n \n-static int rev_is_head(char *head_path, int headlen, char *name,\n+static int rev_is_head(char *head, int headlen, char *name,\n \t\t       unsigned char *head_sha1, unsigned char *sha1)\n {\n-\tint namelen;\n-\tif ((!head_path[0]) ||\n+\tif ((!head[0]) ||\n \t    (head_sha1 && sha1 && hashcmp(head_sha1, sha1)))\n \t\treturn 0;\n-\tnamelen = strlen(name);\n-\tif ((headlen < namelen) ||\n-\t    memcmp(head_path + headlen - namelen, name, namelen))\n-\t\treturn 0;\n-\tif (headlen == namelen ||\n-\t    head_path[headlen - namelen - 1] == '/')\n-\t\treturn 1;\n-\treturn 0;\n+\treturn !strcmp(head, name);\n }\n \n static int show_merge_base(struct commit_list *seen, int num_rev)\n@@ -559,9 +551,9 @@ int cmd_show_branch(int ac, const char *\n \tint all_heads = 0, all_tags = 0;\n \tint all_mask, all_revs;\n \tint lifo = 1;\n-\tchar head_path[128];\n-\tconst char *head_path_p;\n-\tint head_path_len;\n+\tchar head[128];\n+\tconst char *head_p;\n+\tint head_len;\n \tunsigned char head_sha1[20];\n \tint merge_base = 0;\n \tint independent = 0;\n@@ -638,31 +630,31 @@ int cmd_show_branch(int ac, const char *\n \t\tac--; av++;\n \t}\n \n-\thead_path_p = resolve_ref(git_path(\"HEAD\"), head_sha1, 1);\n-\tif (head_path_p) {\n-\t\thead_path_len = strlen(head_path_p);\n-\t\tmemcpy(head_path, head_path_p, head_path_len + 1);\n+\thead_p = resolve_ref(\"HEAD\", head_sha1, 1);\n+\tif (head_p) {\n+\t\thead_len = strlen(head_p);\n+\t\tmemcpy(head, head_p, head_len + 1);\n \t}\n \telse {\n-\t\thead_path_len = 0;\n-\t\thead_path[0] = 0;\n+\t\thead_len = 0;\n+\t\thead[0] = 0;\n \t}\n \n-\tif (with_current_branch && head_path_p) {\n+\tif (with_current_branch && head_p) {\n \t\tint has_head = 0;\n \t\tfor (i = 0; !has_head && i < ref_name_cnt; i++) {\n \t\t\t/* We are only interested in adding the branch\n \t\t\t * HEAD points at.\n \t\t\t */\n-\t\t\tif (rev_is_head(head_path,\n-\t\t\t\t\thead_path_len,\n+\t\t\tif (rev_is_head(head,\n+\t\t\t\t\thead_len,\n \t\t\t\t\tref_name[i],\n \t\t\t\t\thead_sha1, NULL))\n \t\t\t\thas_head++;\n \t\t}\n \t\tif (!has_head) {\n-\t\t\tint pfxlen = strlen(git_path(\"refs/heads/\"));\n-\t\t\tappend_one_rev(head_path + pfxlen);\n+\t\t\tint pfxlen = strlen(\"refs/heads/\");\n+\t\t\tappend_one_rev(head + pfxlen);\n \t\t}\n \t}\n \n@@ -713,8 +705,8 @@ int cmd_show_branch(int ac, const char *\n \tif (1 < num_rev || extra < 0) {\n \t\tfor (i = 0; i < num_rev; i++) {\n \t\t\tint j;\n-\t\t\tint is_head = rev_is_head(head_path,\n-\t\t\t\t\t\t  head_path_len,\n+\t\t\tint is_head = rev_is_head(head,\n+\t\t\t\t\t\t  head_len,\n \t\t\t\t\t\t  ref_name[i],\n \t\t\t\t\t\t  head_sha1,\n \t\t\t\t\t\t  rev[i]->object.sha1);\ndiff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c\nindex 1d3a5e2..6f18db8 100644\n--- a/builtin-symbolic-ref.c\n+++ b/builtin-symbolic-ref.c\n@@ -7,15 +7,11 @@ static const char git_symbolic_ref_usage\n static void check_symref(const char *HEAD)\n {\n \tunsigned char sha1[20];\n-\tconst char *git_HEAD = xstrdup(git_path(\"%s\", HEAD));\n-\tconst char *git_refs_heads_master = resolve_ref(git_HEAD, sha1, 0);\n-\tif (git_refs_heads_master) {\n-\t\t/* we want to strip the .git/ part */\n-\t\tint pfxlen = strlen(git_HEAD) - strlen(HEAD);\n-\t\tputs(git_refs_heads_master + pfxlen);\n-\t}\n-\telse\n+\tconst char *refs_heads_master = resolve_ref(\"HEAD\", sha1, 0);\n+\n+\tif (!refs_heads_master)\n \t\tdie(\"No such ref: %s\", HEAD);\n+\tputs(refs_heads_master);\n }\n \n int cmd_symbolic_ref(int argc, const char **argv, const char *prefix)\n@@ -26,7 +22,7 @@ int cmd_symbolic_ref(int argc, const cha\n \t\tcheck_symref(argv[1]);\n \t\tbreak;\n \tcase 3:\n-\t\tcreate_symref(xstrdup(git_path(\"%s\", argv[1])), argv[2]);\n+\t\tcreate_symref(argv[1], argv[2]);\n \t\tbreak;\n \tdefault:\n \t\tusage(git_symbolic_ref_usage);\ndiff --git a/cache.h b/cache.h\nindex a53204f..5d6c7ee 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -287,8 +287,8 @@ extern int get_sha1_hex(const char *hex,\n extern char *sha1_to_hex(const unsigned char *sha1);\t/* static buffer result! */\n extern int read_ref(const char *filename, unsigned char *sha1);\n extern const char *resolve_ref(const char *path, unsigned char *sha1, int);\n-extern int create_symref(const char *git_HEAD, const char *refs_heads_master);\n-extern int validate_symref(const char *git_HEAD);\n+extern int create_symref(const char *ref, const char *refs_heads_master);\n+extern int validate_symref(const char *ref);\n \n extern int base_name_compare(const char *name1, int len1, int mode1, const char *name2, int len2, int mode2);\n extern int cache_name_compare(const char *name1, int len1, const char *name2, int len2);\ndiff --git a/refs.c b/refs.c\nindex 72e2283..50c25d3 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -93,11 +93,11 @@ static struct ref_list *get_ref_dir(cons\n \tif (dir) {\n \t\tstruct dirent *de;\n \t\tint baselen = strlen(base);\n-\t\tchar *path = xmalloc(baselen + 257);\n+\t\tchar *ref = xmalloc(baselen + 257);\n \n-\t\tmemcpy(path, base, baselen);\n+\t\tmemcpy(ref, base, baselen);\n \t\tif (baselen && base[baselen-1] != '/')\n-\t\t\tpath[baselen++] = '/';\n+\t\t\tref[baselen++] = '/';\n \n \t\twhile ((de = readdir(dir)) != NULL) {\n \t\t\tunsigned char sha1[20];\n@@ -111,20 +111,20 @@ static struct ref_list *get_ref_dir(cons\n \t\t\t\tcontinue;\n \t\t\tif (has_extension(de->d_name, \".lock\"))\n \t\t\t\tcontinue;\n-\t\t\tmemcpy(path + baselen, de->d_name, namelen+1);\n-\t\t\tif (stat(git_path(\"%s\", path), &st) < 0)\n+\t\t\tmemcpy(ref + baselen, de->d_name, namelen+1);\n+\t\t\tif (stat(git_path(\"%s\", ref), &st) < 0)\n \t\t\t\tcontinue;\n \t\t\tif (S_ISDIR(st.st_mode)) {\n-\t\t\t\tlist = get_ref_dir(path, list);\n+\t\t\t\tlist = get_ref_dir(ref, list);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tif (read_ref(git_path(\"%s\", path), sha1) < 0) {\n-\t\t\t\terror(\"%s points nowhere!\", path);\n+\t\t\tif (read_ref(ref, sha1) < 0) {\n+\t\t\t\terror(\"%s points nowhere!\", ref);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tlist = add_ref(path, sha1, list);\n+\t\t\tlist = add_ref(ref, sha1, list);\n \t\t}\n-\t\tfree(path);\n+\t\tfree(ref);\n \t\tclosedir(dir);\n \t}\n \treturn list;\n@@ -145,12 +145,14 @@ static struct ref_list *get_loose_refs(v\n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define MAXDEPTH 5\n \n-const char *resolve_ref(const char *path, unsigned char *sha1, int reading)\n+const char *resolve_ref(const char *ref, unsigned char *sha1, int reading)\n {\n \tint depth = MAXDEPTH, len;\n \tchar buffer[256];\n+\tstatic char ref_buffer[256];\n \n \tfor (;;) {\n+\t\tconst char *path = git_path(\"%s\", ref);\n \t\tstruct stat st;\n \t\tchar *buf;\n \t\tint fd;\n@@ -169,14 +171,16 @@ const char *resolve_ref(const char *path\n \t\t\tif (reading || errno != ENOENT)\n \t\t\t\treturn NULL;\n \t\t\thashclr(sha1);\n-\t\t\treturn path;\n+\t\t\treturn ref;\n \t\t}\n \n \t\t/* Follow \"normalized\" - ie \"refs/..\" symlinks by hand */\n \t\tif (S_ISLNK(st.st_mode)) {\n \t\t\tlen = readlink(path, buffer, sizeof(buffer)-1);\n \t\t\tif (len >= 5 && !memcmp(\"refs/\", buffer, 5)) {\n-\t\t\t\tpath = git_path(\"%.*s\", len, buffer);\n+\t\t\t\tbuffer[len] = 0;\n+\t\t\t\tstrcpy(ref_buffer, buffer);\n+\t\t\t\tref = ref_buffer;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t}\n@@ -201,19 +205,22 @@ const char *resolve_ref(const char *path\n \t\twhile (len && isspace(*buf))\n \t\t\tbuf++, len--;\n \t\twhile (len && isspace(buf[len-1]))\n-\t\t\tbuf[--len] = 0;\n-\t\tpath = git_path(\"%.*s\", len, buf);\n+\t\t\tlen--;\n+\t\tbuf[len] = 0;\n+\t\tmemcpy(ref_buffer, buf, len + 1);\n+\t\tref = ref_buffer;\n \t}\n \tif (len < 40 || get_sha1_hex(buffer, sha1))\n \t\treturn NULL;\n-\treturn path;\n+\treturn ref;\n }\n \n-int create_symref(const char *git_HEAD, const char *refs_heads_master)\n+int create_symref(const char *ref_target, const char *refs_heads_master)\n {\n \tconst char *lockpath;\n \tchar ref[1000];\n \tint fd, len, written;\n+\tconst char *git_HEAD = git_path(\"%s\", ref_target);\n \n #ifndef NO_SYMLINK_HEAD\n \tif (prefer_symlink_refs) {\n@@ -251,9 +258,9 @@ #endif\n \treturn 0;\n }\n \n-int read_ref(const char *filename, unsigned char *sha1)\n+int read_ref(const char *ref, unsigned char *sha1)\n {\n-\tif (resolve_ref(filename, sha1, 1))\n+\tif (resolve_ref(ref, sha1, 1))\n \t\treturn 0;\n \treturn -1;\n }\n@@ -306,7 +313,7 @@ static int do_for_each_ref(const char *b\n int head_ref(int (*fn)(const char *path, const unsigned char *sha1))\n {\n \tunsigned char sha1[20];\n-\tif (!read_ref(git_path(\"HEAD\"), sha1))\n+\tif (!read_ref(\"HEAD\", sha1))\n \t\treturn fn(\"HEAD\", sha1);\n \treturn 0;\n }\n@@ -335,7 +342,7 @@ int get_ref_sha1(const char *ref, unsign\n {\n \tif (check_ref_format(ref))\n \t\treturn -1;\n-\treturn read_ref(git_path(\"refs/%s\", ref), sha1);\n+\treturn read_ref(mkpath(\"refs/%s\", ref), sha1);\n }\n \n /*\n@@ -416,31 +423,30 @@ static struct ref_lock *verify_lock(stru\n \treturn lock;\n }\n \n-static struct ref_lock *lock_ref_sha1_basic(const char *path,\n+static struct ref_lock *lock_ref_sha1_basic(const char *ref,\n \tint plen,\n \tconst unsigned char *old_sha1, int mustexist)\n {\n-\tconst char *orig_path = path;\n+\tconst char *orig_ref = ref;\n \tstruct ref_lock *lock;\n \tstruct stat st;\n \n \tlock = xcalloc(1, sizeof(struct ref_lock));\n \tlock->lock_fd = -1;\n \n-\tplen = strlen(path) - plen;\n-\tpath = resolve_ref(path, lock->old_sha1, mustexist);\n-\tif (!path) {\n+\tref = resolve_ref(ref, lock->old_sha1, mustexist);\n+\tif (!ref) {\n \t\tint last_errno = errno;\n \t\terror(\"unable to resolve reference %s: %s\",\n-\t\t\torig_path, strerror(errno));\n+\t\t\torig_ref, strerror(errno));\n \t\tunlock_ref(lock);\n \t\terrno = last_errno;\n \t\treturn NULL;\n \t}\n \tlock->lk = xcalloc(1, sizeof(struct lock_file));\n \n-\tlock->ref_file = xstrdup(path);\n-\tlock->log_file = xstrdup(git_path(\"logs/%s\", lock->ref_file + plen));\n+\tlock->ref_file = xstrdup(git_path(\"%s\", ref));\n+\tlock->log_file = xstrdup(git_path(\"logs/%s\", ref));\n \tlock->force_write = lstat(lock->ref_file, &st) && errno == ENOENT;\n \n \tif (safe_create_leading_directories(lock->ref_file))\n@@ -455,15 +461,14 @@ struct ref_lock *lock_ref_sha1(const cha\n {\n \tif (check_ref_format(ref))\n \t\treturn NULL;\n-\treturn lock_ref_sha1_basic(git_path(\"refs/%s\", ref),\n+\treturn lock_ref_sha1_basic(mkpath(\"refs/%s\", ref),\n \t\t5 + strlen(ref), old_sha1, mustexist);\n }\n \n struct ref_lock *lock_any_ref_for_update(const char *ref,\n \tconst unsigned char *old_sha1, int mustexist)\n {\n-\treturn lock_ref_sha1_basic(git_path(\"%s\", ref),\n-\t\tstrlen(ref), old_sha1, mustexist);\n+\treturn lock_ref_sha1_basic(ref, strlen(ref), old_sha1, mustexist);\n }\n \n void unlock_ref(struct ref_lock *lock)\ndiff --git a/sha1_name.c b/sha1_name.c\nindex 1fbc443..b497528 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -247,8 +247,8 @@ static int get_sha1_basic(const char *st\n \t\tNULL\n \t};\n \tstatic const char *warning = \"warning: refname '%.*s' is ambiguous.\\n\";\n-\tconst char **p, *pathname;\n-\tchar *real_path = NULL;\n+\tconst char **p, *ref;\n+\tchar *real_ref = NULL;\n \tint refs_found = 0, am;\n \tunsigned long at_time = (unsigned long)-1;\n \tunsigned char *this_result;\n@@ -276,10 +276,10 @@ static int get_sha1_basic(const char *st\n \n \tfor (p = fmt; *p; p++) {\n \t\tthis_result = refs_found ? sha1_from_ref : sha1;\n-\t\tpathname = resolve_ref(git_path(*p, len, str), this_result, 1);\n-\t\tif (pathname) {\n+\t\tref = resolve_ref(mkpath(*p, len, str), this_result, 1);\n+\t\tif (ref) {\n \t\t\tif (!refs_found++)\n-\t\t\t\treal_path = xstrdup(pathname);\n+\t\t\t\treal_ref = xstrdup(ref);\n \t\t\tif (!warn_ambiguous_refs)\n \t\t\t\tbreak;\n \t\t}\n@@ -293,12 +293,12 @@ static int get_sha1_basic(const char *st\n \n \tif (at_time != (unsigned long)-1) {\n \t\tread_ref_at(\n-\t\t\treal_path + strlen(git_path(\".\")) - 1,\n+\t\t\treal_ref,\n \t\t\tat_time,\n \t\t\tsha1);\n \t}\n \n-\tfree(real_path);\n+\tfree(real_ref);\n \treturn 0;\n }\n \n"},{"id":"26765","messageId":"20060912053616.GA6706@coredump.intra.peff.net","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609112010340.27779@g5.osdl.org","subject":"Re: Make ref resolution saner","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2006-09-12T05:36:16Z","receivedAt":"2006-09-12T05:36:16Z","isPatch":false,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Mon, Sep 11, 2006 at 08:17:35PM -0700, Linus Torvalds wrote:\n\n> The old code used to totally mix up the notion of a ref-name and the path \n> that that ref was associated with.  That was not only horribly ugly (a\n\nI assume your patch is against master; it looks like there's exactly one\ncall to resolve_ref that's in next but not master. One-liner fix below.\n\n-Peff\n\n-- >8 --\nwt-status: use simplified resolve_ref to find current branch\n\n---\n wt-status.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/wt-status.c b/wt-status.c\nindex ec2c728..e2f49c7 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -41,7 +41,7 @@ void wt_status_prepare(struct wt_status \n \n \ts->is_initial = get_sha1(\"HEAD\", sha1) ? 1 : 0;\n \n-\thead = resolve_ref(git_path(\"HEAD\"), sha1, 0);\n+\thead = resolve_ref(\"HEAD\", sha1, 0);\n \ts->branch = head ?\n \t\t    strdup(head + strlen(get_git_dir()) + 1) :\n \t\t    NULL;\n-- \n1.4.2.g39f1\n"},{"id":"26772","messageId":"Pine.LNX.4.64.0609120739310.27779@g5.osdl.org","threadId":"5537","inReplyTo":"20060912053616.GA6706@coredump.intra.peff.net","subject":"Re: Make ref resolution saner","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-12T14:41:19Z","receivedAt":"2006-09-12T14:41:19Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Tue, 12 Sep 2006, Jeff King wrote:\n> \n> I assume your patch is against master;\n\nYeah. Well, master plus my previous patches.\n\n> it looks like there's exactly one call to resolve_ref that's in next but \n> not master. One-liner fix below.\n\nNot quite enough. It's the same thing: wt-status.c plays games with the \nreturn value (which _used_ to be a path) in order to turn it back into a \nref. But now that it's all about refs, the games are unnecessary:\n\n> diff --git a/wt-status.c b/wt-status.c\n> index ec2c728..e2f49c7 100644\n> --- a/wt-status.c\n> +++ b/wt-status.c\n> @@ -41,7 +41,7 @@ void wt_status_prepare(struct wt_status \n>  \n>  \ts->is_initial = get_sha1(\"HEAD\", sha1) ? 1 : 0;\n>  \n> -\thead = resolve_ref(git_path(\"HEAD\"), sha1, 0);\n> +\thead = resolve_ref(\"HEAD\", sha1, 0);\n>  \ts->branch = head ?\n>  \t\t    strdup(head + strlen(get_git_dir()) + 1) :\n>  \t\t    NULL;\n\nSo that \"strdup(head + strlen(get_git_dir()) + 1)\" should now be just \n\"strdup(head)\".\n\n\t\tLinus\n"},{"id":"27123","messageId":"7vy7shr5zw.fsf_-_@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609111632050.27779@g5.osdl.org","subject":"[RFC] git-pack-refs --prune","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-18T07:25:23Z","receivedAt":"2006-09-18T07:25:23Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"git pack-refs --prune\", after successfully packing the existing\nrefs, removes the loose ref files.  It tries to protect against\nrace by doing the usual lock_ref_sha1() which makes sure the\ncontents of the ref has not changed since we last looked at.\n\nI am not sure I got the locking right, hence this RFC.\n\nWe would probably need to perform some sort of 'sync' after\nclosing and renaming the lockfile to its final location before\npruning.  Is there a way cheaper than sync(2) to make sure the\neffect of rename(2) hits the disk platter?\n\n---\ndiff --git a/builtin-pack-refs.c b/builtin-pack-refs.c\nindex 0f5d827..2b3a483 100644\n--- a/builtin-pack-refs.c\n+++ b/builtin-pack-refs.c\n@@ -3,6 +3,15 @@ #include \"refs.h\"\n \n static FILE *refs_file;\n static const char *result_path, *lock_path;\n+static const char builtin_pack_refs_usage[] =\n+\"git-pack-refs [--prune]\";\n+\n+static int prune;\n+struct keepref {\n+\tstruct keepref *next;\n+\tunsigned char sha1[20];\n+\tchar name[FLEX_ARRAY];\n+} *keepref;\n \n static void remove_lock_file(void)\n {\n@@ -13,12 +22,50 @@ static void remove_lock_file(void)\n static int handle_one_ref(const char *path, const unsigned char *sha1)\n {\n \tfprintf(refs_file, \"%s %s\\n\", sha1_to_hex(sha1), path);\n+\tif (prune) {\n+\t\tint namelen = strlen(path) + 1;\n+\t\tstruct keepref *n = xcalloc(1, sizeof(*n) + namelen);\n+\t\thashcpy(n->sha1, sha1);\n+\t\tstrcpy(n->name, path);\n+\t\tn->next = keepref;\n+\t\tkeepref = n;\n+\t}\n \treturn 0;\n }\n \n+/* make sure nobody touched the ref, and unlink */\n+static void prune_ref(struct keepref *r)\n+{\n+\tstruct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1);\n+\n+\tif (lock) {\n+\t\tunlink(git_path(r->name));\n+\t\tunlock_ref(lock);\n+\t}\n+}\n+\n+static void prune_refs(void)\n+{\n+\tstruct keepref *r;\n+\tfor (r = keepref; r; r = r->next)\n+\t\tprune_ref(r);\n+}\n+\n int cmd_pack_refs(int argc, const char **argv, const char *prefix)\n {\n-\tint fd;\n+\tint fd, i;\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tif (!strcmp(arg, \"--prune\")) {\n+\t\t\tprune = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\t/* perhaps other parameters later... */\n+\t\tbreak;\n+\t}\n+\tif (i != argc)\n+\t\tusage(builtin_pack_refs_usage);\n \n \tresult_path = xstrdup(git_path(\"packed-refs\"));\n \tlock_path = xstrdup(mkpath(\"%s.lock\", result_path));\n@@ -37,5 +84,7 @@ int cmd_pack_refs(int argc, const char *\n \tif (rename(lock_path, result_path) < 0)\n \t\tdie(\"unable to overwrite old ref-pack file (%s)\", strerror(errno));\n \tlock_path = NULL;\n+\tif (prune)\n+\t\tprune_refs();\n \treturn 0;\n }\n"},{"id":"27134","messageId":"Pine.LNX.4.64.0609180934360.4388@g5.osdl.org","threadId":"5537","inReplyTo":"7vy7shr5zw.fsf_-_@assigned-by-dhcp.cox.net","subject":"Re: [RFC] git-pack-refs --prune","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-18T16:47:33Z","receivedAt":"2006-09-18T16:47:33Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Mon, 18 Sep 2006, Junio C Hamano wrote:\n> \n> I am not sure I got the locking right, hence this RFC.\n\nIt looks correct (the important part to check is that the SHA1 of the ref \nyou remove still matches the SHA1 of the object you packed).\n\nThat said, we should fix it up a bit, notably\n\n - we should _not_ prune refs that are indirect.\n\n   Right now, if we have a symbolic link, we _incorrectly_ pack it as \n   unlinked. The packed format doesn't have any \"link\" format.\n\n   This isn't a problem in practice, because the only link we ever use is \n   the HEAD link, but it's incorrect. As long as we don't prune, it wasn't \n   an issue - a unpacked head will always override a packed one, so \n   packing the thing didn't really matter.\n\n - we should probably avoid even trying to prune stuff that was already \n   packed.\n\nThe way to fix both these problems at once would be to add a flag to the \n\"for_each_ref()\", which says whether it followed a link, or whether it was \nalready packed, so that we wouldn't pack symlinks at all, and we wouldn't \nadd already-packed refs to the \"keeprefs\" list.\n\nBut that requires a sligh semantic extension to \"do_for_each_ref()\" (and \n\"struct ref_list\" needs a flag to say whether it was looked up through a \nsymlink).\n\nI was thinking that the easy way to solve it is to just _pack_ everything \n(the way we do now - incorrectly for symrefs), but never prune a symref.\n\n\t\tLinus\n"},{"id":"27143","messageId":"7vhcz5ow0a.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609180934360.4388@g5.osdl.org","subject":"Re: [RFC] git-pack-refs --prune","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-18T18:44:05Z","receivedAt":"2006-09-18T18:44:05Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> The way to fix both these problems at once would be to add a flag to the \n> \"for_each_ref()\", which says whether it followed a link, or whether it was \n> already packed, so that we wouldn't pack symlinks at all, and we wouldn't \n> add already-packed refs to the \"keeprefs\" list.\n>\n> But that requires a sligh semantic extension to \"do_for_each_ref()\" (and \n> \"struct ref_list\" needs a flag to say whether it was looked up through a \n> symlink).\n>\n> I was thinking that the easy way to solve it is to just _pack_ everything \n> (the way we do now - incorrectly for symrefs), but never prune a symref.\n\nI see.  Thanks for pointing out the issue with symrefs.  I think\nclone with --use-separate-remote creates remote/$that_repo/HEAD\nthat points at the branch the remote side's HEAD points at (to\nbe precise, the one it guessed the remote side's HEAD points\nat), so this is a real issue already.\n\nI wanted to update for_each_ref() anyway for other reasons (it\nreally should take callback data -- the way the current users\nuse global variables to work this around is eyesore), so\nhopefully I'll find time to take a look at it.\n\nRough outline:\n\n - for_each_ref() and friends become:\n\n   typedef int each_ref_fn(const char *refname,\n\t\t\t   const unsigned char *sha1,\n   #define REF_IS_SYMREF 01\n   #define REF_IS_PACKED 02\n                           int flags, /* above bits or'ed */ \n                           void *cb_data);\n   int for_each_ref(each_ref_fn fn, void *cb_data);\n\n - handle_one_ref notices a symref and ignores it; it remembers\n   refs that are not symref and are still loose for later\n   pruning under --prune.\n\nWe might want to update the initial handshake of upload-pack\nprotocol so that peek-remote and fetch-pack can tell which one\nis a symref pointing at what.  Do the usual server_capabilities\ndiscovery in connect.c::get_remote_heads(), and if an extension\n\"symref\" is supported than ask for symref information (typically\nwe would only get \"HEAD points at refs/heads/foo\" and nothing\nelse).  Then git-clone.sh does not have to make a guess.  But\nthat is a separate topic.\n\n\n\n \n"},{"id":"27341","messageId":"7v64fhd7ns.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609180934360.4388@g5.osdl.org","subject":"Re: [RFC] git-pack-refs --prune","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T07:02:15Z","receivedAt":"2006-09-21T07:02:15Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> On Mon, 18 Sep 2006, Junio C Hamano wrote:\n>> \n>> I am not sure I got the locking right, hence this RFC.\n>\n> It looks correct (the important part to check is that the SHA1 of the ref \n> you remove still matches the SHA1 of the object you packed).\n>\n> That said, we should fix it up a bit, notably\n>\n>  - we should _not_ prune refs that are indirect.\n>\n>  - we should probably avoid even trying to prune stuff that was already \n>    packed.\n>\n> The way to fix both these problems at once would be to add a flag to the \n> \"for_each_ref()\", which says whether it followed a link, or whether it was \n> already packed, so that we wouldn't pack symlinks at all, and we wouldn't \n> add already-packed refs to the \"keeprefs\" list.\n>\n> But that requires a sligh semantic extension to \"do_for_each_ref()\" (and \n> \"struct ref_list\" needs a flag to say whether it was looked up through a \n> symlink).\n\nOk, so I did these and the result is a 4-patch series.\n"},{"id":"27343","messageId":"7v4pv1bsx1.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"7v64fhd7ns.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 1/5] symbolit-ref: fix resolve_ref conversion.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T07:06:02Z","receivedAt":"2006-09-21T07:06:02Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"An earlier conversion accidentally hardcoded \"HEAD\" to be passed to\nresolve_ref(), thereby causing git-symbolic-ref command to always\nreport where the HEAD points at, ignoring the command line parameter.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-symbolic-ref.c |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c\nindex 6f18db8..13163ba 100644\n--- a/builtin-symbolic-ref.c\n+++ b/builtin-symbolic-ref.c\n@@ -7,7 +7,7 @@ static const char git_symbolic_ref_usage\n static void check_symref(const char *HEAD)\n {\n \tunsigned char sha1[20];\n-\tconst char *refs_heads_master = resolve_ref(\"HEAD\", sha1, 0);\n+\tconst char *refs_heads_master = resolve_ref(HEAD, sha1, 0);\n \n \tif (!refs_heads_master)\n \t\tdie(\"No such ref: %s\", HEAD);\n-- \n1.4.2.1.g4dc7\n"},{"id":"27346","messageId":"7vy7sdaeck.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"7v64fhd7ns.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 2/5] Add callback data to for_each_ref() family.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T07:06:03Z","receivedAt":"2006-09-21T07:06:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This is a long overdue fix to the API for for_each_ref() family\nof functions.  It allows the callers to specify a callback data\npointer, so that the caller does not have to use static\nvariables to communicate with the callback funciton.\n\nThe updated for_each_ref() family takes a function of type\n\n\tint (*fn)(const char *, const unsigned char *, void *)\n\nand a void pointer as parameters, and calls the function with\nthe name of the ref and its SHA-1 with the caller-supplied void\npointer as parameters.\n\nThe commit updates two callers, builtin-name-rev.c and\nbuiltin-pack-refs.c as an example.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-name-rev.c    |    8 ++++----\n builtin-pack-refs.c   |    8 +++++---\n builtin-prune.c       |    4 ++--\n builtin-push.c        |    4 ++--\n builtin-rev-parse.c   |   10 +++++-----\n builtin-show-branch.c |   26 +++++++++++++-------------\n describe.c            |    4 ++--\n fetch-pack.c          |    8 ++++----\n fetch.c               |    4 ++--\n fsck-objects.c        |    4 ++--\n http-push.c           |    4 ++--\n receive-pack.c        |    6 +++---\n refs.c                |   31 ++++++++++++++++++-------------\n refs.h                |   11 ++++++-----\n revision.c            |    4 ++--\n send-pack.c           |    4 ++--\n server-info.c         |    4 ++--\n upload-pack.c         |    6 +++---\n 18 files changed, 79 insertions(+), 71 deletions(-)\n\ndiff --git a/builtin-name-rev.c b/builtin-name-rev.c\nindex 52886b6..9e3e537 100644\n--- a/builtin-name-rev.c\n+++ b/builtin-name-rev.c\n@@ -75,11 +75,10 @@ copy_data:\n \t}\n }\n \n-static int tags_only;\n-\n-static int name_ref(const char *path, const unsigned char *sha1)\n+static int name_ref(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct object *o = parse_object(sha1);\n+\tint tags_only = *(int*)cb_data;\n \tint deref = 0;\n \n \tif (tags_only && strncmp(path, \"refs/tags/\", 10))\n@@ -131,6 +130,7 @@ int cmd_name_rev(int argc, const char **\n {\n \tstruct object_array revs = { 0, 0, NULL };\n \tint as_is = 0, all = 0, transform_stdin = 0;\n+\tint tags_only = 0;\n \n \tgit_config(git_default_config);\n \n@@ -186,7 +186,7 @@ int cmd_name_rev(int argc, const char **\n \t\tadd_object_array((struct object *)commit, *argv, &revs);\n \t}\n \n-\tfor_each_ref(name_ref);\n+\tfor_each_ref(name_ref, &tags_only);\n \n \tif (transform_stdin) {\n \t\tchar buffer[2048];\ndiff --git a/builtin-pack-refs.c b/builtin-pack-refs.c\nindex 0f5d827..b3d5470 100644\n--- a/builtin-pack-refs.c\n+++ b/builtin-pack-refs.c\n@@ -1,7 +1,6 @@\n #include \"cache.h\"\n #include \"refs.h\"\n \n-static FILE *refs_file;\n static const char *result_path, *lock_path;\n \n static void remove_lock_file(void)\n@@ -10,8 +9,10 @@ static void remove_lock_file(void)\n \t\tunlink(lock_path);\n }\n \n-static int handle_one_ref(const char *path, const unsigned char *sha1)\n+static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data)\n {\n+\tFILE *refs_file = cb_data;\n+\n \tfprintf(refs_file, \"%s %s\\n\", sha1_to_hex(sha1), path);\n \treturn 0;\n }\n@@ -19,6 +20,7 @@ static int handle_one_ref(const char *pa\n int cmd_pack_refs(int argc, const char **argv, const char *prefix)\n {\n \tint fd;\n+\tFILE *refs_file;\n \n \tresult_path = xstrdup(git_path(\"packed-refs\"));\n \tlock_path = xstrdup(mkpath(\"%s.lock\", result_path));\n@@ -31,7 +33,7 @@ int cmd_pack_refs(int argc, const char *\n \trefs_file = fdopen(fd, \"w\");\n \tif (!refs_file)\n \t\tdie(\"unable to create ref-pack file structure (%s)\", strerror(errno));\n-\tfor_each_ref(handle_one_ref);\n+\tfor_each_ref(handle_one_ref, refs_file);\n \tfsync(fd);\n \tfclose(refs_file);\n \tif (rename(lock_path, result_path) < 0)\ndiff --git a/builtin-prune.c b/builtin-prune.c\nindex 6228c79..e21c29b 100644\n--- a/builtin-prune.c\n+++ b/builtin-prune.c\n@@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_\n \t}\n }\n \n-static int add_one_ref(const char *path, const unsigned char *sha1)\n+static int add_one_ref(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct object *object = parse_object(sha1);\n \tif (!object)\n@@ -240,7 +240,7 @@ int cmd_prune(int argc, const char **arg\n \trevs.tree_objects = 1;\n \n \t/* Add all external refs */\n-\tfor_each_ref(add_one_ref);\n+\tfor_each_ref(add_one_ref, NULL);\n \n \t/* Add all refs from the index file */\n \tadd_cache_refs();\ndiff --git a/builtin-push.c b/builtin-push.c\nindex c43f256..88fc8e2 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -27,7 +27,7 @@ static void add_refspec(const char *ref)\n \trefspec_nr = nr;\n }\n \n-static int expand_one_ref(const char *ref, const unsigned char *sha1)\n+static int expand_one_ref(const char *ref, const unsigned char *sha1, void *cb_data)\n {\n \t/* Ignore the \"refs/\" at the beginning of the refname */\n \tref += 5;\n@@ -51,7 +51,7 @@ static void expand_refspecs(void)\n \t}\n \tif (!tags)\n \t\treturn;\n-\tfor_each_ref(expand_one_ref);\n+\tfor_each_ref(expand_one_ref, NULL);\n }\n \n static void set_refspecs(const char **refs, int nr)\ndiff --git a/builtin-rev-parse.c b/builtin-rev-parse.c\nindex fd3ccc8..c771274 100644\n--- a/builtin-rev-parse.c\n+++ b/builtin-rev-parse.c\n@@ -137,7 +137,7 @@ static void show_default(void)\n \t}\n }\n \n-static int show_reference(const char *refname, const unsigned char *sha1)\n+static int show_reference(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tshow_rev(NORMAL, sha1, refname);\n \treturn 0;\n@@ -299,19 +299,19 @@ int cmd_rev_parse(int argc, const char *\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--all\")) {\n-\t\t\t\tfor_each_ref(show_reference);\n+\t\t\t\tfor_each_ref(show_reference, NULL);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--branches\")) {\n-\t\t\t\tfor_each_branch_ref(show_reference);\n+\t\t\t\tfor_each_branch_ref(show_reference, NULL);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--tags\")) {\n-\t\t\t\tfor_each_tag_ref(show_reference);\n+\t\t\t\tfor_each_tag_ref(show_reference, NULL);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--remotes\")) {\n-\t\t\t\tfor_each_remote_ref(show_reference);\n+\t\t\t\tfor_each_remote_ref(show_reference, NULL);\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t\tif (!strcmp(arg, \"--show-prefix\")) {\ndiff --git a/builtin-show-branch.c b/builtin-show-branch.c\nindex 4d8db0c..b3548ae 100644\n--- a/builtin-show-branch.c\n+++ b/builtin-show-branch.c\n@@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, i\n \t      compare_ref_name);\n }\n \n-static int append_ref(const char *refname, const unsigned char *sha1)\n+static int append_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n \tint i;\n@@ -369,7 +369,7 @@ static int append_ref(const char *refnam\n \treturn 0;\n }\n \n-static int append_head_ref(const char *refname, const unsigned char *sha1)\n+static int append_head_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tunsigned char tmp[20];\n \tint ofs = 11;\n@@ -380,14 +380,14 @@ static int append_head_ref(const char *r\n \t */\n \tif (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))\n \t\tofs = 5;\n-\treturn append_ref(refname + ofs, sha1);\n+\treturn append_ref(refname + ofs, sha1, cb_data);\n }\n \n-static int append_tag_ref(const char *refname, const unsigned char *sha1)\n+static int append_tag_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tif (strncmp(refname, \"refs/tags/\", 10))\n \t\treturn 0;\n-\treturn append_ref(refname + 5, sha1);\n+\treturn append_ref(refname + 5, sha1, cb_data);\n }\n \n static const char *match_ref_pattern = NULL;\n@@ -401,7 +401,7 @@ static int count_slash(const char *s)\n \treturn cnt;\n }\n \n-static int append_matching_ref(const char *refname, const unsigned char *sha1)\n+static int append_matching_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \t/* we want to allow pattern hold/<asterisk> to show all\n \t * branches under refs/heads/hold/, and v0.99.9? to show\n@@ -417,22 +417,22 @@ static int append_matching_ref(const cha\n \tif (fnmatch(match_ref_pattern, tail, 0))\n \t\treturn 0;\n \tif (!strncmp(\"refs/heads/\", refname, 11))\n-\t\treturn append_head_ref(refname, sha1);\n+\t\treturn append_head_ref(refname, sha1, cb_data);\n \tif (!strncmp(\"refs/tags/\", refname, 10))\n-\t\treturn append_tag_ref(refname, sha1);\n-\treturn append_ref(refname, sha1);\n+\t\treturn append_tag_ref(refname, sha1, cb_data);\n+\treturn append_ref(refname, sha1, cb_data);\n }\n \n static void snarf_refs(int head, int tag)\n {\n \tif (head) {\n \t\tint orig_cnt = ref_name_cnt;\n-\t\tfor_each_ref(append_head_ref);\n+\t\tfor_each_ref(append_head_ref, NULL);\n \t\tsort_ref_range(orig_cnt, ref_name_cnt);\n \t}\n \tif (tag) {\n \t\tint orig_cnt = ref_name_cnt;\n-\t\tfor_each_ref(append_tag_ref);\n+\t\tfor_each_ref(append_tag_ref, NULL);\n \t\tsort_ref_range(orig_cnt, ref_name_cnt);\n \t}\n }\n@@ -487,7 +487,7 @@ static void append_one_rev(const char *a\n {\n \tunsigned char revkey[20];\n \tif (!get_sha1(av, revkey)) {\n-\t\tappend_ref(av, revkey);\n+\t\tappend_ref(av, revkey, NULL);\n \t\treturn;\n \t}\n \tif (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {\n@@ -495,7 +495,7 @@ static void append_one_rev(const char *a\n \t\tint saved_matches = ref_name_cnt;\n \t\tmatch_ref_pattern = av;\n \t\tmatch_ref_slash = count_slash(av);\n-\t\tfor_each_ref(append_matching_ref);\n+\t\tfor_each_ref(append_matching_ref, NULL);\n \t\tif (saved_matches == ref_name_cnt &&\n \t\t    ref_name_cnt < MAX_REVS)\n \t\t\terror(\"no matching refs with %s\", av);\ndiff --git a/describe.c b/describe.c\nindex ab192f8..ea0f2ce 100644\n--- a/describe.c\n+++ b/describe.c\n@@ -53,7 +53,7 @@ static void add_to_known_names(const cha\n \tnames = ++idx;\n }\n \n-static int get_name(const char *path, const unsigned char *sha1)\n+static int get_name(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n \tstruct object *object;\n@@ -113,7 +113,7 @@ static void describe(const char *arg, in\n \n \tif (!initialized) {\n \t\tinitialized = 1;\n-\t\tfor_each_ref(get_name);\n+\t\tfor_each_ref(get_name, NULL);\n \t\tqsort(name_array, names, sizeof(*name_array), compare_names);\n \t}\n \ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex e8708aa..6264ea1 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -42,7 +42,7 @@ static void rev_list_push(struct commit \n \t}\n }\n \n-static int rev_list_insert_ref(const char *path, const unsigned char *sha1)\n+static int rev_list_insert_ref(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct object *o = deref_tag(parse_object(sha1), path, 0);\n \n@@ -143,7 +143,7 @@ static int find_common(int fd[2], unsign\n \tunsigned in_vain = 0;\n \tint got_continue = 0;\n \n-\tfor_each_ref(rev_list_insert_ref);\n+\tfor_each_ref(rev_list_insert_ref, NULL);\n \n \tfetching = 0;\n \tfor ( ; refs ; refs = refs->next) {\n@@ -253,7 +253,7 @@ done:\n \n static struct commit_list *complete;\n \n-static int mark_complete(const char *path, const unsigned char *sha1)\n+static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct object *o = parse_object(sha1);\n \n@@ -365,7 +365,7 @@ static int everything_local(struct ref *\n \t\t}\n \t}\n \n-\tfor_each_ref(mark_complete);\n+\tfor_each_ref(mark_complete, NULL);\n \tif (cutoff)\n \t\tmark_recent_complete_commits(cutoff);\n \ndiff --git a/fetch.c b/fetch.c\nindex 34df8d3..36d1e76 100644\n--- a/fetch.c\n+++ b/fetch.c\n@@ -201,7 +201,7 @@ static int interpret_target(char *target\n \treturn -1;\n }\n \n-static int mark_complete(const char *path, const unsigned char *sha1)\n+static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n \tif (commit) {\n@@ -274,7 +274,7 @@ int pull(int targets, char **target, con\n \t}\n \n \tif (!get_recover)\n-\t\tfor_each_ref(mark_complete);\n+\t\tfor_each_ref(mark_complete, NULL);\n \n \tfor (i = 0; i < targets; i++) {\n \t\tif (interpret_target(target[i], &sha1[20 * i])) {\ndiff --git a/fsck-objects.c b/fsck-objects.c\nindex 456c17e..bb0c94e 100644\n--- a/fsck-objects.c\n+++ b/fsck-objects.c\n@@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path)\n \n static int default_refs;\n \n-static int fsck_handle_ref(const char *refname, const unsigned char *sha1)\n+static int fsck_handle_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tstruct object *obj;\n \n@@ -424,7 +424,7 @@ static int fsck_handle_ref(const char *r\n \n static void get_default_heads(void)\n {\n-\tfor_each_ref(fsck_handle_ref);\n+\tfor_each_ref(fsck_handle_ref, NULL);\n \n \t/*\n \t * Not having any default heads isn't really fatal, but\ndiff --git a/http-push.c b/http-push.c\nindex 670ff00..460c9be 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *\n static struct ref *local_refs, **local_tail;\n static struct ref *remote_refs, **remote_tail;\n \n-static int one_local_ref(const char *refname, const unsigned char *sha1)\n+static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tstruct ref *ref;\n \tint len = strlen(refname) + 1;\n@@ -1913,7 +1913,7 @@ static void one_remote_ref(char *refname\n static void get_local_heads(void)\n {\n \tlocal_tail = &local_refs;\n-\tfor_each_ref(one_local_ref);\n+\tfor_each_ref(one_local_ref, NULL);\n }\n \n static void get_dav_remote_heads(void)\ndiff --git a/receive-pack.c b/receive-pack.c\nindex 78f75da..7abc921 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -12,7 +12,7 @@ static int report_status;\n static char capabilities[] = \"report-status\";\n static int capabilities_sent;\n \n-static int show_ref(const char *path, const unsigned char *sha1)\n+static int show_ref(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tif (capabilities_sent)\n \t\tpacket_write(1, \"%s %s\\n\", sha1_to_hex(sha1), path);\n@@ -25,9 +25,9 @@ static int show_ref(const char *path, co\n \n static void write_head_info(void)\n {\n-\tfor_each_ref(show_ref);\n+\tfor_each_ref(show_ref, NULL);\n \tif (!capabilities_sent)\n-\t\tshow_ref(\"capabilities^{}\", null_sha1);\n+\t\tshow_ref(\"capabilities^{}\", null_sha1, NULL);\n \n }\n \ndiff --git a/refs.c b/refs.c\nindex 7bd36e4..85564f0 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -275,7 +275,7 @@ int read_ref(const char *ref, unsigned c\n \treturn -1;\n }\n \n-static int do_for_each_ref(const char *base, int (*fn)(const char *path, const unsigned char *sha1), int trim)\n+static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data)\n {\n \tint retval;\n \tstruct ref_list *packed = get_packed_refs();\n@@ -303,7 +303,7 @@ static int do_for_each_ref(const char *b\n \t\t\terror(\"%s does not point to a valid object!\", entry->name);\n \t\t\tcontinue;\n \t\t}\n-\t\tretval = fn(entry->name + trim, entry->sha1);\n+\t\tretval = fn(entry->name + trim, entry->sha1, cb_data);\n \t\tif (retval)\n \t\t\treturn retval;\n \t}\n@@ -311,7 +311,7 @@ static int do_for_each_ref(const char *b\n \tpacked = packed ? packed : loose;\n \twhile (packed) {\n \t\tif (!strncmp(base, packed->name, trim)) {\n-\t\t\tretval = fn(packed->name + trim, packed->sha1);\n+\t\t\tretval = fn(packed->name + trim, packed->sha1, cb_data);\n \t\t\tif (retval)\n \t\t\t\treturn retval;\n \t\t}\n@@ -320,34 +320,39 @@ static int do_for_each_ref(const char *b\n \treturn 0;\n }\n \n-int head_ref(int (*fn)(const char *path, const unsigned char *sha1))\n+int head_ref(each_ref_fn fn, void *cb_data)\n {\n \tunsigned char sha1[20];\n \tif (!read_ref(\"HEAD\", sha1))\n-\t\treturn fn(\"HEAD\", sha1);\n+\t\treturn fn(\"HEAD\", sha1, cb_data);\n \treturn 0;\n }\n \n-int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1))\n+int for_each_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(\"refs/\", fn, 0);\n+\treturn do_for_each_ref(\"refs/\", fn, 0, cb_data);\n }\n \n-int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1))\n+int for_each_tag_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(\"refs/tags/\", fn, 10);\n+\treturn do_for_each_ref(\"refs/tags/\", fn, 10, cb_data);\n }\n \n-int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1))\n+int for_each_branch_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(\"refs/heads/\", fn, 11);\n+\treturn do_for_each_ref(\"refs/heads/\", fn, 11, cb_data);\n }\n \n-int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1))\n+int for_each_remote_ref(each_ref_fn fn, void *cb_data)\n {\n-\treturn do_for_each_ref(\"refs/remotes/\", fn, 13);\n+\treturn do_for_each_ref(\"refs/remotes/\", fn, 13, cb_data);\n }\n \n+/* NEEDSWORK: This is only used by ssh-upload and it should go; the\n+ * caller should do resolve_ref or read_ref like everybody else.  Or\n+ * maybe everybody else should use get_ref_sha1() instead of doing\n+ * read_ref().\n+ */\n int get_ref_sha1(const char *ref, unsigned char *sha1)\n {\n \tif (check_ref_format(ref))\ndiff --git a/refs.h b/refs.h\nindex af347e6..886c857 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -14,11 +14,12 @@ struct ref_lock {\n  * Calls the specified function for each ref file until it returns nonzero,\n  * and returns the value\n  */\n-extern int head_ref(int (*fn)(const char *path, const unsigned char *sha1));\n-extern int for_each_ref(int (*fn)(const char *path, const unsigned char *sha1));\n-extern int for_each_tag_ref(int (*fn)(const char *path, const unsigned char *sha1));\n-extern int for_each_branch_ref(int (*fn)(const char *path, const unsigned char *sha1));\n-extern int for_each_remote_ref(int (*fn)(const char *path, const unsigned char *sha1));\n+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data);\n+extern int head_ref(each_ref_fn, void *);\n+extern int for_each_ref(each_ref_fn, void *);\n+extern int for_each_tag_ref(each_ref_fn, void *);\n+extern int for_each_branch_ref(each_ref_fn, void *);\n+extern int for_each_remote_ref(each_ref_fn, void *);\n \n /** Reads the refs file specified into sha1 **/\n extern int get_ref_sha1(const char *ref, unsigned char *sha1);\ndiff --git a/revision.c b/revision.c\nindex 6a2539b..0e84b8a 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -466,7 +466,7 @@ static void limit_list(struct rev_info *\n static int all_flags;\n static struct rev_info *all_revs;\n \n-static int handle_one_ref(const char *path, const unsigned char *sha1)\n+static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct object *object = get_reference(all_revs, path, sha1, all_flags);\n \tadd_pending_object(all_revs, object, \"\");\n@@ -477,7 +477,7 @@ static void handle_all(struct rev_info *\n {\n \tall_revs = revs;\n \tall_flags = flags;\n-\tfor_each_ref(handle_one_ref);\n+\tfor_each_ref(handle_one_ref, NULL);\n }\n \n static int add_parents_only(struct rev_info *revs, const char *arg, int flags)\ndiff --git a/send-pack.c b/send-pack.c\nindex 5bb123a..ee13093 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -215,7 +215,7 @@ static int ref_newer(const unsigned char\n static struct ref *local_refs, **local_tail;\n static struct ref *remote_refs, **remote_tail;\n \n-static int one_local_ref(const char *refname, const unsigned char *sha1)\n+static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tstruct ref *ref;\n \tint len = strlen(refname) + 1;\n@@ -230,7 +230,7 @@ static int one_local_ref(const char *ref\n static void get_local_heads(void)\n {\n \tlocal_tail = &local_refs;\n-\tfor_each_ref(one_local_ref);\n+\tfor_each_ref(one_local_ref, NULL);\n }\n \n static int receive_status(int in)\ndiff --git a/server-info.c b/server-info.c\nindex 2fb8f57..7667b41 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -7,7 +7,7 @@ #include \"tag.h\"\n /* refs */\n static FILE *info_ref_fp;\n \n-static int add_info_ref(const char *path, const unsigned char *sha1)\n+static int add_info_ref(const char *path, const unsigned char *sha1, void *cb_data)\n {\n \tstruct object *o = parse_object(sha1);\n \n@@ -34,7 +34,7 @@ static int update_info_refs(int force)\n \tinfo_ref_fp = fopen(path1, \"w\");\n \tif (!info_ref_fp)\n \t\treturn error(\"unable to update %s\", path0);\n-\tfor_each_ref(add_info_ref);\n+\tfor_each_ref(add_info_ref, NULL);\n \tfclose(info_ref_fp);\n \trename(path1, path0);\n \tfree(path0);\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 189b239..10237eb 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -416,7 +416,7 @@ static void receive_needs(void)\n \t}\n }\n \n-static int send_ref(const char *refname, const unsigned char *sha1)\n+static int send_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band side-band-64k\";\n \tstruct object *o = parse_object(sha1);\n@@ -444,8 +444,8 @@ static int send_ref(const char *refname,\n static void upload_pack(void)\n {\n \treset_timeout();\n-\thead_ref(send_ref);\n-\tfor_each_ref(send_ref);\n+\thead_ref(send_ref, NULL);\n+\tfor_each_ref(send_ref, NULL);\n \tpacket_flush(1);\n \treceive_needs();\n \tif (want_obj.nr) {\n-- \n1.4.2.1.g4dc7\n"},{"id":"27342","messageId":"7vslilaecj.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"7v64fhd7ns.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 3/5] Tell between packed, unpacked and symbolic refs.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T07:06:04Z","receivedAt":"2006-09-21T07:06:04Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This adds a \"int *flag\" parameter to resolve_ref() and makes\nfor_each_ref() family to call callback function with an extra\n\"int flag\" parameter.  They are used to give two bits of\ninformation (REF_ISSYMREF and REF_ISPACKED) about the ref.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-fmt-merge-msg.c |    2 +-\n builtin-name-rev.c      |    2 +-\n builtin-pack-refs.c     |    3 ++-\n builtin-prune.c         |    2 +-\n builtin-push.c          |    2 +-\n builtin-rev-parse.c     |    2 +-\n builtin-show-branch.c   |   22 +++++++++++-----------\n builtin-symbolic-ref.c  |    6 +++++-\n cache.h                 |    2 +-\n describe.c              |    2 +-\n fetch-pack.c            |    4 ++--\n fetch.c                 |    2 +-\n fsck-objects.c          |    7 ++++---\n http-push.c             |    2 +-\n receive-pack.c          |    4 ++--\n refs.c                  |   44 +++++++++++++++++++++++++++++++-------------\n refs.h                  |    4 +++-\n revision.c              |    2 +-\n send-pack.c             |    2 +-\n server-info.c           |    2 +-\n sha1_name.c             |    2 +-\n upload-pack.c           |    2 +-\n wt-status.c             |    2 +-\n 23 files changed, 75 insertions(+), 49 deletions(-)\n\ndiff --git a/builtin-fmt-merge-msg.c b/builtin-fmt-merge-msg.c\nindex b93c17c..3d3097d 100644\n--- a/builtin-fmt-merge-msg.c\n+++ b/builtin-fmt-merge-msg.c\n@@ -277,7 +277,7 @@ int cmd_fmt_merge_msg(int argc, const ch\n \t\tusage(fmt_merge_msg_usage);\n \n \t/* get current branch */\n-\tcurrent_branch = resolve_ref(\"HEAD\", head_sha1, 1);\n+\tcurrent_branch = resolve_ref(\"HEAD\", head_sha1, 1, NULL);\n \tif (!strncmp(current_branch, \"refs/heads/\", 11))\n \t\tcurrent_branch += 11;\n \ndiff --git a/builtin-name-rev.c b/builtin-name-rev.c\nindex 9e3e537..618aa31 100644\n--- a/builtin-name-rev.c\n+++ b/builtin-name-rev.c\n@@ -75,7 +75,7 @@ copy_data:\n \t}\n }\n \n-static int name_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)\n {\n \tstruct object *o = parse_object(sha1);\n \tint tags_only = *(int*)cb_data;\ndiff --git a/builtin-pack-refs.c b/builtin-pack-refs.c\nindex b3d5470..9871089 100644\n--- a/builtin-pack-refs.c\n+++ b/builtin-pack-refs.c\n@@ -9,7 +9,8 @@ static void remove_lock_file(void)\n \t\tunlink(lock_path);\n }\n \n-static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int handle_one_ref(const char *path, const unsigned char *sha1,\n+\t\t\t  int flags, void *cb_data)\n {\n \tFILE *refs_file = cb_data;\n \ndiff --git a/builtin-prune.c b/builtin-prune.c\nindex e21c29b..e79b515 100644\n--- a/builtin-prune.c\n+++ b/builtin-prune.c\n@@ -174,7 +174,7 @@ static void walk_commit_list(struct rev_\n \t}\n }\n \n-static int add_one_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int add_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *object = parse_object(sha1);\n \tif (!object)\ndiff --git a/builtin-push.c b/builtin-push.c\nindex 88fc8e2..581c44b 100644\n--- a/builtin-push.c\n+++ b/builtin-push.c\n@@ -27,7 +27,7 @@ static void add_refspec(const char *ref)\n \trefspec_nr = nr;\n }\n \n-static int expand_one_ref(const char *ref, const unsigned char *sha1, void *cb_data)\n+static int expand_one_ref(const char *ref, const unsigned char *sha1, int flag, void *cb_data)\n {\n \t/* Ignore the \"refs/\" at the beginning of the refname */\n \tref += 5;\ndiff --git a/builtin-rev-parse.c b/builtin-rev-parse.c\nindex c771274..3b716fb 100644\n--- a/builtin-rev-parse.c\n+++ b/builtin-rev-parse.c\n@@ -137,7 +137,7 @@ static void show_default(void)\n \t}\n }\n \n-static int show_reference(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tshow_rev(NORMAL, sha1, refname);\n \treturn 0;\ndiff --git a/builtin-show-branch.c b/builtin-show-branch.c\nindex b3548ae..5d6ce56 100644\n--- a/builtin-show-branch.c\n+++ b/builtin-show-branch.c\n@@ -346,7 +346,7 @@ static void sort_ref_range(int bottom, i\n \t      compare_ref_name);\n }\n \n-static int append_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int append_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n \tint i;\n@@ -369,7 +369,7 @@ static int append_ref(const char *refnam\n \treturn 0;\n }\n \n-static int append_head_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int append_head_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tunsigned char tmp[20];\n \tint ofs = 11;\n@@ -380,14 +380,14 @@ static int append_head_ref(const char *r\n \t */\n \tif (get_sha1(refname + ofs, tmp) || hashcmp(tmp, sha1))\n \t\tofs = 5;\n-\treturn append_ref(refname + ofs, sha1, cb_data);\n+\treturn append_ref(refname + ofs, sha1, flag, cb_data);\n }\n \n-static int append_tag_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int append_tag_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tif (strncmp(refname, \"refs/tags/\", 10))\n \t\treturn 0;\n-\treturn append_ref(refname + 5, sha1, cb_data);\n+\treturn append_ref(refname + 5, sha1, flag, cb_data);\n }\n \n static const char *match_ref_pattern = NULL;\n@@ -401,7 +401,7 @@ static int count_slash(const char *s)\n \treturn cnt;\n }\n \n-static int append_matching_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int append_matching_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \t/* we want to allow pattern hold/<asterisk> to show all\n \t * branches under refs/heads/hold/, and v0.99.9? to show\n@@ -417,10 +417,10 @@ static int append_matching_ref(const cha\n \tif (fnmatch(match_ref_pattern, tail, 0))\n \t\treturn 0;\n \tif (!strncmp(\"refs/heads/\", refname, 11))\n-\t\treturn append_head_ref(refname, sha1, cb_data);\n+\t\treturn append_head_ref(refname, sha1, flag, cb_data);\n \tif (!strncmp(\"refs/tags/\", refname, 10))\n-\t\treturn append_tag_ref(refname, sha1, cb_data);\n-\treturn append_ref(refname, sha1, cb_data);\n+\t\treturn append_tag_ref(refname, sha1, flag, cb_data);\n+\treturn append_ref(refname, sha1, flag, cb_data);\n }\n \n static void snarf_refs(int head, int tag)\n@@ -487,7 +487,7 @@ static void append_one_rev(const char *a\n {\n \tunsigned char revkey[20];\n \tif (!get_sha1(av, revkey)) {\n-\t\tappend_ref(av, revkey, NULL);\n+\t\tappend_ref(av, revkey, 0, NULL);\n \t\treturn;\n \t}\n \tif (strchr(av, '*') || strchr(av, '?') || strchr(av, '[')) {\n@@ -630,7 +630,7 @@ int cmd_show_branch(int ac, const char *\n \t\tac--; av++;\n \t}\n \n-\thead_p = resolve_ref(\"HEAD\", head_sha1, 1);\n+\thead_p = resolve_ref(\"HEAD\", head_sha1, 1, NULL);\n \tif (head_p) {\n \t\thead_len = strlen(head_p);\n \t\tmemcpy(head, head_p, head_len + 1);\ndiff --git a/builtin-symbolic-ref.c b/builtin-symbolic-ref.c\nindex 13163ba..d8be052 100644\n--- a/builtin-symbolic-ref.c\n+++ b/builtin-symbolic-ref.c\n@@ -1,5 +1,6 @@\n #include \"builtin.h\"\n #include \"cache.h\"\n+#include \"refs.h\"\n \n static const char git_symbolic_ref_usage[] =\n \"git-symbolic-ref name [ref]\";\n@@ -7,10 +8,13 @@ static const char git_symbolic_ref_usage\n static void check_symref(const char *HEAD)\n {\n \tunsigned char sha1[20];\n-\tconst char *refs_heads_master = resolve_ref(HEAD, sha1, 0);\n+\tint flag;\n+\tconst char *refs_heads_master = resolve_ref(HEAD, sha1, 0, &flag);\n \n \tif (!refs_heads_master)\n \t\tdie(\"No such ref: %s\", HEAD);\n+\telse if (!(flag & REF_ISSYMREF))\n+\t\tdie(\"ref %s is not a symbolic ref\", HEAD);\n \tputs(refs_heads_master);\n }\n \ndiff --git a/cache.h b/cache.h\nindex 282eed6..6def155 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -286,7 +286,7 @@ extern int get_sha1(const char *str, uns\n extern int get_sha1_hex(const char *hex, unsigned char *sha1);\n extern char *sha1_to_hex(const unsigned char *sha1);\t/* static buffer result! */\n extern int read_ref(const char *filename, unsigned char *sha1);\n-extern const char *resolve_ref(const char *path, unsigned char *sha1, int);\n+extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *);\n extern int create_symref(const char *ref, const char *refs_heads_master);\n extern int validate_symref(const char *ref);\n \ndiff --git a/describe.c b/describe.c\nindex ea0f2ce..f4029ee 100644\n--- a/describe.c\n+++ b/describe.c\n@@ -53,7 +53,7 @@ static void add_to_known_names(const cha\n \tnames = ++idx;\n }\n \n-static int get_name(const char *path, const unsigned char *sha1, void *cb_data)\n+static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n \tstruct object *object;\ndiff --git a/fetch-pack.c b/fetch-pack.c\nindex 6264ea1..99ac08b 100644\n--- a/fetch-pack.c\n+++ b/fetch-pack.c\n@@ -42,7 +42,7 @@ static void rev_list_push(struct commit \n \t}\n }\n \n-static int rev_list_insert_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int rev_list_insert_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *o = deref_tag(parse_object(sha1), path, 0);\n \n@@ -253,7 +253,7 @@ done:\n \n static struct commit_list *complete;\n \n-static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data)\n+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *o = parse_object(sha1);\n \ndiff --git a/fetch.c b/fetch.c\nindex 36d1e76..a2cbdfb 100644\n--- a/fetch.c\n+++ b/fetch.c\n@@ -201,7 +201,7 @@ static int interpret_target(char *target\n \treturn -1;\n }\n \n-static int mark_complete(const char *path, const unsigned char *sha1, void *cb_data)\n+static int mark_complete(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct commit *commit = lookup_commit_reference_gently(sha1, 1);\n \tif (commit) {\ndiff --git a/fsck-objects.c b/fsck-objects.c\nindex bb0c94e..46b628c 100644\n--- a/fsck-objects.c\n+++ b/fsck-objects.c\n@@ -402,7 +402,7 @@ static void fsck_dir(int i, char *path)\n \n static int default_refs;\n \n-static int fsck_handle_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int fsck_handle_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *obj;\n \n@@ -458,9 +458,10 @@ static void fsck_object_dir(const char *\n static int fsck_head_link(void)\n {\n \tunsigned char sha1[20];\n-\tconst char *head_points_at = resolve_ref(\"HEAD\", sha1, 1);\n+\tint flag;\n+\tconst char *head_points_at = resolve_ref(\"HEAD\", sha1, 1, &flag);\n \n-\tif (!head_points_at)\n+\tif (!head_points_at || !(flag & REF_ISSYMREF))\n \t\treturn error(\"HEAD is not a symbolic ref\");\n \tif (strncmp(head_points_at, \"refs/heads/\", 11))\n \t\treturn error(\"HEAD points to something strange (%s)\",\ndiff --git a/http-push.c b/http-push.c\nindex 460c9be..ecefdfd 100644\n--- a/http-push.c\n+++ b/http-push.c\n@@ -1864,7 +1864,7 @@ static int update_remote(unsigned char *\n static struct ref *local_refs, **local_tail;\n static struct ref *remote_refs, **remote_tail;\n \n-static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct ref *ref;\n \tint len = strlen(refname) + 1;\ndiff --git a/receive-pack.c b/receive-pack.c\nindex 7abc921..abbcb6a 100644\n--- a/receive-pack.c\n+++ b/receive-pack.c\n@@ -12,7 +12,7 @@ static int report_status;\n static char capabilities[] = \"report-status\";\n static int capabilities_sent;\n \n-static int show_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int show_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tif (capabilities_sent)\n \t\tpacket_write(1, \"%s %s\\n\", sha1_to_hex(sha1), path);\n@@ -27,7 +27,7 @@ static void write_head_info(void)\n {\n \tfor_each_ref(show_ref, NULL);\n \tif (!capabilities_sent)\n-\t\tshow_ref(\"capabilities^{}\", null_sha1, NULL);\n+\t\tshow_ref(\"capabilities^{}\", null_sha1, 0, NULL);\n \n }\n \ndiff --git a/refs.c b/refs.c\nindex 85564f0..40f16af 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -5,6 +5,7 @@ #include <errno.h>\n \n struct ref_list {\n \tstruct ref_list *next;\n+\tunsigned char flag; /* ISSYMREF? ISPACKED? */\n \tunsigned char sha1[20];\n \tchar name[FLEX_ARRAY];\n };\n@@ -36,7 +37,8 @@ static const char *parse_ref_line(char *\n \treturn line;\n }\n \n-static struct ref_list *add_ref(const char *name, const unsigned char *sha1, struct ref_list *list)\n+static struct ref_list *add_ref(const char *name, const unsigned char *sha1,\n+\t\t\t\tint flag, struct ref_list *list)\n {\n \tint len;\n \tstruct ref_list **p = &list, *entry;\n@@ -58,6 +60,7 @@ static struct ref_list *add_ref(const ch\n \tentry = xmalloc(sizeof(struct ref_list) + len);\n \thashcpy(entry->sha1, sha1);\n \tmemcpy(entry->name, name, len);\n+\tentry->flag = flag;\n \tentry->next = *p;\n \t*p = entry;\n \treturn list;\n@@ -78,7 +81,7 @@ static struct ref_list *get_packed_refs(\n \t\t\t\tconst char *name = parse_ref_line(refline, sha1);\n \t\t\t\tif (!name)\n \t\t\t\t\tcontinue;\n-\t\t\t\tlist = add_ref(name, sha1, list);\n+\t\t\t\tlist = add_ref(name, sha1, REF_ISPACKED, list);\n \t\t\t}\n \t\t\tfclose(f);\n \t\t\trefs = list;\n@@ -104,6 +107,7 @@ static struct ref_list *get_ref_dir(cons\n \t\twhile ((de = readdir(dir)) != NULL) {\n \t\t\tunsigned char sha1[20];\n \t\t\tstruct stat st;\n+\t\t\tint flag;\n \t\t\tint namelen;\n \n \t\t\tif (de->d_name[0] == '.')\n@@ -120,11 +124,11 @@ static struct ref_list *get_ref_dir(cons\n \t\t\t\tlist = get_ref_dir(ref, list);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tif (read_ref(ref, sha1) < 0) {\n+\t\t\tif (!resolve_ref(ref, sha1, 1, &flag)) {\n \t\t\t\terror(\"%s points nowhere!\", ref);\n \t\t\t\tcontinue;\n \t\t\t}\n-\t\t\tlist = add_ref(ref, sha1, list);\n+\t\t\tlist = add_ref(ref, sha1, flag, list);\n \t\t}\n \t\tfree(ref);\n \t\tclosedir(dir);\n@@ -147,12 +151,15 @@ static struct ref_list *get_loose_refs(v\n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define MAXDEPTH 5\n \n-const char *resolve_ref(const char *ref, unsigned char *sha1, int reading)\n+const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag)\n {\n \tint depth = MAXDEPTH, len;\n \tchar buffer[256];\n \tstatic char ref_buffer[256];\n \n+\tif (flag)\n+\t\t*flag = 0;\n+\n \tfor (;;) {\n \t\tconst char *path = git_path(\"%s\", ref);\n \t\tstruct stat st;\n@@ -174,6 +181,8 @@ const char *resolve_ref(const char *ref,\n \t\t\twhile (list) {\n \t\t\t\tif (!strcmp(ref, list->name)) {\n \t\t\t\t\thashcpy(sha1, list->sha1);\n+\t\t\t\t\tif (flag)\n+\t\t\t\t\t\t*flag |= REF_ISPACKED;\n \t\t\t\t\treturn ref;\n \t\t\t\t}\n \t\t\t\tlist = list->next;\n@@ -191,6 +200,8 @@ const char *resolve_ref(const char *ref,\n \t\t\t\tbuffer[len] = 0;\n \t\t\t\tstrcpy(ref_buffer, buffer);\n \t\t\t\tref = ref_buffer;\n+\t\t\t\tif (flag)\n+\t\t\t\t\t*flag |= REF_ISSYMREF;\n \t\t\t\tcontinue;\n \t\t\t}\n \t\t}\n@@ -219,6 +230,8 @@ const char *resolve_ref(const char *ref,\n \t\tbuf[len] = 0;\n \t\tmemcpy(ref_buffer, buf, len + 1);\n \t\tref = ref_buffer;\n+\t\tif (flag)\n+\t\t\t*flag |= REF_ISSYMREF;\n \t}\n \tif (len < 40 || get_sha1_hex(buffer, sha1))\n \t\treturn NULL;\n@@ -270,12 +283,13 @@ #endif\n \n int read_ref(const char *ref, unsigned char *sha1)\n {\n-\tif (resolve_ref(ref, sha1, 1))\n+\tif (resolve_ref(ref, sha1, 1, NULL))\n \t\treturn 0;\n \treturn -1;\n }\n \n-static int do_for_each_ref(const char *base, each_ref_fn fn, int trim, void *cb_data)\n+static int do_for_each_ref(const char *base, each_ref_fn fn, int trim,\n+\t\t\t   void *cb_data)\n {\n \tint retval;\n \tstruct ref_list *packed = get_packed_refs();\n@@ -303,7 +317,8 @@ static int do_for_each_ref(const char *b\n \t\t\terror(\"%s does not point to a valid object!\", entry->name);\n \t\t\tcontinue;\n \t\t}\n-\t\tretval = fn(entry->name + trim, entry->sha1, cb_data);\n+\t\tretval = fn(entry->name + trim, entry->sha1,\n+\t\t\t    entry->flag, cb_data);\n \t\tif (retval)\n \t\t\treturn retval;\n \t}\n@@ -311,7 +326,8 @@ static int do_for_each_ref(const char *b\n \tpacked = packed ? packed : loose;\n \twhile (packed) {\n \t\tif (!strncmp(base, packed->name, trim)) {\n-\t\t\tretval = fn(packed->name + trim, packed->sha1, cb_data);\n+\t\t\tretval = fn(packed->name + trim, packed->sha1,\n+\t\t\t\t    packed->flag, cb_data);\n \t\t\tif (retval)\n \t\t\t\treturn retval;\n \t\t}\n@@ -323,8 +339,10 @@ static int do_for_each_ref(const char *b\n int head_ref(each_ref_fn fn, void *cb_data)\n {\n \tunsigned char sha1[20];\n-\tif (!read_ref(\"HEAD\", sha1))\n-\t\treturn fn(\"HEAD\", sha1, cb_data);\n+\tint flag;\n+\n+\tif (resolve_ref(\"HEAD\", sha1, 1, &flag))\n+\t\treturn fn(\"HEAD\", sha1, flag, cb_data);\n \treturn 0;\n }\n \n@@ -415,7 +433,7 @@ int check_ref_format(const char *ref)\n static struct ref_lock *verify_lock(struct ref_lock *lock,\n \tconst unsigned char *old_sha1, int mustexist)\n {\n-\tif (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist)) {\n+\tif (!resolve_ref(lock->ref_name, lock->old_sha1, mustexist, NULL)) {\n \t\terror(\"Can't verify ref %s\", lock->ref_name);\n \t\tunlock_ref(lock);\n \t\treturn NULL;\n@@ -441,7 +459,7 @@ static struct ref_lock *lock_ref_sha1_ba\n \tlock = xcalloc(1, sizeof(struct ref_lock));\n \tlock->lock_fd = -1;\n \n-\tref = resolve_ref(ref, lock->old_sha1, mustexist);\n+\tref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);\n \tif (!ref) {\n \t\tint last_errno = errno;\n \t\terror(\"unable to resolve reference %s: %s\",\ndiff --git a/refs.h b/refs.h\nindex 886c857..305d408 100644\n--- a/refs.h\n+++ b/refs.h\n@@ -14,7 +14,9 @@ struct ref_lock {\n  * Calls the specified function for each ref file until it returns nonzero,\n  * and returns the value\n  */\n-typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data);\n+#define REF_ISSYMREF 01\n+#define REF_ISPACKED 02\n+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);\n extern int head_ref(each_ref_fn, void *);\n extern int for_each_ref(each_ref_fn, void *);\n extern int for_each_tag_ref(each_ref_fn, void *);\ndiff --git a/revision.c b/revision.c\nindex 0e84b8a..cb13b90 100644\n--- a/revision.c\n+++ b/revision.c\n@@ -466,7 +466,7 @@ static void limit_list(struct rev_info *\n static int all_flags;\n static struct rev_info *all_revs;\n \n-static int handle_one_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *object = get_reference(all_revs, path, sha1, all_flags);\n \tadd_pending_object(all_revs, object, \"\");\ndiff --git a/send-pack.c b/send-pack.c\nindex ee13093..fbd792c 100644\n--- a/send-pack.c\n+++ b/send-pack.c\n@@ -215,7 +215,7 @@ static int ref_newer(const unsigned char\n static struct ref *local_refs, **local_tail;\n static struct ref *remote_refs, **remote_tail;\n \n-static int one_local_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int one_local_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct ref *ref;\n \tint len = strlen(refname) + 1;\ndiff --git a/server-info.c b/server-info.c\nindex 7667b41..6cd38be 100644\n--- a/server-info.c\n+++ b/server-info.c\n@@ -7,7 +7,7 @@ #include \"tag.h\"\n /* refs */\n static FILE *info_ref_fp;\n \n-static int add_info_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int add_info_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstruct object *o = parse_object(sha1);\n \ndiff --git a/sha1_name.c b/sha1_name.c\nindex b497528..84d24c6 100644\n--- a/sha1_name.c\n+++ b/sha1_name.c\n@@ -276,7 +276,7 @@ static int get_sha1_basic(const char *st\n \n \tfor (p = fmt; *p; p++) {\n \t\tthis_result = refs_found ? sha1_from_ref : sha1;\n-\t\tref = resolve_ref(mkpath(*p, len, str), this_result, 1);\n+\t\tref = resolve_ref(mkpath(*p, len, str), this_result, 1, NULL);\n \t\tif (ref) {\n \t\t\tif (!refs_found++)\n \t\t\t\treal_ref = xstrdup(ref);\ndiff --git a/upload-pack.c b/upload-pack.c\nindex 10237eb..9412a9b 100644\n--- a/upload-pack.c\n+++ b/upload-pack.c\n@@ -416,7 +416,7 @@ static void receive_needs(void)\n \t}\n }\n \n-static int send_ref(const char *refname, const unsigned char *sha1, void *cb_data)\n+static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)\n {\n \tstatic const char *capabilities = \"multi_ack thin-pack side-band side-band-64k\";\n \tstruct object *o = parse_object(sha1);\ndiff --git a/wt-status.c b/wt-status.c\nindex 050922d..d8e284c 100644\n--- a/wt-status.c\n+++ b/wt-status.c\n@@ -41,7 +41,7 @@ void wt_status_prepare(struct wt_status \n \n \ts->is_initial = get_sha1(\"HEAD\", sha1) ? 1 : 0;\n \n-\thead = resolve_ref(\"HEAD\", sha1, 0);\n+\thead = resolve_ref(\"HEAD\", sha1, 0, NULL);\n \ts->branch = head ? xstrdup(head) : NULL;\n \n \ts->reference = \"HEAD\";\n-- \n1.4.2.1.g4dc7\n"},{"id":"27345","messageId":"7vmz8taeci.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"7v64fhd7ns.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 4/5] pack-refs: do not pack symbolic refs.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T07:06:05Z","receivedAt":"2006-09-21T07:06:05Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Now we can tell which one is symbolic and which one is not, it\nis easy to do so.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-pack-refs.c |    4 +++-\n 1 files changed, 3 insertions(+), 1 deletions(-)\n\ndiff --git a/builtin-pack-refs.c b/builtin-pack-refs.c\nindex 9871089..0fc8a55 100644\n--- a/builtin-pack-refs.c\n+++ b/builtin-pack-refs.c\n@@ -14,7 +14,9 @@ static int handle_one_ref(const char *pa\n {\n \tFILE *refs_file = cb_data;\n \n-\tfprintf(refs_file, \"%s %s\\n\", sha1_to_hex(sha1), path);\n+\t/* Do not pack the symbolic refs */\n+\tif (!(flags & REF_ISSYMREF))\n+\t\tfprintf(refs_file, \"%s %s\\n\", sha1_to_hex(sha1), path);\n \treturn 0;\n }\n \n-- \n1.4.2.1.g4dc7\n"},{"id":"27344","messageId":"7vhcz1aech.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"7v64fhd7ns.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 5/5] git-pack-refs --prune","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-21T07:06:06Z","receivedAt":"2006-09-21T07:06:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"\"git pack-refs --prune\", after successfully packing the existing\nrefs, removes the loose ref files.  It tries to protect against\nrace by doing the usual lock_ref_sha1() which makes sure the\ncontents of the ref has not changed since we last looked at.\n\nAlso we do not bother trying to prune what was already packed, and\nwe do not try pruning symbolic refs.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n builtin-pack-refs.c |   84 ++++++++++++++++++++++++++++++++++++++++++++++-----\n 1 files changed, 75 insertions(+), 9 deletions(-)\n\ndiff --git a/builtin-pack-refs.c b/builtin-pack-refs.c\nindex 0fc8a55..246dd63 100644\n--- a/builtin-pack-refs.c\n+++ b/builtin-pack-refs.c\n@@ -2,6 +2,20 @@ #include \"cache.h\"\n #include \"refs.h\"\n \n static const char *result_path, *lock_path;\n+static const char builtin_pack_refs_usage[] =\n+\"git-pack-refs [--prune]\";\n+\n+struct ref_to_prune {\n+\tstruct ref_to_prune *next;\n+\tunsigned char sha1[20];\n+\tchar name[FLEX_ARRAY];\n+};\n+\n+struct pack_refs_cb_data {\n+\tint prune;\n+\tstruct ref_to_prune *ref_to_prune;\n+\tFILE *refs_file;\n+};\n \n static void remove_lock_file(void)\n {\n@@ -9,21 +23,70 @@ static void remove_lock_file(void)\n \t\tunlink(lock_path);\n }\n \n+static int do_not_prune(int flags)\n+{\n+\t/* If it is already packed or if it is a symref,\n+\t * do not prune it.\n+\t */\n+\treturn (flags & (REF_ISSYMREF|REF_ISPACKED));\n+}\n+\n static int handle_one_ref(const char *path, const unsigned char *sha1,\n \t\t\t  int flags, void *cb_data)\n {\n-\tFILE *refs_file = cb_data;\n+\tstruct pack_refs_cb_data *cb = cb_data;\n \n \t/* Do not pack the symbolic refs */\n \tif (!(flags & REF_ISSYMREF))\n-\t\tfprintf(refs_file, \"%s %s\\n\", sha1_to_hex(sha1), path);\n+\t\tfprintf(cb->refs_file, \"%s %s\\n\", sha1_to_hex(sha1), path);\n+\tif (cb->prune && !do_not_prune(flags)) {\n+\t\tint namelen = strlen(path) + 1;\n+\t\tstruct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);\n+\t\thashcpy(n->sha1, sha1);\n+\t\tstrcpy(n->name, path);\n+\t\tn->next = cb->ref_to_prune;\n+\t\tcb->ref_to_prune = n;\n+\t}\n \treturn 0;\n }\n \n+/* make sure nobody touched the ref, and unlink */\n+static void prune_ref(struct ref_to_prune *r)\n+{\n+\tstruct ref_lock *lock = lock_ref_sha1(r->name + 5, r->sha1, 1);\n+\n+\tif (lock) {\n+\t\tunlink(git_path(r->name));\n+\t\tunlock_ref(lock);\n+\t}\n+}\n+\n+static void prune_refs(struct ref_to_prune *r)\n+{\n+\twhile (r) {\n+\t\tprune_ref(r);\n+\t\tr = r->next;\n+\t}\n+}\n+\n int cmd_pack_refs(int argc, const char **argv, const char *prefix)\n {\n-\tint fd;\n-\tFILE *refs_file;\n+\tint fd, i;\n+\tstruct pack_refs_cb_data cbdata;\n+\n+\tmemset(&cbdata, 0, sizeof(cbdata));\n+\n+\tfor (i = 1; i < argc; i++) {\n+\t\tconst char *arg = argv[i];\n+\t\tif (!strcmp(arg, \"--prune\")) {\n+\t\t\tcbdata.prune = 1;\n+\t\t\tcontinue;\n+\t\t}\n+\t\t/* perhaps other parameters later... */\n+\t\tbreak;\n+\t}\n+\tif (i != argc)\n+\t\tusage(builtin_pack_refs_usage);\n \n \tresult_path = xstrdup(git_path(\"packed-refs\"));\n \tlock_path = xstrdup(mkpath(\"%s.lock\", result_path));\n@@ -33,14 +96,17 @@ int cmd_pack_refs(int argc, const char *\n \t\tdie(\"unable to create new ref-pack file (%s)\", strerror(errno));\n \tatexit(remove_lock_file);\n \n-\trefs_file = fdopen(fd, \"w\");\n-\tif (!refs_file)\n-\t\tdie(\"unable to create ref-pack file structure (%s)\", strerror(errno));\n-\tfor_each_ref(handle_one_ref, refs_file);\n+\tcbdata.refs_file = fdopen(fd, \"w\");\n+\tif (!cbdata.refs_file)\n+\t\tdie(\"unable to create ref-pack file structure (%s)\",\n+\t\t    strerror(errno));\n+\tfor_each_ref(handle_one_ref, &cbdata);\n \tfsync(fd);\n-\tfclose(refs_file);\n+\tfclose(cbdata.refs_file);\n \tif (rename(lock_path, result_path) < 0)\n \t\tdie(\"unable to overwrite old ref-pack file (%s)\", strerror(errno));\n \tlock_path = NULL;\n+\tif (cbdata.prune)\n+\t\tprune_refs(cbdata.ref_to_prune);\n \treturn 0;\n }\n-- \n1.4.2.1.g4dc7\n"},{"id":"27362","messageId":"Pine.LNX.4.64.0609210815100.4388@g5.osdl.org","threadId":"5537","inReplyTo":"7v64fhd7ns.fsf@assigned-by-dhcp.cox.net","subject":"Re: [RFC] git-pack-refs --prune","fromName":"Linus Torvalds","fromEmail":"torvalds@osdl.org","sentAt":"2006-09-21T15:19:04Z","receivedAt":"2006-09-21T15:19:04Z","isPatch":false,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Thu, 21 Sep 2006, Junio C Hamano wrote:\n> \n> Ok, so I did these and the result is a 4-patch series.\n\nLooks good to me. I would have combined 2/3, since the bulk of them is the \ncalling conversion change, and they both add a new argument to the same \nfunction, so combining them would make just one patch that isn't even \nnoticeably larger than either of the two originals, but that's just a \nsmall nitpick.\n\nHaving callback data is clearly good, and the \"flags\" may end up being \nuseful for other things in the future too (ie if we add attributes to \nbranches, we could at some point have \"hidden\" and \"read-only\" etc flags)\n\n\t\tLinus\n"},{"id":"27403","messageId":"7vzmcs5wh9.fsf@assigned-by-dhcp.cox.net","threadId":"5537","inReplyTo":"Pine.LNX.4.64.0609210815100.4388@g5.osdl.org","subject":"Re: [RFC] git-pack-refs --prune","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-22T04:57:54Z","receivedAt":"2006-09-22T04:57:54Z","isPatch":false,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@osdl.org> writes:\n\n> On Thu, 21 Sep 2006, Junio C Hamano wrote:\n>> \n>> Ok, so I did these and the result is a 4-patch series.\n>\n> Looks good to me. I would have combined 2/3, since the bulk of them is the \n> calling conversion change, and they both add a new argument to the same \n> function, so combining them would make just one patch that isn't even \n> noticeably larger than either of the two originals, but that's just a \n> small nitpick.\n\nActually I did these two as a single change and then later split\nthem.  Unfortunately cherry-picking only 3 without 2 would not\nhave worked anyway in this particular case with git, but an\nideal SCM would have done a better job.  It's just taste and\nprinciple and splitting them into two would not have any\npractical advantage in the real world.\n\nSide note to interested bystanders.\n\nPatch 2 changes function signature of for_each_ref() and\nfriends and adds one parameter, void *cbdata, to them and the\ncalled-back functions.  Typical changes looked like these:\n\n+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data);\n-static int name_ref(const char *path, const unsigned char *sha1)\n+static int name_ref(const char *path, const unsigned char *sha1, void *cb_data)\n-\tfor_each_ref(name_ref);\n+\tfor_each_ref(name_ref, &tags_only);\n\nThen patch 3 adds another parameter, int flags, to the same functions involved:\n\n-typedef int each_ref_fn(const char *refname, const unsigned char *sha1, void *cb_data);\n+#define REF_ISSYMREF 01\n+#define REF_ISPACKED 02\n+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags, void *cb_data);\n-static int name_ref(const char *path, const unsigned char *sha1, void *cb_data)\n+static int name_ref(const char *path, const unsigned char *sha1, int flags, void *cb_data)\n\nAn ideal SCM should help the user who wants to cherry pick patch\n3 without patch 2, effectively applying a patch like this:\n\n* Type definition of for_each_ref() callback function (with\n  flags but without callback data):\n\n+#define REF_ISSYMREF 01\n+#define REF_ISPACKED 02\n+typedef int each_ref_fn(const char *refname, const unsigned char *sha1, int flags);\n\n* An actual callback function:\n\n-static int name_ref(const char *path, const unsigned char *sha1)\n+static int name_ref(const char *path, const unsigned char *sha1, int flags)\n\n* The call-site of for_each_ref (note patch2 and patch2+patch3\n  have the same effect):\n\n-\tfor_each_ref(name_ref);\n+\tfor_each_ref(name_ref, &tags_only);\n"}]}