{"thread":{"id":"5766","subject":"[PATCH 1/2] ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.","startedAt":"2006-09-30T19:49:00Z","lastAt":"2006-09-30T19:49:00Z","messageCount":1,"participants":["Junio C Hamano"],"isPatch":true,"patchVersion":1,"patchTotal":2},"messages":[{"id":"28032","messageId":"7vsli9cez7.fsf@assigned-by-dhcp.cox.net","threadId":"5766","inReplyTo":null,"subject":"[PATCH 1/2] ref locking: allow 'foo' when 'foo/bar' used to exist but not anymore.","fromName":"Junio C Hamano","fromEmail":"junkio@cox.net","sentAt":"2006-09-30T19:49:00Z","receivedAt":"2006-09-30T19:49:00Z","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\n * Recent git-branch in \"next\" does the moral equivalent of this\n   itself, but other users of git-update-ref would not be helped\n   without something like this.\n\n   We would need to do the same for $GIT_DIR/logs/ but that is\n   for a later round.  I want to get the deletion of packed refs\n   right first but before doing that the code needs some more\n   clean-ups.\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"}]}