{"thread":{"id":"15389","subject":"[JGIT PATCH] Fix: RefUpdate.delete does not prune empty directories","startedAt":"2008-09-05T22:56:47Z","lastAt":"2008-09-05T22:56:47Z","messageCount":1,"participants":["Charles O'Farrell"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"89878","messageId":"1220655407-5817-1-git-send-email-charleso@charleso.org","threadId":"15389","inReplyTo":null,"subject":"[JGIT PATCH] Fix: RefUpdate.delete does not prune empty directories","fromName":"Charles O'Farrell","fromEmail":"charleso@charleso.org","sentAt":"2008-09-05T22:56:47Z","receivedAt":"2008-09-05T22:56:47Z","isPatch":true,"sender":{"key":"charleso@charleso.org","avatar":"https://gravatar.com/avatar/ce96d531daa37646a38a81e48113865af86a6d290ad92931f3b217896bdf86ea?d=mp&s=160"},"body":"When the last loose ref (or reflog) is removed from a directory the\ndirectory itself should also be removed, up to refs/{heads,tags,remotes}.\nOtherwise we may fail when doing something like:\n\n  delete refs/heads/foo/bar\n  create refs/heads/foo\n\nas refs/heads/foo is still a directory and cannot be a file.\n\nhttp://code.google.com/p/egit/issues/detail?id=10\n\nSigned-off-by: Charles O'Farrell <charleso@charleso.org>\n---\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |   34 ++++++++++++++--\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   41 +++++++++++++++++--\n 2 files changed, 65 insertions(+), 10 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java\nindex 1ade2ef..6e2cfa8 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefUpdateTest.java\n@@ -76,16 +76,22 @@ public void testDeleteHead() throws IOException {\n \t}\n \n \tpublic void testLogDeleted() throws IOException {\n-\t\tfinal File log = new File(db.getDirectory(), Constants.LOGS\n-\t\t\t\t+ \"/refs/heads/a\");\n-\t\tlog.getParentFile().mkdirs();\n-\t\tlog.createNewFile();\n+\t\tString refName = \"refs/heads/a\";\n+\t\tfinal File log = createLog(refName);\n \t\tassertTrue(log.exists());\n-\t\tfinal RefUpdate ref = updateRef(\"refs/heads/a\");\n+\t\tfinal RefUpdate ref = updateRef(refName);\n \t\tdelete(ref, Result.FAST_FORWARD);\n \t\tassertFalse(log.exists());\n \t}\n \n+\tprivate File createLog(String name) throws IOException {\n+\t\tfinal File log = new File(db.getDirectory(), Constants.LOGS + \"/\"\n+\t\t\t\t+ name);\n+\t\tlog.getParentFile().mkdirs();\n+\t\tlog.createNewFile();\n+\t\treturn log;\n+\t}\n+\n \tpublic void testDeleteNotFound() throws IOException {\n \t\tfinal RefUpdate ref = updateRef(\"refs/heads/xyz\");\n \t\tdelete(ref, Result.NEW, false, true);\n@@ -103,4 +109,22 @@ public void testDeleteForce() throws IOException {\n \t\tref.setForceUpdate(true);\n \t\tdelete(ref, Result.FORCED);\n \t}\n+\n+\tpublic void testDeleteEmptyDirs() throws IOException {\n+\t\tfinal String top = \"refs/heads/a\";\n+\t\tfinal String newRef = top + \"/b/c\";\n+\t\tfinal String newRef2 = top + \"/d\";\n+\t\tupdateRef(newRef).update();\n+\t\tupdateRef(newRef2).update();\n+\t\tdelete(updateRef(newRef2), Result.NO_CHANGE);\n+\t\tassertExists(true, top);\n+\t\tcreateLog(newRef);\n+\t\tdelete(updateRef(newRef), Result.NO_CHANGE);\n+\t\tassertExists(false, top);\n+\t\tassertExists(false, Constants.LOGS + \"/\" + top);\n+\t}\n+\n+\tprivate void assertExists(final boolean expected, final String name) {\n+\t\tassertEquals(expected, new File(db.getDirectory(), name).exists());\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java\nindex e9c0e77..86b44c5 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java\n@@ -459,13 +459,44 @@ Result store(LockFile lock, Result status) throws IOException {\n \t\t\t\treturn status;\n \t\t\tif (storage.isPacked())\n \t\t\t\tdb.removePackedRef(ref.getName());\n+\n+\t\t\tfinal int levels = count(ref.getName(), '/') - 2;\n+\n+\t\t\t// Delete logs _before_ unlocking\n+\t\t\tfinal File gitDir = db.getRepository().getDirectory();\n+\t\t\tfinal File logDir = new File(gitDir, Constants.LOGS);\n+\t\t\tdeleteFileAndEmptyDir(new File(logDir, ref.getName()), levels);\n+\n+\t\t\t// We have to unlock before (maybe) deleting the parent directories\n+\t\t\tlock.unlock();\n \t\t\tif (storage.isLoose())\n-\t\t\t\tif (!looseFile.delete())\n-\t\t\t\t\tthrow new IOException(\"File cannot be deleted: \"\n-\t\t\t\t\t\t\t+ looseFile);\n-\t\t\tnew File(db.getRepository().getDirectory(), Constants.LOGS + \"/\"\n-\t\t\t\t\t+ ref.getName()).delete();\n+\t\t\t\tdeleteFileAndEmptyDir(looseFile, levels);\n \t\t\treturn status;\n \t\t}\n+\n+\t\tprivate void deleteFileAndEmptyDir(final File file, final int depth)\n+\t\t\t\tthrows IOException {\n+\t\t\tif (file.exists()) {\n+\t\t\t\tif (!file.delete())\n+\t\t\t\t\tthrow new IOException(\"File cannot be deleted: \" + file);\n+\t\t\t\tdeleteEmptyDir(file.getParentFile(), depth);\n+\t\t\t}\n+\t\t}\n+\n+\t\tprivate void deleteEmptyDir(File dir, int depth) {\n+\t\t\tfor (; depth > 0 && dir != null; depth--) {\n+\t\t\t\tif (!dir.delete())\n+\t\t\t\t\tbreak;\n+\t\t\t\tdir = dir.getParentFile();\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate static int count(final String s, final char c) {\n+\t\tint count = 0;\n+\t\tfor (int p = s.indexOf(c); p >= 0; p = s.indexOf(c, p + 1)) {\n+\t\t\tcount++;\n+\t\t}\n+\t\treturn count;\n \t}\n }\n-- \n1.6.0.1.220.g80d1\n"}]}