{"thread":{"id":"5768","subject":"[PATCH 1/2] Move code resolving packed refs into its own function.","startedAt":"2006-09-30T20:01:58Z","lastAt":"2006-10-03T17:39:20Z","messageCount":14,"participants":["Christian Couder","Junio C Hamano","Jeff King","Jakub Narebski"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"28034","messageId":"20060930220158.d331bb7c.chriscool@tuxfamily.org","threadId":"5768","inReplyTo":null,"subject":"[PATCH 1/2] Move code resolving packed refs into its own function.","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2006-09-30T20:01:58Z","receivedAt":"2006-09-30T20:01:58Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"This patch move Linus' packed refs resolving code from\n\"resolve_ref\" into a new \"resolve_packed_ref\" extern\nfunction so that it can be reused when needed.\n\nSigned-off-by: Christian Couder <chriscool@tuxfamily.org>\n---\n cache.h |    2 ++\n refs.c  |   34 ++++++++++++++++++++--------------\n 2 files changed, 22 insertions(+), 14 deletions(-)\n\ndiff --git a/cache.h b/cache.h\nindex 4e01a74..f37bc18 100644\n--- a/cache.h\n+++ b/cache.h\n@@ -296,6 +296,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, int *);\n+extern const char *resolve_packed_ref(const char *ref, unsigned char *sha1, int, int *);\n+\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/refs.c b/refs.c\nindex 3d4cdd1..5e3988b 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -148,6 +148,25 @@ static struct ref_list *get_loose_refs(v\n \treturn refs;\n }\n \n+const char *resolve_packed_ref(const char *ref, unsigned char *sha1,\n+\t\t\t       int reading, int *flag)\n+{\n+\tstruct ref_list *list = get_packed_refs();\n+\twhile (list) {\n+\t\tif (!strcmp(ref, list->name)) {\n+\t\t\thashcpy(sha1, list->sha1);\n+\t\t\tif (flag)\n+\t\t\t\t*flag |= REF_ISPACKED;\n+\t\t\treturn ref;\n+\t\t}\n+\t\tlist = list->next;\n+\t}\n+\tif (reading || errno != ENOENT)\n+\t\treturn NULL;\n+\thashclr(sha1);\n+\treturn ref;\n+}\n+\n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n #define MAXDEPTH 5\n \n@@ -177,20 +196,7 @@ const char *resolve_ref(const char *ref,\n \t\t * reading.\n \t\t */\n \t\tif (lstat(path, &st) < 0) {\n-\t\t\tstruct ref_list *list = get_packed_refs();\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-\t\t\t}\n-\t\t\tif (reading || errno != ENOENT)\n-\t\t\t\treturn NULL;\n-\t\t\thashclr(sha1);\n-\t\t\treturn ref;\n+\t\t\treturn resolve_packed_ref(ref, sha1, reading, flag);\n \t\t}\n \n \t\t/* Follow \"normalized\" - ie \"refs/..\" symlinks by hand */\n-- \n1.4.2.1.g7cf04\n"},{"id":"28036","messageId":"7vmz8hccxl.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"20060930220158.d331bb7c.chriscool@tuxfamily.org","subject":"Re: [PATCH 1/2] Move code resolving packed refs into its own function.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T20:33:10Z","receivedAt":"2006-09-30T20:33:10Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> This patch move Linus' packed refs resolving code from\n> \"resolve_ref\" into a new \"resolve_packed_ref\" extern\n> function so that it can be reused when needed.\n\nI think we are stepping on each other's toes.  How far into the\nprocess of making correct branch deletion are you?\n"},{"id":"28038","messageId":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"7vmz8hccxl.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 0/6] ref deletion and D/F conflict avoidance with packed-refs.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T22:26:25Z","receivedAt":"2006-09-30T22:26:25Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This series cleans up the area that was affected by the recent\naddition of \"packed-refs\".  Christian Couder and Jeff King CC'ed\nsince they seem to be touching in the general vicinity of the\ncode these patches touch.\n\n[1/6] ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.\n[2/6] refs: minor restructuring of cached refs data.\n[3/6] lock_ref_sha1(): do not sometimes error() and sometimes die().\n[4/6] lock_ref_sha1(): check D/F conflict with packed ref when creating.\n[5/6] delete_ref(): delete packed ref\n[6/6] git-branch: remove D/F check done by hand.\n\nI opted for removing from the packed-ref file when a ref that is\npacked is deleted.  \n"},{"id":"28040","messageId":"7vzmch9eeg.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 1/6] ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T22:29:43Z","receivedAt":"2006-09-30T22:29:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"It is normal to have .git/refs/heads/foo directory which is\nempty after the last branch whose name starts with foo/ is\nremoved.  Make sure we notice this case and allow creation of\nbranch foo by removing the empty directory.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n * This and the next one are essentially the same as my previous\n   two patch series.\n\n refs.c |   64 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++\n 1 files changed, 64 insertions(+), 0 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 3d4cdd1..b433c0c 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -473,6 +473,59 @@ static struct ref_lock *verify_lock(stru\n \treturn lock;\n }\n \n+static int remove_empty_dir_recursive(char *path, int len)\n+{\n+\tDIR *dir = opendir(path);\n+\tstruct dirent *e;\n+\tint ret = 0;\n+\n+\tif (!dir)\n+\t\treturn -1;\n+\tif (path[len-1] != '/')\n+\t\tpath[len++] = '/';\n+\twhile ((e = readdir(dir)) != NULL) {\n+\t\tstruct stat st;\n+\t\tint namlen;\n+\t\tif ((e->d_name[0] == '.') &&\n+\t\t    ((e->d_name[1] == 0) ||\n+\t\t     ((e->d_name[1] == '.') && e->d_name[2] == 0)))\n+\t\t\tcontinue; /* \".\" and \"..\" */\n+\n+\t\tnamlen = strlen(e->d_name);\n+\t\tif ((len + namlen < PATH_MAX) &&\n+\t\t    strcpy(path + len, e->d_name) &&\n+\t\t    !lstat(path, &st) &&\n+\t\t    S_ISDIR(st.st_mode) &&\n+\t\t    remove_empty_dir_recursive(path, len + namlen))\n+\t\t\tcontinue; /* happy */\n+\n+\t\t/* path too long, stat fails, or non-directory still exists */\n+\t\tret = -1;\n+\t\tbreak;\n+\t}\n+\tclosedir(dir);\n+\tif (!ret) {\n+\t\tpath[len] = 0;\n+\t\tret = rmdir(path);\n+\t}\n+\treturn ret;\n+}\n+\n+static int remove_empty_directories(char *file)\n+{\n+\t/* we want to create a file but there is a directory there;\n+\t * if that is an empty directory (or a directory that contains\n+\t * only empty directories), remove them.\n+\t */\n+\tchar path[PATH_MAX];\n+\tint len = strlen(file);\n+\n+\tif (len >= PATH_MAX) /* path too long ;-) */\n+\t\treturn -1;\n+\tstrcpy(path, file);\n+\treturn remove_empty_dir_recursive(path, len);\n+}\n+\n static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)\n {\n \tchar *ref_file;\n@@ -485,6 +538,17 @@ static struct ref_lock *lock_ref_sha1_ba\n \tlock->lock_fd = -1;\n \n \tref = resolve_ref(ref, lock->old_sha1, mustexist, NULL);\n+\tif (!ref && errno == EISDIR) {\n+\t\t/* we are trying to lock foo but we used to\n+\t\t * have foo/bar which now does not exist;\n+\t\t * it is normal for the empty directory 'foo'\n+\t\t * to remain.\n+\t\t */\n+\t\tref_file = git_path(\"%s\", orig_ref);\n+\t\tif (remove_empty_directories(ref_file))\n+\t\t\tdie(\"there are still refs under '%s'\", orig_ref);\n+\t\tref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);\n+\t}\n \tif (!ref) {\n \t\tint last_errno = errno;\n \t\terror(\"unable to resolve reference %s: %s\",\n-- \n1.4.2.1.g5a98f\n"},{"id":"28041","messageId":"7vr6xt9edt.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 2/6] refs: minor restructuring of cached refs data.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T22:30:06Z","receivedAt":"2006-09-30T22:30:06Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Once we read packed and loose refs, for_each_ref() and friends\nkept using them even after write_ref_sha1() and delete_ref()\nchanged the refs.  This adds invalidate_cached_refs() as a way\nto flush the cached information.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n * This and the previous one are essentially the same as my previous\n   two patch series.\n\n   getting rid of \"we run only once\" mentality.  otherwise we\n   can never libify the thing...\n\n refs.c |   56 +++++++++++++++++++++++++++++++++++++++++++-------------\n 1 files changed, 43 insertions(+), 13 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex b433c0c..6ee5f96 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -66,12 +66,42 @@ static struct ref_list *add_ref(const ch\n \treturn list;\n }\n \n-static struct ref_list *get_packed_refs(void)\n+/*\n+ * Future: need to be in \"struct repository\"\n+ * when doing a full libification.\n+ */\n+struct cached_refs {\n+\tchar did_loose;\n+\tchar did_packed;\n+\tstruct ref_list *loose;\n+\tstruct ref_list *packed;\n+} cached_refs;\n+\n+static void free_ref_list(struct ref_list *list)\n+{\n+\tstruct ref_list *next;\n+\tfor ( ; list; list = next) {\n+\t\tnext = list->next;\n+\t\tfree(list);\n+\t}\n+}\n+\n+static void invalidate_cached_refs(void)\n {\n-\tstatic int did_refs = 0;\n-\tstatic struct ref_list *refs = NULL;\n+\tstruct cached_refs *ca = &cached_refs;\n+\n+\tif (ca->did_loose && ca->loose)\n+\t\tfree_ref_list(ca->loose);\n+\tif (ca->did_packed && ca->packed)\n+\t\tfree_ref_list(ca->packed);\n+\tca->loose = ca->packed = NULL;\n+\tca->did_loose = ca->did_packed = 0;\n+}\n \n-\tif (!did_refs) {\n+static struct ref_list *get_packed_refs(void)\n+{\n+\tif (!cached_refs.did_packed) {\n+\t\tstruct ref_list *refs = NULL;\n \t\tFILE *f = fopen(git_path(\"packed-refs\"), \"r\");\n \t\tif (f) {\n \t\t\tstruct ref_list *list = NULL;\n@@ -86,9 +116,10 @@ static struct ref_list *get_packed_refs(\n \t\t\tfclose(f);\n \t\t\trefs = list;\n \t\t}\n-\t\tdid_refs = 1;\n+\t\tcached_refs.packed = refs;\n+\t\tcached_refs.did_packed = 1;\n \t}\n-\treturn refs;\n+\treturn cached_refs.packed;\n }\n \n static struct ref_list *get_ref_dir(const char *base, struct ref_list *list)\n@@ -138,14 +169,11 @@ static struct ref_list *get_ref_dir(cons\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+\tif (!cached_refs.did_loose) {\n+\t\tcached_refs.loose = get_ref_dir(\"refs\", NULL);\n+\t\tcached_refs.did_loose = 1;\n \t}\n-\treturn refs;\n+\treturn cached_refs.loose;\n }\n \n /* We allow \"recursive\" symbolic refs. Only within reason, though */\n@@ -401,6 +429,7 @@ int delete_ref(const char *refname, unsi\n \t\tfprintf(stderr, \"warning: unlink(%s) failed: %s\",\n \t\t\tlock->log_file, strerror(errno));\n \n+\tinvalidate_cached_refs();\n \treturn ret;\n }\n \n@@ -665,6 +694,7 @@ int write_ref_sha1(struct ref_lock *lock\n \t\tunlock_ref(lock);\n \t\treturn -1;\n \t}\n+\tinvalidate_cached_refs();\n \tif (log_ref_write(lock, sha1, logmsg) < 0) {\n \t\tunlock_ref(lock);\n \t\treturn -1;\n-- \n1.4.2.1.g5a98f\n"},{"id":"28045","messageId":"7vlko19ed1.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 3/6] lock_ref_sha1(): do not sometimes error() and sometimes die().","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T22:30:34Z","receivedAt":"2006-09-30T22:30:34Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This cleans up the error path in the function so it does not\ndie() itself sometimes while signalling an error with NULL some\nother times which was inconsistent and confusing.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n refs.c |   26 ++++++++++++++++++--------\n 1 files changed, 18 insertions(+), 8 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 6ee5f96..157de43 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -561,6 +561,7 @@ static struct ref_lock *lock_ref_sha1_ba\n \tconst char *orig_ref = ref;\n \tstruct ref_lock *lock;\n \tstruct stat st;\n+\tint last_errno = 0;\n \tint mustexist = (old_sha1 && !is_null_sha1(old_sha1));\n \n \tlock = xcalloc(1, sizeof(struct ref_lock));\n@@ -574,17 +575,18 @@ static struct ref_lock *lock_ref_sha1_ba\n \t\t * to remain.\n \t\t */\n \t\tref_file = git_path(\"%s\", orig_ref);\n-\t\tif (remove_empty_directories(ref_file))\n-\t\t\tdie(\"there are still refs under '%s'\", orig_ref);\n+\t\tif (remove_empty_directories(ref_file)) {\n+\t\t\tlast_errno = errno;\n+\t\t\terror(\"there are still refs under '%s'\", orig_ref);\n+\t\t\tgoto error_return;\n+\t\t}\n \t\tref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);\n \t}\n \tif (!ref) {\n-\t\tint last_errno = errno;\n+\t\tlast_errno = errno;\n \t\terror(\"unable to resolve reference %s: %s\",\n \t\t\torig_ref, strerror(errno));\n-\t\tunlock_ref(lock);\n-\t\terrno = last_errno;\n-\t\treturn NULL;\n+\t\tgoto error_return;\n \t}\n \tlock->lk = xcalloc(1, sizeof(struct lock_file));\n \n@@ -593,11 +595,19 @@ static struct ref_lock *lock_ref_sha1_ba\n \tref_file = git_path(\"%s\", ref);\n \tlock->force_write = lstat(ref_file, &st) && errno == ENOENT;\n \n-\tif (safe_create_leading_directories(ref_file))\n-\t\tdie(\"unable to create directory for %s\", ref_file);\n+\tif (safe_create_leading_directories(ref_file)) {\n+\t\tlast_errno = errno;\n+\t\terror(\"unable to create directory for %s\", ref_file);\n+\t\tgoto error_return;\n+\t}\n \tlock->lock_fd = hold_lock_file_for_update(lock->lk, ref_file, 1);\n \n \treturn old_sha1 ? verify_lock(lock, old_sha1, mustexist) : lock;\n+\n+ error_return:\n+\tunlock_ref(lock);\n+\terrno = last_errno;\n+\treturn NULL;\n }\n \n struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1)\n-- \n1.4.2.1.g5a98f\n"},{"id":"28042","messageId":"7vfye99ecs.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 4/6] lock_ref_sha1(): check D/F conflict with packed ref when creating.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T22:30:43Z","receivedAt":"2006-09-30T22:30:43Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This makes the ref locking codepath to notice if an existing ref\noverlaps with the ref we are creating.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n refs.c |   24 ++++++++++++++++++++++++\n 1 files changed, 24 insertions(+), 0 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 157de43..2bfa92a 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -588,6 +588,30 @@ static struct ref_lock *lock_ref_sha1_ba\n \t\t\torig_ref, strerror(errno));\n \t\tgoto error_return;\n \t}\n+\tif (is_null_sha1(lock->old_sha1)) {\n+\t\t/* The ref did not exist and we are creating it.\n+\t\t * Make sure there is no existing ref that is packed\n+\t\t * whose name begins with our refname, nor a ref whose\n+\t\t * name is a proper prefix of our refname.\n+\t\t */\n+\t\tint namlen = strlen(ref); /* e.g. 'foo/bar' */\n+\t\tstruct ref_list *list = get_packed_refs();\n+\t\twhile (list) {\n+\t\t\t/* list->name could be 'foo' or 'foo/bar/baz' */\n+\t\t\tint len = strlen(list->name);\n+\t\t\tint cmplen = (namlen < len) ? namlen : len;\n+\t\t\tconst char *lead = (namlen < len) ? list->name : ref;\n+\n+\t\t\tif (!strncmp(ref, list->name, cmplen) &&\n+\t\t\t    lead[cmplen] == '/') {\n+\t\t\t\terror(\"'%s' exists; cannot create '%s'\",\n+\t\t\t\t      list->name, ref);\n+\t\t\t\tgoto error_return;\n+\t\t\t}\n+\t\t\tlist = list->next;\n+\t\t}\n+\t}\n+\n \tlock->lk = xcalloc(1, sizeof(struct lock_file));\n \n \tlock->ref_name = xstrdup(ref);\n-- \n1.4.2.1.g5a98f\n"},{"id":"28043","messageId":"7vac4h9ecp.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 5/6] delete_ref(): delete packed ref","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T22:30:46Z","receivedAt":"2006-09-30T22:30:46Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"This implements deletion of a packed ref.  Since it is a very\nrare event to delete a ref compared to looking up, creating and\nupdating, this opts to remove the ref from the packed-ref file\ninstead of doing any of the filesystem based \"negative ref\" trick\nto optimize the deletion path.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n * This looks larger than it actually is, because it moves delete_ref()\n   down so that it can use lock_ref_sha1_basic().\n\n refs.c |  109 +++++++++++++++++++++++++++++++++++++++++++++-------------------\n 1 files changed, 77 insertions(+), 32 deletions(-)\n\ndiff --git a/refs.c b/refs.c\nindex 2bfa92a..858c534 100644\n--- a/refs.c\n+++ b/refs.c\n@@ -406,33 +406,6 @@ int get_ref_sha1(const char *ref, unsign\n \treturn read_ref(mkpath(\"refs/%s\", ref), sha1);\n }\n \n-int delete_ref(const char *refname, unsigned char *sha1)\n-{\n-\tstruct ref_lock *lock;\n-\tint err, i, ret = 0;\n-\n-\tlock = lock_any_ref_for_update(refname, sha1);\n-\tif (!lock)\n-\t\treturn 1;\n-\ti = strlen(lock->lk->filename) - 5; /* .lock */\n-\tlock->lk->filename[i] = 0;\n-\terr = unlink(lock->lk->filename);\n-\tif (err) {\n-\t\tret = 1;\n-\t\terror(\"unlink(%s) failed: %s\",\n-\t\t      lock->lk->filename, strerror(errno));\n-\t}\n-\tlock->lk->filename[i] = '.';\n-\n-\terr = unlink(lock->log_file);\n-\tif (err && errno != ENOENT)\n-\t\tfprintf(stderr, \"warning: unlink(%s) failed: %s\",\n-\t\t\tlock->log_file, strerror(errno));\n-\n-\tinvalidate_cached_refs();\n-\treturn ret;\n-}\n-\n /*\n  * Make sure \"ref\" is something reasonable to have under \".git/refs/\";\n  * We do not like it if:\n@@ -555,7 +528,7 @@ static int remove_empty_directories(char\n \treturn remove_empty_dir_recursive(path, len);\n }\n \n-static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1)\n+static struct ref_lock *lock_ref_sha1_basic(const char *ref, const unsigned char *old_sha1, int *flag)\n {\n \tchar *ref_file;\n \tconst char *orig_ref = ref;\n@@ -567,7 +540,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, NULL);\n+\tref = resolve_ref(ref, lock->old_sha1, mustexist, flag);\n \tif (!ref && errno == EISDIR) {\n \t\t/* we are trying to lock foo but we used to\n \t\t * have foo/bar which now does not exist;\n@@ -580,7 +553,7 @@ static struct ref_lock *lock_ref_sha1_ba\n \t\t\terror(\"there are still refs under '%s'\", orig_ref);\n \t\t\tgoto error_return;\n \t\t}\n-\t\tref = resolve_ref(orig_ref, lock->old_sha1, mustexist, NULL);\n+\t\tref = resolve_ref(orig_ref, lock->old_sha1, mustexist, flag);\n \t}\n \tif (!ref) {\n \t\tlast_errno = errno;\n@@ -640,12 +613,84 @@ struct ref_lock *lock_ref_sha1(const cha\n \tif (check_ref_format(ref))\n \t\treturn NULL;\n \tstrcpy(refpath, mkpath(\"refs/%s\", ref));\n-\treturn lock_ref_sha1_basic(refpath, old_sha1);\n+\treturn lock_ref_sha1_basic(refpath, old_sha1, NULL);\n }\n \n struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1)\n {\n-\treturn lock_ref_sha1_basic(ref, old_sha1);\n+\treturn lock_ref_sha1_basic(ref, old_sha1, NULL);\n+}\n+\n+static int repack_without_ref(const char *refname)\n+{\n+\tstruct ref_list *list, *packed_ref_list;\n+\tint fd;\n+\tint found = 0;\n+\tstruct lock_file packlock;\n+\n+\tpacked_ref_list = get_packed_refs();\n+\tfor (list = packed_ref_list; list; list = list->next) {\n+\t\tif (!strcmp(refname, list->name)) {\n+\t\t\tfound = 1;\n+\t\t\tbreak;\n+\t\t}\n+\t}\n+\tif (!found)\n+\t\treturn 0;\n+\tmemset(&packlock, 0, sizeof(packlock));\n+\tfd = hold_lock_file_for_update(&packlock, git_path(\"packed-refs\"), 0);\n+\tif (fd < 0)\n+\t\treturn error(\"cannot delete '%s' from packed refs\", refname);\n+\n+\tfor (list = packed_ref_list; list; list = list->next) {\n+\t\tchar line[PATH_MAX + 100];\n+\t\tint len;\n+\n+\t\tif (!strcmp(refname, list->name))\n+\t\t\tcontinue;\n+\t\tlen = snprintf(line, sizeof(line), \"%s %s\\n\",\n+\t\t\t       sha1_to_hex(list->sha1), list->name);\n+\t\t/* this should not happen but just being defensive */\n+\t\tif (len > sizeof(line))\n+\t\t\tdie(\"too long a refname '%s'\", list->name);\n+\t\twrite_or_die(fd, line, len);\n+\t}\n+\treturn commit_lock_file(&packlock);\n+}\n+\n+int delete_ref(const char *refname, unsigned char *sha1)\n+{\n+\tstruct ref_lock *lock;\n+\tint err, i, ret = 0, flag = 0;\n+\n+\tlock = lock_ref_sha1_basic(refname, sha1, &flag);\n+\tif (!lock)\n+\t\treturn 1;\n+\tif (!(flag & REF_ISPACKED)) {\n+\t\t/* loose */\n+\t\ti = strlen(lock->lk->filename) - 5; /* .lock */\n+\t\tlock->lk->filename[i] = 0;\n+\t\terr = unlink(lock->lk->filename);\n+\t\tif (err) {\n+\t\t\tret = 1;\n+\t\t\terror(\"unlink(%s) failed: %s\",\n+\t\t\t      lock->lk->filename, strerror(errno));\n+\t\t}\n+\t\tlock->lk->filename[i] = '.';\n+\t}\n+\t/* removing the loose one could have resurrected an earlier\n+\t * packed one.  Also, if it was not loose we need to repack\n+\t * without it.\n+\t */\n+\tret |= repack_without_ref(refname);\n+\n+\terr = unlink(lock->log_file);\n+\tif (err && errno != ENOENT)\n+\t\tfprintf(stderr, \"warning: unlink(%s) failed: %s\",\n+\t\t\tlock->log_file, strerror(errno));\n+\tinvalidate_cached_refs();\n+\tunlock_ref(lock);\n+\treturn ret;\n }\n \n void unlock_ref(struct ref_lock *lock)\n-- \n1.4.2.1.g5a98f\n"},{"id":"28044","messageId":"7v4pup9ecj.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","subject":"[PATCH 6/6] git-branch: remove D/F check done by hand.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T22:30:52Z","receivedAt":"2006-09-30T22:30:52Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Now ref creation codepath in lock_ref_sha1() and friends notices\nthe directory/file conflict situation, we do not do this by hand\nin git-branch anymore.\n\nSigned-off-by: Junio C Hamano <junkio@cox.net>\n---\n git-branch.sh |   10 ----------\n 1 files changed, 0 insertions(+), 10 deletions(-)\n\ndiff --git a/git-branch.sh b/git-branch.sh\nindex c616830..bf84b30 100755\n--- a/git-branch.sh\n+++ b/git-branch.sh\n@@ -121,16 +121,6 @@ then\n \tdone\n fi\n \n-branchdir=$(dirname $branchname)\n-while test \"$branchdir\" != \".\"\n-do\n-\tif git-show-ref --verify --quiet -- \"refs/heads/$branchdir\"\n-\tthen\n-\t\tdie \"$branchdir already exists.\"\n-\tfi\n-\tbranchdir=$(dirname $branchdir)\n-done\n-\n prev=''\n if git-show-ref --verify --quiet -- \"refs/heads/$branchname\"\n then\n-- \n1.4.2.1.g5a98f\n"},{"id":"28046","messageId":"20060930223652.GA11274@coredump.intra.peff.net","threadId":"5768","inReplyTo":"7v1wptc7ou.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 0/6] ref deletion and D/F conflict avoidance with packed-refs.","fromName":"Jeff King","fromEmail":"peff@peff.net","sentAt":"2006-09-30T22:36:52Z","receivedAt":"2006-09-30T22:36:52Z","isPatch":true,"sender":{"key":"peff@peff.net","avatar":"https://avatars.githubusercontent.com/u/45925?v=4"},"body":"On Sat, Sep 30, 2006 at 03:26:25PM -0700, Junio C Hamano wrote:\n\n> This series cleans up the area that was affected by the recent\n> addition of \"packed-refs\".  Christian Couder and Jeff King CC'ed\n> since they seem to be touching in the general vicinity of the\n> code these patches touch.\n\nI don't believe there is any conflict between my patches and yours.\n\n-Peff\n"},{"id":"28053","messageId":"200610010606.32561.chriscool@tuxfamily.org","threadId":"5768","inReplyTo":"7vmz8hccxl.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Move code resolving packed refs into its own function.","fromName":"Christian Couder","fromEmail":"chriscool@tuxfamily.org","sentAt":"2006-10-01T04:06:32Z","receivedAt":"2006-10-01T04:06:32Z","isPatch":true,"sender":{"key":"christian.couder@gmail.com","avatar":"https://avatars.githubusercontent.com/u/208954?v=4"},"body":"Junio C Hamano wrote:\n> Christian Couder <chriscool@tuxfamily.org> writes:\n> > This patch move Linus' packed refs resolving code from\n> > \"resolve_ref\" into a new \"resolve_packed_ref\" extern\n> > function so that it can be reused when needed.\n>\n> I think we are stepping on each other's toes.  How far into the\n> process of making correct branch deletion are you?\n\nI am not farther than the 2 patches I sent yesterday (before going to bed).\n\nNow I will probably add a few more test cases and see what needs to be done \nfor tags. Tell me when you have finished with branch deletion so I can see \nwhat I can do in this area.\n\nThanks,\nChristian.\n"},{"id":"28058","messageId":"7vodsw2w9g.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"200610010606.32561.chriscool@tuxfamily.org","subject":"Re: [PATCH 1/2] Move code resolving packed refs into its own function.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-01T09:58:03Z","receivedAt":"2006-10-01T09:58:03Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Christian Couder <chriscool@tuxfamily.org> writes:\n\n> Junio C Hamano wrote:\n>> Christian Couder <chriscool@tuxfamily.org> writes:\n>> > This patch move Linus' packed refs resolving code from\n>> > \"resolve_ref\" into a new \"resolve_packed_ref\" extern\n>> > function so that it can be reused when needed.\n>>\n>> I think we are stepping on each other's toes.  How far into the\n>> process of making correct branch deletion are you?\n>\n> I am not farther than the 2 patches I sent yesterday (before going to bed).\n\nThanks.  I just did not want to waste your work with overlapping\nduplicated efforts.\n\nI think what we have in \"next\" tonight is in a more-or-less\ntestable shape, although it has still a long way to reach\n\"master\".  Things I know we need to address:\n\n - I've updated lock_ref_sha1_basic() to remove empty left-over\n   directories and to notice conflicts between 'foo/bar' vs\n   'foo' when creating a new ref, hopefully in the same spirit\n   as your patch to safe_create_leading_directories(), but done\n   differently (safe_... function is meant to be used anywhere\n   not just $GIT_DIR/refs/, and it felt wrong for it to take\n   exception for packed refs).  We should do the same for the\n   reflog hierarchy but we currently don't.\n\n - We need to audit our shell scripts to make sure they do not\n   depend on being able to look directly into $GIT_DIR/refs to\n   see if the ref they are interested in exists.  I've fixed a\n   few in git-fetch while handling the patch to clean up its\n   output from Santi, but I would not be surprised if there are\n   more.  The code in git-branch and git-tag to list what's\n   there are Ok; they use \"rev-parse --symbolic --all/--tags\".\n\n - I think gitweb should be Ok; it does peek-remote on the\n   repository.  Although we would probably want to update\n   git_get_references and git_get_refs_list sub to use\n   for-each-ref there, that can be done as a later optimization.\n\n - Dumb transports are not aware of packed refs on the remote\n   side.  The underlying commit walkers (anything that links\n   with fetch.c) needs their fetch_ref() implementation updated\n   to look at the packed-refs file from the remote side and we\n   should be fine after that.  I haven't looked at rsync\n   transport but the change necessary there shouldn't be too\n   involved.\n"},{"id":"28150","messageId":"eftjua$2ii$1@sea.gmane.org","threadId":"5768","inReplyTo":"7vodsw2w9g.fsf@assigned-by-dhcp.cox.net","subject":"Re: [PATCH 1/2] Move code resolving packed refs into its own function.","fromName":"Jakub Narebski","fromEmail":"jnareb@gmail.com","sentAt":"2006-10-03T12:10:15Z","receivedAt":"2006-10-03T12:10:15Z","isPatch":true,"sender":{"key":"jnareb@gmail.com","avatar":"https://avatars.githubusercontent.com/u/2706?v=4"},"body":"Junio C Hamano wrote:\n\n>  - I think gitweb should be Ok; it does peek-remote on the\n>    repository.  Although we would probably want to update\n>    git_get_references and git_get_refs_list sub to use\n>    for-each-ref there, that can be done as a later optimization.\n\nI'd rather have gitweb use git-for-each-ref after the command\nis in the official release... if not, then keep it in the 'next'\nbranch.\n\nBy the way, I have an idea to join the git-show-ref and git-for-each-ref:\nsimply add --deref option to git-for-each-ref, which would output\ndereferenced tag just after the reference itself (well, perhaps with the\nexception when sorting, perhaps not), with the deref name like in\ngit-show-ref and git-peek-remote, i.e. <ref>^{}\n-- \nJakub Narebski\nWarsaw, Poland\nShadeHawk on #git\n"},{"id":"28168","messageId":"7v4pulpad3.fsf@assigned-by-dhcp.cox.net","threadId":"5768","inReplyTo":"eftjua$2ii$1@sea.gmane.org","subject":"Re: [PATCH 1/2] Move code resolving packed refs into its own function.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-10-03T17:39:20Z","receivedAt":"2006-10-03T17:39:20Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Jakub Narebski <jnareb@gmail.com> writes:\n\n> Junio C Hamano wrote:\n>\n>>  - I think gitweb should be Ok; it does peek-remote on the\n>>    repository.  Although we would probably want to update\n>>    git_get_references and git_get_refs_list sub to use\n>>    for-each-ref there, that can be done as a later optimization.\n>\n> I'd rather have gitweb use git-for-each-ref after the command\n> is in the official release... if not, then keep it in the 'next'\n> branch.\n\nOf course.  What else did you expect?\n\n> By the way, I have an idea to join the git-show-ref and git-for-each-ref:\n> simply add --deref option to git-for-each-ref, which would output\n> dereferenced tag just after the reference itself (well, perhaps with the\n> exception when sorting, perhaps not), with the deref name like in\n> git-show-ref and git-peek-remote, i.e. <ref>^{}\n\nPatches welcome.  --verify needs to use totally separate\ncodepath though.  for-each-ref is primarily for easy displaying\nand expects the caller to script in order to skip ones that are\nirrelevant while listing (e.g. asking for *objectname does not\nerror for a non-tag but the caller can deduce that ref is not a\ntag by it being empty and skip it); show-ref --verify is for\neasy checking for an exact match and should not even walk.\n"}]}