{"thread":{"id":"19215","subject":"[EGIT PATCH 0/3] Rename refs","startedAt":"2009-05-06T23:32:58Z","lastAt":"2009-06-14T19:47:29Z","messageCount":43,"participants":["Robin Rosenberg","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":3},"messages":[{"id":"113151","messageId":"1241652781-16873-1-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":null,"subject":"[EGIT PATCH 0/3] Rename refs","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-06T23:32:58Z","receivedAt":"2009-05-06T23:32:58Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Not very pretty, but my alternative design was much worse, Having the\nrefs in a databae would make things simpler.\n\n-- robin\n\nRobin Rosenberg (3):\n  Add ref rename support to JGit\n  Use Constants.R_* in Branch dialog\n  Add ref rename support to the branch dialog\n\n .../src/org/spearce/egit/ui/UIText.java            |    6 +\n .../ui/internal/dialogs/BranchSelectionDialog.java |  107 ++++++++++++++-----\n .../src/org/spearce/egit/ui/uitext.properties      |    3 +\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  109 +++++++++++++++++++\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   32 +++++-\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   21 ++++-\n .../src/org/spearce/jgit/lib/RefUpdate.java        |  112 +++++++++++++++++++-\n .../src/org/spearce/jgit/lib/Repository.java       |   33 ++++++\n 8 files changed, 390 insertions(+), 33 deletions(-)\n"},{"id":"113153","messageId":"1241652781-16873-2-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1241652781-16873-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 1/3] Add ref rename support to JGit","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-06T23:32:59Z","receivedAt":"2009-05-06T23:32:59Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Now refs can be renamed. The intent is that should be safe. Only the named\nrefs and associated logs are updated. Any symbolic refs referring to the renames\nbranches are unaffected, except HEAD.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  109 +++++++++++++++++++\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   32 +++++-\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   21 ++++-\n .../src/org/spearce/jgit/lib/RefUpdate.java        |  112 +++++++++++++++++++-\n .../src/org/spearce/jgit/lib/Repository.java       |   33 ++++++\n 5 files changed, 303 insertions(+), 4 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 55d7441..b02773d 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@@ -42,6 +42,7 @@\n import java.util.Map;\n import java.util.Map.Entry;\n \n+import org.spearce.jgit.lib.RefUpdate.RenameRefUpdates;\n import org.spearce.jgit.lib.RefUpdate.Result;\n import org.spearce.jgit.revwalk.RevCommit;\n \n@@ -155,4 +156,112 @@ public void testRefKeySameAsOrigName() {\n \n \t\t}\n \t}\n+\n+\tpublic void testRenameBranchNoPreviousLog() throws IOException {\n+\t\tassertFalse(new File(db.getDirectory(),\"logs/refs/heads/b\").exists()); // no log on old branch\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(rb.equals(oldHead)); // assumption for this test\n+\t\tRenameRefUpdates renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(),\"logs/refs/heads/new/name\").exists());\n+\t\tassertFalse(new File(db.getDirectory(),\"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD));\n+\t\t// TODO: test coprivate void assertNotEquals(ObjectId rb, ObjectId oldHead) {\n+\t\t// TODO Auto-generated method stub\n+\t}\n+\n+\tpublic void testRenameBranchHasPreviousLog() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(new File(db.getDirectory(),\"logs/refs/heads/b\").exists()); // no log on old branch\n+\t\tRenameRefUpdates renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(),\"logs/refs/heads/new/name\").exists());\n+\t\tassertFalse(new File(db.getDirectory(),\"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD));\n+\t\t// TODO: test content of log file\n+\t}\n+\n+\tpublic void testRenameCurrentBranch() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.link(Constants.HEAD, \"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(new File(db.getDirectory(),\"logs/refs/heads/b\").exists()); // no log on old branch\n+\t\tRenameRefUpdates renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(),\"logs/refs/heads/new/name\").exists());\n+\t\tassertFalse(new File(db.getDirectory(),\"logs/refs/heads/b\").exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t\t// TODO: test content of log file\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockFirstBranch() throws IOException {\n+\t\t// \"someone\" has branch b locked\n+\t\tassertTrue(new LockFile(new File(db.getDirectory(), \"refs/heads/b\")).lock());\n+\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.link(Constants.HEAD, \"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(new File(db.getDirectory(),\"logs/refs/heads/b\").exists()); // no log on old branch\n+\n+\t\t// Now this is our test\n+\t\tRenameRefUpdates renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.LOCK_FAILURE, result);\n+\n+\t\t// Check that the involved refs are sane despite the failure\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name.lock\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b/lock\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/new/name.lock\").exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockHEAD() throws IOException {\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.link(Constants.HEAD, \"refs/heads/b\");\n+\n+\t\t// \"someone\" has branch b locked\n+\t\tassertTrue(new LockFile(new File(db.getDirectory(), \"HEAD\")).lock());\n+\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(new File(db.getDirectory(),\"logs/refs/heads/b\").exists()); // no log on old branch\n+\n+\t\t// Now this is our test\n+\t\tRenameRefUpdates renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.LOCK_FAILURE, result);\n+\n+\t\t// Check that the involved refs are sane despite the failure\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name.lock\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b/lock\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/new/name.lock\").exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 87f26bf..a73467a 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -52,6 +52,7 @@\n \n import org.spearce.jgit.errors.ObjectWritingException;\n import org.spearce.jgit.lib.Ref.Storage;\n+import org.spearce.jgit.lib.RefUpdate.RenameRefUpdates;\n import org.spearce.jgit.util.FS;\n import org.spearce.jgit.util.NB;\n \n@@ -148,6 +149,25 @@ synchronized (this) {\n \t}\n \n \t/**\n+\t * An set of update operations for renaming a ref\n+\t *\n+\t * @param fromRef Old ref name\n+\t * @param toRef New ref name\n+\t * @return a RefUpdate operation to rename a ref\n+\t * @throws IOException\n+\t */\n+\tpublic RenameRefUpdates newRename(String fromRef, String toRef) throws IOException {\n+\t\trefreshPackedRefs();\n+\t\tRef f = readRefBasic(fromRef, 0);\n+\t\tRef t = readRefBasic(toRef, 0);\n+\t\tif (t != null)\n+\t\t\tthrow new IOException(\"Ref rename target exists: \" + t.getName());\n+\t\tRefUpdate refUpdateFrom = new RefUpdate(this, f, fileForRef(f.getName()));\n+\t\tRefUpdate refUpdateTo = db.updateRef(toRef);\n+\t\treturn new RenameRefUpdates(refUpdateTo, refUpdateFrom, null);\n+\t}\n+\n+\t/**\n \t * Writes a symref (e.g. HEAD) to disk\n \t * \n \t * @param name\n@@ -160,11 +180,19 @@ void link(final String name, final String target) throws IOException {\n \t\tfinal byte[] content = Constants.encode(\"ref: \" + target + \"\\n\");\n \t\tlockAndWriteFile(fileForRef(name), content);\n \t\tsynchronized (this) {\n+\t\t\tlooseSymRefs.remove(name);\n \t\t\tsetModified();\n \t\t}\n \t\tdb.fireRefsMaybeChanged();\n \t}\n \n+\tvoid uncacheSymRef(String name) {\n+\t\tsynchronized(this) {\n+\t\t\tlooseSymRefs.remove(name);\n+\t\t\tsetModified();\n+\t\t}\n+\t}\n+\n \tprivate void setModified() {\n \t\tlastRefModification = refModificationCounter++;\n \t}\n@@ -484,8 +512,8 @@ private void lockAndWriteFile(File file, byte[] content) throws IOException {\n \t}\n \n \tsynchronized void removePackedRef(String name) throws IOException {\n-\t\tpackedRefs.remove(name);\n-\t\twritePackedRefs();\n+\t\tif (packedRefs.remove(name) != null)\n+\t\t\twritePackedRefs();\n \t}\n \n \tprivate void writePackedRefs() throws IOException {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\nindex a077051..bbf26eb 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n@@ -44,7 +44,7 @@\n import java.io.IOException;\n \n /**\n- * Utility class to add reflog entries\n+ * Utility class to work with reflog files\n  * \n  * @author Dave Watson\n  */\n@@ -58,6 +58,25 @@ static void append(final RefUpdate u, final String msg) throws IOException {\n \t\tappendOneRecord(oldId, newId, ident, msg, db, u.getName());\n \t}\n \n+\tstatic boolean renameTo(final Repository db, final RefUpdate from,\n+\t\t\tfinal RefUpdate to) throws IOException {\n+\t\tfinal File logdir = new File(db.getDirectory(), Constants.LOGS);\n+\t\tfinal File reflogFrom = new File(logdir, from.getName());\n+\t\tif (!reflogFrom.exists())\n+\t\t\treturn true;\n+\t\tfinal File reflogTo = new File(logdir, to.getName());\n+\t\tfinal File refdirTo = reflogTo.getParentFile();\n+\t\tif (!refdirTo.exists() && !refdirTo.mkdirs()) {\n+\t\t\tthrow new IOException(\"Cannot create directory \" + refdirTo);\n+\t\t}\n+\t\tif (!reflogFrom.renameTo(reflogTo)) {\n+\t\t\treflogTo.delete(); // try\n+\t\t\tthrow new IOException(\"Cannot rename \" + reflogFrom + \" to \"\n+\t\t\t\t\t+ reflogTo);\n+\t\t}\n+\t\treturn true;\n+\t}\n+\n \tprivate static void appendOneRecord(final ObjectId oldId,\n \t\t\tfinal ObjectId newId, PersonIdent ident, final String msg,\n \t\t\tfinal Repository db, final String refName) throws IOException {\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 a9ab73b..8ecccfe 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@@ -50,6 +50,100 @@\n  * Updates any locally stored ref.\n  */\n public class RefUpdate {\n+\t/**\n+\t * A RefUpdate combination for renaming a ref\n+\t */\n+\tpublic static class RenameRefUpdates {\n+\t\tRenameRefUpdates(final RefUpdate toUpdate, final RefUpdate fromUpdate,\n+\t\t\t\tfinal RefUpdate headUpdate) {\n+\t\t\tnewToUpdate = toUpdate;\n+\t\t\toldFromDelete = fromUpdate;\n+\t\t}\n+\t\tprivate RefUpdate newToUpdate;\n+\n+\t\tprivate RefUpdate oldFromDelete;\n+\n+\t\tprivate Result renameResult;\n+\n+\t\t/**\n+\t\t * @return result of rename operation\n+\t\t */\n+\t\tpublic Result getResult() {\n+\t\t\treturn renameResult;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the result of the new ref update\n+\t\t * @throws IOException\n+\t\t */\n+\t\tpublic Result rename() throws IOException {\n+\t\t\tLockFile lockFileFrom = new LockFile(oldFromDelete.looseFile);\n+\t\t\tboolean lockFrom = lockFileFrom.lock();\n+\t\t\tif (!lockFrom)\n+\t\t\t\treturn Result.LOCK_FAILURE;\n+\t\t\tLockFile lockFileTo = null;\n+\t\t\ttry {\n+\t\t\t\tlockFileTo = new LockFile(newToUpdate.looseFile);\n+\t\t\t\tboolean lockTo = lockFileTo.lock();\n+\t\t\t\tif (!lockTo) {\n+\t\t\t\t\tlockFileFrom.unlock();\n+\t\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n+\t\t\t\t}\n+\t\t\t} catch (IOException e) {\n+\t\t\t\tlockFileFrom.unlock();\n+\t\t\t\tthrow e;\n+\t\t\t}\n+\t\t\tLockFile lockFileHEAD = new LockFile(new File(oldFromDelete.db.getRepository().getDirectory(), Constants.HEAD));\n+\t\t\tboolean renameHEADtoo;\n+\t\t\ttry {\n+\t\t\t\tboolean lockHEAD = lockFileHEAD.lock();\n+\t\t\t\trenameHEADtoo = oldFromDelete.db.readRef(Constants.HEAD).getName().equals(oldFromDelete.getName());\n+\t\t\t\tif (!renameHEADtoo)\n+\t\t\t\t\tlockFileHEAD.unlock();\n+\t\t\t\telse {\n+\t\t\t\t\tif (!lockHEAD) {\n+\t\t\t\t\t\tlockFileFrom.unlock();\n+\t\t\t\t\t\tlockFileTo.unlock();\n+\t\t\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t} catch (IOException e) {\n+\t\t\t\tlockFileHEAD.unlock();\n+\t\t\t\tlockFileFrom.unlock();\n+\t\t\t\tlockFileTo.unlock();\n+\t\t\t\tthrow e;\n+\t\t\t}\n+\t\t\ttry {\n+\t\t\t\tUpdateStore toStore = newToUpdate.newUpdateStore();\n+\t\t\t\tif (RefLogWriter.renameTo(oldFromDelete.getRepository(), oldFromDelete, newToUpdate)) {\n+\t\t\t\t\tnewToUpdate.setNewObjectId(oldFromDelete.getOldObjectId());\n+\t\t\t\t\tnewToUpdate.setExpectedOldObjectId(oldFromDelete.getOldObjectId());\n+\t\t\t\t\tnewToUpdate.setRefLogMessage(\"jgit branch: renamed \" + oldFromDelete.getName() + \" to \" + oldFromDelete.getName(), false);\n+\t\t\t\t\tnewToUpdate.result = toStore.store(lockFileTo, Result.RENAMED);\n+\t\t\t\t\tDeleteStore fromStore = oldFromDelete.newDeleteStore();\n+\t\t\t\t\tResult store = fromStore.store(lockFileFrom, Result.RENAMED);\n+\t\t\t\t\tif (renameHEADtoo) {\n+\t\t\t\t\t\tfinal byte[] content = Constants.encode(\"ref: \" + newToUpdate.getName() + \"\\n\");\n+\t\t\t\t\t\tlockFileHEAD.write(content);\n+\t\t\t\t\t\tsynchronized (this) {\n+\t\t\t\t\t\t\toldFromDelete.db.getRepository().uncacheSymRef(Constants.HEAD);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (!lockFileHEAD.commit())\n+\t\t\t\t\t\t\tthrow new IOException(\"Failed to commit HEAD during rename\");\n+\t\t\t\t\t}\n+\t\t\t\t\toldFromDelete.db.getRepository().fireRefsMaybeChanged();\n+\t\t\t\t\treturn store;\n+\t\t\t\t} else {\n+\t\t\t\t\treturn Result.IO_FAILURE;\n+\t\t\t\t}\n+\t\t\t} finally {\n+\t\t\t\tlockFileFrom.unlock();\n+\t\t\t\tlockFileTo.unlock();\n+\t\t\t\tlockFileHEAD.unlock();\n+\t\t\t}\n+\t\t}\n+\t}\n+\n \t/** Status of an update request. */\n \tpublic static enum Result {\n \t\t/** The ref update/delete has not been attempted by the caller. */\n@@ -125,7 +219,13 @@\n \t\t * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a\n \t\t * different case.\n \t\t */\n-\t\tIO_FAILURE\n+\t\tIO_FAILURE,\n+\n+\t\t/**\n+\t\t * The ref was renamed from another name\n+\t\t * <p>\n+\t\t */\n+\t\tRENAMED\n \t}\n \n \t/** Repository the ref is stored in. */\n@@ -478,6 +578,8 @@ else if (status == Result.FAST_FORWARD)\n \t\t\t\tmsg += \": fast forward\";\n \t\t\telse if (status == Result.NEW)\n \t\t\t\tmsg += \": created\";\n+\t\t\telse if (status == Result.RENAMED)\n+\t\t\t\tmsg += \": renamed\";\n \t\t}\n \t\tRefLogWriter.append(this, msg);\n \t\tif (!lock.commit())\n@@ -553,4 +655,12 @@ private static int count(final String s, final char c) {\n \t\t}\n \t\treturn count;\n \t}\n+\n+\tprivate UpdateStore newUpdateStore() {\n+\t\treturn new UpdateStore();\n+\t}\n+\n+\tprivate DeleteStore newDeleteStore() {\n+\t\treturn new DeleteStore();\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 5baa7a0..e704aeb 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -468,6 +468,22 @@ public RefUpdate updateRef(final String ref) throws IOException {\n \t}\n \n \t/**\n+\t * Create a command to rename a ref in this repository\n+\t *\n+\t * @param fromRef\n+\t *            name of ref to rename from\n+\t * @param toRef\n+\t *            name of ref to rename to\n+\t * @return an update command that knows how to rename a branch to another.\n+\t * @throws IOException\n+\t *             the rename could not be performed.\n+\t *\n+\t */\n+\tpublic RefUpdate.RenameRefUpdates renameRef(final String fromRef, final String toRef) throws IOException {\n+\t\treturn refs.newRename(fromRef, toRef);\n+\t}\n+\n+\t/**\n \t * Parse a git revision string and return an object id.\n \t *\n \t * Currently supported is combinations of these.\n@@ -1067,4 +1083,21 @@ public void scanForRepoChanges() throws IOException {\n \t\tgetAllRefs(); // This will look for changes to refs\n \t\tgetIndex(); // This will detect changes in the index\n \t}\n+\n+\t/**\n+\t * Writes a symref (e.g. HEAD) to disk\n+\t *\n+\t * @param name\n+\t *            symref name\n+\t * @param target\n+\t *            pointed to ref\n+\t * @throws IOException\n+\t */\n+\tpublic void link(final String name, final String target) throws IOException {\n+\t\trefs.link(name, target);\n+\t}\n+\n+\tvoid uncacheSymRef(String name) {\n+\t\trefs.uncacheSymRef(name);\n+\t}\n }\n-- \n1.6.3.rc2.1.g868b6\n"},{"id":"113152","messageId":"1241652781-16873-3-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1241652781-16873-2-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 2/3] Use Constants.R_* in Branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-06T23:33:00Z","receivedAt":"2009-05-06T23:33:00Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"There were a few places that didn't use these constants\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../ui/internal/dialogs/BranchSelectionDialog.java |   18 +++++++++---------\n 1 files changed, 9 insertions(+), 9 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 1866086..9aad95b 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -154,19 +154,19 @@ private void fillTreeWithBranches(String select) throws IOException {\n \n \t\tfor (String ref : branches) {\n \t\t\tString shortName = ref;\n-\t\t\tif (ref.startsWith(\"refs/heads/\")) {\n+\t\t\tif (ref.startsWith(Constants.R_HEADS)) {\n \t\t\t\tshortName = ref.substring(11);\n-\t\t\t\tif (!\"refs/heads/\".equals(curPrefix)) {\n-\t\t\t\t\tcurPrefix = \"refs/heads/\";\n+\t\t\t\tif (!Constants.R_HEADS.equals(curPrefix)) {\n+\t\t\t\t\tcurPrefix = Constants.R_HEADS;\n \t\t\t\t\tcurSubPrefix = null;\n \t\t\t\t\tcurSubItem = null;\n \t\t\t\t\tcurItem = new TreeItem(branchTree, SWT.NONE);\n \t\t\t\t\tcurItem.setText(UIText.BranchSelectionDialog_LocalBranches);\n \t\t\t\t}\n-\t\t\t} else if (ref.startsWith(\"refs/remotes/\")) {\n+\t\t\t} else if (ref.startsWith(Constants.R_REMOTES)) {\n \t\t\t\tshortName = ref.substring(13);\n-\t\t\t\tif (!\"refs/remotes/\".equals(curPrefix)) {\n-\t\t\t\t\tcurPrefix = \"refs/remotes/\";\n+\t\t\t\tif (!Constants.R_REMOTES.equals(curPrefix)) {\n+\t\t\t\t\tcurPrefix = Constants.R_REMOTES;\n \t\t\t\t\tcurItem = new TreeItem(branchTree, SWT.NONE);\n \t\t\t\t\tcurItem.setText(UIText.BranchSelectionDialog_RemoteBranches);\n \t\t\t\t\tcurSubItem = null;\n@@ -186,10 +186,10 @@ private void fillTreeWithBranches(String select) throws IOException {\n \t\t\t\t\tcurSubItem = null;\n \t\t\t\t\tcurSubPrefix = null;\n \t\t\t\t}\n-\t\t\t} else if (ref.startsWith(\"refs/tags/\")) {\n+\t\t\t} else if (ref.startsWith(Constants.R_TAGS)) {\n \t\t\t\tshortName = ref.substring(10);\n-\t\t\t\tif (!\"refs/tags/\".equals(curPrefix)) {\n-\t\t\t\t\tcurPrefix = \"refs/tags/\";\n+\t\t\t\tif (!Constants.R_HEADS.equals(curPrefix)) {\n+\t\t\t\t\tcurPrefix = Constants.R_TAGS;\n \t\t\t\t\tcurSubPrefix = null;\n \t\t\t\t\tcurSubItem = null;\n \t\t\t\t\tcurItem = new TreeItem(branchTree, SWT.NONE);\n-- \n1.6.3.rc2.1.g868b6\n"},{"id":"113154","messageId":"1241652781-16873-4-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1241652781-16873-3-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 3/3] Add ref rename support to the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-06T23:33:01Z","receivedAt":"2009-05-06T23:33:01Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    6 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |   89 +++++++++++++++-----\n .../src/org/spearce/egit/ui/uitext.properties      |    3 +\n 3 files changed, 78 insertions(+), 20 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 654e155..1bf2dd3 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -893,6 +893,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRefresh;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 9aad95b..a518c2a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -50,6 +50,8 @@\n import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.RefUpdate;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RefUpdate.RenameRefUpdates;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n \n /**\n  * The branch and reset selection dialog\n@@ -277,6 +279,30 @@ private void refNameFromDialog() {\n \t\t}\n \t}\n \n+\tprivate InputDialog getRefNameInputDialog() {\n+\t\tInputDialog labelDialog = new InputDialog(\n+\t\t\t\tgetShell(),\n+\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n+\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchMessage,\n+\t\t\t\tnull, new IInputValidator() {\n+\t\t\t\t\tpublic String isValid(String newText) {\n+\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n+\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n+\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n+\t\t\t\t\t\treturn null;\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\tlabelDialog.setBlockOnOpen(true);\n+\t\treturn labelDialog;\n+\t}\n+\n \t@Override\n \tprotected void createButtonsForButtonBar(Composite parent) {\n \t\tif (!showResetType) {\n@@ -284,32 +310,54 @@ protected void createButtonsForButtonBar(Composite parent) {\n \t\t\tnewButton.setFont(JFaceResources.getDialogFont());\n \t\t\tnewButton.setText(UIText.BranchSelectionDialog_NewBranch);\n \t\t\t((GridLayout)parent.getLayout()).numColumns++;\n+\t\t\tButton renameButton = new Button(parent, SWT.PUSH);\n+\t\t\trenameButton.setText(\"&Rename\");\n+\t\t\trenameButton.addSelectionListener(new SelectionListener() {\n+\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t// check what ref name the user selected, if any.\n+\t\t\t\t\trefNameFromDialog();\n+\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog();\n+\t\t\t\t\tif (labelDialog.open() == Window.OK) {\n+\t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tRenameRefUpdates renameRef = repo.renameRef(refName, newRefName);\n+\t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n+\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n+\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n+\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n+\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t// FIXME: Update HEAD\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tbranchTree.removeAll();\n+\t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n+\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n+\t\t\t\t\twidgetSelected(e);\n+\t\t\t\t}\n+\t\t\t});\n \t\t\tnewButton.addSelectionListener(new SelectionListener() {\n \n \t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t// check what ref name the user selected, if any.\n \t\t\t\t\trefNameFromDialog();\n \n-\t\t\t\t\tInputDialog labelDialog = new InputDialog(\n-\t\t\t\t\t\t\tgetShell(),\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchMessage,\n-\t\t\t\t\t\t\tnull, new IInputValidator() {\n-\t\t\t\t\t\t\t\tpublic String isValid(String newText) {\n-\t\t\t\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n-\t\t\t\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n-\t\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n-\t\t\t\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n-\t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n-\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n-\t\t\t\t\t\t\t\t\treturn null;\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t});\n-\t\t\t\t\tlabelDialog.setBlockOnOpen(true);\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog();\n \t\t\t\t\tif (labelDialog.open() == Window.OK) {\n \t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n \t\t\t\t\t\tRefUpdate updateRef;\n@@ -338,6 +386,7 @@ public String isValid(String newText) {\n \t\t\t\t\t}\n \t\t\t\t}\n \n+\n \t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n \t\t\t\t\twidgetSelected(e);\n \t\t\t\t}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 1d21c81..193f3ad 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -340,6 +340,9 @@ BranchSelectionDialog_ErrorCouldNotRefresh=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotRefreshBranchList=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotResolve=Could not attempt to resolve {0}\n BranchSelectionDialog_ErrorInvalidRefName=Invalid ref name\n+BranchSelectionDialog_ErrorCouldNotRenameRef=Failed to rename branch {0} -> {1}, status={2}\n+BranchSelectionDialog_ErrorCouldNotRenameRef2=Failed to rename branch {0} -> {1}\n+\n BranchSelectionDialog_LocalBranches=Local Branches\n BranchSelectionDialog_NewBranch=&New branch\n BranchSelectionDialog_NoBranchSeletectMessage=You must select a valid ref.\n-- \n1.6.3.rc2.1.g868b6\n"},{"id":"113229","messageId":"20090507155117.GS30527@spearce.org","threadId":"19215","inReplyTo":"1241652781-16873-2-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 1/3] Add ref rename support to JGit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-05-07T15:51:17Z","receivedAt":"2009-05-07T15:51:17Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> Now refs can be renamed. The intent is that should be safe. Only the named\n> refs and associated logs are updated. Any symbolic refs referring to the renames\n> branches are unaffected, except HEAD.\n\nSee below.  I'm not going to apply as-is, because its really quite\nmessy, as you stated in the cover letter...\n \n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n> index 87f26bf..a73467a 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n> @@ -148,6 +149,25 @@ synchronized (this) {\n>  \t}\n>  \n>  \t/**\n> +\t * An set of update operations for renaming a ref\n> +\t *\n> +\t * @param fromRef Old ref name\n> +\t * @param toRef New ref name\n> +\t * @return a RefUpdate operation to rename a ref\n> +\t * @throws IOException\n> +\t */\n> +\tpublic RenameRefUpdates newRename(String fromRef, String toRef) throws IOException {\n\nThis class is not public; none of its methods are public; please\ndon't mark this public.\n\n> +\t\trefreshPackedRefs();\n> +\t\tRef f = readRefBasic(fromRef, 0);\n> +\t\tRef t = readRefBasic(toRef, 0);\n> +\t\tif (t != null)\n> +\t\t\tthrow new IOException(\"Ref rename target exists: \" + t.getName());\n> +\t\tRefUpdate refUpdateFrom = new RefUpdate(this, f, fileForRef(f.getName()));\n> +\t\tRefUpdate refUpdateTo = db.updateRef(toRef);\n\ndb.updateRef is just a redirection to this.newUpdate(), which scans\nthe refs (done above) and then makes a new RefUpdate instance.\nWhy not just reproduce the three lines that matter and avoid this\nmessy indirection?\n\n  if (t == null)\n    t = new Ref(Ref.Storage.NEW, name, null)\n  RefUpdate refUpdateTo = new RefUpdate(this, t, fileForRef(t.getName()));\n\n> @@ -484,8 +512,8 @@ private void lockAndWriteFile(File file, byte[] content) throws IOException {\n>  \t}\n>  \n>  \tsynchronized void removePackedRef(String name) throws IOException {\n> -\t\tpackedRefs.remove(name);\n> -\t\twritePackedRefs();\n> +\t\tif (packedRefs.remove(name) != null)\n> +\t\t\twritePackedRefs();\n>  \t}\n\nHow did we get here when Ref.Storage.isPacked() == false?  IMHO this\nshouldn't have been invoked if isPacked is false.\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n> index a077051..bbf26eb 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n> @@ -58,6 +58,25 @@ static void append(final RefUpdate u, final String msg) throws IOException {\n>  \t\tappendOneRecord(oldId, newId, ident, msg, db, u.getName());\n>  \t}\n>  \n> +\tstatic boolean renameTo(final Repository db, final RefUpdate from,\n> +\t\t\tfinal RefUpdate to) throws IOException {\n\nHmm, this method returns only true, or throws an IOException.\nI wonder, why bother returning boolean?  Why not make it void?\n\n> +\t\tfinal File logdir = new File(db.getDirectory(), Constants.LOGS);\n> +\t\tfinal File reflogFrom = new File(logdir, from.getName());\n> +\t\tif (!reflogFrom.exists())\n> +\t\t\treturn true;\n> +\t\tfinal File reflogTo = new File(logdir, to.getName());\n> +\t\tfinal File refdirTo = reflogTo.getParentFile();\n> +\t\tif (!refdirTo.exists() && !refdirTo.mkdirs()) {\n> +\t\t\tthrow new IOException(\"Cannot create directory \" + refdirTo);\n> +\t\t}\n> +\t\tif (!reflogFrom.renameTo(reflogTo)) {\n> +\t\t\treflogTo.delete(); // try\n> +\t\t\tthrow new IOException(\"Cannot rename \" + reflogFrom + \" to \"\n> +\t\t\t\t\t+ reflogTo);\n\nSo if the rename fails, we delete the target and then throw an\nexception anyway?  Why delete the target in this case?  I think we\nshould just fail here.\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java\n> index a9ab73b..8ecccfe 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> @@ -50,6 +50,100 @@\n>   * Updates any locally stored ref.\n>   */\n>  public class RefUpdate {\n> +\t/**\n> +\t * A RefUpdate combination for renaming a ref\n> +\t */\n> +\tpublic static class RenameRefUpdates {\n\nI would prefer this to be called \"RefRename\", and to make it a\ntop level class in the lib package, rather than a nested class\nof RefUpdate.\n\n> +\t\tRenameRefUpdates(final RefUpdate toUpdate, final RefUpdate fromUpdate,\n> +\t\t\t\tfinal RefUpdate headUpdate) {\n> +\t\t\tnewToUpdate = toUpdate;\n> +\t\t\toldFromDelete = fromUpdate;\n\nWhat happened to headUpdate?\n\n> +\t\t}\n> +\t\tprivate RefUpdate newToUpdate;\n> +\n> +\t\tprivate RefUpdate oldFromDelete;\n> +\n> +\t\tprivate Result renameResult;\n\nConstructor after instance member field decls please.\n\n> +\t\t/**\n> +\t\t * @return the result of the new ref update\n> +\t\t * @throws IOException\n> +\t\t */\n> +\t\tpublic Result rename() throws IOException {\n> +\t\t\tLockFile lockFileFrom = new LockFile(oldFromDelete.looseFile);\n> +\t\t\tboolean lockFrom = lockFileFrom.lock();\n> +\t\t\tif (!lockFrom)\n> +\t\t\t\treturn Result.LOCK_FAILURE;\n> +\t\t\tLockFile lockFileTo = null;\n> +\t\t\ttry {\n> +\t\t\t\tlockFileTo = new LockFile(newToUpdate.looseFile);\n> +\t\t\t\tboolean lockTo = lockFileTo.lock();\n> +\t\t\t\tif (!lockTo) {\n> +\t\t\t\t\tlockFileFrom.unlock();\n> +\t\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n> +\t\t\t\t}\n> +\t\t\t} catch (IOException e) {\n> +\t\t\t\tlockFileFrom.unlock();\n> +\t\t\t\tthrow e;\n> +\t\t\t}\n> +\t\t\tLockFile lockFileHEAD = new LockFile(new File(oldFromDelete.db.getRepository().getDirectory(), Constants.HEAD));\n> +\t\t\tboolean renameHEADtoo;\n> +\t\t\ttry {\n> +\t\t\t\tboolean lockHEAD = lockFileHEAD.lock();\n> +\t\t\t\trenameHEADtoo = oldFromDelete.db.readRef(Constants.HEAD).getName().equals(oldFromDelete.getName());\n> +\t\t\t\tif (!renameHEADtoo)\n> +\t\t\t\t\tlockFileHEAD.unlock();\n> +\t\t\t\telse {\n> +\t\t\t\t\tif (!lockHEAD) {\n> +\t\t\t\t\t\tlockFileFrom.unlock();\n> +\t\t\t\t\t\tlockFileTo.unlock();\n> +\t\t\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n> +\t\t\t\t\t}\n> +\t\t\t\t}\n> +\t\t\t} catch (IOException e) {\n> +\t\t\t\tlockFileHEAD.unlock();\n> +\t\t\t\tlockFileFrom.unlock();\n> +\t\t\t\tlockFileTo.unlock();\n\nI wonder if this code would be easier to follow if we made a\nSet<LockFile> as an instace member, and made some utility methods\nlike:\n\n  private final Set<LockFile> allLocks = new HashSet<LockFile>();\n\n  private boolean takeLock(final LockFile f) {\n    if (f.lock()) {\n      allLocks.add(f);\n      return true;\n    }\n    return false;\n  }\n\n  private void unlock(final LockFile f) {\n    f.unlock();\n    allLocks.remove(f);\n  }\n\n  private void abort() {\n    for (final LockFile f : allLocks)\n      f.unlock();\n  }\n\nThen a lot of this local variable management code might simplify out.\n\nAnother approach might be to make LockFile a member of RefUpdate,\nand allow the RefUpdate members for oldFromDelete and newToUpdate\nto keep track of their associated LockFile for you.  This may clean\nup the code related to constructing the store operation.\n\n> +\t\t\t\tthrow e;\n> +\t\t\t}\n> +\t\t\ttry {\n> +\t\t\t\tUpdateStore toStore = newToUpdate.newUpdateStore();\n> +\t\t\t\tif (RefLogWriter.renameTo(oldFromDelete.getRepository(), oldFromDelete, newToUpdate)) {\n> +\t\t\t\t\tnewToUpdate.setNewObjectId(oldFromDelete.getOldObjectId());\n> +\t\t\t\t\tnewToUpdate.setExpectedOldObjectId(oldFromDelete.getOldObjectId());\n> +\t\t\t\t\tnewToUpdate.setRefLogMessage(\"jgit branch: renamed \" + oldFromDelete.getName() + \" to \" + oldFromDelete.getName(), false);\n\nI would prefer if the caller can set the prefix part of the reflog\nmessage (\"jgit branch\" portion) so we can set it by command.\n\n> +\t\t\t\t\tnewToUpdate.result = toStore.store(lockFileTo, Result.RENAMED);\n> +\t\t\t\t\tDeleteStore fromStore = oldFromDelete.newDeleteStore();\n> +\t\t\t\t\tResult store = fromStore.store(lockFileFrom, Result.RENAMED);\n> +\t\t\t\t\tif (renameHEADtoo) {\n> +\t\t\t\t\t\tfinal byte[] content = Constants.encode(\"ref: \" + newToUpdate.getName() + \"\\n\");\n> +\t\t\t\t\t\tlockFileHEAD.write(content);\n> +\t\t\t\t\t\tsynchronized (this) {\n\nWhat effect could \"synchronized (this)\" really have here, other\nthan to waste CPU cycles?  Who else could have a reference to this\nRefRenames instance and be able to lock on it?\n\n> @@ -478,6 +578,8 @@ else if (status == Result.FAST_FORWARD)\n>  \t\t\t\tmsg += \": fast forward\";\n>  \t\t\telse if (status == Result.NEW)\n>  \t\t\t\tmsg += \": created\";\n> +\t\t\telse if (status == Result.RENAMED)\n> +\t\t\t\tmsg += \": renamed\";\n>  \t\t}\n\nI don't think this is useful.  We probably shouldn't append\nstatus during a rename as the rename method had already done\nsetRefLogMessage(\"renamed A to B\") earlier.  Doing status here\nwould yield \"renamed A to B: renamed\" in the reflog; ugly.\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n> index 5baa7a0..e704aeb 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n> @@ -1067,4 +1083,21 @@ public void scanForRepoChanges() throws IOException {\n>  \t\tgetAllRefs(); // This will look for changes to refs\n>  \t\tgetIndex(); // This will detect changes in the index\n>  \t}\n> +\n> +\t/**\n> +\t * Writes a symref (e.g. HEAD) to disk\n> +\t *\n> +\t * @param name\n> +\t *            symref name\n> +\t * @param target\n> +\t *            pointed to ref\n> +\t * @throws IOException\n> +\t */\n> +\tpublic void link(final String name, final String target) throws IOException {\n> +\t\trefs.link(name, target);\n> +\t}\n\nUhm, isn't this already called writeSymref() ?\n\n> +\tvoid uncacheSymRef(String name) {\n> +\t\trefs.uncacheSymRef(name);\n> +\t}\n\nWhy is this in Repository?\n\n-- \nShawn.\n"},{"id":"114307","messageId":"1242774798-23639-1-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"20090507155117.GS30527@spearce.org","subject":"[EGIT PATCH 0/6] Ref rename","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-19T23:13:12Z","receivedAt":"2009-05-19T23:13:12Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Ok, an update with most of your comments and a mix of other fixes\nfounding since then during testing.\n\nRobin Rosenberg (6):\n  Make sure we get the right storage for loose/pack/loose and packed\n    refs\n  Add ref rename support to JGit\n  Add ref rename support to the branch dialog\n  Allow non-ASCII ref names when writing the packed-refs file\n  Use Constants.PACKED_REFS in RefWriter\n  Improve error reporting in the branch dialog\n\n .../src/org/spearce/egit/ui/UIText.java            |   15 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |  119 ++++++++++++----\n .../src/org/spearce/egit/ui/uitext.properties      |    6 +\n .../tst/org/spearce/jgit/lib/RefTest.java          |   94 ++++++++++++\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  152 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   56 +++++++-\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   20 +++-\n .../src/org/spearce/jgit/lib/RefRename.java        |  101 +++++++++++++\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   29 +++-\n .../src/org/spearce/jgit/lib/RefWriter.java        |    2 +-\n .../src/org/spearce/jgit/lib/Repository.java       |   16 ++\n 11 files changed, 567 insertions(+), 43 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n"},{"id":"114310","messageId":"1242774798-23639-2-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1242774798-23639-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-19T23:13:13Z","receivedAt":"2009-05-19T23:13:13Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Also adds a few some more test for refs, though not complete.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/RefTest.java          |   94 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   26 ++++--\n 2 files changed, 114 insertions(+), 6 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\nindex cae5143..7b0c9a9 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\n@@ -36,8 +36,14 @@\n  */\n package org.spearce.jgit.lib;\n \n+import java.io.File;\n+import java.io.FileOutputStream;\n+import java.io.IOException;\n import java.util.Map;\n \n+import org.spearce.jgit.lib.Ref.Storage;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n+\n /**\n  * Misc tests for refs. A lot of things are tested elsewhere so not having a\n  * test for a ref related method, does not mean it is untested.\n@@ -69,4 +75,92 @@ public void testReadAllIncludingSymrefs() throws Exception {\n \t\tassertFalse(refmaster.isPeeled());\n \t\tassertNull(refmaster.getPeeledObjectId());\n \t}\n+\n+\tpublic void testReadSymRefToPacked() throws IOException {\n+\t\tdb.writeSymref(\"HEAD\", \"refs/heads/master\");\n+\t\tRef ref = db.getRef(\"HEAD\");\n+\t\tassertEquals(Ref.Storage.LOOSE, ref.getStorage());\n+\t}\n+\n+\tpublic void testReadSymRefToLoosePacked() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update); // internal\n+\n+\t\tdb.writeSymref(\"HEAD\", \"refs/heads/master\");\n+\t\tRef ref = db.getRef(\"HEAD\");\n+\t\tassertEquals(Ref.Storage.LOOSE, ref.getStorage());\n+\t}\n+\n+\tpublic void testReadLooseRef() throws IOException {\n+\t\tRefUpdate updateRef = db.updateRef(\"ref/heads/new\");\n+\t\tupdateRef.setNewObjectId(db.resolve(\"refs/heads/master\"));\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.NEW, update);\n+\t\tRef ref = db.getRef(\"ref/heads/new\");\n+\t\tassertEquals(Storage.LOOSE, ref.getStorage());\n+\t}\n+\n+\t/**\n+\t * Let an \"outsider\" create a loose ref with the same name as a packed one\n+\t *\n+\t * @throws IOException\n+\t * @throws InterruptedException\n+\t */\n+\tpublic void testReadLoosePackedRef() throws IOException,\n+\t\t\tInterruptedException {\n+\t\tRef ref = db.getRef(\"refs/heads/master\");\n+\t\tassertEquals(Storage.PACKED, ref.getStorage());\n+\t\tFileOutputStream os = new FileOutputStream(new File(db.getDirectory(),\n+\t\t\t\t\"refs/heads/master\"));\n+\t\tos.write(ref.getObjectId().name().getBytes());\n+\t\tos.write('\\n');\n+\t\tos.close();\n+\n+\t\tref = db.getRef(\"refs/heads/master\");\n+\t\tassertEquals(Storage.LOOSE_PACKED, ref.getStorage());\n+\t}\n+\n+\t/**\n+\t * Modify a packed ref using the API. This creates a loose ref too, ie.\n+\t * LOOSE_PACKED\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testReadSimplePackedRefSameRepo() throws IOException {\n+\t\tRef ref = db.getRef(\"refs/heads/master\");\n+\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n+\t\tassertEquals(Storage.PACKED, ref.getStorage());\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update);\n+\n+\t\tref = db.getRef(\"refs/heads/master\");\n+\t\tassertEquals(Storage.LOOSE_PACKED, ref.getStorage());\n+\t}\n+\n+\t/**\n+\t * Delete a ref that exists both as packed and loose. Make sure the ref\n+\t * cannot be resolved after delete.\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testDeleteLoosePacked() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update); // internal\n+\n+\t\tRefUpdate updateRef2 = db.updateRef(\"refs/heads/master\");\n+\t\tResult delete = updateRef2.delete();\n+\t\tassertEquals(Result.FORCED, delete);\n+\t\tassertNull(db.resolve(\"refs/heads/master\"));\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 155ed9a..573fcac 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -283,8 +283,14 @@ private synchronized Ref readRefBasic(final String origName,\n \t\tfinal long mtime = loose.lastModified();\n \t\tif (ref != null) {\n \t\t\tLong cachedlastModified = looseRefsMTime.get(name);\n-\t\t\tif (cachedlastModified != null && cachedlastModified == mtime)\n-\t\t\t\treturn ref;\n+\t\t\tif (cachedlastModified != null && cachedlastModified == mtime) {\n+\t\t\t\tif (packedRefs.containsKey(origName))\n+\t\t\t\t\treturn new Ref(Storage.LOOSE_PACKED, origName, ref\n+\t\t\t\t\t\t\t.getObjectId(), ref.getPeeledObjectId(), ref\n+\t\t\t\t\t\t\t.isPeeled());\n+\t\t\t\telse\n+\t\t\t\t\treturn ref;\n+\t\t\t}\n \t\t\tlooseRefs.remove(origName);\n \t\t\tlooseRefsMTime.remove(origName);\n \t\t}\n@@ -349,12 +355,20 @@ private synchronized Ref readRefBasic(final String origName,\n \t\t\tthrow new IOException(\"Not a ref: \" + name + \": \" + line);\n \t\t}\n \n-\t\tref = new Ref(Ref.Storage.LOOSE, origName, name, id);\n-\n-\t\tlooseRefs.put(origName, ref);\n-\t\tref = new Ref(Ref.Storage.LOOSE, origName, id);\n+\t\tStorage storage;\n+\t\tif (packedRefs.containsKey(name))\n+\t\t\tstorage = Ref.Storage.LOOSE_PACKED;\n+\t\telse\n+\t\t\tstorage = Ref.Storage.LOOSE;\n+\t\tref = new Ref(storage, name, id);\n \t\tlooseRefs.put(name, ref);\n \t\tlooseRefsMTime.put(name, mtime);\n+\n+\t\tif (!origName.equals(name)) {\n+\t\t\tref = new Ref(Ref.Storage.LOOSE, origName, name, id);\n+\t\t\tlooseRefs.put(origName, ref);\n+\t\t}\n+\n \t\treturn ref;\n \t}\n \n-- \n1.6.3.dirty\n"},{"id":"114312","messageId":"1242774798-23639-3-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1242774798-23639-2-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 2/6] Add ref rename support to JGit","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-19T23:13:14Z","receivedAt":"2009-05-19T23:13:14Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Now refs can be renamed. The intent is that should be safe. Only the named\nrefs and associated logs are updated. Any symbolic refs referring to the renames\nbranches are unaffected, except HEAD.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  152 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   28 ++++\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   20 +++-\n .../src/org/spearce/jgit/lib/RefRename.java        |  101 +++++++++++++\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   29 +++-\n .../src/org/spearce/jgit/lib/Repository.java       |   16 ++\n 6 files changed, 339 insertions(+), 7 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\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 55d7441..cd66eb1 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@@ -155,4 +155,156 @@ public void testRefKeySameAsOrigName() {\n \n \t\t}\n \t}\n+\n+\tpublic void testRenameBranchNoPreviousLog() throws IOException {\n+\t\tassertFalse(\"precondition, no log on old branchg\", new File(db\n+\t\t\t\t.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(rb.equals(oldHead)); // assumption for this test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD));\n+\t}\n+\n+\tpublic void testRenameBranchHasPreviousLog() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(\"precondition for this test, branch b != HEAD\", rb\n+\t\t\t\t.equals(oldHead));\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD));\n+\t\t// TODO: test content of log file\n+\t}\n+\n+\tpublic void testRenameCurrentBranch() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(\"internal test condition, b == HEAD\", rb.equals(oldHead));\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t\t// TODO: test content of log file\n+\t}\n+\n+\tpublic void testRenameBranchAlsoInPack() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId rb2 = db.resolve(\"refs/heads/b~1\");\n+\t\tassertEquals(Ref.Storage.PACKED, db.getRef(\"refs/heads/b\").getStorage());\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/b\");\n+\t\tupdateRef.setNewObjectId(rb2);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(\"internal check new ref is loose\", Result.FORCED, update);\n+\t\tassertEquals(Ref.Storage.LOOSE_PACKED, db.getRef(\"refs/heads/b\").getStorage());\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb2, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\n+\t\t// Create new Repository instance, to reread caches and make sure our\n+\t\t// assumptions are persistent.\n+\t\tRepository ndb = new Repository(db.getDirectory());\n+\t\tassertEquals(rb2, ndb.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(ndb.resolve(\"refs/heads/b\"));\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockFirstBranch() throws IOException {\n+\t\t// \"someone\" has branch b locked\n+\t\tassertTrue(new LockFile(new File(db.getDirectory(), \"refs/heads/b\"))\n+\t\t\t\t.lock());\n+\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"internal check, we have a log\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.LOCK_FAILURE, result);\n+\n+\t\t// Check that the involved refs are sane despite the failure\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name.lock\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b/lock\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/new/name.lock\")\n+\t\t\t\t.exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockHEAD() throws IOException {\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/b\");\n+\n+\t\t// \"someone\" has branch b locked\n+\t\tassertTrue(new LockFile(new File(db.getDirectory(), \"HEAD\")).lock());\n+\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(\"internal test branch b == HEAD\", rb.equals(oldHead));\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.LOCK_FAILURE, result);\n+\n+\t\t// Check that the involved refs are sane despite the failure\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name\").exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"refs/heads/new/name.lock\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b/lock\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/new/name.lock\")\n+\t\t\t\t.exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 573fcac..9c0d114 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -146,6 +146,26 @@ synchronized (this) {\n \t}\n \n \t/**\n+\t * An set of update operations for renaming a ref\n+\t *\n+\t * @param fromRef Old ref name\n+\t * @param toRef New ref name\n+\t * @return a RefUpdate operation to rename a ref\n+\t * @throws IOException\n+\t */\n+\tRefRename newRename(String fromRef, String toRef) throws IOException {\n+\t\trefreshPackedRefs();\n+\t\tRef f = readRefBasic(fromRef, 0);\n+\t\tRef t = readRefBasic(toRef, 0);\n+\t\tif (t != null)\n+\t\t\tthrow new IOException(\"Ref rename target exists: \" + t.getName());\n+\t\tt = new Ref(Ref.Storage.NEW, toRef, null);\n+\t\tRefUpdate refUpdateFrom = new RefUpdate(this, f, fileForRef(f.getName()));\n+\t\tRefUpdate refUpdateTo = new RefUpdate(this, t, fileForRef(t.getName()));\n+\t\treturn new RefRename(refUpdateTo, refUpdateFrom);\n+\t}\n+\n+\t/**\n \t * Writes a symref (e.g. HEAD) to disk\n \t * \n \t * @param name\n@@ -158,11 +178,19 @@ void link(final String name, final String target) throws IOException {\n \t\tfinal byte[] content = Constants.encode(\"ref: \" + target + \"\\n\");\n \t\tlockAndWriteFile(fileForRef(name), content);\n \t\tsynchronized (this) {\n+\t\t\tlooseSymRefs.remove(name);\n \t\t\tsetModified();\n \t\t}\n \t\tdb.fireRefsMaybeChanged();\n \t}\n \n+\tvoid uncacheSymRef(String name) {\n+\t\tsynchronized(this) {\n+\t\t\tlooseSymRefs.remove(name);\n+\t\t\tsetModified();\n+\t\t}\n+\t}\n+\n \tprivate void setModified() {\n \t\tlastRefModification = refModificationCounter++;\n \t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\nindex a077051..2efa12d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n@@ -44,7 +44,7 @@\n import java.io.IOException;\n \n /**\n- * Utility class to add reflog entries\n+ * Utility class to work with reflog files\n  * \n  * @author Dave Watson\n  */\n@@ -58,6 +58,24 @@ static void append(final RefUpdate u, final String msg) throws IOException {\n \t\tappendOneRecord(oldId, newId, ident, msg, db, u.getName());\n \t}\n \n+\tstatic void renameTo(final Repository db, final RefUpdate from,\n+\t\t\tfinal RefUpdate to) throws IOException {\n+\t\tfinal File logdir = new File(db.getDirectory(), Constants.LOGS);\n+\t\tfinal File reflogFrom = new File(logdir, from.getName());\n+\t\tif (!reflogFrom.exists())\n+\t\t\treturn;\n+\t\tfinal File reflogTo = new File(logdir, to.getName());\n+\t\tfinal File refdirTo = reflogTo.getParentFile();\n+\t\tif (!refdirTo.exists() && !refdirTo.mkdirs()) {\n+\t\t\tthrow new IOException(\"Cannot create directory \" + refdirTo);\n+\t\t}\n+\t\tif (!reflogFrom.renameTo(reflogTo)) {\n+\t\t\treflogTo.delete(); // try\n+\t\t\tthrow new IOException(\"Cannot rename \" + reflogFrom + \" to \"\n+\t\t\t\t\t+ reflogTo);\n+\t\t}\n+\t}\n+\n \tprivate static void appendOneRecord(final ObjectId oldId,\n \t\t\tfinal ObjectId newId, PersonIdent ident, final String msg,\n \t\t\tfinal Repository db, final String refName) throws IOException {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\nnew file mode 100644\nindex 0000000..4ea9cfa\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n@@ -0,0 +1,101 @@\n+package org.spearce.jgit.lib;\n+\n+import java.io.File;\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.List;\n+\n+import org.spearce.jgit.lib.RefUpdate.DeleteStore;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n+import org.spearce.jgit.lib.RefUpdate.UpdateStore;\n+\n+/**\n+ * A RefUpdate combination for renaming a ref\n+ */\n+public class RefRename {\n+\tprivate RefUpdate newToUpdate;\n+\n+\tprivate RefUpdate oldFromDelete;\n+\n+\tprivate Result renameResult;\n+\n+\tRefRename(final RefUpdate toUpdate, final RefUpdate fromUpdate) {\n+\t\tnewToUpdate = toUpdate;\n+\t\toldFromDelete = fromUpdate;\n+\t}\n+\n+\t/**\n+\t * @return result of rename operation\n+\t */\n+\tpublic Result getResult() {\n+\t\treturn renameResult;\n+\t}\n+\n+\t/**\n+\t * @return the result of the new ref update\n+\t * @throws IOException\n+\t */\n+\tpublic Result rename() throws IOException {\n+\t\tList<LockFile> lockFiles = new ArrayList<LockFile>();\n+\t\tLockFile lockFileFrom = new LockFile(oldFromDelete.looseFile);\n+\t\tLockFile lockFileTo = new LockFile(newToUpdate.looseFile);\n+\t\tLockFile lockFileHEAD = new LockFile(new File(oldFromDelete.db\n+\t\t\t\t.getRepository().getDirectory(), Constants.HEAD));\n+\t\tlockFiles.add(lockFileTo);\n+\t\tlockFiles.add(lockFileFrom);\n+\t\tlockFiles.add(lockFileHEAD);\n+\t\ttry {\n+\t\t\tfor (LockFile l : lockFiles) {\n+\t\t\t\tif (!l.lock()) {\n+\t\t\t\t\tunlock(lockFiles);\n+\t\t\t\t\treturn Result.LOCK_FAILURE;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} catch (RuntimeException e) {\n+\t\t\tunlock(lockFiles);\n+\t\t\tthrow e;\n+\t\t} catch (IOException e) {\n+\t\t\tunlock(lockFiles);\n+\t\t\tthrow e;\n+\t\t}\n+\t\tboolean renameHEADtoo = oldFromDelete.db.readRef(Constants.HEAD)\n+\t\t\t\t.getName().equals(oldFromDelete.getName());\n+\t\ttry {\n+\t\t\tUpdateStore toStore = newToUpdate.newUpdateStore();\n+\t\t\tRefLogWriter.renameTo(oldFromDelete.getRepository(), oldFromDelete,\n+\t\t\t\t\tnewToUpdate);\n+\t\t\tnewToUpdate.setNewObjectId(oldFromDelete.getOldObjectId());\n+\t\t\tnewToUpdate.setExpectedOldObjectId(oldFromDelete.getOldObjectId());\n+\t\t\tnewToUpdate.setRefLogMessage(\"jgit branch: renamed \"\n+\t\t\t\t\t+ oldFromDelete.getName() + \" to \"\n+\t\t\t\t\t+ oldFromDelete.getName(), false);\n+\t\t\tnewToUpdate.result = toStore.store(lockFileTo, Result.RENAMED);\n+\t\t\tDeleteStore fromStore = oldFromDelete.newDeleteStore();\n+\t\t\tResult store = fromStore.store(lockFileFrom, Result.RENAMED);\n+\t\t\tif (store == Result.RENAMED && renameHEADtoo) {\n+\t\t\t\tfinal byte[] content = Constants.encode(\"ref: \"\n+\t\t\t\t\t\t+ newToUpdate.getName() + \"\\n\");\n+\t\t\t\tlockFileHEAD.write(content);\n+\t\t\t\toldFromDelete.db.uncacheSymRef(Constants.HEAD);\n+\t\t\t\tif (!lockFileHEAD.commit())\n+\t\t\t\t\tthrow new IOException(\"Failed to commit HEAD during rename\");\n+\t\t\t}\n+\t\t\toldFromDelete.db.getRepository().fireRefsMaybeChanged();\n+\t\t\treturn store;\n+\t\t} catch (RuntimeException e) {\n+\t\t\tunlock(lockFiles);\n+\t\t\tthrow e;\n+\t\t} catch (IOException e) {\n+\t\t\tSystem.err.println(e);\n+\t\t\treturn Result.IO_FAILURE;\n+\t\t} finally {\n+\t\t\tunlock(lockFiles);\n+\t\t}\n+\t}\n+\n+\tprivate void unlock(List<LockFile> lockFiles) {\n+\t\tfor (LockFile lockFile : lockFiles) {\n+\t\t\tlockFile.unlock();\n+\t\t}\n+\t}\n+}\n\\ No newline at end of file\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 a9ab73b..7a1074d 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@@ -125,14 +125,20 @@\n \t\t * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a\n \t\t * different case.\n \t\t */\n-\t\tIO_FAILURE\n+\t\tIO_FAILURE,\n+\n+\t\t/**\n+\t\t * The ref was renamed from another name\n+\t\t * <p>\n+\t\t */\n+\t\tRENAMED\n \t}\n \n \t/** Repository the ref is stored in. */\n-\tprivate final RefDatabase db;\n+\tfinal RefDatabase db;\n \n \t/** Location of the loose file holding the value of this ref. */\n-\tprivate final File looseFile;\n+\tfinal File looseFile;\n \n \t/** New value the caller wants this ref to have. */\n \tprivate ObjectId newValue;\n@@ -156,7 +162,7 @@\n \tprivate ObjectId expValue;\n \n \t/** Result of the update operation. */\n-\tprivate Result result = Result.NOT_ATTEMPTED;\n+\tResult result = Result.NOT_ATTEMPTED;\n \n \tprivate final Ref ref;\n \n@@ -478,6 +484,9 @@ else if (status == Result.FAST_FORWARD)\n \t\t\t\tmsg += \": fast forward\";\n \t\t\telse if (status == Result.NEW)\n \t\t\t\tmsg += \": created\";\n+\t\t\telse if (status == Result.RENAMED) {\n+\t\t\t\t// don't amend the message here\n+\t\t\t}\n \t\t}\n \t\tRefLogWriter.append(this, msg);\n \t\tif (!lock.commit())\n@@ -495,7 +504,7 @@ abstract Result store(final LockFile lock, final Result status)\n \t\t\t\tthrows IOException;\n \t}\n \n-\tprivate class UpdateStore extends Store {\n+\tclass UpdateStore extends Store {\n \n \t\t@Override\n \t\tResult store(final LockFile lock, final Result status)\n@@ -504,7 +513,7 @@ Result store(final LockFile lock, final Result status)\n \t\t}\n \t}\n \n-\tprivate class DeleteStore extends Store {\n+\tclass DeleteStore extends Store {\n \n \t\t@Override\n \t\tResult store(LockFile lock, Result status) throws IOException {\n@@ -553,4 +562,12 @@ private static int count(final String s, final char c) {\n \t\t}\n \t\treturn count;\n \t}\n+\n+\tUpdateStore newUpdateStore() {\n+\t\treturn new UpdateStore();\n+\t}\n+\n+\tDeleteStore newDeleteStore() {\n+\t\treturn new DeleteStore();\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 9bed1b7..08539fa 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -468,6 +468,22 @@ public RefUpdate updateRef(final String ref) throws IOException {\n \t}\n \n \t/**\n+\t * Create a command to rename a ref in this repository\n+\t *\n+\t * @param fromRef\n+\t *            name of ref to rename from\n+\t * @param toRef\n+\t *            name of ref to rename to\n+\t * @return an update command that knows how to rename a branch to another.\n+\t * @throws IOException\n+\t *             the rename could not be performed.\n+\t *\n+\t */\n+\tpublic RefRename renameRef(final String fromRef, final String toRef) throws IOException {\n+\t\treturn refs.newRename(fromRef, toRef);\n+\t}\n+\n+\t/**\n \t * Parse a git revision string and return an object id.\n \t *\n \t * Currently supported is combinations of these.\n-- \n1.6.3.dirty\n"},{"id":"114311","messageId":"1242774798-23639-4-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1242774798-23639-3-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 3/6] Add ref rename support to the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-19T23:13:15Z","receivedAt":"2009-05-19T23:13:15Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    9 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |   92 +++++++++++++++----\n .../src/org/spearce/egit/ui/uitext.properties      |    4 +\n 3 files changed, 85 insertions(+), 20 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 654e155..1d52973 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -893,6 +893,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRefresh;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\n@@ -929,6 +935,9 @@\n \tpublic static String BranchSelectionDialog_QuestionNewBranchTitle;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_QuestionNewBranchNameMessage;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_QuestionNewBranchMessage;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 9aad95b..6700768 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -48,8 +48,10 @@\n import org.spearce.egit.ui.UIText;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.RefRename;\n import org.spearce.jgit.lib.RefUpdate;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n \n /**\n  * The branch and reset selection dialog\n@@ -277,6 +279,30 @@ private void refNameFromDialog() {\n \t\t}\n \t}\n \n+\tprivate InputDialog getRefNameInputDialog(String prompt) {\n+\t\tInputDialog labelDialog = new InputDialog(\n+\t\t\t\tgetShell(),\n+\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n+\t\t\t\tprompt,\n+\t\t\t\tnull, new IInputValidator() {\n+\t\t\t\t\tpublic String isValid(String newText) {\n+\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n+\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n+\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n+\t\t\t\t\t\treturn null;\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\tlabelDialog.setBlockOnOpen(true);\n+\t\treturn labelDialog;\n+\t}\n+\n \t@Override\n \tprotected void createButtonsForButtonBar(Composite parent) {\n \t\tif (!showResetType) {\n@@ -284,32 +310,57 @@ protected void createButtonsForButtonBar(Composite parent) {\n \t\t\tnewButton.setFont(JFaceResources.getDialogFont());\n \t\t\tnewButton.setText(UIText.BranchSelectionDialog_NewBranch);\n \t\t\t((GridLayout)parent.getLayout()).numColumns++;\n+\t\t\tButton renameButton = new Button(parent, SWT.PUSH);\n+\t\t\trenameButton.setText(\"&Rename\");\n+\t\t\trenameButton.addSelectionListener(new SelectionListener() {\n+\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t// check what ref name the user selected, if any.\n+\t\t\t\t\trefNameFromDialog();\n+\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(NLS\n+\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchNameMessage,\n+\t\t\t\t\t\t\t\t\trefName));\n+\t\t\t\t\tif (labelDialog.open() == Window.OK) {\n+\t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n+\t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n+\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n+\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n+\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n+\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t// FIXME: Update HEAD\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tbranchTree.removeAll();\n+\t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n+\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n+\t\t\t\t\twidgetSelected(e);\n+\t\t\t\t}\n+\t\t\t});\n \t\t\tnewButton.addSelectionListener(new SelectionListener() {\n \n \t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t// check what ref name the user selected, if any.\n \t\t\t\t\trefNameFromDialog();\n \n-\t\t\t\t\tInputDialog labelDialog = new InputDialog(\n-\t\t\t\t\t\t\tgetShell(),\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchMessage,\n-\t\t\t\t\t\t\tnull, new IInputValidator() {\n-\t\t\t\t\t\t\t\tpublic String isValid(String newText) {\n-\t\t\t\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n-\t\t\t\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n-\t\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n-\t\t\t\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n-\t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n-\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n-\t\t\t\t\t\t\t\t\treturn null;\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t});\n-\t\t\t\t\tlabelDialog.setBlockOnOpen(true);\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(UIText.BranchSelectionDialog_QuestionNewBranchMessage);\n \t\t\t\t\tif (labelDialog.open() == Window.OK) {\n \t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n \t\t\t\t\t\tRefUpdate updateRef;\n@@ -338,6 +389,7 @@ public String isValid(String newText) {\n \t\t\t\t\t}\n \t\t\t\t}\n \n+\n \t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n \t\t\t\t\twidgetSelected(e);\n \t\t\t\t}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 1d21c81..2a40439 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -340,6 +340,9 @@ BranchSelectionDialog_ErrorCouldNotRefresh=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotRefreshBranchList=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotResolve=Could not attempt to resolve {0}\n BranchSelectionDialog_ErrorInvalidRefName=Invalid ref name\n+BranchSelectionDialog_ErrorCouldNotRenameRef=Failed to rename branch {0} -> {1}, status={2}\n+BranchSelectionDialog_ErrorCouldNotRenameRef2=Failed to rename branch {0} -> {1}\n+\n BranchSelectionDialog_LocalBranches=Local Branches\n BranchSelectionDialog_NewBranch=&New branch\n BranchSelectionDialog_NoBranchSeletectMessage=You must select a valid ref.\n@@ -347,6 +350,7 @@ BranchSelectionDialog_NoBranchSeletectTitle=No branch/tag selected\n BranchSelectionDialog_OkCheckout=&Checkout\n BranchSelectionDialog_OkReset=&Reset\n BranchSelectionDialog_QuestionNewBranchMessage=Enter name of new branch. It will branch from the selected branch. refs/heads/ will be prepended to the name you type\n+BranchSelectionDialog_QuestionNewBranchNameMessage=Enter new name of the {0} branch. refs/heads/ will be prepended to the name you type\n BranchSelectionDialog_QuestionNewBranchTitle=New branch\n BranchSelectionDialog_ReallyResetMessage=Resetting will overwrite any changes in your working directory.\\n\\nDo you wish to continue?\n BranchSelectionDialog_ReallyResetTitle=Really reset?\n-- \n1.6.3.dirty\n"},{"id":"114308","messageId":"1242774798-23639-5-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1242774798-23639-4-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 4/6] Allow non-ASCII ref names when writing the packed-refs file","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-19T23:13:16Z","receivedAt":"2009-05-19T23:13:16Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Previously we crashed. Now we encode using UTF-8. This may possibly\nchange the encoding in the packed-refs file as we try to be smart about\nreading the file when converting to Java strings.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/lib/RefWriter.java        |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefWriter.java\nindex 2d39713..0a1bac8 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefWriter.java\n@@ -145,7 +145,7 @@ public void writePackedRefs() throws IOException {\n \t\t\t\tw.write('\\n');\n \t\t\t}\n \t\t}\n-\t\twriteFile(Constants.PACKED_REFS, Constants.encodeASCII(w.toString()));\n+\t\twriteFile(Constants.PACKED_REFS, Constants.encode(w.toString()));\n \t}\n \n \t/**\n-- \n1.6.3.dirty\n"},{"id":"114309","messageId":"1242774798-23639-6-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1242774798-23639-5-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 5/6] Use Constants.PACKED_REFS in RefWriter","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-19T23:13:17Z","receivedAt":"2009-05-19T23:13:17Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/lib/RefDatabase.java      |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 9c0d114..de987ab 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -87,7 +87,7 @@ RefDatabase(final Repository r) {\n \t\tdb = r;\n \t\tgitDir = db.getDirectory();\n \t\trefsDir = FS.resolve(gitDir, \"refs\");\n-\t\tpackedRefsFile = FS.resolve(gitDir, \"packed-refs\");\n+\t\tpackedRefsFile = FS.resolve(gitDir, Constants.PACKED_REFS);\n \t\tclearCache();\n \t}\n \n-- \n1.6.3.dirty\n"},{"id":"114313","messageId":"1242774798-23639-7-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1242774798-23639-6-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 6/6] Improve error reporting in the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-19T23:13:18Z","receivedAt":"2009-05-19T23:13:18Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    6 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |   59 ++++++++++++--------\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +\n 3 files changed, 43 insertions(+), 24 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 1d52973..7fdd470 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -899,6 +899,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 6700768..e8c083a 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -103,7 +103,7 @@ getShell().setText(\n \n \t\ttry {\n \t\t\tfillTreeWithBranches(null);\n-\t\t} catch (IOException e) {\n+\t\t} catch (Throwable e) {\n \t\t\tActivator.logError(UIText.BranchSelectionDialog_ErrorCouldNotRefresh, e);\n \t\t}\n \n@@ -293,6 +293,7 @@ public String isValid(String newText) {\n \t\t\t\t\t\t} catch (IOException e1) {\n \t\t\t\t\t\t\tActivator.logError(NLS.bind(\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t\treturn e1.getMessage();\n \t\t\t\t\t\t}\n \t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n \t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n@@ -326,27 +327,28 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n \t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n-\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n-\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n-\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n-\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\t\tnull,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\trefName, newRefName, renameRef\n+\t\t\t\t\t\t\t\t\t\t\t\t.getResult());\n \t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t// FIXME: Update HEAD\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\trefName, newRefName, e1.getMessage());\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -373,18 +375,20 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\t\t\tstartAt = repo.resolve(refName);\n \t\t\t\t\t\t\tupdateRef.setNewObjectId(startAt);\n \t\t\t\t\t\t\tupdateRef.update();\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotCreateNewRef,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t\t\t\tnewRefName);\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(e1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -405,4 +409,11 @@ createButton(parent, IDialogConstants.OK_ID,\n \tprotected int getShellStyle() {\n \t\treturn super.getShellStyle() | SWT.RESIZE;\n \t}\n+\n+\tprivate void reportError(Throwable e, String title, String message,\n+\t\t\tObject... args) {\n+\t\tString msg = NLS.bind(message, args);\n+\t\tMessageDialog.openError(getShell(), title, msg);\n+\t\tActivator.logError(msg, e);\n+\t}\n }\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 2a40439..2edef25 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -333,6 +333,8 @@ WindowCachePreferencePage_packedGitMMAP=Use virtual memory mapping\n \n BranchSelectionDialog_TitleCheckout=Checkout: {0}\n BranchSelectionDialog_TitleReset=Reset: {0}\n+BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle=New branch creation failed\n+BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle=Rename failed\n BranchSelectionDialog_BranchSuffix_Current=\\ (current)\n BranchSelectionDialog_ErrorAlreadyExists=Already exists\n BranchSelectionDialog_ErrorCouldNotCreateNewRef=Could not create new ref {0}\n-- \n1.6.3.dirty\n"},{"id":"114371","messageId":"20090520214359.GQ30527@spearce.org","threadId":"19215","inReplyTo":"1242774798-23639-2-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-05-20T21:43:59Z","receivedAt":"2009-05-20T21:43:59Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> Also adds a few some more test for refs, though not complete.\n\nHmm, tests fail, wrong expected values?\n \n> +\tpublic void testReadSymRefToPacked() throws IOException {\n> +\t\tassertEquals(Ref.Storage.LOOSE, ref.getStorage());\n\nActual was LOOSE_PACKED.\n\n> +\tpublic void testReadSymRefToLoosePacked() throws IOException {\n> +\t\tassertEquals(Ref.Storage.LOOSE, ref.getStorage());\n\nActual was LOOSE_PACKED.\n\n> +\tpublic void testDeleteLoosePacked() throws IOException {\n> +\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n> +\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n> +\t\tupdateRef.setNewObjectId(pid);\n> +\t\tupdateRef.setForceUpdate(true);\n> +\t\tResult update = updateRef.update();\n> +\t\tassertEquals(Result.FORCED, update); // internal\n> +\n> +\t\tRefUpdate updateRef2 = db.updateRef(\"refs/heads/master\");\n> +\t\tResult delete = updateRef2.delete();\n> +\t\tassertEquals(Result.FORCED, delete);\n\nActual was Result.REJECTED_CURRENT_BRANCH.\n\n> @@ -349,12 +355,20 @@ private synchronized Ref readRefBasic(final String origName,\n> +\n> +\t\tif (!origName.equals(name)) {\n> +\t\t\tref = new Ref(Ref.Storage.LOOSE, origName, name, id);\n> +\t\t\tlooseRefs.put(origName, ref);\n> +\t\t}\n\nSeems fine, but I'm starting to hate our current way of handling\nsymrefs.  Not for this series.  But its starting to worry me.\n\n-- \nShawn.\n"},{"id":"114374","messageId":"20090520221651.GR30527@spearce.org","threadId":"19215","inReplyTo":"1242774798-23639-3-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 2/6] Add ref rename support to JGit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-05-20T22:16:51Z","receivedAt":"2009-05-20T22:16:51Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> Now refs can be renamed. The intent is that should be safe. Only the named\n> refs and associated logs are updated. Any symbolic refs referring to the renames\n> branches are unaffected, except HEAD.\n...\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n> index a077051..2efa12d 100644\n> --- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n> @@ -58,6 +58,24 @@ static void append(final RefUpdate u, final String msg) throws IOException {\n>  \t\tappendOneRecord(oldId, newId, ident, msg, db, u.getName());\n>  \t}\n>  \n> +\tstatic void renameTo(final Repository db, final RefUpdate from,\n> +\t\t\tfinal RefUpdate to) throws IOException {\n> +\t\tfinal File logdir = new File(db.getDirectory(), Constants.LOGS);\n> +\t\tfinal File reflogFrom = new File(logdir, from.getName());\n> +\t\tif (!reflogFrom.exists())\n> +\t\t\treturn;\n> +\t\tfinal File reflogTo = new File(logdir, to.getName());\n> +\t\tfinal File refdirTo = reflogTo.getParentFile();\n> +\t\tif (!refdirTo.exists() && !refdirTo.mkdirs()) {\n> +\t\t\tthrow new IOException(\"Cannot create directory \" + refdirTo);\n> +\t\t}\n> +\t\tif (!reflogFrom.renameTo(reflogTo)) {\n\nWhat happens if from = \"refs/heads/a\" and to = \"refs/heads/a/b\" ?\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n> new file mode 100644\n> index 0000000..4ea9cfa\n> --- /dev/null\n> +++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n> @@ -0,0 +1,101 @@\n> +package org.spearce.jgit.lib;\n\nCopyright header?\n\n> +\n> +import java.io.File;\n> +import java.io.IOException;\n> +import java.util.ArrayList;\n> +import java.util.List;\n> +\n> +import org.spearce.jgit.lib.RefUpdate.DeleteStore;\n> +import org.spearce.jgit.lib.RefUpdate.Result;\n> +import org.spearce.jgit.lib.RefUpdate.UpdateStore;\n> +\n> +/**\n> + * A RefUpdate combination for renaming a ref\n> + */\n> +public class RefRename {\n> +\tprivate RefUpdate newToUpdate;\n> +\n> +\tprivate RefUpdate oldFromDelete;\n> +\n> +\tprivate Result renameResult;\n> +\n> +\tRefRename(final RefUpdate toUpdate, final RefUpdate fromUpdate) {\n> +\t\tnewToUpdate = toUpdate;\n> +\t\toldFromDelete = fromUpdate;\n> +\t}\n> +\n> +\t/**\n> +\t * @return result of rename operation\n> +\t */\n> +\tpublic Result getResult() {\n> +\t\treturn renameResult;\n> +\t}\n> +\n> +\t/**\n> +\t * @return the result of the new ref update\n> +\t * @throws IOException\n> +\t */\n> +\tpublic Result rename() throws IOException {\n> +\t\tList<LockFile> lockFiles = new ArrayList<LockFile>();\n> +\t\tLockFile lockFileFrom = new LockFile(oldFromDelete.looseFile);\n> +\t\tLockFile lockFileTo = new LockFile(newToUpdate.looseFile);\n> +\t\tLockFile lockFileHEAD = new LockFile(new File(oldFromDelete.db\n> +\t\t\t\t.getRepository().getDirectory(), Constants.HEAD));\n> +\t\tlockFiles.add(lockFileTo);\n> +\t\tlockFiles.add(lockFileFrom);\n> +\t\tlockFiles.add(lockFileHEAD);\n> +\t\ttry {\n> +\t\t\tfor (LockFile l : lockFiles) {\n> +\t\t\t\tif (!l.lock()) {\n> +\t\t\t\t\tunlock(lockFiles);\n> +\t\t\t\t\treturn Result.LOCK_FAILURE;\n> +\t\t\t\t}\n> +\t\t\t}\n> +\t\t} catch (RuntimeException e) {\n> +\t\t\tunlock(lockFiles);\n> +\t\t\tthrow e;\n> +\t\t} catch (IOException e) {\n> +\t\t\tunlock(lockFiles);\n> +\t\t\tthrow e;\n> +\t\t}\n> +\t\tboolean renameHEADtoo = oldFromDelete.db.readRef(Constants.HEAD)\n> +\t\t\t\t.getName().equals(oldFromDelete.getName());\n> +\t\ttry {\n> +\t\t\tUpdateStore toStore = newToUpdate.newUpdateStore();\n> +\t\t\tRefLogWriter.renameTo(oldFromDelete.getRepository(), oldFromDelete,\n> +\t\t\t\t\tnewToUpdate);\n> +\t\t\tnewToUpdate.setNewObjectId(oldFromDelete.getOldObjectId());\n> +\t\t\tnewToUpdate.setExpectedOldObjectId(oldFromDelete.getOldObjectId());\n\nThe dst ref can't expect its current value to be the src ref's\ncurrent value, the dst ref shouldn't exist, right?  So I'm not sure\nhow your code is working with this particular assignment occurring\nin it.  Shouldn't the update store be failing over the compare on\nexpected old id?\n\nI think we always want\n\n  newToUpdate.setExpectedOldObjectId(ObjectId.zeroId())\n\nas the JavaDoc for that method says zeroId should be used if the\nref isn't supposed to exist, which should be the case for the\ndestination side of a rename.\n\nOoooh.  I see.  Here UpdateStore is bypassing the CAS check, as\nit runs inside of the RefUpdate.update() method, before the Store\ninstance is constructed.\n\nPost taking the LockFile successfully we need to validate that\n\n  ObjectId.zeroId().equals(db.idOf(newToUpdate.getName()))\n\nis still true, otherwise it means someone else created the\ndestination ref and we're about to overwrite it.\n\n> +\t\t\tnewToUpdate.setRefLogMessage(\"jgit branch: renamed \"\n> +\t\t\t\t\t+ oldFromDelete.getName() + \" to \"\n> +\t\t\t\t\t+ oldFromDelete.getName(), false);\n\nTypo, you meant newToUpdate here in the 2nd case.\n\n-- \nShawn.\n"},{"id":"114425","messageId":"200905211722.33995.robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"20090520214359.GQ30527@spearce.org","subject":"Re: [EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-21T15:22:33Z","receivedAt":"2009-05-21T15:22:33Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"onsdag 20 maj 2009 23:43:59 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> > Also adds a few some more test for refs, though not complete.\n> \n> Hmm, tests fail, wrong expected values?\n\nGah, not sure how that passed. It's not random failures...\n\n> Actual was Result.REJECTED_CURRENT_BRANCH.\n> \n> > @@ -349,12 +355,20 @@ private synchronized Ref readRefBasic(final String origName,\n> > +\n> > +\t\tif (!origName.equals(name)) {\n> > +\t\t\tref = new Ref(Ref.Storage.LOOSE, origName, name, id);\n> > +\t\t\tlooseRefs.put(origName, ref);\n> > +\t\t}\n> \n> Seems fine, but I'm starting to hate our current way of handling\n> symrefs.  Not for this series.  But its starting to worry me.\n\nI agree. For decorating the history it's convenient, but it's inconsistent. For some reason\nit was harder to see without the unit tests. All kinds of things slip by when testing is\ndone on the surface only.\n\nI'll update, and and even more tests. \n\nShould we use multiple Ref objects for symrefs? I.e. a Ref referring to another in a chain,\nwith all symrefs in between visible?\n\n-- robin\n"},{"id":"114429","messageId":"20090521154831.GV30527@spearce.org","threadId":"19215","inReplyTo":"200905211722.33995.robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 1/6] Make sure we get the right storage for loose/pack/loose and packed refs","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-05-21T15:48:31Z","receivedAt":"2009-05-21T15:48:31Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> onsdag 20 maj 2009 23:43:59 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> \n> I agree. For decorating the history it's convenient, but it's inconsistent. For some reason\n> it was harder to see without the unit tests. All kinds of things slip by when testing is\n> done on the surface only.\n> \n> I'll update, and and even more tests. \n\nThanks.\n \n> Should we use multiple Ref objects for symrefs? I.e. a Ref referring to another in a chain,\n> with all symrefs in between visible?\n\nYea, I'm starting to think that we should expose a symref as a Ref that\ncontains a nested Ref.  The getObjectId() / getPeeledObjectId() of any\nof those Refs in that chain should return the leaf node value, but the\nname of each Ref should be the name of the symref.\n\nSo readRef(HEAD) yields:\n\n  Ref {\n    name = \"HEAD\"\n    next = Ref {\n      name = \"refs/heads/master\"\n      next = null\n      objectId = ....\n    }\n  }\n\nThat may break some callers though, as they'd now get HEAD instead\nof master as the name of the return value.\n\n-- \nShawn.\n"},{"id":"114849","messageId":"1243462137-24133-1-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"20090520221651.GR30527@spearce.org","subject":"[EGIT PATCH 00/10] Rename support","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:47Z","receivedAt":"2009-05-27T22:08:47Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"This is basically a rewrite. While adding tests to handle some of\nthe case mentioned in Shawns comments, I had to give up the locking\nstrategy and go for something more like git, i.e. rename proceeds\nin small steps and the I try to undo if a problem is encountered.\n\nAdd to that some small enchancments to ref support. Not the big\nchange we discussed earlier.\n\nThe branch command is touched by one of the patches, but it does\nnot include rename.\n\n-- robin\n\nRobin Rosenberg (10):\n  Make sure we get the right storage for loose/pack/loose and packed\n    refs\n  Add a toString for debugging to RemoteRefUpdate\n  Add a toString to LockFile\n  Add more tests for RefUpdate\n  Do not write to the reflog unless the refupdate logmessage is set\n  Add a utility method for shortening long ref names to short ones.\n  Set a nice reflog message in the branch command\n  Add ref rename support to JGit\n  Add ref rename support to the branch dialog\n  Improve error reporting in the branch dialog\n\n .../src/org/spearce/egit/ui/UIText.java            |   18 +\n .../ui/internal/dialogs/BranchSelectionDialog.java |  133 +++++--\n .../src/org/spearce/egit/ui/uitext.properties      |    7 +\n .../src/org/spearce/jgit/pgm/Branch.java           |   15 +-\n .../org/spearce/jgit/test/resources/packed-refs    |    1 +\n .../tst/org/spearce/jgit/lib/RefTest.java          |   75 +++\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  483 ++++++++++++++++++--\n .../org/spearce/jgit/transport/TransportTest.java  |    2 +-\n .../src/org/spearce/jgit/lib/LockFile.java         |    5 +\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   60 +++-\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   34 ++-\n .../src/org/spearce/jgit/lib/RefRename.java        |  163 +++++++\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   65 ++-\n .../src/org/spearce/jgit/lib/Repository.java       |   31 ++\n .../spearce/jgit/transport/RemoteRefUpdate.java    |   13 +-\n 15 files changed, 1015 insertions(+), 90 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n"},{"id":"114848","messageId":"1243462137-24133-2-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 01/10] Make sure we get the right storage for loose/pack/loose and packed refs","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:48Z","receivedAt":"2009-05-27T22:08:48Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Also adds a few some more test for refs, though not complete.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/RefTest.java          |   75 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   26 +++++--\n 2 files changed, 95 insertions(+), 6 deletions(-)\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\nindex cae5143..440686d 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RefTest.java\n@@ -36,8 +36,14 @@\n  */\n package org.spearce.jgit.lib;\n \n+import java.io.File;\n+import java.io.FileOutputStream;\n+import java.io.IOException;\n import java.util.Map;\n \n+import org.spearce.jgit.lib.Ref.Storage;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n+\n /**\n  * Misc tests for refs. A lot of things are tested elsewhere so not having a\n  * test for a ref related method, does not mean it is untested.\n@@ -69,4 +75,73 @@ public void testReadAllIncludingSymrefs() throws Exception {\n \t\tassertFalse(refmaster.isPeeled());\n \t\tassertNull(refmaster.getPeeledObjectId());\n \t}\n+\n+\tpublic void testReadSymRefToPacked() throws IOException {\n+\t\tdb.writeSymref(\"HEAD\", \"refs/heads/b\");\n+\t\tRef ref = db.getRef(\"HEAD\");\n+\t\tassertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage());\n+\t}\n+\n+\tpublic void testReadSymRefToLoosePacked() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update); // internal\n+\n+\t\tdb.writeSymref(\"HEAD\", \"refs/heads/master\");\n+\t\tRef ref = db.getRef(\"HEAD\");\n+\t\tassertEquals(Ref.Storage.LOOSE_PACKED, ref.getStorage());\n+\t}\n+\n+\tpublic void testReadLooseRef() throws IOException {\n+\t\tRefUpdate updateRef = db.updateRef(\"ref/heads/new\");\n+\t\tupdateRef.setNewObjectId(db.resolve(\"refs/heads/master\"));\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.NEW, update);\n+\t\tRef ref = db.getRef(\"ref/heads/new\");\n+\t\tassertEquals(Storage.LOOSE, ref.getStorage());\n+\t}\n+\n+\t/**\n+\t * Let an \"outsider\" create a loose ref with the same name as a packed one\n+\t *\n+\t * @throws IOException\n+\t * @throws InterruptedException\n+\t */\n+\tpublic void testReadLoosePackedRef() throws IOException,\n+\t\t\tInterruptedException {\n+\t\tRef ref = db.getRef(\"refs/heads/master\");\n+\t\tassertEquals(Storage.PACKED, ref.getStorage());\n+\t\tFileOutputStream os = new FileOutputStream(new File(db.getDirectory(),\n+\t\t\t\t\"refs/heads/master\"));\n+\t\tos.write(ref.getObjectId().name().getBytes());\n+\t\tos.write('\\n');\n+\t\tos.close();\n+\n+\t\tref = db.getRef(\"refs/heads/master\");\n+\t\tassertEquals(Storage.LOOSE_PACKED, ref.getStorage());\n+\t}\n+\n+\t/**\n+\t * Modify a packed ref using the API. This creates a loose ref too, ie.\n+\t * LOOSE_PACKED\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testReadSimplePackedRefSameRepo() throws IOException {\n+\t\tRef ref = db.getRef(\"refs/heads/master\");\n+\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n+\t\tassertEquals(Storage.PACKED, ref.getStorage());\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update);\n+\n+\t\tref = db.getRef(\"refs/heads/master\");\n+\t\tassertEquals(Storage.LOOSE_PACKED, ref.getStorage());\n+\t}\n+\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 8a47412..869d012 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -283,8 +283,14 @@ private synchronized Ref readRefBasic(final String origName,\n \t\tfinal long mtime = loose.lastModified();\n \t\tif (ref != null) {\n \t\t\tLong cachedlastModified = looseRefsMTime.get(name);\n-\t\t\tif (cachedlastModified != null && cachedlastModified == mtime)\n-\t\t\t\treturn ref;\n+\t\t\tif (cachedlastModified != null && cachedlastModified == mtime) {\n+\t\t\t\tif (packedRefs.containsKey(origName))\n+\t\t\t\t\treturn new Ref(Storage.LOOSE_PACKED, origName, ref\n+\t\t\t\t\t\t\t.getObjectId(), ref.getPeeledObjectId(), ref\n+\t\t\t\t\t\t\t.isPeeled());\n+\t\t\t\telse\n+\t\t\t\t\treturn ref;\n+\t\t\t}\n \t\t\tlooseRefs.remove(origName);\n \t\t\tlooseRefsMTime.remove(origName);\n \t\t}\n@@ -349,12 +355,20 @@ private synchronized Ref readRefBasic(final String origName,\n \t\t\tthrow new IOException(\"Not a ref: \" + name + \": \" + line);\n \t\t}\n \n-\t\tref = new Ref(Ref.Storage.LOOSE, origName, name, id);\n-\n-\t\tlooseRefs.put(origName, ref);\n-\t\tref = new Ref(Ref.Storage.LOOSE, origName, id);\n+\t\tStorage storage;\n+\t\tif (packedRefs.containsKey(name))\n+\t\t\tstorage = Ref.Storage.LOOSE_PACKED;\n+\t\telse\n+\t\t\tstorage = Ref.Storage.LOOSE;\n+\t\tref = new Ref(storage, name, id);\n \t\tlooseRefs.put(name, ref);\n \t\tlooseRefsMTime.put(name, mtime);\n+\n+\t\tif (!origName.equals(name)) {\n+\t\t\tref = new Ref(Ref.Storage.LOOSE, origName, name, id);\n+\t\t\tlooseRefs.put(origName, ref);\n+\t\t}\n+\n \t\treturn ref;\n \t}\n \n-- \n1.6.3.dirty\n"},{"id":"114847","messageId":"1243462137-24133-3-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-2-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 02/10] Add a toString for debugging to RemoteRefUpdate","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:49Z","receivedAt":"2009-05-27T22:08:49Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n\n---\n .../spearce/jgit/transport/RemoteRefUpdate.java    |   13 ++++++++++++-\n 1 files changed, 12 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\nindex 9b054eb..8af7d50 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/RemoteRefUpdate.java\n@@ -299,7 +299,8 @@ public Status getStatus() {\n \n \t/**\n \t * Check whether update was fast-forward. Note that this result is\n-\t * meaningful only after successful update (when status is {@link Status#OK}).\n+\t * meaningful only after successful update (when status is {@link Status#OK}\n+\t * ).\n \t *\n \t * @return true if update was fast-forward; false otherwise.\n \t */\n@@ -341,4 +342,14 @@ protected void updateTrackingRef(final RevWalk walk) throws IOException {\n \t\telse\n \t\t\ttrackingRefUpdate.update(walk);\n \t}\n+\n+\t@Override\n+\tpublic String toString() {\n+\t\treturn \"RemoteRefUpdate[remoteName=\" + remoteName + \", \" + status\n+\t\t\t\t+ \", \" + (expectedOldObjectId!=null?expectedOldObjectId.abbreviate(localDb).name() :\"(null)\")\n+\t\t\t\t+ \"...\" + (newObjectId != null ? newObjectId.abbreviate(localDb).name() : \"(null)\")\n+\t\t\t\t+ (fastForward ? \", fastForward\" : \"\")\n+\t\t\t\t+ \", srcRef=\" + srcRef + (forceUpdate ? \", forceUpdate\" : \"\") + \", message=\" + (message != null ? \"\\\"\"\n+\t\t\t\t+ message + \"\\\"\" : \"null\") + \", \" + localDb.getDirectory() + \"]\";\n+\t}\n }\n-- \n1.6.3.dirty\n"},{"id":"114846","messageId":"1243462137-24133-4-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-3-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 03/10] Add a toString to LockFile","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:50Z","receivedAt":"2009-05-27T22:08:50Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/lib/LockFile.java         |    5 +++++\n 1 files changed, 5 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/LockFile.java b/org.spearce.jgit/src/org/spearce/jgit/lib/LockFile.java\nindex 9f19efe..abe8b0d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/LockFile.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/LockFile.java\n@@ -397,4 +397,9 @@ public void unlock() {\n \t\t\tlck.delete();\n \t\t}\n \t}\n+\n+\t@Override\n+\tpublic String toString() {\n+\t\treturn \"LockFile[\" + lck + \", haveLck=\" + haveLck + \"]\";\n+\t}\n }\n-- \n1.6.3.dirty\n"},{"id":"114851","messageId":"1243462137-24133-5-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-4-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 04/10] Add more tests for RefUpdate","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:51Z","receivedAt":"2009-05-27T22:08:51Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Also checks more in one of the old tests\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  189 ++++++++++++++++----\n 1 files changed, 156 insertions(+), 33 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 55d7441..6b1975a 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@@ -72,7 +72,8 @@ public void testNoCacheObjectIdSubclass() throws IOException {\n \t\t\t// empty\n \t\t};\n \t\tru.setNewObjectId(newid);\n-\t\tru.update();\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.NEW, update);\n \t\tfinal Ref r = db.getAllRefs().get(newRef);\n \t\tassertNotNull(r);\n \t\tassertEquals(newRef, r.getName());\n@@ -82,6 +83,25 @@ public void testNoCacheObjectIdSubclass() throws IOException {\n \t\tassertEquals(newid.copy(), r.getObjectId());\n \t}\n \n+\t/**\n+\t * Delete a ref that is pointed to by HEAD\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testDeleteHEADreferencedRef() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update); // internal\n+\n+\t\tRefUpdate updateRef2 = db.updateRef(\"refs/heads/master\");\n+\t\tResult delete = updateRef2.delete();\n+\t\tassertEquals(Result.REJECTED_CURRENT_BRANCH, delete);\n+\t\tassertEquals(pid, db.resolve(\"refs/heads/master\"));\n+\t}\n+\n \tpublic void testLooseDelete() throws IOException {\n \t\tfinal String newRef = \"refs/heads/abc\";\n \t\tRefUpdate ref = updateRef(newRef);\n@@ -95,21 +115,32 @@ public void testDeleteHead() throws IOException {\n \t\tdelete(ref, Result.REJECTED_CURRENT_BRANCH, true, false);\n \t}\n \n-\tpublic void testLogDeleted() throws IOException {\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(refName);\n-\t\tdelete(ref, Result.FAST_FORWARD);\n-\t\tassertFalse(log.exists());\n-\t}\n+\t/**\n+\t * Delete a loose ref and make sure the directory in refs is deleted too,\n+\t * and the reflog dir too\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testDeleteLooseAndItsDirectory() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/c^\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/z/c\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.NEW, update); // internal\n+\t\tassertTrue(new File(db.getDirectory(), Constants.R_HEADS + \"z\")\n+\t\t\t\t.exists());\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/z\").exists());\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\t// The real test here\n+\t\tRefUpdate updateRef2 = db.updateRef(\"refs/heads/z/c\");\n+\t\tupdateRef2.setForceUpdate(true);\n+\t\tResult delete = updateRef2.delete();\n+\t\tassertEquals(Result.FORCED, delete);\n+\t\tassertNull(db.resolve(\"refs/heads/z/c\"));\n+\t\tassertFalse(new File(db.getDirectory(), Constants.R_HEADS + \"z\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/z\").exists());\n \t}\n \n \tpublic void testDeleteNotFound() throws IOException {\n@@ -130,24 +161,6 @@ public void testDeleteForce() throws IOException {\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-\n \tpublic void testRefKeySameAsOrigName() {\n \t\tMap<String, Ref> allRefs = db.getAllRefs();\n \t\tfor (Entry<String, Ref> e : allRefs.entrySet()) {\n@@ -155,4 +168,114 @@ public void testRefKeySameAsOrigName() {\n \n \t\t}\n \t}\n+\n+\t/**\n+\t * Try modify a ref forward, fast forward\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testUpdateRefForward() throws IOException {\n+\t\tObjectId ppid = db.resolve(\"refs/heads/master^\");\n+\t\tObjectId pid = db.resolve(\"refs/heads/master\");\n+\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(ppid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update);\n+\t\tassertEquals(ppid, db.resolve(\"refs/heads/master\"));\n+\n+\t\t// real test\n+\t\tRefUpdate updateRef2 = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef2.setNewObjectId(pid);\n+\t\tResult update2 = updateRef2.update();\n+\t\tassertEquals(Result.FAST_FORWARD, update2);\n+\t\tassertEquals(pid, db.resolve(\"refs/heads/master\"));\n+\t}\n+\n+\t/**\n+\t * Delete a ref that exists both as packed and loose. Make sure the ref\n+\t * cannot be resolved after delete.\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testDeleteLoosePacked() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/c^\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/c\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.FORCED, update); // internal\n+\n+\t\t// The real test here\n+\t\tRefUpdate updateRef2 = db.updateRef(\"refs/heads/c\");\n+\t\tupdateRef2.setForceUpdate(true);\n+\t\tResult delete = updateRef2.delete();\n+\t\tassertEquals(Result.FORCED, delete);\n+\t\tassertNull(db.resolve(\"refs/heads/c\"));\n+\t}\n+\n+\t/**\n+\t * Try modify a ref to same\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testUpdateRefNoChange() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/master\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.NO_CHANGE, update);\n+\t\tassertEquals(pid, db.resolve(\"refs/heads/master\"));\n+\t}\n+\n+\t/**\n+\t * Try modify a ref, but get wrong expected old value\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testUpdateRefLockFailureWrongOldValue() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/master\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tupdateRef.setExpectedOldObjectId(db.resolve(\"refs/heads/master^\"));\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update);\n+\t\tassertEquals(pid, db.resolve(\"refs/heads/master\"));\n+\t}\n+\n+\t/**\n+\t * Try modify a ref that is locked\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testUpdateRefLockFailureLocked() throws IOException {\n+\t\tObjectId opid = db.resolve(\"refs/heads/master\");\n+\t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tLockFile lockFile1 = new LockFile(new File(db.getDirectory(),\"refs/heads/master\"));\n+\t\tassertTrue(lockFile1.lock()); // precondition to test\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update);\n+\t\tassertEquals(opid, db.resolve(\"refs/heads/master\"));\n+\t\tLockFile lockFile2 = new LockFile(new File(db.getDirectory(),\"refs/heads/master\"));\n+\t\tassertFalse(lockFile2.lock()); // was locked, still is\n+\t}\n+\n+\t/**\n+\t * Try to delete a ref. Delete requires force.\n+\t *\n+\t * @throws IOException\n+\t */\n+\tpublic void testDeleteLoosePackedRejected() throws IOException {\n+\t\tObjectId pid = db.resolve(\"refs/heads/c^\");\n+\t\tObjectId oldpid = db.resolve(\"refs/heads/c\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/c\");\n+\t\tupdateRef.setNewObjectId(pid);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(Result.REJECTED, update);\n+\t\tassertEquals(oldpid, db.resolve(\"refs/heads/c\"));\n+\t}\n+\n }\n-- \n1.6.3.dirty\n"},{"id":"114854","messageId":"1243462137-24133-6-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-5-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:52Z","receivedAt":"2009-05-27T22:08:52Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |    1 +\n .../src/org/spearce/jgit/lib/RefUpdate.java        |    3 ++-\n 2 files changed, 3 insertions(+), 1 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 6b1975a..84653c8 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@@ -126,6 +126,7 @@ public void testDeleteLooseAndItsDirectory() throws IOException {\n \t\tRefUpdate updateRef = db.updateRef(\"refs/heads/z/c\");\n \t\tupdateRef.setNewObjectId(pid);\n \t\tupdateRef.setForceUpdate(true);\n+\t\tupdateRef.setRefLogMessage(\"new test ref\", false);\n \t\tResult update = updateRef.update();\n \t\tassertEquals(Result.NEW, update); // internal\n \t\tassertTrue(new File(db.getDirectory(), Constants.R_HEADS + \"z\")\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 a9ab73b..79d9f2d 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@@ -479,7 +479,8 @@ else if (status == Result.FAST_FORWARD)\n \t\t\telse if (status == Result.NEW)\n \t\t\t\tmsg += \": created\";\n \t\t}\n-\t\tRefLogWriter.append(this, msg);\n+\t\tif (msg != null)\n+\t\t\tRefLogWriter.append(this, msg);\n \t\tif (!lock.commit())\n \t\t\treturn Result.LOCK_FAILURE;\n \t\tdb.stored(this.ref.getOrigName(),  ref.getName(), newValue, lock.getCommitLastModified());\n-- \n1.6.3.dirty\n"},{"id":"114853","messageId":"1243462137-24133-7-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-6-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 06/10] Add a utility method for shortening long ref names to short ones.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:53Z","receivedAt":"2009-05-27T22:08:53Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"refs/heads/x -> x etc.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/lib/Repository.java       |   15 +++++++++++++++\n 1 files changed, 15 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 9bed1b7..55dae27 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -1074,4 +1074,19 @@ public void scanForRepoChanges() throws IOException {\n \t\tgetAllRefs(); // This will look for changes to refs\n \t\tgetIndex(); // This will detect changes in the index\n \t}\n+\n+\t/**\n+\t * @param refName\n+\t * \n+\t * @return a more user friendly ref name\n+\t */\n+\tpublic String shortenRefName(String refName) {\n+\t\tif (refName.startsWith(Constants.R_HEADS))\n+\t\t\treturn refName.substring(Constants.R_HEADS.length());\n+\t\tif (refName.startsWith(Constants.R_TAGS))\n+\t\t\treturn refName.substring(Constants.R_TAGS.length());\n+\t\tif (refName.startsWith(Constants.R_REMOTES))\n+\t\t\treturn refName.substring(Constants.R_REMOTES.length());\n+\t\treturn refName;\n+\t}\n }\n-- \n1.6.3.dirty\n"},{"id":"114850","messageId":"1243462137-24133-8-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-7-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 07/10] Set a nice reflog message in the branch command","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:54Z","receivedAt":"2009-05-27T22:08:54Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/pgm/Branch.java           |   15 +++++++++++----\n 1 files changed, 11 insertions(+), 4 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Branch.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Branch.java\nindex 11002f0..dbe297e 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Branch.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Branch.java\n@@ -98,12 +98,18 @@ protected void run() throws Exception {\n \n \t\t\tif (branches.size() > 0) {\n \t\t\t\tString newHead = branches.get(0);\n-\t\t\t\tObjectId startAt;\n+\t\t\t\tString startBranch;\n \t\t\t\tif (branches.size() == 2)\n-\t\t\t\t\tstartAt = db.resolve(branches.get(1) + \"^0\");\n+\t\t\t\t\tstartBranch = branches.get(1);\n \t\t\t\telse\n-\t\t\t\t\tstartAt = db.resolve(Constants.HEAD + \"^0\");\n-\n+\t\t\t\t\tstartBranch = Constants.HEAD;\n+\t\t\t\tRef startRef = db.getRef(startBranch);\n+\t\t\t\tObjectId startAt = db.resolve(startBranch + \"^0\");\n+\t\t\t\tif (startRef != null)\n+\t\t\t\t\tstartBranch = startRef.getName();\n+\t\t\t\telse\n+\t\t\t\t\tstartBranch = startAt.name();\n+\t\t\t\tstartBranch = db.shortenRefName(startBranch);\n \t\t\t\tString newRefName = newHead;\n \t\t\t\tif (!newRefName.startsWith(Constants.R_HEADS))\n \t\t\t\t\tnewRefName = Constants.R_HEADS + newRefName;\n@@ -114,6 +120,7 @@ protected void run() throws Exception {\n \t\t\t\tRefUpdate updateRef = db.updateRef(newRefName);\n \t\t\t\tupdateRef.setNewObjectId(startAt);\n \t\t\t\tupdateRef.setForceUpdate(createForce);\n+\t\t\t\tupdateRef.setRefLogMessage(\"branch: Created from \" + startBranch, false);\n \t\t\t\tResult update = updateRef.update();\n \t\t\t\tif (update == Result.REJECTED)\n \t\t\t\t\tthrow die(String.format(\"Could not create branch %s: %s\", newHead, update.toString()));\n-- \n1.6.3.dirty\n"},{"id":"114855","messageId":"1243462137-24133-9-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-8-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 08/10] Add ref rename support to JGit","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:55Z","receivedAt":"2009-05-27T22:08:55Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Now refs can be renamed. The intent is that should be safe. Only the named\nrefs and associated logs are updated. Any symbolic refs referring to the renames\nbranches are unaffected, except HEAD, which is updated if the branch it refers\nto is being renamed.\n\nIn order to perform operations like rename from a to a/b and vice verse, we\nperform the rename similar to the way C git does it, i.e. one ref update at\na time using the same temporary intermediate names as the C version does.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../org/spearce/jgit/test/resources/packed-refs    |    1 +\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  293 ++++++++++++++++++++\n .../org/spearce/jgit/transport/TransportTest.java  |    2 +-\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   34 +++\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   34 +++-\n .../src/org/spearce/jgit/lib/RefRename.java        |  163 +++++++++++\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   62 +++-\n .../src/org/spearce/jgit/lib/Repository.java       |   16 +\n 8 files changed, 588 insertions(+), 17 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n\ndiff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs\nindex 38a70ec..a6a14f2 100644\n--- a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs\n+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs\n@@ -6,6 +6,7 @@ f73b95671f326616d66b2afb3bdfcdbbce110b44 refs/heads/d\n d0114ab8ac326bab30e3a657a0397578c5a1af88 refs/heads/e\n 47d3697c3747e8184e0dc479ccbd01e359023577 refs/heads/f\n 175d5b80bd9768884d8fced02e9bd33488174396 refs/heads/g\n+175d5b80bd9768884d8fced02e9bd33488174396 refs/heads/prefix/a\n 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master\n d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864 refs/heads/pa\n 6db9c2ebf75590eef973081736730a9ea169a0c4 refs/tags/A\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 84653c8..8c6ca0c 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@@ -83,6 +83,68 @@ public void testNoCacheObjectIdSubclass() throws IOException {\n \t\tassertEquals(newid.copy(), r.getObjectId());\n \t}\n \n+\tpublic void testNewNamespaceConflictWithLoosePrefixNameExists() throws IOException {\n+\t\tfinal String newRef = \"refs/heads/z\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.NEW, update);\n+\t\t// end setup\n+\t\tfinal String newRef2 = \"refs/heads/z/a\";\n+\t\tfinal RefUpdate ru2 = updateRef(newRef2);\n+\t\tfinal RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid2);\n+\t\tResult update2 = ru2.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update2);\n+\t}\n+\n+\tpublic void testNewNamespaceConflictWithPackedPrefixNameExists() throws IOException {\n+\t\tfinal String newRef = \"refs/heads/master/x\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update);\n+\t}\n+\n+\tpublic void testNewNamespaceConflictWithLoosePrefixOfExisting() throws IOException {\n+\t\tfinal String newRef = \"refs/heads/z/a\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.NEW, update);\n+\t\t// end setup\n+\t\tfinal String newRef2 = \"refs/heads/z\";\n+\t\tfinal RefUpdate ru2 = updateRef(newRef2);\n+\t\tfinal RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid2);\n+\t\tResult update2 = ru2.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update2);\n+\t}\n+\n+\tpublic void testNewNamespaceConflictWithPackedPrefixOfExisting() throws IOException {\n+\t\tfinal String newRef = \"refs/heads/prefix\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update);\n+\t}\n+\n \t/**\n \t * Delete a ref that is pointed to by HEAD\n \t *\n@@ -279,4 +341,235 @@ public void testDeleteLoosePackedRejected() throws IOException {\n \t\tassertEquals(oldpid, db.resolve(\"refs/heads/c\"));\n \t}\n \n+\tpublic void testRenameBranchNoPreviousLog() throws IOException {\n+\t\tassertFalse(\"precondition, no log on old branchg\", new File(db\n+\t\t\t\t.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(rb.equals(oldHead)); // assumption for this test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD));\n+\t}\n+\n+\tpublic void testRenameBranchHasPreviousLog() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(\"precondition for this test, branch b != HEAD\", rb\n+\t\t\t\t.equals(oldHead));\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD));\n+\t\t// TODO: test content of log file\n+\t}\n+\n+\tpublic void testRenameCurrentBranch() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(\"internal test condition, b == HEAD\", rb.equals(oldHead));\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t\t// TODO: test content of log file\n+\t}\n+\n+\tpublic void testRenameBranchAlsoInPack() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId rb2 = db.resolve(\"refs/heads/b~1\");\n+\t\tassertEquals(Ref.Storage.PACKED, db.getRef(\"refs/heads/b\").getStorage());\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/b\");\n+\t\tupdateRef.setNewObjectId(rb2);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(\"internal check new ref is loose\", Result.FORCED, update);\n+\t\tassertEquals(Ref.Storage.LOOSE_PACKED, db.getRef(\"refs/heads/b\").getStorage());\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb2, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertTrue(new File(db.getDirectory(), \"logs/refs/heads/new/name\")\n+\t\t\t\t.exists());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\n+\t\t// Create new Repository instance, to reread caches and make sure our\n+\t\t// assumptions are persistent.\n+\t\tRepository ndb = new Repository(db.getDirectory());\n+\t\tassertEquals(rb2, ndb.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(ndb.resolve(\"refs/heads/b\"));\n+\t}\n+\n+\tpublic void tryRenameWhenLocked(String toLock, String fromName,\n+\t\t\tString toName, String headPointsTo) throws IOException {\n+\t\t// setup\n+\t\tdb.writeSymref(Constants.HEAD, headPointsTo);\n+\t\tObjectId oldfromId = db.resolve(fromName);\n+\t\tObjectId oldHeadId = db.resolve(Constants.HEAD);\n+\n+\t\tRefLogWriter.writeReflog(db, oldfromId, oldfromId, \"Just a message\", fromName);\n+\t\tassertTrue(\"internal check, we have a log\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/\" + fromName).exists());\n+\n+\t\t// \"someone\" has branch X locked\n+\t\tassertTrue(new LockFile(new File(db.getDirectory(), toLock)).lock());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(fromName, toName);\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.LOCK_FAILURE, result);\n+\n+\t\t// Check that the involved refs are sane despite the failure\n+\t\tassertExists(false, toName);\n+\t\tif (!toLock.equals(toName))\n+\t\t\tassertExists(false, toName + \".lock\");\n+\t\tassertExists(true, toLock + \".lock\");\n+\t\tif (!toLock.equals(fromName))\n+\t\t\tassertExists(false, \"logs/\" + fromName + \".lock\");\n+\t\tassertExists(false, \"logs/\" + toName + \".lock\");\n+\t\tassertEquals(oldHeadId, db.resolve(Constants.HEAD));\n+\t\tassertEquals(oldfromId, db.resolve(fromName));\n+\t\tassertNull(db.resolve(toName));\n+\t}\n+\n+\tprivate void assertExists(boolean positive, String toName) {\n+\t\tassertEquals(toName + (positive ? \" \" : \" does not \") + \"exist\",\n+\t\t\t\tpositive, new File(db.getDirectory(), toName).exists());\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisFromLockHEAD() throws IOException {\n+\t\ttryRenameWhenLocked(\"HEAD\", \"refs/heads/b\", \"refs/heads/new/name\",\n+\t\t\t\t\"refs/heads/b\");\n+\t}\n+\tpublic void testRenameBranchCannotLockAFileHEADisFromLockFrom() throws IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/b\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/b\");\n+\t}\n+\tpublic void testRenameBranchCannotLockAFileHEADisFromLockTo() throws IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/new/name\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/b\");\n+\t}\n+\tpublic void testRenameBranchCannotLockAFileHEADisFromLockTmp() throws IOException {\n+\t\ttryRenameWhenLocked(\"RENAMED-REF\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/b\");\n+\t}\n+\n+//\tpublic void testRenameBranchCannotLockAFileHEADisToLockHead() throws IOException {\n+//\t\tan example following the rename failure pattern, but this one is ok!\n+//\t\ttryRenameWhenLocked(\"HEAD\", \"refs/heads/b\", \"refs/heads/new/name\",\n+//\t\t\t\t\"refs/heads/new/name\");\n+//\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisToLockFrom() throws IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/b\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/new/name\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisToLockTo() throws IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/new/name\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/new/name\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisToLockTmp() throws IOException {\n+\t\ttryRenameWhenLocked(\"RENAMED-REF\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/new/name\");\n+\t}\n+\n+//\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockHead() throws IOException {\n+//\t\tan example following the rename failure pattern, but this one is ok!\n+//\t\ttryRenameWhenLocked(\"HEAD\", \"refs/heads/b\", \"refs/heads/new/name\",\n+//\t\t\t\t\"refs/heads/a\");\n+//\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockFrom() throws IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/b\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/a\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockTo() throws IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/new/name\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/a\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockTmp() throws IOException {\n+\t\ttryRenameWhenLocked(\"RENAMED-REF\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/a\");\n+\t}\n+\n+\tpublic void testRenameRefNameColission1avoided() throws IOException {\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/a\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/a\");\n+\t\tupdateRef.setNewObjectId(rb);\n+\t\tassertEquals(Result.FAST_FORWARD, updateRef.update());\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/a\");\n+\t\tassertTrue(\"internal check, we have a log\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/a\").exists());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/a\",\n+\t\t\t\t\"refs/heads/a/b\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertNull(db.resolve(\"refs/heads/a\"));\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/a/b\"));\n+\t}\n+\n+\tpublic void testRenameRefNameColission2avoided() throws IOException {\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/prefix/a\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/prefix/a\");\n+\t\tupdateRef.setNewObjectId(rb);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tassertEquals(Result.FORCED, updateRef.update());\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/prefix/a\");\n+\t\tassertTrue(\"internal check, we have a log\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/prefix/a\").exists());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/prefix/a\",\n+\t\t\t\t\"refs/heads/prefix\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\n+\t\tassertNull(db.resolve(\"refs/heads/prefix/a\"));\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/prefix\"));\n+\t}\n }\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\nindex c6e3335..fcf3f5c 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\n@@ -123,7 +123,7 @@ public void testFindRemoteRefUpdatesWildcardNoTracking() throws IOException {\n \t\t\t\t.findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(\n \t\t\t\t\t\t\"+refs/heads/*:refs/heads/test/*\")));\n \n-\t\tassertEquals(9, result.size());\n+\t\tassertEquals(10, result.size());\n \t\tboolean foundA = false;\n \t\tboolean foundB = false;\n \t\tfor (final RemoteRefUpdate rru : result) {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 869d012..babef0b 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -146,6 +146,26 @@ synchronized (this) {\n \t}\n \n \t/**\n+\t * An set of update operations for renaming a ref\n+\t *\n+\t * @param fromRef Old ref name\n+\t * @param toRef New ref name\n+\t * @return a RefUpdate operation to rename a ref\n+\t * @throws IOException\n+\t */\n+\tRefRename newRename(String fromRef, String toRef) throws IOException {\n+\t\trefreshPackedRefs();\n+\t\tRef f = readRefBasic(fromRef, 0);\n+\t\tRef t = readRefBasic(toRef, 0);\n+\t\tif (t != null)\n+\t\t\tthrow new IOException(\"Ref rename target exists: \" + t.getName());\n+\t\tt = new Ref(Ref.Storage.NEW, toRef, null);\n+\t\tRefUpdate refUpdateFrom = new RefUpdate(this, f, fileForRef(f.getName()));\n+\t\tRefUpdate refUpdateTo = new RefUpdate(this, t, fileForRef(t.getName()));\n+\t\treturn new RefRename(refUpdateTo, refUpdateFrom);\n+\t}\n+\n+\t/**\n \t * Writes a symref (e.g. HEAD) to disk\n \t * \n \t * @param name\n@@ -158,11 +178,25 @@ void link(final String name, final String target) throws IOException {\n \t\tfinal byte[] content = Constants.encode(\"ref: \" + target + \"\\n\");\n \t\tlockAndWriteFile(fileForRef(name), content);\n \t\tsynchronized (this) {\n+\t\t\tlooseSymRefs.remove(name);\n \t\t\tsetModified();\n \t\t}\n \t\tdb.fireRefsMaybeChanged();\n \t}\n \n+\tvoid uncacheSymRef(String name) {\n+\t\tsynchronized(this) {\n+\t\t\tlooseSymRefs.remove(name);\n+\t\t\tsetModified();\n+\t\t}\n+\t}\n+\n+\tvoid uncacheRef(String name) {\n+\t\tlooseRefs.remove(name);\n+\t\tlooseRefsMTime.remove(name);\n+\t\tpackedRefs.remove(name);\n+\t}\n+\n \tprivate void setModified() {\n \t\tlastRefModification = refModificationCounter++;\n \t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\nindex a077051..18cf2ed 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n@@ -44,7 +44,7 @@\n import java.io.IOException;\n \n /**\n- * Utility class to add reflog entries\n+ * Utility class to work with reflog files\n  * \n  * @author Dave Watson\n  */\n@@ -58,6 +58,38 @@ static void append(final RefUpdate u, final String msg) throws IOException {\n \t\tappendOneRecord(oldId, newId, ident, msg, db, u.getName());\n \t}\n \n+\tstatic void append(RefRename refRename, String msg) throws IOException {\n+\t\tfinal ObjectId id = refRename.getObjectId();\n+\t\tfinal Repository db = refRename.getRepository();\n+\t\tfinal PersonIdent ident = refRename.getRefLogIdent();\n+\t\tappendOneRecord(id, id, ident, msg, db, refRename.getToName());\n+\t}\n+\n+\tstatic void renameTo(final Repository db, final RefUpdate from,\n+\t\t\tfinal RefUpdate to) throws IOException {\n+\t\tfinal File logdir = new File(db.getDirectory(), Constants.LOGS);\n+\t\tfinal File reflogFrom = new File(logdir, from.getName());\n+\t\tif (!reflogFrom.exists())\n+\t\t\treturn;\n+\t\tfinal File reflogTo = new File(logdir, to.getName());\n+\t\tfinal File reflogToDir = reflogTo.getParentFile();\n+\t\tFile tmp = new File(logdir,\"tmp-renamed-log\");\n+\t\ttmp.delete(); // if any exists\n+\t\tif (!reflogFrom.renameTo(tmp)) {\n+\t\t\tthrow new IOException(\"Cannot rename \" + reflogFrom + \" to (\" + tmp\n+\t\t\t\t\t+ \")\" + reflogTo);\n+\t\t}\n+\t\tRefUpdate.deleteEmptyDir(reflogFrom, RefUpdate.count(from.getName(),\n+\t\t\t\t'/'));\n+\t\tif (!reflogToDir.exists() && !reflogToDir.mkdirs()) {\n+\t\t\tthrow new IOException(\"Cannot create directory \" + reflogToDir);\n+\t\t}\n+\t\tif (!tmp.renameTo(reflogTo)) {\n+\t\t\tthrow new IOException(\"Cannot rename (\" + tmp + \")\" + reflogFrom\n+\t\t\t\t\t+ \" to \" + reflogTo);\n+\t\t}\n+\t}\n+\n \tprivate static void appendOneRecord(final ObjectId oldId,\n \t\t\tfinal ObjectId newId, PersonIdent ident, final String msg,\n \t\t\tfinal Repository db, final String refName) throws IOException {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\nnew file mode 100644\nindex 0000000..589d752\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n@@ -0,0 +1,163 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.IOException;\n+\n+import org.spearce.jgit.lib.RefUpdate.Result;\n+\n+/**\n+ * A RefUpdate combination for renaming a ref\n+ */\n+public class RefRename {\n+\tprivate RefUpdate newToUpdate;\n+\n+\tprivate RefUpdate oldFromDelete;\n+\n+\tprivate Result renameResult = Result.NOT_ATTEMPTED;\n+\n+\tRefRename(final RefUpdate toUpdate, final RefUpdate fromUpdate) {\n+\t\tnewToUpdate = toUpdate;\n+\t\toldFromDelete = fromUpdate;\n+\t}\n+\n+\t/**\n+\t * @return result of rename operation\n+\t */\n+\tpublic Result getResult() {\n+\t\treturn renameResult;\n+\t}\n+\n+\t/**\n+\t * @return the result of the new ref update\n+\t * @throws IOException\n+\t */\n+\tpublic Result rename() throws IOException {\n+\t\tRef oldRef = oldFromDelete.db.readRef(Constants.HEAD);\n+\t\tboolean renameHEADtoo = oldRef != null\n+\t\t\t\t&& oldRef.getName().equals(oldFromDelete.getName());\n+\t\ttry {\n+\t\t\tRepository db = oldFromDelete.getRepository();\n+\t\t\tRefLogWriter.renameTo(db, oldFromDelete,\n+\t\t\t\t\tnewToUpdate);\n+\t\t\tnewToUpdate.setRefLogMessage(null, false);\n+\t\t\tRefUpdate tmpUpdateRef = oldFromDelete.db.getRepository().updateRef(\n+\t\t\t\t\t\"RENAMED-REF\");\n+\t\t\tif (renameHEADtoo) {\n+\t\t\t\ttry {\n+\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, \"RENAMED-REF\");\n+\t\t\t\t} catch (IOException e) {\n+\t\t\t\t\tRefLogWriter.renameTo(db,\n+\t\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n+\t\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\ttmpUpdateRef.setNewObjectId(oldFromDelete.getOldObjectId());\n+\t\t\ttmpUpdateRef.setForceUpdate(true);\n+\t\t\tResult update = tmpUpdateRef.update();\n+\t\t\tif (update != Result.FORCED && update != Result.NEW && update != Result.NO_CHANGE) {\n+\t\t\t\tRefLogWriter.renameTo(db,\n+\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n+\t\t\t\tif (renameHEADtoo) {\n+\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, oldFromDelete.getName());\n+\t\t\t\t}\n+\t\t\t\treturn renameResult = update;\n+\t\t\t}\n+\n+\t\t\toldFromDelete.setForceUpdate(true);\n+\t\t\tResult delete = oldFromDelete.delete();\n+\t\t\tif (delete != Result.FORCED) {\n+\t\t\t\tif (db.getRef(\n+\t\t\t\t\t\toldFromDelete.getName()) != null) {\n+\t\t\t\t\tRefLogWriter.renameTo(db,\n+\t\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n+\t\t\t\t\tif (renameHEADtoo) {\n+\t\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, oldFromDelete\n+\t\t\t\t\t\t\t\t.getName());\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\treturn renameResult = delete;\n+\t\t\t}\n+\n+\t\t\tnewToUpdate.setNewObjectId(tmpUpdateRef.getNewObjectId());\n+\t\t\tResult updateResult = newToUpdate.update();\n+\t\t\tif (updateResult != Result.NEW) {\n+\t\t\t\tRefLogWriter.renameTo(db,\n+\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n+\t\t\t\tif (renameHEADtoo) {\n+\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, oldFromDelete.getName());\n+\t\t\t\t}\n+\t\t\t\toldFromDelete.setNewObjectId(oldFromDelete.getOldObjectId());\n+\t\t\t\tResult undelete = oldFromDelete.update();\n+\t\t\t\tif (undelete != Result.NEW)\n+\t\t\t\t\treturn renameResult = Result.IO_FAILURE;\n+\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n+\t\t\t}\n+\n+\t\t\tif (renameHEADtoo) {\n+\t\t\t\toldFromDelete.db.link(Constants.HEAD, newToUpdate.getName());\n+\t\t\t} else {\n+\t\t\t\toldFromDelete.db.getRepository().fireRefsMaybeChanged();\n+\t\t\t}\n+\t\t\tRefLogWriter.append(this, \"jgit branch: renamed \"\n+\t\t\t\t\t+ db.shortenRefName(oldFromDelete.getName()) + \" to \"\n+\t\t\t\t\t+ db.shortenRefName(newToUpdate.getName()));\n+\t\t\treturn renameResult = Result.RENAMED;\n+\t\t} catch (RuntimeException e) {\n+\t\t\tthrow e;\n+\t\t} catch (IOException e) {\n+\t\t\tSystem.err.println(e);\n+\t\t\treturn renameResult = Result.IO_FAILURE;\n+\t\t}\n+\t}\n+\n+\tObjectId getObjectId() {\n+\t\treturn oldFromDelete.getOldObjectId();\n+\t}\n+\n+\tRepository getRepository() {\n+\t\treturn oldFromDelete.getRepository();\n+\t}\n+\n+\tPersonIdent getRefLogIdent() {\n+\t\treturn newToUpdate.getRefLogIdent();\n+\t}\n+\n+\tString getToName() {\n+\t\treturn newToUpdate.getName();\n+\t}\n+}\n\\ No newline at end of file\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 79d9f2d..a2041de 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@@ -125,14 +125,20 @@\n \t\t * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a\n \t\t * different case.\n \t\t */\n-\t\tIO_FAILURE\n+\t\tIO_FAILURE,\n+\n+\t\t/**\n+\t\t * The ref was renamed from another name\n+\t\t * <p>\n+\t\t */\n+\t\tRENAMED\n \t}\n \n \t/** Repository the ref is stored in. */\n-\tprivate final RefDatabase db;\n+\tfinal RefDatabase db;\n \n \t/** Location of the loose file holding the value of this ref. */\n-\tprivate final File looseFile;\n+\tfinal File looseFile;\n \n \t/** New value the caller wants this ref to have. */\n \tprivate ObjectId newValue;\n@@ -156,7 +162,7 @@\n \tprivate ObjectId expValue;\n \n \t/** Result of the update operation. */\n-\tprivate Result result = Result.NOT_ATTEMPTED;\n+\tResult result = Result.NOT_ATTEMPTED;\n \n \tprivate final Ref ref;\n \n@@ -418,6 +424,15 @@ private Result updateImpl(final RevWalk walk, final Store store)\n \t\tRevObject newObj;\n \t\tRevObject oldObj;\n \n+\t\tint lastSlash = getName().lastIndexOf('/');\n+\t\tif (lastSlash > 0)\n+\t\t\tif (db.getRepository().getRef(getName().substring(0, lastSlash)) != null)\n+\t\t\t\treturn Result.LOCK_FAILURE;\n+\t\tString rName = getName() + \"/\";\n+\t\tfor (Ref r : db.getAllRefs().values()) {\n+\t\t\tif (r.getName().startsWith(rName))\n+\t\t\t\treturn Result.LOCK_FAILURE;\n+\t\t}\n \t\tlock = new LockFile(looseFile);\n \t\tif (!lock.lock())\n \t\t\treturn Result.LOCK_FAILURE;\n@@ -478,6 +493,9 @@ else if (status == Result.FAST_FORWARD)\n \t\t\t\tmsg += \": fast forward\";\n \t\t\telse if (status == Result.NEW)\n \t\t\t\tmsg += \": created\";\n+\t\t\telse if (status == Result.RENAMED) {\n+\t\t\t\t// don't amend the message here\n+\t\t\t}\n \t\t}\n \t\tif (msg != null)\n \t\t\tRefLogWriter.append(this, msg);\n@@ -496,7 +514,7 @@ abstract Result store(final LockFile lock, final Result status)\n \t\t\t\tthrows IOException;\n \t}\n \n-\tprivate class UpdateStore extends Store {\n+\tclass UpdateStore extends Store {\n \n \t\t@Override\n \t\tResult store(final LockFile lock, final Result status)\n@@ -505,7 +523,7 @@ Result store(final LockFile lock, final Result status)\n \t\t}\n \t}\n \n-\tprivate class DeleteStore extends Store {\n+\tclass DeleteStore extends Store {\n \n \t\t@Override\n \t\tResult store(LockFile lock, Result status) throws IOException {\n@@ -526,28 +544,42 @@ Result store(LockFile lock, Result status) throws IOException {\n \t\t\tlock.unlock();\n \t\t\tif (storage.isLoose())\n \t\t\t\tdeleteFileAndEmptyDir(looseFile, levels);\n+\t\t\tdb.uncacheRef(ref.getName());\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\tif (file.isFile()) {\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\tFile dir = file.getParentFile();\n+\t\t\t\tfor  (int i = 0; i < depth; ++i) {\n+\t\t\t\t\tif (!dir.delete())\n+\t\t\t\t\t\tbreak; // ignore problem here\n+\t\t\t\t\tdir = dir.getParentFile();\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n+\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+\tUpdateStore newUpdateStore() {\n+\t\treturn new UpdateStore();\n+\t}\n+\n+\tDeleteStore newDeleteStore() {\n+\t\treturn new DeleteStore();\n+\t}\n+\n+\tstatic void deleteEmptyDir(File dir, int depth) {\n+\t\tfor (; depth > 0 && dir != null; depth--) {\n+\t\t\tif (dir.exists() && !dir.delete())\n+\t\t\t\tbreak;\n+\t\t\tdir = dir.getParentFile();\n \t\t}\n \t}\n \n-\tprivate static int count(final String s, final char c) {\n+\tstatic 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++;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 55dae27..06e0c7b 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -468,6 +468,22 @@ public RefUpdate updateRef(final String ref) throws IOException {\n \t}\n \n \t/**\n+\t * Create a command to rename a ref in this repository\n+\t *\n+\t * @param fromRef\n+\t *            name of ref to rename from\n+\t * @param toRef\n+\t *            name of ref to rename to\n+\t * @return an update command that knows how to rename a branch to another.\n+\t * @throws IOException\n+\t *             the rename could not be performed.\n+\t *\n+\t */\n+\tpublic RefRename renameRef(final String fromRef, final String toRef) throws IOException {\n+\t\treturn refs.newRename(fromRef, toRef);\n+\t}\n+\n+\t/**\n \t * Parse a git revision string and return an object id.\n \t *\n \t * Currently supported is combinations of these.\n-- \n1.6.3.dirty\n"},{"id":"114852","messageId":"1243462137-24133-10-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-9-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 09/10] Add ref rename support to the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:56Z","receivedAt":"2009-05-27T22:08:56Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |   12 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |  106 ++++++++++++++++----\n .../src/org/spearce/egit/ui/uitext.properties      |    5 +\n 3 files changed, 103 insertions(+), 20 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 654e155..aa31a93 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -893,6 +893,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRefresh;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\n@@ -929,6 +935,12 @@\n \tpublic static String BranchSelectionDialog_QuestionNewBranchTitle;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_QuestionNewBranchNameTitle;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_QuestionNewBranchNameMessage;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_QuestionNewBranchMessage;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 9aad95b..28105af 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -48,8 +48,11 @@\n import org.spearce.egit.ui.UIText;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.RefRename;\n import org.spearce.jgit.lib.RefUpdate;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n \n /**\n  * The branch and reset selection dialog\n@@ -277,6 +280,30 @@ private void refNameFromDialog() {\n \t\t}\n \t}\n \n+\tprivate InputDialog getRefNameInputDialog(String prompt) {\n+\t\tInputDialog labelDialog = new InputDialog(\n+\t\t\t\tgetShell(),\n+\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n+\t\t\t\tprompt,\n+\t\t\t\tnull, new IInputValidator() {\n+\t\t\t\t\tpublic String isValid(String newText) {\n+\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n+\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n+\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n+\t\t\t\t\t\treturn null;\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\tlabelDialog.setBlockOnOpen(true);\n+\t\treturn labelDialog;\n+\t}\n+\n \t@Override\n \tprotected void createButtonsForButtonBar(Composite parent) {\n \t\tif (!showResetType) {\n@@ -284,43 +311,81 @@ protected void createButtonsForButtonBar(Composite parent) {\n \t\t\tnewButton.setFont(JFaceResources.getDialogFont());\n \t\t\tnewButton.setText(UIText.BranchSelectionDialog_NewBranch);\n \t\t\t((GridLayout)parent.getLayout()).numColumns++;\n+\t\t\tButton renameButton = new Button(parent, SWT.PUSH);\n+\t\t\trenameButton.setText(\"&Rename\");\n+\t\t\trenameButton.addSelectionListener(new SelectionListener() {\n+\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t// check what ref name the user selected, if any.\n+\t\t\t\t\trefNameFromDialog();\n+\n+\t\t\t\t\tString branchName;\n+\t\t\t\t\tif (refName.startsWith(Constants.R_HEADS))\n+\t\t\t\t\t\tbranchName = refName.substring(Constants.R_HEADS.length());\n+\t\t\t\t\telse\n+\t\t\t\t\t\tbranchName = refName;\t\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(NLS\n+\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchNameMessage,\n+\t\t\t\t\t\t\t\t\tbranchName));\n+\t\t\t\t\tif (labelDialog.open() == Window.OK) {\n+\t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n+\t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n+\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n+\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n+\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n+\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t// FIXME: Update HEAD\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tbranchTree.removeAll();\n+\t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n+\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n+\t\t\t\t\twidgetSelected(e);\n+\t\t\t\t}\n+\t\t\t});\n \t\t\tnewButton.addSelectionListener(new SelectionListener() {\n \n \t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t// check what ref name the user selected, if any.\n \t\t\t\t\trefNameFromDialog();\n \n-\t\t\t\t\tInputDialog labelDialog = new InputDialog(\n-\t\t\t\t\t\t\tgetShell(),\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchMessage,\n-\t\t\t\t\t\t\tnull, new IInputValidator() {\n-\t\t\t\t\t\t\t\tpublic String isValid(String newText) {\n-\t\t\t\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n-\t\t\t\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n-\t\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n-\t\t\t\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n-\t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n-\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n-\t\t\t\t\t\t\t\t\treturn null;\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t});\n-\t\t\t\t\tlabelDialog.setBlockOnOpen(true);\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(UIText.BranchSelectionDialog_QuestionNewBranchNameMessage);\n \t\t\t\t\tif (labelDialog.open() == Window.OK) {\n \t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n \t\t\t\t\t\tRefUpdate updateRef;\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tupdateRef = repo.updateRef(newRefName);\n+\t\t\t\t\t\t\tRef startRef = repo.getRef(refName);\n \t\t\t\t\t\t\tObjectId startAt;\n \t\t\t\t\t\t\tif (refName == null)\n \t\t\t\t\t\t\t\tstartAt = repo.resolve(Constants.HEAD);\n \t\t\t\t\t\t\telse\n \t\t\t\t\t\t\t\tstartAt = repo.resolve(refName);\n+\t\t\t\t\t\t\tString startBranch;\n+\t\t\t\t\t\t\tif (startRef != null)\n+\t\t\t\t\t\t\t\tstartBranch = refName;\n+\t\t\t\t\t\t\telse\n+\t\t\t\t\t\t\t\tstartBranch = startAt.name();\n+\t\t\t\t\t\t\tstartBranch = repo.shortenRefName(startBranch);\n \t\t\t\t\t\t\tupdateRef.setNewObjectId(startAt);\n+\t\t\t\t\t\t\tupdateRef.setRefLogMessage(\"branch: Created from \" + startBranch, false);\n \t\t\t\t\t\t\tupdateRef.update();\n \t\t\t\t\t\t} catch (IOException e1) {\n \t\t\t\t\t\t\tActivator.logError(NLS.bind(\n@@ -338,6 +403,7 @@ public String isValid(String newText) {\n \t\t\t\t\t}\n \t\t\t\t}\n \n+\n \t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n \t\t\t\t\twidgetSelected(e);\n \t\t\t\t}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 1d21c81..f19db60 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -340,6 +340,9 @@ BranchSelectionDialog_ErrorCouldNotRefresh=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotRefreshBranchList=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotResolve=Could not attempt to resolve {0}\n BranchSelectionDialog_ErrorInvalidRefName=Invalid ref name\n+BranchSelectionDialog_ErrorCouldNotRenameRef=Failed to rename branch {0} -> {1}, status={2}\n+BranchSelectionDialog_ErrorCouldNotRenameRef2=Failed to rename branch {0} -> {1}\n+\n BranchSelectionDialog_LocalBranches=Local Branches\n BranchSelectionDialog_NewBranch=&New branch\n BranchSelectionDialog_NoBranchSeletectMessage=You must select a valid ref.\n@@ -347,7 +350,9 @@ BranchSelectionDialog_NoBranchSeletectTitle=No branch/tag selected\n BranchSelectionDialog_OkCheckout=&Checkout\n BranchSelectionDialog_OkReset=&Reset\n BranchSelectionDialog_QuestionNewBranchMessage=Enter name of new branch. It will branch from the selected branch. refs/heads/ will be prepended to the name you type\n+BranchSelectionDialog_QuestionNewBranchNameMessage=Enter new name of the {0} branch. refs/heads/ will be prepended to the name you type\n BranchSelectionDialog_QuestionNewBranchTitle=New branch\n+BranchSelectionDialog_QuestionNewBranchNameTitle=Rename branch\n BranchSelectionDialog_ReallyResetMessage=Resetting will overwrite any changes in your working directory.\\n\\nDo you wish to continue?\n BranchSelectionDialog_ReallyResetTitle=Really reset?\n BranchSelectionDialog_RemoteBranches=Remote Branches\n-- \n1.6.3.dirty\n"},{"id":"114856","messageId":"1243462137-24133-11-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1243462137-24133-10-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH 10/10] Improve error reporting in the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-05-27T22:08:57Z","receivedAt":"2009-05-27T22:08:57Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    6 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |   59 ++++++++++++--------\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +\n 3 files changed, 43 insertions(+), 24 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex aa31a93..c120327 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -899,6 +899,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 28105af..43588ae 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -104,7 +104,7 @@ getShell().setText(\n \n \t\ttry {\n \t\t\tfillTreeWithBranches(null);\n-\t\t} catch (IOException e) {\n+\t\t} catch (Throwable e) {\n \t\t\tActivator.logError(UIText.BranchSelectionDialog_ErrorCouldNotRefresh, e);\n \t\t}\n \n@@ -294,6 +294,7 @@ public String isValid(String newText) {\n \t\t\t\t\t\t} catch (IOException e1) {\n \t\t\t\t\t\t\tActivator.logError(NLS.bind(\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t\treturn e1.getMessage();\n \t\t\t\t\t\t}\n \t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n \t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n@@ -332,27 +333,28 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n \t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n-\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n-\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n-\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n-\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\t\tnull,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\trefName, newRefName, renameRef\n+\t\t\t\t\t\t\t\t\t\t\t\t.getResult());\n \t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t// FIXME: Update HEAD\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\trefName, newRefName, e1.getMessage());\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -387,18 +389,20 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\t\tupdateRef.setNewObjectId(startAt);\n \t\t\t\t\t\t\tupdateRef.setRefLogMessage(\"branch: Created from \" + startBranch, false);\n \t\t\t\t\t\t\tupdateRef.update();\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotCreateNewRef,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t\t\t\tnewRefName);\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(e1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -419,4 +423,11 @@ createButton(parent, IDialogConstants.OK_ID,\n \tprotected int getShellStyle() {\n \t\treturn super.getShellStyle() | SWT.RESIZE;\n \t}\n+\n+\tprivate void reportError(Throwable e, String title, String message,\n+\t\t\tObject... args) {\n+\t\tString msg = NLS.bind(message, args);\n+\t\tMessageDialog.openError(getShell(), title, msg);\n+\t\tActivator.logError(msg, e);\n+\t}\n }\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex f19db60..93ce3c9 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -333,6 +333,8 @@ WindowCachePreferencePage_packedGitMMAP=Use virtual memory mapping\n \n BranchSelectionDialog_TitleCheckout=Checkout: {0}\n BranchSelectionDialog_TitleReset=Reset: {0}\n+BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle=New branch creation failed\n+BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle=Rename failed\n BranchSelectionDialog_BranchSuffix_Current=\\ (current)\n BranchSelectionDialog_ErrorAlreadyExists=Already exists\n BranchSelectionDialog_ErrorCouldNotCreateNewRef=Could not create new ref {0}\n-- \n1.6.3.dirty\n"},{"id":"115348","messageId":"20090603154129.GG3355@spearce.org","threadId":"19215","inReplyTo":"1243462137-24133-6-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-06-03T15:41:29Z","receivedAt":"2009-06-03T15:41:29Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> Do not write to the reflog unless the refupdate logmessage is set\n\nWhy not?  What is the justification for this?  Isn't a reflog record\nstill useful, at least to point out that something happened at this\npoint in time, and here's the old/new values?\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java\n> index a9ab73b..79d9f2d 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> @@ -479,7 +479,8 @@ else if (status == Result.FAST_FORWARD)\n>  \t\t\telse if (status == Result.NEW)\n>  \t\t\t\tmsg += \": created\";\n>  \t\t}\n> -\t\tRefLogWriter.append(this, msg);\n> +\t\tif (msg != null)\n> +\t\t\tRefLogWriter.append(this, msg);\n>  \t\tif (!lock.commit())\n>  \t\t\treturn Result.LOCK_FAILURE;\n>  \t\tdb.stored(this.ref.getOrigName(),  ref.getName(), newValue, lock.getCommitLastModified());\n\n-- \nShawn.\n"},{"id":"115352","messageId":"20090603164330.GI3355@spearce.org","threadId":"19215","inReplyTo":"1243462137-24133-9-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH 08/10] Add ref rename support to JGit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-06-03T16:43:30Z","receivedAt":"2009-06-03T16:43:30Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> Now refs can be renamed. The intent is that should be safe. Only the named\n> refs and associated logs are updated. Any symbolic refs referring to the renames\n> branches are unaffected, except HEAD, which is updated if the branch it refers\n> to is being renamed.\n...\n> +//\tpublic void testRenameBranchCannotLockAFileHEADisToLockHead() throws IOException {\n> +//\t\tan example following the rename failure pattern, but this one is ok!\n> +//\t\ttryRenameWhenLocked(\"HEAD\", \"refs/heads/b\", \"refs/heads/new/name\",\n> +//\t\t\t\t\"refs/heads/new/name\");\n> +//\t}\n\nCommented out test case?  Eh?\n\n> +//\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockHead() throws IOException {\n> +//\t\tan example following the rename failure pattern, but this one is ok!\n> +//\t\ttryRenameWhenLocked(\"HEAD\", \"refs/heads/b\", \"refs/heads/new/name\",\n> +//\t\t\t\t\"refs/heads/a\");\n> +//\t}\n\nDitto.\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n...\n> +\tRefRename newRename(String fromRef, String toRef) throws IOException {\n> +\t\trefreshPackedRefs();\n> +\t\tRef f = readRefBasic(fromRef, 0);\n> +\t\tRef t = readRefBasic(toRef, 0);\n> +\t\tif (t != null)\n> +\t\t\tthrow new IOException(\"Ref rename target exists: \" + t.getName());\n\nNAK, I'd prefer to have the RefRename return successfully,\nbut when we try to execute it, it has a result that fails with\nRefUpdate.REJECTED.\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n...\n> +\tstatic void renameTo(final Repository db, final RefUpdate from,\n> +\t\t\tfinal RefUpdate to) throws IOException {\n> +\t\tfinal File logdir = new File(db.getDirectory(), Constants.LOGS);\n> +\t\tfinal File reflogFrom = new File(logdir, from.getName());\n> +\t\tif (!reflogFrom.exists())\n> +\t\t\treturn;\n> +\t\tfinal File reflogTo = new File(logdir, to.getName());\n> +\t\tfinal File reflogToDir = reflogTo.getParentFile();\n> +\t\tFile tmp = new File(logdir,\"tmp-renamed-log\");\n> +\t\ttmp.delete(); // if any exists\n\nThat's not thread safe.  Most of the rest of JGit is actually\nthread safe.  I think we should be here too.  We should use\na temporary file name inside logdir, one that isn't a legal\nref name at all.  Maybe:\n\n  File tmp = File.createTempFile(\"tmp..renamed\", \"-log\", logdir);\n  if (!reflogFrom.renameTo(tmp)) {\n    tmp.delete();\n    if (!reflogFrom.renameTo(tmp))\n      throw new IOException....\n  }\n\n> +\t\tif (!reflogToDir.exists() && !reflogToDir.mkdirs()) {\n> +\t\t\tthrow new IOException(\"Cannot create directory \" + reflogToDir);\n> +\t\t}\n> +\t\tif (!tmp.renameTo(reflogTo)) {\n> +\t\t\tthrow new IOException(\"Cannot rename (\" + tmp + \")\" + reflogFrom\n> +\t\t\t\t\t+ \" to \" + reflogTo);\n> +\t\t}\n\nIf we fail here, should we attempt to put the tmp log file back\ninto the old name?  Or leave it orphaned under the tmp file?\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n...\n> +\tpublic Result rename() throws IOException {\n> +\t\tRef oldRef = oldFromDelete.db.readRef(Constants.HEAD);\n> +\t\tboolean renameHEADtoo = oldRef != null\n> +\t\t\t\t&& oldRef.getName().equals(oldFromDelete.getName());\n> +\t\ttry {\n> +\t\t\tRepository db = oldFromDelete.getRepository();\n\nStyle nit: Hoist to top of method, so oldRef init can use it.\n\n> +\t\t\tRefLogWriter.renameTo(db, oldFromDelete,\n> +\t\t\t\t\tnewToUpdate);\n\nStyle nit: This fits on one line.\n\n> +\t\t\tnewToUpdate.setRefLogMessage(null, false);\n> +\t\t\tRefUpdate tmpUpdateRef = oldFromDelete.db.getRepository().updateRef(\n> +\t\t\t\t\t\"RENAMED-REF\");\n\nLike the log, this isn't thread safe.\n\n> +\t\t\tif (renameHEADtoo) {\n> +\t\t\t\ttry {\n> +\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, \"RENAMED-REF\");\n> +\t\t\t\t} catch (IOException e) {\n> +\t\t\t\t\tRefLogWriter.renameTo(db,\n> +\t\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n> +\t\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n> +\t\t\t\t}\n> +\t\t\t}\n> +\t\t\ttmpUpdateRef.setNewObjectId(oldFromDelete.getOldObjectId());\n\nWe should also do:\n\n  oldFromDelete.setExpectedOldObjectId(oldFromDelete.getOldObjectId())\n\nso that if the old ref was modified between the time we read it\nand the time we delete it, we fail on the delete, and don't whack\na ref that was modified by another thread.\n\n> +\t\t\tRefLogWriter.append(this, \"jgit branch: renamed \"\n\nI think this should just be \"renamed: $old to $new\".\n\n> +\t\t\t\t\t+ db.shortenRefName(oldFromDelete.getName()) + \" to \"\n> +\t\t\t\t\t+ db.shortenRefName(newToUpdate.getName()));\n> +\t\t\treturn renameResult = Result.RENAMED;\n> +\t\t} catch (RuntimeException e) {\n> +\t\t\tthrow e;\n\nWhat's the point of this catch block?\n\n> +\t\t} catch (IOException e) {\n> +\t\t\tSystem.err.println(e);\n\nPlease don't print to System.err from such a low level method.\n\n> diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefUpdate.java\n...\n> +\t\t/**\n> +\t\t * The ref was renamed from another name\n> +\t\t * <p>\n> +\t\t */\n> +\t\tRENAMED\n\nIs a new state really necessary?  Can't we use NEW on success?\nAfter all, the dst is now created.\n\n>  \t}\n>  \n>  \t/** Repository the ref is stored in. */\n> -\tprivate final RefDatabase db;\n> +\tfinal RefDatabase db;\n\nI wonder if this shouldn't just be a public accessor method; we expose\ngetRepository() on other application visible objects like RevWalk.\n  \n>  \t/** Location of the loose file holding the value of this ref. */\n> -\tprivate final File looseFile;\n> +\tfinal File looseFile;\n\nI don't see this used outside of the class, is it still necessary?\n\n>  \t/** Result of the update operation. */\n> -\tprivate Result result = Result.NOT_ATTEMPTED;\n> +\tResult result = Result.NOT_ATTEMPTED;\n\nDitto.\n  \n> +\tstatic void deleteEmptyDir(File dir, int depth) {\n> +\t\tfor (; depth > 0 && dir != null; depth--) {\n> +\t\t\tif (dir.exists() && !dir.delete())\n\nWhy bother with a stat() call, just to do an rmdir()?\n\n> -\tprivate static int count(final String s, final char c) {\n> +\tstatic int count(final String s, final char c) {\n\nSince all callers pass '/', maybe we should just change this to\nuse '/' as a constant in the loop and remove the char c parameter?\nI think that would fix the nasty line wrapping in RefLogWriter too.\n\n-- \nShawn.\n"},{"id":"115747","messageId":"200906080027.16338.robin.rosenberg.lists@dewire.com","threadId":"19215","inReplyTo":"20090603154129.GG3355@spearce.org","subject":"Re: [EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-06-07T22:27:16Z","receivedAt":"2009-06-07T22:27:16Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"onsdag 03 juni 2009 17:41:29 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> > Do not write to the reflog unless the refupdate logmessage is set\n> \n> Why not?  What is the justification for this?  Isn't a reflog record\n> still useful, at least to point out that something happened at this\n> point in time, and here's the old/new values?\n\nI need to do an update without logging to avoid a \"dummy\" entry in the\nreflog since I use the RefUpdate mechanism. Since this is an API and\nnot and end user function, I was thinking this might be ok after all.\n\nSome other optione are:\n1) set a special flag.\n2) set a default message and allow the caller to set it to null and\nnot write if null. Today the default message is null leading to the\nmessage \"null\" in the log, which is kind of ugly so we should\nprobably change this in any case.\n\n-- robin\n"},{"id":"115750","messageId":"20090607224446.GE16497@spearce.org","threadId":"19215","inReplyTo":"200906080027.16338.robin.rosenberg.lists@dewire.com","subject":"Re: [EGIT PATCH 05/10] Do not write to the reflog unless the refupdate logmessage is set","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-06-07T22:44:46Z","receivedAt":"2009-06-07T22:44:46Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg.lists@dewire.com> wrote:\n> onsdag 03 juni 2009 17:41:29 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> > Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> > > Do not write to the reflog unless the refupdate logmessage is set\n> > \n> > Why not?  What is the justification for this?  Isn't a reflog record\n> > still useful, at least to point out that something happened at this\n> > point in time, and here's the old/new values?\n> \n> I need to do an update without logging to avoid a \"dummy\" entry in the\n> reflog since I use the RefUpdate mechanism. Since this is an API and\n> not and end user function, I was thinking this might be ok after all.\n\nACK, I agree.  How about this then?\n \n--8<--\nFrom: Robin Rosenberg <robin.rosenberg@dewire.com>\nSubject: [PATCH] Do not write to the reflog if refLogMessage is null\n\nThis permits micro-update steps (or otherwise uninteresting states)\nto be skipped in the reflog.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |    1 +\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   53 +++++++++++++++----\n 2 files changed, 43 insertions(+), 11 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 6b1975a..84653c8 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@@ -126,6 +126,7 @@ public void testDeleteLooseAndItsDirectory() throws IOException {\n \t\tRefUpdate updateRef = db.updateRef(\"refs/heads/z/c\");\n \t\tupdateRef.setNewObjectId(pid);\n \t\tupdateRef.setForceUpdate(true);\n+\t\tupdateRef.setRefLogMessage(\"new test ref\", false);\n \t\tResult update = updateRef.update();\n \t\tassertEquals(Result.NEW, update); // internal\n \t\tassertTrue(new File(db.getDirectory(), Constants.R_HEADS + \"z\")\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 a9ab73b..17fe3be 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@@ -165,6 +165,7 @@ RefUpdate(final RefDatabase r, final Ref ref, final File f) {\n \t\tthis.ref = ref;\n \t\toldValue = ref.getObjectId();\n \t\tlooseFile = f;\n+\t\trefLogMessage = \"\";\n \t}\n \n \t/** @return the repository the updated ref resides in */\n@@ -264,7 +265,8 @@ public void setRefLogIdent(final PersonIdent pi) {\n \t/**\n \t * Get the message to include in the reflog.\n \t * \n-\t * @return message the caller wants to include in the reflog.\n+\t * @return message the caller wants to include in the reflog; null if the\n+\t *         update should not be logged.\n \t */\n \tpublic String getRefLogMessage() {\n \t\treturn refLogMessage;\n@@ -281,8 +283,21 @@ public String getRefLogMessage() {\n \t *            message.\n \t */\n \tpublic void setRefLogMessage(final String msg, final boolean appendStatus) {\n-\t\trefLogMessage = msg;\n-\t\trefLogIncludeResult = appendStatus;\n+\t\tif (msg == null && !appendStatus)\n+\t\t\tdisableRefLog();\n+\t\telse if (msg == null && appendStatus) {\n+\t\t\trefLogMessage = \"\";\n+\t\t\trefLogIncludeResult = true;\n+\t\t} else {\n+\t\t\trefLogMessage = msg;\n+\t\t\trefLogIncludeResult = appendStatus;\n+\t\t}\n+\t}\n+\n+\t/** Don't record this update in the ref's associated reflog. */\n+\tpublic void disableRefLog() {\n+\t\trefLogMessage = null;\n+\t\trefLogIncludeResult = false;\n \t}\n \n \t/**\n@@ -471,21 +486,37 @@ private Result updateStore(final LockFile lock, final Result status)\n \t\tlock.setNeedStatInformation(true);\n \t\tlock.write(newValue);\n \t\tString msg = getRefLogMessage();\n-\t\tif (msg != null && refLogIncludeResult) {\n-\t\t\tif (status == Result.FORCED)\n-\t\t\t\tmsg += \": forced-update\";\n-\t\t\telse if (status == Result.FAST_FORWARD)\n-\t\t\t\tmsg += \": fast forward\";\n-\t\t\telse if (status == Result.NEW)\n-\t\t\t\tmsg += \": created\";\n+\t\tif (msg != null) {\n+\t\t\tif (refLogIncludeResult) {\n+\t\t\t\tString strResult = toResultString(status);\n+\t\t\t\tif (strResult != null) {\n+\t\t\t\t\tif (msg.length() > 0)\n+\t\t\t\t\t\tmsg = msg + \": \" + strResult;\n+\t\t\t\t\telse\n+\t\t\t\t\t\tmsg = strResult;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tRefLogWriter.append(this, msg);\n \t\t}\n-\t\tRefLogWriter.append(this, msg);\n \t\tif (!lock.commit())\n \t\t\treturn Result.LOCK_FAILURE;\n \t\tdb.stored(this.ref.getOrigName(),  ref.getName(), newValue, lock.getCommitLastModified());\n \t\treturn status;\n \t}\n \n+\tprivate static String toResultString(final Result status) {\n+\t\tswitch (status) {\n+\t\tcase FORCED:\n+\t\t\treturn \"forced-update\";\n+\t\tcase FAST_FORWARD:\n+\t\t\treturn \"fast forward\";\n+\t\tcase NEW:\n+\t\t\treturn \"created\";\n+\t\tdefault:\n+\t\t\treturn null;\n+\t\t}\n+\t}\n+\n \t/**\n \t * Handle the abstraction of storing a ref update. This is because both\n \t * updating and deleting of a ref have merge testing in common.\n-- \n1.6.3.2.322.g117de\n\n-- \nShawn.\n"},{"id":"116037","messageId":"1244668945-12622-1-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1241652781-16873-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 0/7] Ref rename support again","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:18Z","receivedAt":"2009-06-10T21:22:18Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"More tests, leading to fixes of minor bugs, plus corrections\naccording to Shawn's comments.\n\n-- robin\n\nRobin Rosenberg (6):\n  Add methods to RawParseUtils for scanning backwards.\n  Add a ref log reader class\n  Add ref rename support to JGit\n  Add ref rename support to the branch dialog\n  Improve error reporting in the branch dialog\n  Remove a TAB from the message Egit generates into the reflog on\n    commit\n\nShawn O. Pearce (1):\n  Do not write to the reflog unless the refupdate logmessage is set\n\n .../src/org/spearce/egit/ui/UIText.java            |   18 +\n .../egit/ui/internal/actions/CommitAction.java     |    2 +-\n .../ui/internal/dialogs/BranchSelectionDialog.java |  133 ++++++--\n .../src/org/spearce/egit/ui/uitext.properties      |    7 +\n .../org/spearce/jgit/test/resources/packed-refs    |    1 +\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  348 +++++++++++++++++++-\n .../tst/org/spearce/jgit/lib/ReflogReaderTest.java |  166 ++++++++++\n .../org/spearce/jgit/lib/RepositoryTestCase.java   |   18 +\n .../org/spearce/jgit/transport/TransportTest.java  |    2 +-\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   31 ++\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   33 ++-\n .../src/org/spearce/jgit/lib/RefRename.java        |  163 +++++++++\n .../src/org/spearce/jgit/lib/RefUpdate.java        |  115 +++++--\n .../src/org/spearce/jgit/lib/ReflogReader.java     |  187 +++++++++++\n .../src/org/spearce/jgit/lib/Repository.java       |   30 ++\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   84 +++++-\n 16 files changed, 1269 insertions(+), 69 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n\n\nShort log of my patch queue before rebase:\n\nMine\n      Add methods to RawParseUtils for scanning backwards.\n      Add a ref log reader class\n      Do not write to the reflog unless the refupdate logmessage is set\n      Revert \"Do not write to the reflog unless the refupdate logmessage is set\"\n      Add ref rename support to JGit\n      Add ref rename support to the branch dialog\n      Improve error reporting in the branch dialog\n      Shawns comments, except the no-reflog update\n      Remove a TAB from the message Egit generated into the reflog on commit\n      Follow C Git's format for renames in the reflog\n      Improve refupdate comment\n      ..ReflogReader\n      Move reflog creation to testcase base class\n      ..rename tests\n\nShawn:\n      Do not write to the reflog unless the refupdate logmessage is set\n"},{"id":"116034","messageId":"1244668945-12622-2-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1244668945-12622-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 1/7] Add methods to RawParseUtils for scanning backwards.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:19Z","receivedAt":"2009-06-10T21:22:19Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/jgit/util/RawParseUtils.java   |   84 +++++++++++++++++++-\n 1 files changed, 83 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\nindex 79ebe41..bdd6a11 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/util/RawParseUtils.java\n@@ -321,6 +321,67 @@ public static final int nextLF(final byte[] b, int ptr, final char chrA) {\n \t}\n \n \t/**\n+\t * Locate the first position before a given character.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for chrA at.\n+\t * @param chrA\n+\t *            character to find.\n+\t * @return new position just before chrA, -1 for not found\n+\t */\n+\tpublic static final int prev(final byte[] b, int ptr, final char chrA) {\n+\t\tif (ptr == b.length)\n+\t\t\t--ptr;\n+\t\twhile (ptr >= 0) {\n+\t\t\tif (b[ptr--] == chrA)\n+\t\t\t\treturn ptr;\n+\t\t}\n+\t\treturn ptr;\n+\t}\n+\n+\t/**\n+\t * Locate the first position before the previous LF.\n+\t * <p>\n+\t * This method stops on the first '\\n' it finds.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for LF at.\n+\t * @return new position just before the first LF found, -1 for not found\n+\t */\n+\tpublic static final int prevLF(final byte[] b, int ptr) {\n+\t\treturn prev(b, ptr, '\\n');\n+\t}\n+\n+\t/**\n+\t * Locate the previous position before either the given character or LF.\n+\t * <p>\n+\t * This method stops on the first match it finds from either chrA or '\\n'.\n+\t * \n+\t * @param b\n+\t *            buffer to scan.\n+\t * @param ptr\n+\t *            position within buffer to start looking for chrA or LF at.\n+\t * @param chrA\n+\t *            character to find.\n+\t * @return new position just before the first chrA or LF to be found, -1 for\n+\t *         not found\n+\t */\n+\tpublic static final int prevLF(final byte[] b, int ptr, final char chrA) {\n+\t\tif (ptr == b.length)\n+\t\t\t--ptr;\n+\t\twhile (ptr >= 0) {\n+\t\t\tfinal byte c = b[ptr--];\n+\t\t\tif (c == chrA || c == '\\n')\n+\t\t\t\treturn ptr;\n+\t\t}\n+\t\treturn ptr;\n+\t}\n+\n+\t/**\n \t * Index the region between <code>[ptr, end)</code> to find line starts.\n \t * <p>\n \t * The returned list is 1 indexed. Index 0 contains\n@@ -519,7 +580,28 @@ public static PersonIdent parsePersonIdent(final byte[] raw, final int nameB) {\n \t *         after decoding the region through the specified character set.\n \t */\n \tpublic static String decode(final byte[] buffer) {\n-\t\treturn decode(Constants.CHARSET, buffer, 0, buffer.length);\n+\t\treturn decode(buffer, 0, buffer.length);\n+\t}\n+\n+\t/**\n+\t * Decode a buffer under UTF-8, if possible.\n+\t * \n+\t * If the byte stream cannot be decoded that way, the platform default is\n+\t * tried and if that too fails, the fail-safe ISO-8859-1 encoding is tried.\n+\t * \n+\t * @param buffer\n+\t *            buffer to pull raw bytes from.\n+\t * @param start\n+\t *            start position in buffer\n+\t * @param end\n+\t *            one position past the last location within the buffer to take\n+\t *            data from.\n+\t * @return a string representation of the range <code>[start,end)</code>,\n+\t *         after decoding the region through the specified character set.\n+\t */\n+\tpublic static String decode(final byte[] buffer, final int start,\n+\t\t\tfinal int end) {\n+\t\treturn decode(Constants.CHARSET, buffer, start, end);\n \t}\n \n \t/**\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116036","messageId":"1244668945-12622-3-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1244668945-12622-2-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 2/7] Add a ref log reader class","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:20Z","receivedAt":"2009-06-10T21:22:20Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"This reader allows the caller to the reflog entries for a given ref.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../tst/org/spearce/jgit/lib/ReflogReaderTest.java |  166 +++++++++++++++++\n .../org/spearce/jgit/lib/RepositoryTestCase.java   |   18 ++\n .../src/org/spearce/jgit/lib/ReflogReader.java     |  187 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/Repository.java       |   14 ++\n 4 files changed, 385 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\nnew file mode 100644\nindex 0000000..2888da6\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ReflogReaderTest.java\n@@ -0,0 +1,166 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.text.SimpleDateFormat;\n+import java.util.Date;\n+import java.util.List;\n+\n+import org.spearce.jgit.lib.ReflogReader.Entry;\n+\n+public class ReflogReaderTest extends RepositoryTestCase {\n+\n+\tstatic byte[] oneLine = \"da85355dfc525c9f6f3927b876f379f46ccf826e 3e7549db262d1e836d9bf0af7e22355468f1717c Robin Rosenberg <robin.rosenberg@dewire.com> 1243028200 +0200\\tcommit: Add a toString for debugging to RemoteRefUpdate\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] twoLine = (\"0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: Created from rr/renamebranchv4\\n\"\n+\t\t\t+ \"c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\\n\")\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] twoLineWithAppendInProgress = (\"0000000000000000000000000000000000000000 c6734895958052a9dbc396cff4459dc1a25029ab A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: Created from rr/renamebranchv4\\n\"\n+\t\t\t+ \"c6734895958052a9dbc396cff4459dc1a25029ab 54794942a18a237c57a80719afed44bb78172b10 Same A U Thor <same.author@example.com> 1243028202 +0100\\trebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\\n\"\n+\t\t\t+ \"54794942a18a237c57a80719afed44bb78172b10 \")\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] aLine = \"1111111111111111111111111111111111111111 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to a\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] masterLine = \"2222222222222222222222222222222222222222 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to master\\n\"\n+\t\t\t.getBytes();\n+\n+\tstatic byte[] headLine = \"3333333333333333333333333333333333333333 3e7549db262d1e836d9bf0af7e22355468f1717c A U Thor <thor@committer.au> 1243028201 -0100\\tbranch: change to HEAD\\n\"\n+\t\t\t.getBytes();\n+\n+\tpublic void testReadOneLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", oneLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tEntry e = reader.getLastEntry();\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"da85355dfc525c9f6f3927b876f379f46ccf826e\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"3e7549db262d1e836d9bf0af7e22355468f1717c\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Robin Rosenberg\", e.getWho().getName());\n+\t\tassertEquals(\"robin.rosenberg@dewire.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(120, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:40\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\"commit: Add a toString for debugging to RemoteRefUpdate\",\n+\t\t\t\te.getComment());\n+\t}\n+\n+\tprivate String iso(Date d) {\n+\t\treturn new SimpleDateFormat(\"yyyy-MM-dd'T'HH:mm:ss\").format(d);\n+\t}\n+\n+\tpublic void testReadTwoLine() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", twoLine);\n+\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tList<Entry> reverseEntries = reader.getReverseEntries();\n+\t\tassertEquals(2, reverseEntries.size());\n+\t\tEntry e = reverseEntries.get(0);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"54794942a18a237c57a80719afed44bb78172b10\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Same A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"same.author@example.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:42\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\n+\t\t\t\t\"rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\",\n+\t\t\t\te.getComment());\n+\n+\t\te = reverseEntries.get(1);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"0000000000000000000000000000000000000000\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"thor@committer.au\", e.getWho().getEmailAddress());\n+\t\tassertEquals(-60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:41\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\"branch: Created from rr/renamebranchv4\", e.getComment());\n+\t}\n+\n+\tpublic void testReadWhileAppendIsInProgress() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/master\", twoLineWithAppendInProgress);\n+\t\tReflogReader reader = new ReflogReader(db, \"refs/heads/master\");\n+\t\tList<Entry> reverseEntries = reader.getReverseEntries();\n+\t\tassertEquals(2, reverseEntries.size());\n+\t\tEntry e = reverseEntries.get(0);\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"c6734895958052a9dbc396cff4459dc1a25029ab\"), e\n+\t\t\t\t.getOldId());\n+\t\tassertEquals(ObjectId\n+\t\t\t\t.fromString(\"54794942a18a237c57a80719afed44bb78172b10\"), e\n+\t\t\t\t.getNewId());\n+\t\tassertEquals(\"Same A U Thor\", e.getWho().getName());\n+\t\tassertEquals(\"same.author@example.com\", e.getWho().getEmailAddress());\n+\t\tassertEquals(60, e.getWho().getTimeZoneOffset());\n+\t\tassertEquals(\"2009-05-22T23:36:42\", iso(e.getWho().getWhen()));\n+\t\tassertEquals(\n+\t\t\t\t\"rebase finished: refs/heads/rr/renamebranch5 onto c6e3b9fe2da0293f11eae202ec35fb343191a82d\",\n+\t\t\t\te.getComment());\n+\t\t// while similar to testReadTwoLine, we can assume that if we get the last entry\n+\t\t// right, everything else is too\n+\t}\n+\n+\n+\tpublic void testReadRightLog() throws Exception {\n+\t\tsetupReflog(\"logs/refs/heads/a\", aLine);\n+\t\tsetupReflog(\"logs/refs/heads/master\", masterLine);\n+\t\tsetupReflog(\"logs/HEAD\", headLine);\n+\t\tassertEquals(\"branch: change to master\", db.getReflogReader(\"master\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"branch: change to a\", db.getReflogReader(\"a\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"branch: change to HEAD\", db.getReflogReader(\"HEAD\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t}\n+\n+\tpublic void testNoLog() throws Exception {\n+\t\tassertEquals(0, db.getReflogReader(\"master\").getReverseEntries().size());\n+\t\tassertNull(db.getReflogReader(\"master\").getLastEntry());\n+\t}\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\nindex 3b03ac1..3837ea9 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/RepositoryTestCase.java\n@@ -40,6 +40,7 @@\n \n import java.io.File;\n import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n import java.io.FileOutputStream;\n import java.io.IOException;\n import java.io.InputStreamReader;\n@@ -348,4 +349,21 @@ public void run() {\n \t\treturn newRepo;\n \t}\n \n+\tprotected void setupReflog(String logName, byte[] data)\n+\t\t\tthrows FileNotFoundException, IOException {\n+\t\t\t\tFile logfile = new File(db.getDirectory(), logName);\n+\t\t\t\tif (!logfile.getParentFile().mkdirs()\n+\t\t\t\t\t\t&& !logfile.getParentFile().isDirectory()) {\n+\t\t\t\t\tthrow new IOException(\n+\t\t\t\t\t\t\t\"oops, cannot create the directory for the test reflog file\"\n+\t\t\t\t\t\t\t\t\t+ logfile);\n+\t\t\t\t}\n+\t\t\t\tFileOutputStream fileOutputStream = new FileOutputStream(logfile);\n+\t\t\t\ttry {\n+\t\t\t\t\tfileOutputStream.write(data);\n+\t\t\t\t} finally {\n+\t\t\t\t\tfileOutputStream.close();\n+\t\t\t\t}\n+\t\t\t}\n+\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\nnew file mode 100644\nindex 0000000..223347d\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ReflogReader.java\n@@ -0,0 +1,187 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.List;\n+\n+import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n+\n+/**\n+ * Utility for reading reflog entries\n+ */\n+public class ReflogReader {\n+\t/**\n+\t * Parsed reflog entry\n+\t */\n+\tstatic public class Entry {\n+\t\tprivate ObjectId oldId;\n+\n+\t\tprivate ObjectId newId;\n+\n+\t\tprivate PersonIdent who;\n+\n+\t\tprivate String comment;\n+\n+\t\tEntry(byte[] raw, int pos) {\n+\t\t\toldId = ObjectId.fromString(raw, pos);\n+\t\t\tpos += Constants.OBJECT_ID_LENGTH * 2;\n+\t\t\tif (raw[pos++] != ' ')\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\tnewId = ObjectId.fromString(raw, pos);\n+\t\t\tpos += Constants.OBJECT_ID_LENGTH * 2;\n+\t\t\tif (raw[pos++] != ' ') {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\twho = RawParseUtils.parsePersonIdent(raw, pos);\n+\t\t\tint p0 = RawParseUtils.next(raw, pos, '\\t'); // personident has no\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\t// \\t\n+\t\t\tif (p0 == -1) {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\tint p1 = RawParseUtils.nextLF(raw, p0);\n+\t\t\tif (p1 == -1) {\n+\t\t\t\tthrow new IllegalArgumentException(\n+\t\t\t\t\t\t\"Raw log message does not parse as log entry\");\n+\t\t\t}\n+\t\t\tcomment = RawParseUtils.decode(raw, p0, p1 - 1);\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id before the change\n+\t\t */\n+\t\tpublic ObjectId getOldId() {\n+\t\t\treturn oldId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return the commit id after the change\n+\t\t */\n+\t\tpublic ObjectId getNewId() {\n+\t\t\treturn newId;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return user performin the change\n+\t\t */\n+\t\tpublic PersonIdent getWho() {\n+\t\t\treturn who;\n+\t\t}\n+\n+\t\t/**\n+\t\t * @return textual description of the change\n+\t\t */\n+\t\tpublic String getComment() {\n+\t\t\treturn comment;\n+\t\t}\n+\t\t\n+\t\t@Override\n+\t\tpublic String toString() {\n+\t\t\treturn \"Entry[\" + oldId.name() + \", \" + newId.name() + \", \" + getWho() + \", \"\n+\t\t\t\t\t+ getComment() + \"]\";\n+\t\t}\n+\t}\n+\n+\tprivate File logName;\n+\n+\tReflogReader(Repository db, String refname) {\n+\t\tlogName = new File(db.getDirectory(), \"logs/\" + refname);\n+\t}\n+\n+\t/**\n+\t * Get the last entry in the reflog\n+\t * \n+\t * @return the latest reflog entry, or null if no log\n+\t * @throws IOException\n+\t */\n+\tpublic Entry getLastEntry() throws IOException {\n+\t\tList<Entry> entries = getReverseEntries(1);\n+\t\treturn entries.size() > 0 ? entries.get(0) : null;\n+\t}\n+\n+\t/**\n+\t * @return all reflog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries() throws IOException {\n+\t\treturn getReverseEntries(Integer.MAX_VALUE);\n+\t}\n+\n+\t/**\n+\t * @param max\n+\t *            max numer of entries to read\n+\t * @return all reflog entries in reverse order\n+\t * @throws IOException\n+\t */\n+\tpublic List<Entry> getReverseEntries(int max) throws IOException {\n+\t\tFileInputStream fileInputStream;\n+\t\ttry {\n+\t\t\tfileInputStream = new FileInputStream(logName);\n+\t\t} catch (FileNotFoundException e) {\n+\t\t\treturn Collections.emptyList();\n+\t\t}\n+\t\ttry {\n+\t\t\tlong logSize = fileInputStream.getChannel().size();\n+\t\t\tif (logSize > Integer.MAX_VALUE) {\n+\t\t\t\t// implementation limit, will suck with smaller files too\n+\t\t\t\tthrow new IOException(\"Cannot handle reflog larger than \"\n+\t\t\t\t\t\t+ Integer.MAX_VALUE + \" bytes\");\n+\t\t\t}\n+\t\t\tbyte[] log = new byte[(int) logSize];\n+\t\t\tNB.readFully(fileInputStream, log, 0, log.length);\n+\t\t\tint rs = RawParseUtils.prevLF(log, log.length);\n+\t\t\tList<Entry> ret = new ArrayList<Entry>();\n+\t\t\twhile (rs >= 0 && max-- > 0) {\n+\t\t\t\trs = RawParseUtils.prevLF(log, rs);\n+\t\t\t\tEntry entry = new Entry(log, rs < 0 ? 0 : rs + 2);\n+\t\t\t\tret.add(entry);\n+\t\t\t}\n+\t\t\treturn ret;\n+\t\t} finally {\n+\t\t\tfileInputStream.close();\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 5def5d3..971215c 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -1089,4 +1089,18 @@ public String shortenRefName(String refName) {\n \t\t\treturn refName.substring(Constants.R_REMOTES.length());\n \t\treturn refName;\n \t}\n+\n+\t/**\n+\t * @param refName\n+\t * @return a {@link ReflogReader} for the refname, or null if the names ref\n+\t *         does not exist.\n+\t * @throws IOException \n+\t */\n+\tpublic ReflogReader getReflogReader(String refName) throws IOException {\n+\t\tRef ref = getRef(refName);\n+\t\tif (ref != null)\n+\t\t\treturn new ReflogReader(this, ref.getOrigName());\n+\t\telse\n+\t\t\treturn null;\n+\t}\n }\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116035","messageId":"1244668945-12622-4-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1244668945-12622-3-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 3/7] Do not write to the reflog unless the refupdate logmessage is set","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:21Z","receivedAt":"2009-06-10T21:22:21Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"From: Shawn O. Pearce <spearce@spearce.org>\n\nThis permits micro-update steps (or otherwise uninteresting states)\nto be skipped in the reflog.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |    1 +\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   56 +++++++++++++++----\n 2 files changed, 45 insertions(+), 12 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 6b1975a..84653c8 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@@ -126,6 +126,7 @@ public void testDeleteLooseAndItsDirectory() throws IOException {\n \t\tRefUpdate updateRef = db.updateRef(\"refs/heads/z/c\");\n \t\tupdateRef.setNewObjectId(pid);\n \t\tupdateRef.setForceUpdate(true);\n+\t\tupdateRef.setRefLogMessage(\"new test ref\", false);\n \t\tResult update = updateRef.update();\n \t\tassertEquals(Result.NEW, update); // internal\n \t\tassertTrue(new File(db.getDirectory(), Constants.R_HEADS + \"z\")\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 a9ab73b..8044c6e 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@@ -165,6 +165,7 @@ RefUpdate(final RefDatabase r, final Ref ref, final File f) {\n \t\tthis.ref = ref;\n \t\toldValue = ref.getObjectId();\n \t\tlooseFile = f;\n+\t\trefLogMessage = \"\";\n \t}\n \n \t/** @return the repository the updated ref resides in */\n@@ -264,7 +265,8 @@ public void setRefLogIdent(final PersonIdent pi) {\n \t/**\n \t * Get the message to include in the reflog.\n \t * \n-\t * @return message the caller wants to include in the reflog.\n+\t * @return message the caller wants to include in the reflog; null if the\n+\t *         update should not be logged.\n \t */\n \tpublic String getRefLogMessage() {\n \t\treturn refLogMessage;\n@@ -274,15 +276,29 @@ public String getRefLogMessage() {\n \t * Set the message to include in the reflog.\n \t * \n \t * @param msg\n-\t *            the message to describe this change.\n+\t *            the message to describe this change. It may be null\n+\t *            if appendStatus is null in order not to append to the reflog\n \t * @param appendStatus\n \t *            true if the status of the ref change (fast-forward or\n \t *            forced-update) should be appended to the user supplied\n \t *            message.\n \t */\n \tpublic void setRefLogMessage(final String msg, final boolean appendStatus) {\n-\t\trefLogMessage = msg;\n-\t\trefLogIncludeResult = appendStatus;\n+\t\tif (msg == null && !appendStatus)\n+\t\t\tdisableRefLog();\n+\t\telse if (msg == null && appendStatus) {\n+\t\t\trefLogMessage = \"\";\n+\t\t\trefLogIncludeResult = true;\n+\t\t} else {\n+\t\t\trefLogMessage = msg;\n+\t\t\trefLogIncludeResult = appendStatus;\n+\t\t}\n+\t}\n+\n+\t/** Don't record this update in the ref's associated reflog. */\n+\tpublic void disableRefLog() {\n+\t\trefLogMessage = null;\n+\t\trefLogIncludeResult = false;\n \t}\n \n \t/**\n@@ -471,21 +487,37 @@ private Result updateStore(final LockFile lock, final Result status)\n \t\tlock.setNeedStatInformation(true);\n \t\tlock.write(newValue);\n \t\tString msg = getRefLogMessage();\n-\t\tif (msg != null && refLogIncludeResult) {\n-\t\t\tif (status == Result.FORCED)\n-\t\t\t\tmsg += \": forced-update\";\n-\t\t\telse if (status == Result.FAST_FORWARD)\n-\t\t\t\tmsg += \": fast forward\";\n-\t\t\telse if (status == Result.NEW)\n-\t\t\t\tmsg += \": created\";\n+\t\tif (msg != null) {\n+\t\t\tif (refLogIncludeResult) {\n+\t\t\t\tString strResult = toResultString(status);\n+\t\t\t\tif (strResult != null) {\n+\t\t\t\t\tif (msg.length() > 0)\n+\t\t\t\t\t\tmsg = msg + \": \" + strResult;\n+\t\t\t\t\telse\n+\t\t\t\t\t\tmsg = strResult;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tRefLogWriter.append(this, msg);\n \t\t}\n-\t\tRefLogWriter.append(this, msg);\n \t\tif (!lock.commit())\n \t\t\treturn Result.LOCK_FAILURE;\n \t\tdb.stored(this.ref.getOrigName(),  ref.getName(), newValue, lock.getCommitLastModified());\n \t\treturn status;\n \t}\n \n+\tprivate static String toResultString(final Result status) {\n+\t\tswitch (status) {\n+\t\tcase FORCED:\n+\t\t\treturn \"forced-update\";\n+\t\tcase FAST_FORWARD:\n+\t\t\treturn \"fast forward\";\n+\t\tcase NEW:\n+\t\t\treturn \"created\";\n+\t\tdefault:\n+\t\t\treturn null;\n+\t\t}\n+\t}\n+\n \t/**\n \t * Handle the abstraction of storing a ref update. This is because both\n \t * updating and deleting of a ref have merge testing in common.\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116041","messageId":"1244668945-12622-5-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1244668945-12622-4-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 4/7] Add ref rename support to JGit","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:22Z","receivedAt":"2009-06-10T21:22:22Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Now refs can be renamed. The intent is that should be safe. Only the named\nrefs and associated logs are updated. Any symbolic refs referring to the renames\nbranches are unaffected, except HEAD, which is updated if the branch it refers\nto is being renamed.\n\nIn order to perform operations like rename from a to a/b and vice verse, we\nperform the rename similar to the way C git does it, i.e. one ref update at\na time using the same temporary intermediate names as the C version does.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../org/spearce/jgit/test/resources/packed-refs    |    1 +\n .../tst/org/spearce/jgit/lib/RefUpdateTest.java    |  347 +++++++++++++++++++-\n .../org/spearce/jgit/transport/TransportTest.java  |    2 +-\n .../src/org/spearce/jgit/lib/RefDatabase.java      |   31 ++\n .../src/org/spearce/jgit/lib/RefLogWriter.java     |   33 ++-\n .../src/org/spearce/jgit/lib/RefRename.java        |  163 +++++++++\n .../src/org/spearce/jgit/lib/RefUpdate.java        |   59 +++-\n .../src/org/spearce/jgit/lib/Repository.java       |   16 +\n 8 files changed, 625 insertions(+), 27 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n\ndiff --git a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs\nindex 38a70ec..a6a14f2 100644\n--- a/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs\n+++ b/org.spearce.jgit.test/tst-rsrc/org/spearce/jgit/test/resources/packed-refs\n@@ -6,6 +6,7 @@ f73b95671f326616d66b2afb3bdfcdbbce110b44 refs/heads/d\n d0114ab8ac326bab30e3a657a0397578c5a1af88 refs/heads/e\n 47d3697c3747e8184e0dc479ccbd01e359023577 refs/heads/f\n 175d5b80bd9768884d8fced02e9bd33488174396 refs/heads/g\n+175d5b80bd9768884d8fced02e9bd33488174396 refs/heads/prefix/a\n 49322bb17d3acc9146f98c97d078513228bbf3c0 refs/heads/master\n d86a2aada2f5e7ccf6f11880bfb9ab404e8a8864 refs/heads/pa\n 6db9c2ebf75590eef973081736730a9ea169a0c4 refs/tags/A\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 84653c8..c505b79 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@@ -39,6 +39,7 @@\n \n import java.io.File;\n import java.io.IOException;\n+import java.util.List;\n import java.util.Map;\n import java.util.Map.Entry;\n \n@@ -83,9 +84,75 @@ public void testNoCacheObjectIdSubclass() throws IOException {\n \t\tassertEquals(newid.copy(), r.getObjectId());\n \t}\n \n+\tpublic void testNewNamespaceConflictWithLoosePrefixNameExists()\n+\t\t\tthrows IOException {\n+\t\tfinal String newRef = \"refs/heads/z\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.NEW, update);\n+\t\t// end setup\n+\t\tfinal String newRef2 = \"refs/heads/z/a\";\n+\t\tfinal RefUpdate ru2 = updateRef(newRef2);\n+\t\tfinal RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid2);\n+\t\tResult update2 = ru2.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update2);\n+\t}\n+\n+\tpublic void testNewNamespaceConflictWithPackedPrefixNameExists()\n+\t\t\tthrows IOException {\n+\t\tfinal String newRef = \"refs/heads/master/x\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update);\n+\t}\n+\n+\tpublic void testNewNamespaceConflictWithLoosePrefixOfExisting()\n+\t\t\tthrows IOException {\n+\t\tfinal String newRef = \"refs/heads/z/a\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.NEW, update);\n+\t\t// end setup\n+\t\tfinal String newRef2 = \"refs/heads/z\";\n+\t\tfinal RefUpdate ru2 = updateRef(newRef2);\n+\t\tfinal RevCommit newid2 = new RevCommit(ru2.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid2);\n+\t\tResult update2 = ru2.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update2);\n+\t}\n+\n+\tpublic void testNewNamespaceConflictWithPackedPrefixOfExisting()\n+\t\t\tthrows IOException {\n+\t\tfinal String newRef = \"refs/heads/prefix\";\n+\t\tfinal RefUpdate ru = updateRef(newRef);\n+\t\tfinal RevCommit newid = new RevCommit(ru.getNewObjectId()) {\n+\t\t\t// empty\n+\t\t};\n+\t\tru.setNewObjectId(newid);\n+\t\tResult update = ru.update();\n+\t\tassertEquals(Result.LOCK_FAILURE, update);\n+\t}\n+\n \t/**\n \t * Delete a ref that is pointed to by HEAD\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testDeleteHEADreferencedRef() throws IOException {\n@@ -118,7 +185,7 @@ public void testDeleteHead() throws IOException {\n \t/**\n \t * Delete a loose ref and make sure the directory in refs is deleted too,\n \t * and the reflog dir too\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testDeleteLooseAndItsDirectory() throws IOException {\n@@ -172,7 +239,7 @@ public void testRefKeySameAsOrigName() {\n \n \t/**\n \t * Try modify a ref forward, fast forward\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testUpdateRefForward() throws IOException {\n@@ -197,7 +264,7 @@ public void testUpdateRefForward() throws IOException {\n \t/**\n \t * Delete a ref that exists both as packed and loose. Make sure the ref\n \t * cannot be resolved after delete.\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testDeleteLoosePacked() throws IOException {\n@@ -218,7 +285,7 @@ public void testDeleteLoosePacked() throws IOException {\n \n \t/**\n \t * Try modify a ref to same\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testUpdateRefNoChange() throws IOException {\n@@ -232,7 +299,7 @@ public void testUpdateRefNoChange() throws IOException {\n \n \t/**\n \t * Try modify a ref, but get wrong expected old value\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testUpdateRefLockFailureWrongOldValue() throws IOException {\n@@ -247,7 +314,7 @@ public void testUpdateRefLockFailureWrongOldValue() throws IOException {\n \n \t/**\n \t * Try modify a ref that is locked\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testUpdateRefLockFailureLocked() throws IOException {\n@@ -255,18 +322,20 @@ public void testUpdateRefLockFailureLocked() throws IOException {\n \t\tObjectId pid = db.resolve(\"refs/heads/master^\");\n \t\tRefUpdate updateRef = db.updateRef(\"refs/heads/master\");\n \t\tupdateRef.setNewObjectId(pid);\n-\t\tLockFile lockFile1 = new LockFile(new File(db.getDirectory(),\"refs/heads/master\"));\n+\t\tLockFile lockFile1 = new LockFile(new File(db.getDirectory(),\n+\t\t\t\t\"refs/heads/master\"));\n \t\tassertTrue(lockFile1.lock()); // precondition to test\n \t\tResult update = updateRef.update();\n \t\tassertEquals(Result.LOCK_FAILURE, update);\n \t\tassertEquals(opid, db.resolve(\"refs/heads/master\"));\n-\t\tLockFile lockFile2 = new LockFile(new File(db.getDirectory(),\"refs/heads/master\"));\n+\t\tLockFile lockFile2 = new LockFile(new File(db.getDirectory(),\n+\t\t\t\t\"refs/heads/master\"));\n \t\tassertFalse(lockFile2.lock()); // was locked, still is\n \t}\n \n \t/**\n \t * Try to delete a ref. Delete requires force.\n-\t *\n+\t * \n \t * @throws IOException\n \t */\n \tpublic void testDeleteLoosePackedRejected() throws IOException {\n@@ -279,4 +348,262 @@ public void testDeleteLoosePackedRejected() throws IOException {\n \t\tassertEquals(oldpid, db.resolve(\"refs/heads/c\"));\n \t}\n \n+\tpublic void testRenameBranchNoPreviousLog() throws IOException {\n+\t\tassertFalse(\"precondition, no log on old branchg\", new File(db\n+\t\t\t\t.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(rb.equals(oldHead)); // assumption for this test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertEquals(1, db.getReflogReader(\"new/name\").getReverseEntries().size());\n+\t\tassertEquals(\"Branch: renamed b to new/name\", db.getReflogReader(\"new/name\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged\n+\t}\n+\n+\tpublic void testRenameBranchHasPreviousLog() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertFalse(\"precondition for this test, branch b != HEAD\", rb\n+\t\t\t\t.equals(oldHead));\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertEquals(2, db.getReflogReader(\"new/name\").getReverseEntries().size());\n+\t\tassertEquals(\"Branch: renamed b to new/name\", db.getReflogReader(\"new/name\")\n+\t\t\t\t.getLastEntry().getComment());\n+\t\tassertEquals(\"Just a message\", db.getReflogReader(\"new/name\")\n+\t\t\t\t.getReverseEntries().get(1).getComment());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(oldHead, db.resolve(Constants.HEAD)); // unchanged\n+\t}\n+\n+\tpublic void testRenameCurrentBranch() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/b\");\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(\"internal test condition, b == HEAD\", rb.equals(oldHead));\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertEquals(\"Branch: renamed b to new/name\", db.getReflogReader(\n+\t\t\t\t\"new/name\").getLastEntry().getComment());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\t\tassertEquals(rb, db.resolve(Constants.HEAD));\n+\t\tassertEquals(2, db.getReflogReader(\"new/name\").getReverseEntries().size());\n+\t\tassertEquals(\"Branch: renamed b to new/name\", db.getReflogReader(\"new/name\").getReverseEntries().get(0).getComment());\n+\t\tassertEquals(\"Just a message\", db.getReflogReader(\"new/name\").getReverseEntries().get(1).getComment());\n+\t}\n+\n+\tpublic void testRenameBranchAlsoInPack() throws IOException {\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tObjectId rb2 = db.resolve(\"refs/heads/b~1\");\n+\t\tassertEquals(Ref.Storage.PACKED, db.getRef(\"refs/heads/b\").getStorage());\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/b\");\n+\t\tupdateRef.setNewObjectId(rb2);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tResult update = updateRef.update();\n+\t\tassertEquals(\"internal check new ref is loose\", Result.FORCED, update);\n+\t\tassertEquals(Ref.Storage.LOOSE_PACKED, db.getRef(\"refs/heads/b\")\n+\t\t\t\t.getStorage());\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/b\");\n+\t\tassertTrue(\"no log on old branch\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/b\").exists());\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertEquals(rb2, db.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(db.resolve(\"refs/heads/b\"));\n+\t\tassertEquals(\"Branch: renamed b to new/name\", db.getReflogReader(\n+\t\t\t\t\"new/name\").getLastEntry().getComment());\n+\t\tassertFalse(new File(db.getDirectory(), \"logs/refs/heads/b\").exists());\n+\n+\t\t// Create new Repository instance, to reread caches and make sure our\n+\t\t// assumptions are persistent.\n+\t\tRepository ndb = new Repository(db.getDirectory());\n+\t\tassertEquals(rb2, ndb.resolve(\"refs/heads/new/name\"));\n+\t\tassertNull(ndb.resolve(\"refs/heads/b\"));\n+\t}\n+\n+\tpublic void tryRenameWhenLocked(String toLock, String fromName,\n+\t\t\tString toName, String headPointsTo) throws IOException {\n+\t\t// setup\n+\t\tdb.writeSymref(Constants.HEAD, headPointsTo);\n+\t\tObjectId oldfromId = db.resolve(fromName);\n+\t\tObjectId oldHeadId = db.resolve(Constants.HEAD);\n+\t\tRefLogWriter.writeReflog(db, oldfromId, oldfromId, \"Just a message\",\n+\t\t\t\tfromName);\n+\t\tList<org.spearce.jgit.lib.ReflogReader.Entry> oldFromLog = db\n+\t\t\t\t.getReflogReader(fromName).getReverseEntries();\n+\t\tList<org.spearce.jgit.lib.ReflogReader.Entry> oldHeadLog = oldHeadId != null ? db\n+\t\t\t\t.getReflogReader(Constants.HEAD).getReverseEntries() : null;\n+\n+\t\tassertTrue(\"internal check, we have a log\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/\" + fromName).exists());\n+\n+\t\t// \"someone\" has branch X locked\n+\t\tassertTrue(new LockFile(new File(db.getDirectory(), toLock)).lock());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(fromName, toName);\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.LOCK_FAILURE, result);\n+\n+\t\t// Check that the involved refs are the same despite the failure\n+\t\tassertExists(false, toName);\n+\t\tif (!toLock.equals(toName))\n+\t\t\tassertExists(false, toName + \".lock\");\n+\t\tassertExists(true, toLock + \".lock\");\n+\t\tif (!toLock.equals(fromName))\n+\t\t\tassertExists(false, \"logs/\" + fromName + \".lock\");\n+\t\tassertExists(false, \"logs/\" + toName + \".lock\");\n+\t\tassertEquals(oldHeadId, db.resolve(Constants.HEAD));\n+\t\tassertEquals(oldfromId, db.resolve(fromName));\n+\t\tassertNull(db.resolve(toName));\n+\t\tassertEquals(oldFromLog.toString(), db.getReflogReader(fromName)\n+\t\t\t\t.getReverseEntries().toString());\n+\t\tif (oldHeadId != null)\n+\t\t\tassertEquals(oldHeadLog, db.getReflogReader(Constants.HEAD)\n+\t\t\t\t\t.getReverseEntries());\n+\t}\n+\n+\tprivate void assertExists(boolean positive, String toName) {\n+\t\tassertEquals(toName + (positive ? \" \" : \" does not \") + \"exist\",\n+\t\t\t\tpositive, new File(db.getDirectory(), toName).exists());\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisFromLockHEAD()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"HEAD\", \"refs/heads/b\", \"refs/heads/new/name\",\n+\t\t\t\t\"refs/heads/b\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisFromLockFrom()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/b\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/b\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisFromLockTo()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/new/name\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/b\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisToLockFrom()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/b\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/new/name\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisToLockTo()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/new/name\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/new/name\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisToLockTmp()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"RENAMED-REF..\" + Thread.currentThread().getId(),\n+\t\t\t\t\"refs/heads/b\", \"refs/heads/new/name\", \"refs/heads/new/name\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockFrom()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/b\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/a\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockTo()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"refs/heads/new/name\", \"refs/heads/b\",\n+\t\t\t\t\"refs/heads/new/name\", \"refs/heads/a\");\n+\t}\n+\n+\tpublic void testRenameBranchCannotLockAFileHEADisOtherLockTmp()\n+\t\t\tthrows IOException {\n+\t\ttryRenameWhenLocked(\"RENAMED-REF..\" + Thread.currentThread().getId(),\n+\t\t\t\t\"refs/heads/b\", \"refs/heads/new/name\", \"refs/heads/a\");\n+\t}\n+\n+\tpublic void testRenameRefNameColission1avoided() throws IOException {\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/a\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/a\");\n+\t\tupdateRef.setNewObjectId(rb);\n+\t\tupdateRef.setRefLogMessage(\"Setup\", false);\n+\t\tassertEquals(Result.FAST_FORWARD, updateRef.update());\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\", \"refs/heads/a\");\n+\t\tassertTrue(\"internal check, we have a log\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/a\").exists());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/a\", \"refs/heads/a/b\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\t\tassertNull(db.resolve(\"refs/heads/a\"));\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/a/b\"));\n+\t\tassertEquals(3, db.getReflogReader(\"a/b\").getReverseEntries().size());\n+\t\tassertEquals(\"Branch: renamed a to a/b\", db.getReflogReader(\"a/b\")\n+\t\t\t\t.getReverseEntries().get(0).getComment());\n+\t\tassertEquals(\"Just a message\", db.getReflogReader(\"a/b\")\n+\t\t\t\t.getReverseEntries().get(1).getComment());\n+\t\tassertEquals(\"Setup\", db.getReflogReader(\"a/b\").getReverseEntries()\n+\t\t\t\t.get(2).getComment());\n+\t}\n+\n+\tpublic void testRenameRefNameColission2avoided() throws IOException {\n+\t\t// setup\n+\t\tObjectId rb = db.resolve(\"refs/heads/b\");\n+\t\tdb.writeSymref(Constants.HEAD, \"refs/heads/prefix/a\");\n+\t\tRefUpdate updateRef = db.updateRef(\"refs/heads/prefix/a\");\n+\t\tupdateRef.setNewObjectId(rb);\n+\t\tupdateRef.setRefLogMessage(\"Setup\", false);\n+\t\tupdateRef.setForceUpdate(true);\n+\t\tassertEquals(Result.FORCED, updateRef.update());\n+\t\tObjectId oldHead = db.resolve(Constants.HEAD);\n+\t\tassertTrue(rb.equals(oldHead)); // assumption for this test\n+\t\tRefLogWriter.writeReflog(db, rb, rb, \"Just a message\",\n+\t\t\t\t\"refs/heads/prefix/a\");\n+\t\tassertTrue(\"internal check, we have a log\", new File(db.getDirectory(),\n+\t\t\t\t\"logs/refs/heads/prefix/a\").exists());\n+\n+\t\t// Now this is our test\n+\t\tRefRename renameRef = db.renameRef(\"refs/heads/prefix/a\",\n+\t\t\t\t\"refs/heads/prefix\");\n+\t\tResult result = renameRef.rename();\n+\t\tassertEquals(Result.RENAMED, result);\n+\n+\t\tassertNull(db.resolve(\"refs/heads/prefix/a\"));\n+\t\tassertEquals(rb, db.resolve(\"refs/heads/prefix\"));\n+\t\tassertEquals(3, db.getReflogReader(\"prefix\").getReverseEntries().size());\n+\t\tassertEquals(\"Branch: renamed prefix/a to prefix\", db.getReflogReader(\n+\t\t\t\t\"prefix\").getReverseEntries().get(0).getComment());\n+\t\tassertEquals(\"Just a message\", db.getReflogReader(\"prefix\")\n+\t\t\t\t.getReverseEntries().get(1).getComment());\n+\t\tassertEquals(\"Setup\", db.getReflogReader(\"prefix\").getReverseEntries()\n+\t\t\t\t.get(2).getComment());\n+\t}\n }\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\nindex c6e3335..fcf3f5c 100644\n--- a/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/transport/TransportTest.java\n@@ -123,7 +123,7 @@ public void testFindRemoteRefUpdatesWildcardNoTracking() throws IOException {\n \t\t\t\t.findRemoteRefUpdatesFor(Collections.nCopies(1, new RefSpec(\n \t\t\t\t\t\t\"+refs/heads/*:refs/heads/test/*\")));\n \n-\t\tassertEquals(9, result.size());\n+\t\tassertEquals(10, result.size());\n \t\tboolean foundA = false;\n \t\tboolean foundB = false;\n \t\tfor (final RemoteRefUpdate rru : result) {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\nindex 869d012..6d4f374 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefDatabase.java\n@@ -146,6 +146,23 @@ synchronized (this) {\n \t}\n \n \t/**\n+\t * An set of update operations for renaming a ref\n+\t *\n+\t * @param fromRef Old ref name\n+\t * @param toRef New ref name\n+\t * @return a RefUpdate operation to rename a ref\n+\t * @throws IOException\n+\t */\n+\tRefRename newRename(String fromRef, String toRef) throws IOException {\n+\t\trefreshPackedRefs();\n+\t\tRef f = readRefBasic(fromRef, 0);\n+\t\tRef t = new Ref(Ref.Storage.NEW, toRef, null);\n+\t\tRefUpdate refUpdateFrom = new RefUpdate(this, f, fileForRef(f.getName()));\n+\t\tRefUpdate refUpdateTo = new RefUpdate(this, t, fileForRef(t.getName()));\n+\t\treturn new RefRename(refUpdateTo, refUpdateFrom);\n+\t}\n+\n+\t/**\n \t * Writes a symref (e.g. HEAD) to disk\n \t * \n \t * @param name\n@@ -158,11 +175,25 @@ void link(final String name, final String target) throws IOException {\n \t\tfinal byte[] content = Constants.encode(\"ref: \" + target + \"\\n\");\n \t\tlockAndWriteFile(fileForRef(name), content);\n \t\tsynchronized (this) {\n+\t\t\tlooseSymRefs.remove(name);\n \t\t\tsetModified();\n \t\t}\n \t\tdb.fireRefsMaybeChanged();\n \t}\n \n+\tvoid uncacheSymRef(String name) {\n+\t\tsynchronized(this) {\n+\t\t\tlooseSymRefs.remove(name);\n+\t\t\tsetModified();\n+\t\t}\n+\t}\n+\n+\tvoid uncacheRef(String name) {\n+\t\tlooseRefs.remove(name);\n+\t\tlooseRefsMTime.remove(name);\n+\t\tpackedRefs.remove(name);\n+\t}\n+\n \tprivate void setModified() {\n \t\tlastRefModification = refModificationCounter++;\n \t}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\nindex a077051..0864209 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefLogWriter.java\n@@ -44,7 +44,7 @@\n import java.io.IOException;\n \n /**\n- * Utility class to add reflog entries\n+ * Utility class to work with reflog files\n  * \n  * @author Dave Watson\n  */\n@@ -58,6 +58,37 @@ static void append(final RefUpdate u, final String msg) throws IOException {\n \t\tappendOneRecord(oldId, newId, ident, msg, db, u.getName());\n \t}\n \n+\tstatic void append(RefRename refRename, String msg) throws IOException {\n+\t\tfinal ObjectId id = refRename.getObjectId();\n+\t\tfinal Repository db = refRename.getRepository();\n+\t\tfinal PersonIdent ident = refRename.getRefLogIdent();\n+\t\tappendOneRecord(id, id, ident, msg, db, refRename.getToName());\n+\t}\n+\n+\tstatic void renameTo(final Repository db, final RefUpdate from,\n+\t\t\tfinal RefUpdate to) throws IOException {\n+\t\tfinal File logdir = new File(db.getDirectory(), Constants.LOGS);\n+\t\tfinal File reflogFrom = new File(logdir, from.getName());\n+\t\tif (!reflogFrom.exists())\n+\t\t\treturn;\n+\t\tfinal File reflogTo = new File(logdir, to.getName());\n+\t\tfinal File reflogToDir = reflogTo.getParentFile();\n+\t\tFile tmp = new File(logdir, \"tmp-renamed-log..\" + Thread.currentThread().getId());\n+\t\tif (!reflogFrom.renameTo(tmp)) {\n+\t\t\tthrow new IOException(\"Cannot rename \" + reflogFrom + \" to (\" + tmp\n+\t\t\t\t\t+ \")\" + reflogTo);\n+\t\t}\n+\t\tRefUpdate.deleteEmptyDir(reflogFrom, RefUpdate.count(from.getName(),\n+\t\t\t\t'/'));\n+\t\tif (!reflogToDir.exists() && !reflogToDir.mkdirs()) {\n+\t\t\tthrow new IOException(\"Cannot create directory \" + reflogToDir);\n+\t\t}\n+\t\tif (!tmp.renameTo(reflogTo)) {\n+\t\t\tthrow new IOException(\"Cannot rename (\" + tmp + \")\" + reflogFrom\n+\t\t\t\t\t+ \" to \" + reflogTo);\n+\t\t}\n+\t}\n+\n \tprivate static void appendOneRecord(final ObjectId oldId,\n \t\t\tfinal ObjectId newId, PersonIdent ident, final String msg,\n \t\t\tfinal Repository db, final String refName) throws IOException {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\nnew file mode 100644\nindex 0000000..c89459b\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RefRename.java\n@@ -0,0 +1,163 @@\n+/*\n+ * Copyright (C) 2009, Robin Rosenberg\n+ *\n+ * All rights reserved.\n+ *\n+ * Redistribution and use in source and binary forms, with or\n+ * without modification, are permitted provided that the following\n+ * conditions are met:\n+ *\n+ * - Redistributions of source code must retain the above copyright\n+ *   notice, this list of conditions and the following disclaimer.\n+ *\n+ * - Redistributions in binary form must reproduce the above\n+ *   copyright notice, this list of conditions and the following\n+ *   disclaimer in the documentation and/or other materials provided\n+ *   with the distribution.\n+ *\n+ * - Neither the name of the Git Development Community nor the\n+ *   names of its contributors may be used to endorse or promote\n+ *   products derived from this software without specific prior\n+ *   written permission.\n+ *\n+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND\n+ * CONTRIBUTORS \"AS IS\" AND ANY EXPRESS OR IMPLIED WARRANTIES,\n+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES\n+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE\n+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR\n+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,\n+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT\n+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;\n+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER\n+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,\n+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)\n+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF\n+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.\n+ */\n+package org.spearce.jgit.lib;\n+\n+import java.io.IOException;\n+\n+import org.spearce.jgit.lib.RefUpdate.Result;\n+\n+/**\n+ * A RefUpdate combination for renaming a ref\n+ */\n+public class RefRename {\n+\tprivate RefUpdate newToUpdate;\n+\n+\tprivate RefUpdate oldFromDelete;\n+\n+\tprivate Result renameResult = Result.NOT_ATTEMPTED;\n+\n+\tRefRename(final RefUpdate toUpdate, final RefUpdate fromUpdate) {\n+\t\tnewToUpdate = toUpdate;\n+\t\toldFromDelete = fromUpdate;\n+\t}\n+\n+\t/**\n+\t * @return result of rename operation\n+\t */\n+\tpublic Result getResult() {\n+\t\treturn renameResult;\n+\t}\n+\n+\t/**\n+\t * @return the result of the new ref update\n+\t * @throws IOException\n+\t */\n+\tpublic Result rename() throws IOException {\n+\t\tRef oldRef = oldFromDelete.db.readRef(Constants.HEAD);\n+\t\tboolean renameHEADtoo = oldRef != null\n+\t\t\t\t&& oldRef.getName().equals(oldFromDelete.getName());\n+\t\tRepository db = oldFromDelete.getRepository();\n+\t\ttry {\n+\t\t\tRefLogWriter.renameTo(db, oldFromDelete,\n+\t\t\t\t\tnewToUpdate);\n+\t\t\tnewToUpdate.setRefLogMessage(null, false);\n+\t\t\tString tmpRefName = \"RENAMED-REF..\" + Thread.currentThread().getId();\n+\t\t\tRefUpdate tmpUpdateRef = db.updateRef(tmpRefName);\n+\t\t\tif (renameHEADtoo) {\n+\t\t\t\ttry {\n+\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, tmpRefName);\n+\t\t\t\t} catch (IOException e) {\n+\t\t\t\t\tRefLogWriter.renameTo(db,\n+\t\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n+\t\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\ttmpUpdateRef.setNewObjectId(oldFromDelete.getOldObjectId());\n+\t\t\ttmpUpdateRef.setForceUpdate(true);\n+\t\t\tResult update = tmpUpdateRef.update();\n+\t\t\tif (update != Result.FORCED && update != Result.NEW && update != Result.NO_CHANGE) {\n+\t\t\t\tRefLogWriter.renameTo(db,\n+\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n+\t\t\t\tif (renameHEADtoo) {\n+\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, oldFromDelete.getName());\n+\t\t\t\t}\n+\t\t\t\treturn renameResult = update;\n+\t\t\t}\n+\n+\t\t\toldFromDelete.setExpectedOldObjectId(oldFromDelete.getOldObjectId());\n+\t\t\toldFromDelete.setForceUpdate(true);\n+\t\t\tResult delete = oldFromDelete.delete();\n+\t\t\tif (delete != Result.FORCED) {\n+\t\t\t\tif (db.getRef(\n+\t\t\t\t\t\toldFromDelete.getName()) != null) {\n+\t\t\t\t\tRefLogWriter.renameTo(db,\n+\t\t\t\t\t\t\tnewToUpdate, oldFromDelete);\n+\t\t\t\t\tif (renameHEADtoo) {\n+\t\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, oldFromDelete\n+\t\t\t\t\t\t\t\t.getName());\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\treturn renameResult = delete;\n+\t\t\t}\n+\n+\t\t\tnewToUpdate.setNewObjectId(tmpUpdateRef.getNewObjectId());\n+\t\t\tResult updateResult = newToUpdate.update();\n+\t\t\tif (updateResult != Result.NEW) {\n+\t\t\t\tRefLogWriter.renameTo(db, newToUpdate, oldFromDelete);\n+\t\t\t\tif (renameHEADtoo) {\n+\t\t\t\t\toldFromDelete.db.link(Constants.HEAD, oldFromDelete.getName());\n+\t\t\t\t}\n+\t\t\t\toldFromDelete.setExpectedOldObjectId(null);\n+\t\t\t\toldFromDelete.setNewObjectId(oldFromDelete.getOldObjectId());\n+\t\t\t\toldFromDelete.setForceUpdate(true);\n+\t\t\t\toldFromDelete.setRefLogMessage(null, false);\n+\t\t\t\tResult undelete = oldFromDelete.update();\n+\t\t\t\tif (undelete != Result.NEW && undelete != Result.LOCK_FAILURE)\n+\t\t\t\t\treturn renameResult = Result.IO_FAILURE;\n+\t\t\t\treturn renameResult = Result.LOCK_FAILURE;\n+\t\t\t}\n+\n+\t\t\tif (renameHEADtoo) {\n+\t\t\t\toldFromDelete.db.link(Constants.HEAD, newToUpdate.getName());\n+\t\t\t} else {\n+\t\t\t\tdb.fireRefsMaybeChanged();\n+\t\t\t}\n+\t\t\tRefLogWriter.append(this, \"Branch: renamed \"\n+\t\t\t\t\t+ db.shortenRefName(oldFromDelete.getName()) + \" to \"\n+\t\t\t\t\t+ db.shortenRefName(newToUpdate.getName()));\n+\t\t\treturn renameResult = Result.RENAMED;\n+\t\t} catch (RuntimeException e) {\n+\t\t\tthrow e;\n+\t\t}\n+\t}\n+\n+\tObjectId getObjectId() {\n+\t\treturn oldFromDelete.getOldObjectId();\n+\t}\n+\n+\tRepository getRepository() {\n+\t\treturn oldFromDelete.getRepository();\n+\t}\n+\n+\tPersonIdent getRefLogIdent() {\n+\t\treturn newToUpdate.getRefLogIdent();\n+\t}\n+\n+\tString getToName() {\n+\t\treturn newToUpdate.getName();\n+\t}\n+}\n\\ No newline at end of file\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 8044c6e..f8ecc3c 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@@ -125,14 +125,20 @@\n \t\t * This kind of error doesn't include {@link #LOCK_FAILURE}, which is a\n \t\t * different case.\n \t\t */\n-\t\tIO_FAILURE\n+\t\tIO_FAILURE,\n+\n+\t\t/**\n+\t\t * The ref was renamed from another name\n+\t\t * <p>\n+\t\t */\n+\t\tRENAMED\n \t}\n \n \t/** Repository the ref is stored in. */\n-\tprivate final RefDatabase db;\n+\tfinal RefDatabase db;\n \n \t/** Location of the loose file holding the value of this ref. */\n-\tprivate final File looseFile;\n+\tfinal File looseFile;\n \n \t/** New value the caller wants this ref to have. */\n \tprivate ObjectId newValue;\n@@ -156,7 +162,7 @@\n \tprivate ObjectId expValue;\n \n \t/** Result of the update operation. */\n-\tprivate Result result = Result.NOT_ATTEMPTED;\n+\tResult result = Result.NOT_ATTEMPTED;\n \n \tprivate final Ref ref;\n \n@@ -434,6 +440,15 @@ private Result updateImpl(final RevWalk walk, final Store store)\n \t\tRevObject newObj;\n \t\tRevObject oldObj;\n \n+\t\tint lastSlash = getName().lastIndexOf('/');\n+\t\tif (lastSlash > 0)\n+\t\t\tif (db.getRepository().getRef(getName().substring(0, lastSlash)) != null)\n+\t\t\t\treturn Result.LOCK_FAILURE;\n+\t\tString rName = getName() + \"/\";\n+\t\tfor (Ref r : db.getAllRefs().values()) {\n+\t\t\tif (r.getName().startsWith(rName))\n+\t\t\t\treturn Result.LOCK_FAILURE;\n+\t\t}\n \t\tlock = new LockFile(looseFile);\n \t\tif (!lock.lock())\n \t\t\treturn Result.LOCK_FAILURE;\n@@ -527,7 +542,7 @@ abstract Result store(final LockFile lock, final Result status)\n \t\t\t\tthrows IOException;\n \t}\n \n-\tprivate class UpdateStore extends Store {\n+\tclass UpdateStore extends Store {\n \n \t\t@Override\n \t\tResult store(final LockFile lock, final Result status)\n@@ -536,7 +551,7 @@ Result store(final LockFile lock, final Result status)\n \t\t}\n \t}\n \n-\tprivate class DeleteStore extends Store {\n+\tclass DeleteStore extends Store {\n \n \t\t@Override\n \t\tResult store(LockFile lock, Result status) throws IOException {\n@@ -557,28 +572,42 @@ Result store(LockFile lock, Result status) throws IOException {\n \t\t\tlock.unlock();\n \t\t\tif (storage.isLoose())\n \t\t\t\tdeleteFileAndEmptyDir(looseFile, levels);\n+\t\t\tdb.uncacheRef(ref.getName());\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\tif (file.isFile()) {\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\tFile dir = file.getParentFile();\n+\t\t\t\tfor  (int i = 0; i < depth; ++i) {\n+\t\t\t\t\tif (!dir.delete())\n+\t\t\t\t\t\tbreak; // ignore problem here\n+\t\t\t\t\tdir = dir.getParentFile();\n+\t\t\t\t}\n \t\t\t}\n \t\t}\n+\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+\tUpdateStore newUpdateStore() {\n+\t\treturn new UpdateStore();\n+\t}\n+\n+\tDeleteStore newDeleteStore() {\n+\t\treturn new DeleteStore();\n+\t}\n+\n+\tstatic void deleteEmptyDir(File dir, int depth) {\n+\t\tfor (; depth > 0 && dir != null; depth--) {\n+\t\t\tif (dir.exists() && !dir.delete())\n+\t\t\t\tbreak;\n+\t\t\tdir = dir.getParentFile();\n \t\t}\n \t}\n \n-\tprivate static int count(final String s, final char c) {\n+\tstatic 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++;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 971215c..9b97fdc 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -468,6 +468,22 @@ public RefUpdate updateRef(final String ref) throws IOException {\n \t}\n \n \t/**\n+\t * Create a command to rename a ref in this repository\n+\t *\n+\t * @param fromRef\n+\t *            name of ref to rename from\n+\t * @param toRef\n+\t *            name of ref to rename to\n+\t * @return an update command that knows how to rename a branch to another.\n+\t * @throws IOException\n+\t *             the rename could not be performed.\n+\t *\n+\t */\n+\tpublic RefRename renameRef(final String fromRef, final String toRef) throws IOException {\n+\t\treturn refs.newRename(fromRef, toRef);\n+\t}\n+\n+\t/**\n \t * Parse a git revision string and return an object id.\n \t *\n \t * Currently supported is combinations of these.\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116038","messageId":"1244668945-12622-6-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1244668945-12622-5-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 5/7] Add ref rename support to the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:23Z","receivedAt":"2009-06-10T21:22:23Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |   12 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |  106 ++++++++++++++++----\n .../src/org/spearce/egit/ui/uitext.properties      |    5 +\n 3 files changed, 103 insertions(+), 20 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 654e155..aa31a93 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -893,6 +893,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRefresh;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\n@@ -929,6 +935,12 @@\n \tpublic static String BranchSelectionDialog_QuestionNewBranchTitle;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_QuestionNewBranchNameTitle;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_QuestionNewBranchNameMessage;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_QuestionNewBranchMessage;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 9aad95b..28105af 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -48,8 +48,11 @@\n import org.spearce.egit.ui.UIText;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.RefRename;\n import org.spearce.jgit.lib.RefUpdate;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n \n /**\n  * The branch and reset selection dialog\n@@ -277,6 +280,30 @@ private void refNameFromDialog() {\n \t\t}\n \t}\n \n+\tprivate InputDialog getRefNameInputDialog(String prompt) {\n+\t\tInputDialog labelDialog = new InputDialog(\n+\t\t\t\tgetShell(),\n+\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n+\t\t\t\tprompt,\n+\t\t\t\tnull, new IInputValidator() {\n+\t\t\t\t\tpublic String isValid(String newText) {\n+\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n+\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n+\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n+\t\t\t\t\t\treturn null;\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\tlabelDialog.setBlockOnOpen(true);\n+\t\treturn labelDialog;\n+\t}\n+\n \t@Override\n \tprotected void createButtonsForButtonBar(Composite parent) {\n \t\tif (!showResetType) {\n@@ -284,43 +311,81 @@ protected void createButtonsForButtonBar(Composite parent) {\n \t\t\tnewButton.setFont(JFaceResources.getDialogFont());\n \t\t\tnewButton.setText(UIText.BranchSelectionDialog_NewBranch);\n \t\t\t((GridLayout)parent.getLayout()).numColumns++;\n+\t\t\tButton renameButton = new Button(parent, SWT.PUSH);\n+\t\t\trenameButton.setText(\"&Rename\");\n+\t\t\trenameButton.addSelectionListener(new SelectionListener() {\n+\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t// check what ref name the user selected, if any.\n+\t\t\t\t\trefNameFromDialog();\n+\n+\t\t\t\t\tString branchName;\n+\t\t\t\t\tif (refName.startsWith(Constants.R_HEADS))\n+\t\t\t\t\t\tbranchName = refName.substring(Constants.R_HEADS.length());\n+\t\t\t\t\telse\n+\t\t\t\t\t\tbranchName = refName;\t\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(NLS\n+\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchNameMessage,\n+\t\t\t\t\t\t\t\t\tbranchName));\n+\t\t\t\t\tif (labelDialog.open() == Window.OK) {\n+\t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n+\t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n+\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n+\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n+\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n+\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t// FIXME: Update HEAD\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tbranchTree.removeAll();\n+\t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n+\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n+\t\t\t\t\twidgetSelected(e);\n+\t\t\t\t}\n+\t\t\t});\n \t\t\tnewButton.addSelectionListener(new SelectionListener() {\n \n \t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t// check what ref name the user selected, if any.\n \t\t\t\t\trefNameFromDialog();\n \n-\t\t\t\t\tInputDialog labelDialog = new InputDialog(\n-\t\t\t\t\t\t\tgetShell(),\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchMessage,\n-\t\t\t\t\t\t\tnull, new IInputValidator() {\n-\t\t\t\t\t\t\t\tpublic String isValid(String newText) {\n-\t\t\t\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n-\t\t\t\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n-\t\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n-\t\t\t\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n-\t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n-\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n-\t\t\t\t\t\t\t\t\treturn null;\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t});\n-\t\t\t\t\tlabelDialog.setBlockOnOpen(true);\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(UIText.BranchSelectionDialog_QuestionNewBranchNameMessage);\n \t\t\t\t\tif (labelDialog.open() == Window.OK) {\n \t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n \t\t\t\t\t\tRefUpdate updateRef;\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tupdateRef = repo.updateRef(newRefName);\n+\t\t\t\t\t\t\tRef startRef = repo.getRef(refName);\n \t\t\t\t\t\t\tObjectId startAt;\n \t\t\t\t\t\t\tif (refName == null)\n \t\t\t\t\t\t\t\tstartAt = repo.resolve(Constants.HEAD);\n \t\t\t\t\t\t\telse\n \t\t\t\t\t\t\t\tstartAt = repo.resolve(refName);\n+\t\t\t\t\t\t\tString startBranch;\n+\t\t\t\t\t\t\tif (startRef != null)\n+\t\t\t\t\t\t\t\tstartBranch = refName;\n+\t\t\t\t\t\t\telse\n+\t\t\t\t\t\t\t\tstartBranch = startAt.name();\n+\t\t\t\t\t\t\tstartBranch = repo.shortenRefName(startBranch);\n \t\t\t\t\t\t\tupdateRef.setNewObjectId(startAt);\n+\t\t\t\t\t\t\tupdateRef.setRefLogMessage(\"branch: Created from \" + startBranch, false);\n \t\t\t\t\t\t\tupdateRef.update();\n \t\t\t\t\t\t} catch (IOException e1) {\n \t\t\t\t\t\t\tActivator.logError(NLS.bind(\n@@ -338,6 +403,7 @@ public String isValid(String newText) {\n \t\t\t\t\t}\n \t\t\t\t}\n \n+\n \t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n \t\t\t\t\twidgetSelected(e);\n \t\t\t\t}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 1d21c81..f19db60 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -340,6 +340,9 @@ BranchSelectionDialog_ErrorCouldNotRefresh=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotRefreshBranchList=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotResolve=Could not attempt to resolve {0}\n BranchSelectionDialog_ErrorInvalidRefName=Invalid ref name\n+BranchSelectionDialog_ErrorCouldNotRenameRef=Failed to rename branch {0} -> {1}, status={2}\n+BranchSelectionDialog_ErrorCouldNotRenameRef2=Failed to rename branch {0} -> {1}\n+\n BranchSelectionDialog_LocalBranches=Local Branches\n BranchSelectionDialog_NewBranch=&New branch\n BranchSelectionDialog_NoBranchSeletectMessage=You must select a valid ref.\n@@ -347,7 +350,9 @@ BranchSelectionDialog_NoBranchSeletectTitle=No branch/tag selected\n BranchSelectionDialog_OkCheckout=&Checkout\n BranchSelectionDialog_OkReset=&Reset\n BranchSelectionDialog_QuestionNewBranchMessage=Enter name of new branch. It will branch from the selected branch. refs/heads/ will be prepended to the name you type\n+BranchSelectionDialog_QuestionNewBranchNameMessage=Enter new name of the {0} branch. refs/heads/ will be prepended to the name you type\n BranchSelectionDialog_QuestionNewBranchTitle=New branch\n+BranchSelectionDialog_QuestionNewBranchNameTitle=Rename branch\n BranchSelectionDialog_ReallyResetMessage=Resetting will overwrite any changes in your working directory.\\n\\nDo you wish to continue?\n BranchSelectionDialog_ReallyResetTitle=Really reset?\n BranchSelectionDialog_RemoteBranches=Remote Branches\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116040","messageId":"1244668945-12622-7-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1244668945-12622-6-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 6/7] Improve error reporting in the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:24Z","receivedAt":"2009-06-10T21:22:24Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    6 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |   59 ++++++++++++--------\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +\n 3 files changed, 43 insertions(+), 24 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex aa31a93..c120327 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -899,6 +899,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 28105af..43588ae 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -104,7 +104,7 @@ getShell().setText(\n \n \t\ttry {\n \t\t\tfillTreeWithBranches(null);\n-\t\t} catch (IOException e) {\n+\t\t} catch (Throwable e) {\n \t\t\tActivator.logError(UIText.BranchSelectionDialog_ErrorCouldNotRefresh, e);\n \t\t}\n \n@@ -294,6 +294,7 @@ public String isValid(String newText) {\n \t\t\t\t\t\t} catch (IOException e1) {\n \t\t\t\t\t\t\tActivator.logError(NLS.bind(\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t\treturn e1.getMessage();\n \t\t\t\t\t\t}\n \t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n \t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n@@ -332,27 +333,28 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n \t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n-\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n-\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n-\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n-\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\t\tnull,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\trefName, newRefName, renameRef\n+\t\t\t\t\t\t\t\t\t\t\t\t.getResult());\n \t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t// FIXME: Update HEAD\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\trefName, newRefName, e1.getMessage());\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -387,18 +389,20 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\t\tupdateRef.setNewObjectId(startAt);\n \t\t\t\t\t\t\tupdateRef.setRefLogMessage(\"branch: Created from \" + startBranch, false);\n \t\t\t\t\t\t\tupdateRef.update();\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotCreateNewRef,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t\t\t\tnewRefName);\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(e1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -419,4 +423,11 @@ createButton(parent, IDialogConstants.OK_ID,\n \tprotected int getShellStyle() {\n \t\treturn super.getShellStyle() | SWT.RESIZE;\n \t}\n+\n+\tprivate void reportError(Throwable e, String title, String message,\n+\t\t\tObject... args) {\n+\t\tString msg = NLS.bind(message, args);\n+\t\tMessageDialog.openError(getShell(), title, msg);\n+\t\tActivator.logError(msg, e);\n+\t}\n }\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex f19db60..93ce3c9 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -333,6 +333,8 @@ WindowCachePreferencePage_packedGitMMAP=Use virtual memory mapping\n \n BranchSelectionDialog_TitleCheckout=Checkout: {0}\n BranchSelectionDialog_TitleReset=Reset: {0}\n+BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle=New branch creation failed\n+BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle=Rename failed\n BranchSelectionDialog_BranchSuffix_Current=\\ (current)\n BranchSelectionDialog_ErrorAlreadyExists=Already exists\n BranchSelectionDialog_ErrorCouldNotCreateNewRef=Could not create new ref {0}\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116039","messageId":"1244668945-12622-8-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1244668945-12622-7-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v5 7/7] Remove a TAB from the message Egit generates into the reflog on commit","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-10T21:22:25Z","receivedAt":"2009-06-10T21:22:25Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"RefUpdate already adds a TAB\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../egit/ui/internal/actions/CommitAction.java     |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CommitAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CommitAction.java\nindex 03649c6..8415002 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CommitAction.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/CommitAction.java\n@@ -295,7 +295,7 @@ private String buildReflogMessage(String commitMessage) {\n \t\tif (newlineIndex > 0) {\n \t\t\tfirstLine = commitMessage.substring(0, newlineIndex);\n \t\t}\n-\t\tString commitStr = amending ? \"\\tcommit (amend):\" : \"\\tcommit: \";\n+\t\tString commitStr = amending ? \"commit (amend):\" : \"commit: \";\n \t\tString message = commitStr + firstLine;\n \t\treturn message;\n \t}\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116178","messageId":"20090612200228.GR16497@spearce.org","threadId":"19215","inReplyTo":"1244668945-12622-6-git-send-email-robin.rosenberg@dewire.com","subject":"Re: [EGIT PATCH v5 5/7] Add ref rename support to the branch dialog","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-06-12T20:02:28Z","receivedAt":"2009-06-12T20:02:28Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n> index 9aad95b..28105af 100644\n> --- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n> +++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n> @@ -284,43 +311,81 @@ protected void createButtonsForButtonBar(Composite parent) {\n>  \t\t\tnewButton.setFont(JFaceResources.getDialogFont());\n>  \t\t\tnewButton.setText(UIText.BranchSelectionDialog_NewBranch);\n>  \t\t\t((GridLayout)parent.getLayout()).numColumns++;\n> +\t\t\tButton renameButton = new Button(parent, SWT.PUSH);\n> +\t\t\trenameButton.setText(\"&Rename\");\n\nNot localized.\n\n> +\t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n> +\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n> +\t\t\t\t\t\t\t\t\t\t\"Rename failed\",\n\nNot localized.\n\n-- \nShawn.\n"},{"id":"116288","messageId":"1245008849-12132-1-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"20090612200228.GR16497@spearce.org","subject":"[EGIT PATCH v6 5a/7] Warn for unlocalized string literals in ui plugin.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-14T19:47:27Z","receivedAt":"2009-06-14T19:47:27Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Other changes are Eclipse 3.4 defaults.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../.settings/org.eclipse.jdt.core.prefs           |   12 ++++++++++--\n 1 files changed, 10 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/.settings/org.eclipse.jdt.core.prefs b/org.spearce.egit.ui/.settings/org.eclipse.jdt.core.prefs\nindex 6b39a92..b52927e 100644\n--- a/org.spearce.egit.ui/.settings/org.eclipse.jdt.core.prefs\n+++ b/org.spearce.egit.ui/.settings/org.eclipse.jdt.core.prefs\n@@ -1,4 +1,4 @@\n-#Sun Apr 06 22:05:18 CEST 2008\n+#Sun Jun 14 18:21:07 CEST 2009\n eclipse.preferences.version=1\n org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled\n org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.5\n@@ -18,6 +18,7 @@ org.eclipse.jdt.core.compiler.problem.discouragedReference=warning\n org.eclipse.jdt.core.compiler.problem.emptyStatement=warning\n org.eclipse.jdt.core.compiler.problem.enumIdentifier=error\n org.eclipse.jdt.core.compiler.problem.fallthroughCase=warning\n+org.eclipse.jdt.core.compiler.problem.fatalOptionalError=enabled\n org.eclipse.jdt.core.compiler.problem.fieldHiding=warning\n org.eclipse.jdt.core.compiler.problem.finalParameterBound=warning\n org.eclipse.jdt.core.compiler.problem.finallyBlockNotCompletingNormally=error\n@@ -44,7 +45,7 @@ org.eclipse.jdt.core.compiler.problem.missingOverrideAnnotation=ignore\n org.eclipse.jdt.core.compiler.problem.missingSerialVersion=warning\n org.eclipse.jdt.core.compiler.problem.noEffectAssignment=error\n org.eclipse.jdt.core.compiler.problem.noImplicitStringConversion=error\n-org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=ignore\n+org.eclipse.jdt.core.compiler.problem.nonExternalizedStringLiteral=warning\n org.eclipse.jdt.core.compiler.problem.nullReference=error\n org.eclipse.jdt.core.compiler.problem.overridingPackageDefaultMethod=warning\n org.eclipse.jdt.core.compiler.problem.parameterAssignment=warning\n@@ -52,24 +53,31 @@ org.eclipse.jdt.core.compiler.problem.possibleAccidentalBooleanAssignment=error\n org.eclipse.jdt.core.compiler.problem.potentialNullReference=warning\n org.eclipse.jdt.core.compiler.problem.rawTypeReference=ignore\n org.eclipse.jdt.core.compiler.problem.redundantNullCheck=warning\n+org.eclipse.jdt.core.compiler.problem.redundantSuperinterface=ignore\n org.eclipse.jdt.core.compiler.problem.specialParameterHidingField=disabled\n org.eclipse.jdt.core.compiler.problem.staticAccessReceiver=error\n org.eclipse.jdt.core.compiler.problem.suppressWarnings=enabled\n+org.eclipse.jdt.core.compiler.problem.syntheticAccessEmulation=ignore\n org.eclipse.jdt.core.compiler.problem.typeParameterHiding=warning\n org.eclipse.jdt.core.compiler.problem.uncheckedTypeOperation=warning\n org.eclipse.jdt.core.compiler.problem.undocumentedEmptyBlock=warning\n org.eclipse.jdt.core.compiler.problem.unhandledWarningToken=warning\n+org.eclipse.jdt.core.compiler.problem.unnecessaryElse=ignore\n org.eclipse.jdt.core.compiler.problem.unnecessaryTypeCheck=error\n org.eclipse.jdt.core.compiler.problem.unqualifiedFieldAccess=ignore\n org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownException=error\n+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionExemptExceptionAndThrowable=enabled\n+org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionIncludeDocCommentReference=enabled\n org.eclipse.jdt.core.compiler.problem.unusedDeclaredThrownExceptionWhenOverriding=disabled\n org.eclipse.jdt.core.compiler.problem.unusedImport=error\n org.eclipse.jdt.core.compiler.problem.unusedLabel=error\n org.eclipse.jdt.core.compiler.problem.unusedLocal=error\n org.eclipse.jdt.core.compiler.problem.unusedParameter=warning\n+org.eclipse.jdt.core.compiler.problem.unusedParameterIncludeDocCommentReference=enabled\n org.eclipse.jdt.core.compiler.problem.unusedParameterWhenImplementingAbstract=disabled\n org.eclipse.jdt.core.compiler.problem.unusedParameterWhenOverridingConcrete=disabled\n org.eclipse.jdt.core.compiler.problem.unusedPrivateMember=error\n+org.eclipse.jdt.core.compiler.problem.unusedWarningToken=warning\n org.eclipse.jdt.core.compiler.problem.varargsArgumentNeedCast=error\n org.eclipse.jdt.core.compiler.source=1.5\n org.eclipse.jdt.core.formatter.align_type_members_on_columns=false\n-- \n1.6.3.2.199.g7340d\n"},{"id":"116286","messageId":"1245008849-12132-2-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1245008849-12132-1-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v6 5b/6] Add ref rename support to the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-14T19:47:28Z","receivedAt":"2009-06-14T19:47:28Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |   16 +++\n .../ui/internal/dialogs/BranchSelectionDialog.java |  108 ++++++++++++++++----\n .../src/org/spearce/egit/ui/uitext.properties      |    7 ++\n 3 files changed, 110 insertions(+), 21 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 654e155..76764ab 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -893,6 +893,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRefresh;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\n@@ -929,6 +935,12 @@\n \tpublic static String BranchSelectionDialog_QuestionNewBranchTitle;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_QuestionNewBranchNameTitle;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_QuestionNewBranchNameMessage;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_QuestionNewBranchMessage;\n \n \t/** */\n@@ -943,6 +955,8 @@\n \t/** */\n \tpublic static String BranchSelectionDialog_ErrorInvalidRefName;\n \n+\tpublic static String BranchSelectionDialog_ErrorRenameFailed;\n+\n \t/** */\n \tpublic static String BranchSelectionDialog_OkCheckout;\n \n@@ -952,6 +966,8 @@\n \t/** */\n \tpublic static String BranchSelectionDialog_Refs;\n \n+\tpublic static String BranchSelectionDialog_Rename;\n+\n \t/** */\n \tpublic static String HistoryPage_ShowAllVersionsForProject;\n \ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex 9aad95b..f0c13d1 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -48,8 +48,11 @@\n import org.spearce.egit.ui.UIText;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.RefRename;\n import org.spearce.jgit.lib.RefUpdate;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RefUpdate.Result;\n \n /**\n  * The branch and reset selection dialog\n@@ -173,7 +176,7 @@ private void fillTreeWithBranches(String select) throws IOException {\n \t\t\t\t\tcurSubPrefix = null;\n \t\t\t\t}\n \n-\t\t\t\tint slashPos = shortName.indexOf(\"/\");\n+\t\t\t\tint slashPos = shortName.indexOf(\"/\"); //$NON-NLS-1$\n \t\t\t\tif (slashPos > -1) {\n \t\t\t\t\tString remoteName = shortName.substring(0, slashPos);\n \t\t\t\t\tshortName = shortName.substring(slashPos+1);\n@@ -277,6 +280,30 @@ private void refNameFromDialog() {\n \t\t}\n \t}\n \n+\tprivate InputDialog getRefNameInputDialog(String prompt) {\n+\t\tInputDialog labelDialog = new InputDialog(\n+\t\t\t\tgetShell(),\n+\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n+\t\t\t\tprompt,\n+\t\t\t\tnull, new IInputValidator() {\n+\t\t\t\t\tpublic String isValid(String newText) {\n+\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n+\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n+\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n+\t\t\t\t\t\treturn null;\n+\t\t\t\t\t}\n+\t\t\t\t});\n+\t\tlabelDialog.setBlockOnOpen(true);\n+\t\treturn labelDialog;\n+\t}\n+\n \t@Override\n \tprotected void createButtonsForButtonBar(Composite parent) {\n \t\tif (!showResetType) {\n@@ -284,43 +311,81 @@ protected void createButtonsForButtonBar(Composite parent) {\n \t\t\tnewButton.setFont(JFaceResources.getDialogFont());\n \t\t\tnewButton.setText(UIText.BranchSelectionDialog_NewBranch);\n \t\t\t((GridLayout)parent.getLayout()).numColumns++;\n+\t\t\tButton renameButton = new Button(parent, SWT.PUSH);\n+\t\t\trenameButton.setText(UIText.BranchSelectionDialog_Rename);\n+\t\t\trenameButton.addSelectionListener(new SelectionListener() {\n+\t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n+\t\t\t\t\t// check what ref name the user selected, if any.\n+\t\t\t\t\trefNameFromDialog();\n+\n+\t\t\t\t\tString branchName;\n+\t\t\t\t\tif (refName.startsWith(Constants.R_HEADS))\n+\t\t\t\t\t\tbranchName = refName.substring(Constants.R_HEADS.length());\n+\t\t\t\t\telse\n+\t\t\t\t\t\tbranchName = refName;\t\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(NLS\n+\t\t\t\t\t\t\t.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchNameMessage,\n+\t\t\t\t\t\t\t\t\tbranchName));\n+\t\t\t\t\tif (labelDialog.open() == Window.OK) {\n+\t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n+\t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n+\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorRenameFailed,\n+\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n+\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t}\n+\t\t\t\t\t\t\t// FIXME: Update HEAD\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n+\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\ttry {\n+\t\t\t\t\t\t\tbranchTree.removeAll();\n+\t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n+\t\t\t\t\t\t} catch (IOException e1) {\n+\t\t\t\t\t\t\tActivator.logError(\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n+\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n+\t\t\t\t\twidgetSelected(e);\n+\t\t\t\t}\n+\t\t\t});\n \t\t\tnewButton.addSelectionListener(new SelectionListener() {\n \n \t\t\t\tpublic void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t// check what ref name the user selected, if any.\n \t\t\t\t\trefNameFromDialog();\n \n-\t\t\t\t\tInputDialog labelDialog = new InputDialog(\n-\t\t\t\t\t\t\tgetShell(),\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchTitle,\n-\t\t\t\t\t\t\tUIText.BranchSelectionDialog_QuestionNewBranchMessage,\n-\t\t\t\t\t\t\tnull, new IInputValidator() {\n-\t\t\t\t\t\t\t\tpublic String isValid(String newText) {\n-\t\t\t\t\t\t\t\t\tString testFor = Constants.R_HEADS + newText;\n-\t\t\t\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\t\t\t\tif (repo.resolve(testFor) != null)\n-\t\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorAlreadyExists;\n-\t\t\t\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n-\t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n-\t\t\t\t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n-\t\t\t\t\t\t\t\t\treturn null;\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t});\n-\t\t\t\t\tlabelDialog.setBlockOnOpen(true);\n+\t\t\t\t\tInputDialog labelDialog = getRefNameInputDialog(UIText.BranchSelectionDialog_QuestionNewBranchNameMessage);\n \t\t\t\t\tif (labelDialog.open() == Window.OK) {\n \t\t\t\t\t\tString newRefName = Constants.R_HEADS + labelDialog.getValue();\n \t\t\t\t\t\tRefUpdate updateRef;\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tupdateRef = repo.updateRef(newRefName);\n+\t\t\t\t\t\t\tRef startRef = repo.getRef(refName);\n \t\t\t\t\t\t\tObjectId startAt;\n \t\t\t\t\t\t\tif (refName == null)\n \t\t\t\t\t\t\t\tstartAt = repo.resolve(Constants.HEAD);\n \t\t\t\t\t\t\telse\n \t\t\t\t\t\t\t\tstartAt = repo.resolve(refName);\n+\t\t\t\t\t\t\tString startBranch;\n+\t\t\t\t\t\t\tif (startRef != null)\n+\t\t\t\t\t\t\t\tstartBranch = refName;\n+\t\t\t\t\t\t\telse\n+\t\t\t\t\t\t\t\tstartBranch = startAt.name();\n+\t\t\t\t\t\t\tstartBranch = repo.shortenRefName(startBranch);\n \t\t\t\t\t\t\tupdateRef.setNewObjectId(startAt);\n+\t\t\t\t\t\t\tupdateRef.setRefLogMessage(\"branch: Created from \" + startBranch, false); //$NON-NLS-1$\n \t\t\t\t\t\t\tupdateRef.update();\n \t\t\t\t\t\t} catch (IOException e1) {\n \t\t\t\t\t\t\tActivator.logError(NLS.bind(\n@@ -338,6 +403,7 @@ public String isValid(String newText) {\n \t\t\t\t\t}\n \t\t\t\t}\n \n+\n \t\t\t\tpublic void widgetDefaultSelected(SelectionEvent e) {\n \t\t\t\t\twidgetSelected(e);\n \t\t\t\t}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex 1d21c81..a2fd720 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -340,6 +340,10 @@ BranchSelectionDialog_ErrorCouldNotRefresh=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotRefreshBranchList=Could not refresh list of branches\n BranchSelectionDialog_ErrorCouldNotResolve=Could not attempt to resolve {0}\n BranchSelectionDialog_ErrorInvalidRefName=Invalid ref name\n+BranchSelectionDialog_ErrorCouldNotRenameRef=Failed to rename branch {0} -> {1}, status={2}\n+BranchSelectionDialog_ErrorCouldNotRenameRef2=Failed to rename branch {0} -> {1}\n+BranchSelectionDialog_ErrorRenameFailed=Rename failed\n+\n BranchSelectionDialog_LocalBranches=Local Branches\n BranchSelectionDialog_NewBranch=&New branch\n BranchSelectionDialog_NoBranchSeletectMessage=You must select a valid ref.\n@@ -347,7 +351,9 @@ BranchSelectionDialog_NoBranchSeletectTitle=No branch/tag selected\n BranchSelectionDialog_OkCheckout=&Checkout\n BranchSelectionDialog_OkReset=&Reset\n BranchSelectionDialog_QuestionNewBranchMessage=Enter name of new branch. It will branch from the selected branch. refs/heads/ will be prepended to the name you type\n+BranchSelectionDialog_QuestionNewBranchNameMessage=Enter new name of the {0} branch. refs/heads/ will be prepended to the name you type\n BranchSelectionDialog_QuestionNewBranchTitle=New branch\n+BranchSelectionDialog_QuestionNewBranchNameTitle=Rename branch\n BranchSelectionDialog_ReallyResetMessage=Resetting will overwrite any changes in your working directory.\\n\\nDo you wish to continue?\n BranchSelectionDialog_ReallyResetTitle=Really reset?\n BranchSelectionDialog_RemoteBranches=Remote Branches\n@@ -357,6 +363,7 @@ BranchSelectionDialog_ResetTypeMixed=&Mixed (working directory unmodified)\n BranchSelectionDialog_ResetTypeSoft=&Soft (Index and working directory unmodified)\n BranchSelectionDialog_Tags=Tags\n BranchSelectionDialog_Refs=&Refs\n+BranchSelectionDialog_Rename=&Rename\n \n Decorator_exceptionMessage=Errors occurred while applying Git decorations to resources.\n \n-- \n1.6.3.2.199.g7340d\n"},{"id":"116287","messageId":"1245008849-12132-3-git-send-email-robin.rosenberg@dewire.com","threadId":"19215","inReplyTo":"1245008849-12132-2-git-send-email-robin.rosenberg@dewire.com","subject":"[EGIT PATCH v6 6/7] Improve error reporting in the branch dialog","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-06-14T19:47:29Z","receivedAt":"2009-06-14T19:47:29Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n .../src/org/spearce/egit/ui/UIText.java            |    6 ++\n .../ui/internal/dialogs/BranchSelectionDialog.java |   59 ++++++++++++--------\n .../src/org/spearce/egit/ui/uitext.properties      |    2 +\n 3 files changed, 43 insertions(+), 24 deletions(-)\n\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\nindex 76764ab..b0e8c3e 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/UIText.java\n@@ -899,6 +899,12 @@\n \tpublic static String BranchSelectionDialog_ErrorCouldNotRenameRef2;\n \n \t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle;\n+\n+\t/** */\n+\tpublic static String BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle;\n+\n+\t/** */\n \tpublic static String BranchSelectionDialog_BranchSuffix_Current;\n \n \t/** */\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\nindex f0c13d1..09b6381 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/dialogs/BranchSelectionDialog.java\n@@ -104,7 +104,7 @@ getShell().setText(\n \n \t\ttry {\n \t\t\tfillTreeWithBranches(null);\n-\t\t} catch (IOException e) {\n+\t\t} catch (Throwable e) {\n \t\t\tActivator.logError(UIText.BranchSelectionDialog_ErrorCouldNotRefresh, e);\n \t\t}\n \n@@ -294,6 +294,7 @@ public String isValid(String newText) {\n \t\t\t\t\t\t} catch (IOException e1) {\n \t\t\t\t\t\t\tActivator.logError(NLS.bind(\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotResolve, testFor), e1);\n+\t\t\t\t\t\t\treturn e1.getMessage();\n \t\t\t\t\t\t}\n \t\t\t\t\t\tif (!Repository.isValidRefName(testFor))\n \t\t\t\t\t\t\treturn UIText.BranchSelectionDialog_ErrorInvalidRefName;\n@@ -332,27 +333,28 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tRefRename renameRef = repo.renameRef(refName, newRefName);\n \t\t\t\t\t\t\tif (renameRef.rename() != Result.RENAMED) {\n-\t\t\t\t\t\t\t\tMessageDialog.openError(getShell(),\n-\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorRenameFailed,\n-\t\t\t\t\t\t\t\t\t\tNLS.bind(UIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName, renameRef.getResult() }));\n-\t\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\tnew Object[] { refName, newRefName }), null);\n+\t\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\t\tnull,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\t\trefName, newRefName, renameRef\n+\t\t\t\t\t\t\t\t\t\t\t\t.getResult());\n \t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t// FIXME: Update HEAD\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef2,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRenameRef,\n+\t\t\t\t\t\t\t\t\trefName, newRefName, e1.getMessage());\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -387,18 +389,20 @@ public void widgetSelected(SelectionEvent e) {\n \t\t\t\t\t\t\tupdateRef.setNewObjectId(startAt);\n \t\t\t\t\t\t\tupdateRef.setRefLogMessage(\"branch: Created from \" + startBranch, false); //$NON-NLS-1$\n \t\t\t\t\t\t\tupdateRef.update();\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(NLS.bind(\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(\n+\t\t\t\t\t\t\t\t\te1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n \t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotCreateNewRef,\n-\t\t\t\t\t\t\t\t\t\t\t\t\t\t\tnewRefName), e1);\n+\t\t\t\t\t\t\t\t\tnewRefName);\n \t\t\t\t\t\t}\n \t\t\t\t\t\ttry {\n \t\t\t\t\t\t\tbranchTree.removeAll();\n \t\t\t\t\t\t\tfillTreeWithBranches(newRefName);\n-\t\t\t\t\t\t} catch (IOException e1) {\n-\t\t\t\t\t\t\tActivator.logError(\n-\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList,\n-\t\t\t\t\t\t\t\t\t\t\te1);\n+\t\t\t\t\t\t} catch (Throwable e1) {\n+\t\t\t\t\t\t\treportError(e1,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle,\n+\t\t\t\t\t\t\t\t\tUIText.BranchSelectionDialog_ErrorCouldNotRefreshBranchList);\n \t\t\t\t\t\t}\n \t\t\t\t\t}\n \t\t\t\t}\n@@ -419,4 +423,11 @@ createButton(parent, IDialogConstants.OK_ID,\n \tprotected int getShellStyle() {\n \t\treturn super.getShellStyle() | SWT.RESIZE;\n \t}\n+\n+\tprivate void reportError(Throwable e, String title, String message,\n+\t\t\tObject... args) {\n+\t\tString msg = NLS.bind(message, args);\n+\t\tMessageDialog.openError(getShell(), title, msg);\n+\t\tActivator.logError(msg, e);\n+\t}\n }\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\nindex a2fd720..a141c90 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/uitext.properties\n@@ -333,6 +333,8 @@ WindowCachePreferencePage_packedGitMMAP=Use virtual memory mapping\n \n BranchSelectionDialog_TitleCheckout=Checkout: {0}\n BranchSelectionDialog_TitleReset=Reset: {0}\n+BranchSelectionDialog_BranchSelectionDialog_CreateFailedTitle=New branch creation failed\n+BranchSelectionDialog_BranchSelectionDialog_RenamedFailedTitle=Rename failed\n BranchSelectionDialog_BranchSuffix_Current=\\ (current)\n BranchSelectionDialog_ErrorAlreadyExists=Already exists\n BranchSelectionDialog_ErrorCouldNotCreateNewRef=Could not create new ref {0}\n-- \n1.6.3.2.199.g7340d\n"}]}