{"thread":{"id":"18301","subject":"[JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","startedAt":"2009-03-13T14:35:01Z","lastAt":"2009-03-16T23:58:44Z","messageCount":8,"participants":["Shawn O. Pearce","Johannes Schindelin","Robin Rosenberg"],"isPatch":true,"patchVersion":1,"patchTotal":null},"messages":[{"id":"107952","messageId":"1236954901-30990-1-git-send-email-spearce@spearce.org","threadId":"18301","inReplyTo":null,"subject":"[JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-13T14:35:01Z","receivedAt":"2009-03-13T14:35:01Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This is a command line tool which can be used to import only\nthe commit graph of another Git repository, to help debug a\ntopology related bug, without actually needing a clone of the\nproblematic repository.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n\n I used this yesterday to obtain a DAG from someone's private\n repository and recreate the same structure on my own system,\n to debug why RevWalk was busted in their case.\n\n Its only meant for debugging weird cases, I can't see many\n uses of it.  But now that its written, maybe we should add\n it to the project and keep it around.\n\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    1 +\n .../org/spearce/jgit/pgm/RebuildCommitGraph.java   |  276 ++++++++++++++++++++\n 2 files changed, 277 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\n\ndiff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\nindex 1ba29e6..9bba156 100644\n--- a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n@@ -11,6 +11,7 @@ org.spearce.jgit.pgm.LsRemote\n org.spearce.jgit.pgm.LsTree\n org.spearce.jgit.pgm.MergeBase\n org.spearce.jgit.pgm.Push\n+org.spearce.jgit.pgm.RebuildCommitGraph\n org.spearce.jgit.pgm.ReceivePack\n org.spearce.jgit.pgm.RevList\n org.spearce.jgit.pgm.Rm\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\nnew file mode 100644\nindex 0000000..ccee997\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\n@@ -0,0 +1,276 @@\n+/*\n+ * Copyright (C) 2009, Google Inc.\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+\n+package org.spearce.jgit.pgm;\n+\n+import java.io.BufferedReader;\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.IOException;\n+import java.io.InputStreamReader;\n+import java.util.ArrayList;\n+import java.util.Date;\n+import java.util.HashMap;\n+import java.util.List;\n+import java.util.ListIterator;\n+import java.util.Map;\n+\n+import org.kohsuke.args4j.Argument;\n+import org.spearce.jgit.errors.MissingObjectException;\n+import org.spearce.jgit.errors.ObjectWritingException;\n+import org.spearce.jgit.lib.Commit;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.LockFile;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ObjectWriter;\n+import org.spearce.jgit.lib.PersonIdent;\n+import org.spearce.jgit.lib.ProgressMonitor;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.RefUpdate;\n+import org.spearce.jgit.lib.RefWriter;\n+import org.spearce.jgit.lib.TextProgressMonitor;\n+import org.spearce.jgit.lib.Tree;\n+import org.spearce.jgit.revwalk.RevWalk;\n+\n+/**\n+ * Recreates a repository from another one's commit graph.\n+ * <p>\n+ * <b>Do not run this on a repository unless you want to destroy it.</b>\n+ * <p>\n+ * To create the input files, in the source repository use:\n+ * \n+ * <pre>\n+ * git for-each-ref &gt;in.refs\n+ * git log --all '--pretty=format:%H %ct %P' &gt;in.dag\n+ * </pre>\n+ * <p>\n+ * Run the rebuild in either an empty repository, or a clone of the source. Any\n+ * missing commits (which might be the entire graph) will be created. All refs\n+ * will be modified to match the input exactly, which means some refs may be\n+ * deleted from the current repository.\n+ * <p>\n+ */\n+class RebuildCommitGraph extends TextBuiltin {\n+\t@Argument(index = 0, required = true, metaVar = \"REFS\", usage = \"for-each-ref output\")\n+\tFile refList;\n+\n+\t@Argument(index = 1, required = true, metaVar = \"DAG\", usage = \"log --all '--pretty=format:%H %ct %P' output\")\n+\tFile graph;\n+\n+\tprivate final ProgressMonitor pm = new TextProgressMonitor();\n+\n+\tprivate Map<ObjectId, ObjectId> rewrites = new HashMap<ObjectId, ObjectId>();\n+\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\trecreateCommitGraph();\n+\t\tdetachHead();\n+\t\tdeleteAllRefs();\n+\t\trecreateRefs();\n+\t}\n+\n+\tprivate void recreateCommitGraph() throws IOException {\n+\t\tfinal RevWalk rw = new RevWalk(db);\n+\t\tfinal Map<ObjectId, ToRewrite> toRewrite = new HashMap<ObjectId, ToRewrite>();\n+\t\tList<ToRewrite> queue = new ArrayList<ToRewrite>();\n+\t\tfinal BufferedReader br = new BufferedReader(new InputStreamReader(\n+\t\t\t\tnew FileInputStream(graph), Constants.CHARSET));\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = br.readLine()) != null) {\n+\t\t\t\tfinal String[] parts = line.split(\"[ \\t]{1,}\");\n+\t\t\t\tfinal ObjectId oldId = ObjectId.fromString(parts[0]);\n+\t\t\t\ttry {\n+\t\t\t\t\trw.parseCommit(oldId);\n+\t\t\t\t\t// We have it already. Don't rewrite it.\n+\t\t\t\t\tcontinue;\n+\t\t\t\t} catch (MissingObjectException mue) {\n+\t\t\t\t\t// Fall through and rewrite it.\n+\t\t\t\t}\n+\n+\t\t\t\tfinal long time = Long.parseLong(parts[1]) * 1000L;\n+\t\t\t\tfinal ObjectId[] parents = new ObjectId[parts.length - 2];\n+\t\t\t\tfor (int i = 0; i < parents.length; i++) {\n+\t\t\t\t\tparents[i] = ObjectId.fromString(parts[2 + i]);\n+\t\t\t\t}\n+\n+\t\t\t\tfinal ToRewrite t = new ToRewrite(oldId, time, parents);\n+\t\t\t\ttoRewrite.put(oldId, t);\n+\t\t\t\tqueue.add(t);\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tbr.close();\n+\t\t}\n+\n+\t\tpm.beginTask(\"Rewriting commits\", queue.size());\n+\t\tfinal ObjectWriter ow = new ObjectWriter(db);\n+\t\tfinal ObjectId emptyTree = ow.writeTree(new Tree(db));\n+\t\tfinal PersonIdent me = new PersonIdent(\"jgit rebuild-commitgraph\",\n+\t\t\t\t\"rebuild-commitgraph@localhost\");\n+\t\twhile (!queue.isEmpty()) {\n+\t\t\tfinal ListIterator<ToRewrite> itr = queue\n+\t\t\t\t\t.listIterator(queue.size());\n+\t\t\tqueue = new ArrayList<ToRewrite>();\n+\t\t\tREWRITE: while (itr.hasPrevious()) {\n+\t\t\t\tfinal ToRewrite t = itr.previous();\n+\t\t\t\tfinal ObjectId[] newParents = new ObjectId[t.oldParents.length];\n+\t\t\t\tfor (int k = 0; k < t.oldParents.length; k++) {\n+\t\t\t\t\tfinal ToRewrite p = toRewrite.get(t.oldParents[k]);\n+\t\t\t\t\tif (p != null) {\n+\t\t\t\t\t\tif (p.newId == null) {\n+\t\t\t\t\t\t\t// Must defer until after the parent is rewritten.\n+\t\t\t\t\t\t\tqueue.add(t);\n+\t\t\t\t\t\t\tcontinue REWRITE;\n+\t\t\t\t\t\t} else {\n+\t\t\t\t\t\t\tnewParents[k] = p.newId;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\t// We have the old parent object. Use it.\n+\t\t\t\t\t\t//\n+\t\t\t\t\t\tnewParents[k] = t.oldParents[k];\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tfinal Commit newc = new Commit(db);\n+\t\t\t\tnewc.setTreeId(emptyTree);\n+\t\t\t\tnewc.setAuthor(new PersonIdent(me, new Date(t.commitTime)));\n+\t\t\t\tnewc.setCommitter(newc.getAuthor());\n+\t\t\t\tnewc.setParentIds(newParents);\n+\t\t\t\tnewc.setMessage(\"ORIGINAL \" + t.oldId.name() + \"\\n\");\n+\t\t\t\tt.newId = ow.writeCommit(newc);\n+\t\t\t\trewrites.put(t.oldId, t.newId);\n+\t\t\t\tpm.update(1);\n+\t\t\t}\n+\t\t}\n+\t\tpm.endTask();\n+\t}\n+\n+\tprivate static class ToRewrite {\n+\t\tfinal ObjectId oldId;\n+\n+\t\tfinal long commitTime;\n+\n+\t\tfinal ObjectId[] oldParents;\n+\n+\t\tObjectId newId;\n+\n+\t\tToRewrite(final ObjectId o, final long t, final ObjectId[] p) {\n+\t\t\toldId = o;\n+\t\t\tcommitTime = t;\n+\t\t\toldParents = p;\n+\t\t}\n+\t}\n+\n+\tprivate void detachHead() throws IOException {\n+\t\tfinal String head = db.getFullBranch();\n+\t\tfinal ObjectId id = db.resolve(Constants.HEAD);\n+\t\tif (!ObjectId.isId(head) && id != null) {\n+\t\t\tfinal LockFile lf;\n+\t\t\tlf = new LockFile(new File(db.getDirectory(), Constants.HEAD));\n+\t\t\tif (!lf.lock())\n+\t\t\t\tthrow new IOException(\"Cannot lock HEAD\");\n+\t\t\tlf.write(id);\n+\t\t\tif (!lf.commit())\n+\t\t\t\tthrow new IOException(\"Cannot deatch HEAD\");\n+\t\t}\n+\t}\n+\n+\tprivate void deleteAllRefs() throws Exception {\n+\t\tfinal RevWalk rw = new RevWalk(db);\n+\t\tfor (final Ref r : db.getAllRefs().values()) {\n+\t\t\tif (Constants.HEAD.equals(r.getName()))\n+\t\t\t\tcontinue;\n+\t\t\tfinal RefUpdate u = db.updateRef(r.getName());\n+\t\t\tu.setForceUpdate(true);\n+\t\t\tu.delete(rw);\n+\t\t}\n+\t}\n+\n+\tprivate void recreateRefs() throws Exception {\n+\t\tfinal Map<String, Ref> refs = computeNewRefs();\n+\t\tnew RefWriter(refs.values()) {\n+\t\t\t@Override\n+\t\t\tprotected void writeFile(final String name, final byte[] content)\n+\t\t\t\t\tthrows IOException {\n+\t\t\t\tfinal File file = new File(db.getDirectory(), name);\n+\t\t\t\tfinal LockFile lck = new LockFile(file);\n+\t\t\t\tif (!lck.lock())\n+\t\t\t\t\tthrow new ObjectWritingException(\"Can't write \" + file);\n+\t\t\t\ttry {\n+\t\t\t\t\tlck.write(content);\n+\t\t\t\t} catch (IOException ioe) {\n+\t\t\t\t\tthrow new ObjectWritingException(\"Can't write \" + file);\n+\t\t\t\t}\n+\t\t\t\tif (!lck.commit())\n+\t\t\t\t\tthrow new ObjectWritingException(\"Can't write \" + file);\n+\t\t\t}\n+\t\t}.writePackedRefs();\n+\t}\n+\n+\tprivate Map<String, Ref> computeNewRefs() throws IOException {\n+\t\tfinal RevWalk rw = new RevWalk(db);\n+\t\tfinal Map<String, Ref> refs = new HashMap<String, Ref>();\n+\t\tfinal BufferedReader br = new BufferedReader(new InputStreamReader(\n+\t\t\t\tnew FileInputStream(refList), Constants.CHARSET));\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = br.readLine()) != null) {\n+\t\t\t\tfinal String[] parts = line.split(\"[ \\t]{1,}\");\n+\t\t\t\tfinal ObjectId origId = ObjectId.fromString(parts[0]);\n+\t\t\t\tfinal String type = parts[1];\n+\t\t\t\tfinal String name = parts[2];\n+\n+\t\t\t\tObjectId id = rewrites.get(origId);\n+\t\t\t\tif (id == null)\n+\t\t\t\t\tid = origId;\n+\t\t\t\ttry {\n+\t\t\t\t\trw.parseAny(id);\n+\t\t\t\t} catch (MissingObjectException mue) {\n+\t\t\t\t\tif (!Constants.TYPE_COMMIT.equals(type)) {\n+\t\t\t\t\t\tSystem.err.println(\"skipping \" + type + \" \" + name);\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\t}\n+\t\t\t\t\tthrow new MissingObjectException(id, type);\n+\t\t\t\t}\n+\t\t\t\trefs.put(name, new Ref(Ref.Storage.PACKED, name, id));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tbr.close();\n+\t\t}\n+\t\treturn refs;\n+\t}\n+}\n-- \n1.6.2.288.gc3f22\n"},{"id":"107953","messageId":"alpine.DEB.1.00.0903131549200.6288@intel-tinevez-2-302","threadId":"18301","inReplyTo":"1236954901-30990-1-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","fromName":"Johannes Schindelin","fromEmail":"johannes.schindelin@gmx.de","sentAt":"2009-03-13T14:49:42Z","receivedAt":"2009-03-13T14:49:42Z","isPatch":true,"sender":{"key":"johannes.schindelin@gmx.de","avatar":"https://avatars.githubusercontent.com/u/127790?v=4"},"body":"Hi,\n\nOn Fri, 13 Mar 2009, Shawn O. Pearce wrote:\n\n> This is a command line tool which can be used to import only\n> the commit graph of another Git repository, to help debug a\n> topology related bug, without actually needing a clone of the\n> problematic repository.\n> \n> Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n> ---\n> \n>  I used this yesterday to obtain a DAG from someone's private\n>  repository and recreate the same structure on my own system,\n>  to debug why RevWalk was busted in their case.\n\nHeh:\n\nhttp://repo.or.cz/w/git/dscho.git?a=blob;f=my-fake-history.sh;h=e108ce99a5ebd75230c3cfc56b88d9769fec8bc1;hb=master\n\nCiao,\nDscho\n"},{"id":"107954","messageId":"20090313145250.GD22920@spearce.org","threadId":"18301","inReplyTo":"alpine.DEB.1.00.0903131549200.6288@intel-tinevez-2-302","subject":"Re: [JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-13T14:52:51Z","receivedAt":"2009-03-13T14:52:51Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Johannes Schindelin <Johannes.Schindelin@gmx.de> wrote:\n> On Fri, 13 Mar 2009, Shawn O. Pearce wrote:\n> \n> > This is a command line tool which can be used to import only\n> > the commit graph of another Git repository, to help debug a\n> > topology related bug, without actually needing a clone of the\n> > problematic repository.\n> > \n> > Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n> > ---\n> > \n> >  I used this yesterday to obtain a DAG from someone's private\n> >  repository and recreate the same structure on my own system,\n> >  to debug why RevWalk was busted in their case.\n> \n> Heh:\n> \n> http://repo.or.cz/w/git/dscho.git?a=blob;f=my-fake-history.sh;h=e108ce99a5ebd75230c3cfc56b88d9769fec8bc1;hb=master\n\nHeh.  I knew both you and Nico have written something like this in\nthe past, but I couldn't find it.\n\nYour script is almost what I needed.  But I also needed to force\nGIT_COMMITTER_DATE to match the timestamp, as I suspected the commit\ntimes had something to do with the problem I was seeing.\n\nI also whack the entire ref space to match the source repository,\nwhich mattered because I was trying to debug:\n\n  git rev-list $insha1 --not --all\n\nin JGit...\n\n-- \nShawn.\n"},{"id":"108041","messageId":"200903151234.39367.robin.rosenberg@dewire.com","threadId":"18301","inReplyTo":"1236954901-30990-1-git-send-email-spearce@spearce.org","subject":"Re: [JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-03-15T11:34:39Z","receivedAt":"2009-03-15T11:34:39Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"\nI'd hate to put such a dangerous thing in the list of normal tools. If the user\nwant to shoot him/her-self in the foot they should get a license first.\n\n-- robin\n"},{"id":"108086","messageId":"20090316144450.GN22920@spearce.org","threadId":"18301","inReplyTo":"200903151234.39367.robin.rosenberg@dewire.com","subject":"Re: [JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-16T14:44:50Z","receivedAt":"2009-03-16T14:44:50Z","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> \n> I'd hate to put such a dangerous thing in the list of normal tools. If the user\n> want to shoot him/her-self in the foot they should get a license first.\n\nHow about squashing this in?\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\nindex ccee997..cc23438 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\n@@ -50,6 +50,7 @@\n import java.util.Map;\n \n import org.kohsuke.args4j.Argument;\n+import org.kohsuke.args4j.Option;\n import org.spearce.jgit.errors.MissingObjectException;\n import org.spearce.jgit.errors.ObjectWritingException;\n import org.spearce.jgit.lib.Commit;\n@@ -85,6 +86,11 @@\n  * <p>\n  */\n class RebuildCommitGraph extends TextBuiltin {\n+\tprivate final String REALLY = \"--destroy-this-repository\";\n+\n+\t@Option(name = REALLY, usage = \"approve destruction of repository\")\n+\tboolean really;\n+\n \t@Argument(index = 0, required = true, metaVar = \"REFS\", usage = \"for-each-ref output\")\n \tFile refList;\n \n@@ -97,6 +103,30 @@\n \n \t@Override\n \tprotected void run() throws Exception {\n+\t\tif (!really && !db.getAllRefs().isEmpty()) {\n+\t\t\tfinal StringBuilder m = new StringBuilder();\n+\t\t\tm.append(\"fatal: \");\n+\t\t\tm.append(\"This program will destroy the repository:\");\n+\t\t\tm.append(\"\\n\");\n+\t\t\tm.append(\"fatal:\\n\");\n+\t\t\tm.append(\"fatal:    \");\n+\t\t\tm.append(db.getDirectory().getAbsolutePath());\n+\t\t\tm.append(\"\\n\");\n+\t\t\tm.append(\"fatal:\\n\");\n+\t\t\tm.append(\"fatal: \");\n+\t\t\tm.append(\"To continue, add \");\n+\t\t\tm.append(REALLY);\n+\t\t\tm.append(\" to the command line\");\n+\t\t\tm.append(\"\\n\");\n+\t\t\tm.append(\"fatal:\");\n+\t\t\tSystem.err.println(m);\n+\t\t\tthrow die(\"Need approval to destroy current repository\");\n+\t\t}\n+\t\tif (!refList.isFile())\n+\t\t\tthrow die(\"no such file: \" + refList.getPath());\n+\t\tif (!graph.isFile())\n+\t\t\tthrow die(\"no such file: \" + graph.getPath());\n+\n \t\trecreateCommitGraph();\n \t\tdetachHead();\n \t\tdeleteAllRefs();\n-- \nShawn.\n"},{"id":"108118","messageId":"200903162109.46653.robin.rosenberg.lists@dewire.com","threadId":"18301","inReplyTo":"20090316144450.GN22920@spearce.org","subject":"Re: [JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-03-16T20:09:46Z","receivedAt":"2009-03-16T20:09:46Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"måndag 16 mars 2009 15:44:50 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> > \n> > I'd hate to put such a dangerous thing in the list of normal tools. If the user\n> > want to shoot him/her-self in the foot they should get a license first.\n> \n> How about squashing this in?\n\nDoes it have to be a utility accessible through the pgm interface? Why not \njust run ut as java org.spearce.jgit.pgm.debug.RebuildCommitGraph ? \n\n> \n> diff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\n> index ccee997..cc23438 100644\n> --- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\n> +++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/RebuildCommitGraph.java\n> @@ -50,6 +50,7 @@\n>  import java.util.Map;\n>  \n>  import org.kohsuke.args4j.Argument;\n> +import org.kohsuke.args4j.Option;\n>  import org.spearce.jgit.errors.MissingObjectException;\n>  import org.spearce.jgit.errors.ObjectWritingException;\n>  import org.spearce.jgit.lib.Commit;\n> @@ -85,6 +86,11 @@\n>   * <p>\n>   */\n>  class RebuildCommitGraph extends TextBuiltin {\n> +\tprivate final String REALLY = \"--destroy-this-repository\";\n> +\n> +\t@Option(name = REALLY, usage = \"approve destruction of repository\")\n> +\tboolean really;\n> +\n>  \t@Argument(index = 0, required = true, metaVar = \"REFS\", usage = \"for-each-ref output\")\n>  \tFile refList;\n>  \n> @@ -97,6 +103,30 @@\n>  \n>  \t@Override\n>  \tprotected void run() throws Exception {\n> +\t\tif (!really && !db.getAllRefs().isEmpty()) {\n> +\t\t\tfinal StringBuilder m = new StringBuilder();\n> +\t\t\tm.append(\"fatal: \");\n> +\t\t\tm.append(\"This program will destroy the repository:\");\n> +\t\t\tm.append(\"\\n\");\n> +\t\t\tm.append(\"fatal:\\n\");\n> +\t\t\tm.append(\"fatal:    \");\n> +\t\t\tm.append(db.getDirectory().getAbsolutePath());\n> +\t\t\tm.append(\"\\n\");\n> +\t\t\tm.append(\"fatal:\\n\");\n> +\t\t\tm.append(\"fatal: \");\n> +\t\t\tm.append(\"To continue, add \");\n> +\t\t\tm.append(REALLY);\n> +\t\t\tm.append(\" to the command line\");\n> +\t\t\tm.append(\"\\n\");\n> +\t\t\tm.append(\"fatal:\");\n> +\t\t\tSystem.err.println(m);\n> +\t\t\tthrow die(\"Need approval to destroy current repository\");\n\nWhat's wrong with old fashioned '+' ? (which just translated to exactly this series\nof StringBuilder calls anyway?\n\n> +\t\t}\n> +\t\tif (!refList.isFile())\n> +\t\t\tthrow die(\"no such file: \" + refList.getPath());\n> +\t\tif (!graph.isFile())\n> +\t\t\tthrow die(\"no such file: \" + graph.getPath());\n> +\n>  \t\trecreateCommitGraph();\n>  \t\tdetachHead();\n>  \t\tdeleteAllRefs();\n\n-- robin\n"},{"id":"108120","messageId":"20090316201333.GR22920@spearce.org","threadId":"18301","inReplyTo":"200903162109.46653.robin.rosenberg.lists@dewire.com","subject":"Re: [JGIT PATCH] Create a debugging tool \"jgit rebuild-commitgraph\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-16T20:13:33Z","receivedAt":"2009-03-16T20:13:33Z","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> måndag 16 mars 2009 15:44:50 skrev \"Shawn O. Pearce\" <spearce@spearce.org>:\n> > Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> > > \n> > > I'd hate to put such a dangerous thing in the list of normal tools. If the user\n> > > want to shoot him/her-self in the foot they should get a license first.\n> > \n> > How about squashing this in?\n> \n> Does it have to be a utility accessible through the pgm interface? Why not \n> just run ut as java org.spearce.jgit.pgm.debug.RebuildCommitGraph ? \n\nYea, that's fine too.  Then its \"debug-rebuildcommitgraph\" I think,\nbut that's still a reasonable name for it.\n \n> >  \t@Override\n> >  \tprotected void run() throws Exception {\n> > +\t\tif (!really && !db.getAllRefs().isEmpty()) {\n> > +\t\t\tfinal StringBuilder m = new StringBuilder();\n> > +\t\t\tm.append(\"fatal: \");\n...\n> > +\t\t\tSystem.err.println(m);\n> > +\t\t\tthrow die(\"Need approval to destroy current repository\");\n> \n> What's wrong with old fashioned '+' ? (which just translated to exactly this series\n> of StringBuilder calls anyway?\n\nNothing, other than formatting.  + line wraps somewhat ugly sometimes\nunder the Eclipse formatter.  Using StringBuilder with one append per\nline and a short name for the StringBuilder variable makes it easier\nto format.\n\n-- \nShawn.\n"},{"id":"108161","messageId":"20090316235844.GW22920@spearce.org","threadId":"18301","inReplyTo":"20090316201333.GR22920@spearce.org","subject":"[JGIT PATCH v3] Create a debugging tool \"jgit debug-rebuild-commitgraph\"","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-16T23:58:44Z","receivedAt":"2009-03-16T23:58:44Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This is a command line tool which can be used to import only\nthe commit graph of another Git repository, to help debug a\ntopology related bug, without actually needing a clone of the\nproblematic repository.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n > Does it have to be a utility accessible through the pgm interface? Why not \n > just run ut as java org.spearce.jgit.pgm.debug.RebuildCommitGraph ? \n\n Moved.  Prior squash request was also squashed.\n\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    1 +\n .../spearce/jgit/pgm/debug/RebuildCommitGraph.java |  307 ++++++++++++++++++++\n 2 files changed, 308 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/RebuildCommitGraph.java\n\ndiff --git a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\nindex 1ba29e6..2fe6612 100644\n--- a/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n+++ b/org.spearce.jgit.pgm/src/META-INF/services/org.spearce.jgit.pgm.TextBuiltin\n@@ -21,6 +21,7 @@ org.spearce.jgit.pgm.Version\n \n org.spearce.jgit.pgm.debug.MakeCacheTree\n org.spearce.jgit.pgm.debug.ReadDirCache\n+org.spearce.jgit.pgm.debug.RebuildCommitGraph\n org.spearce.jgit.pgm.debug.ShowCacheTree\n org.spearce.jgit.pgm.debug.ShowCommands\n org.spearce.jgit.pgm.debug.ShowDirCache\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/RebuildCommitGraph.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/RebuildCommitGraph.java\nnew file mode 100644\nindex 0000000..63e189c\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/RebuildCommitGraph.java\n@@ -0,0 +1,307 @@\n+/*\n+ * Copyright (C) 2009, Google Inc.\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+\n+package org.spearce.jgit.pgm.debug;\n+\n+import java.io.BufferedReader;\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.IOException;\n+import java.io.InputStreamReader;\n+import java.util.ArrayList;\n+import java.util.Date;\n+import java.util.HashMap;\n+import java.util.List;\n+import java.util.ListIterator;\n+import java.util.Map;\n+\n+import org.kohsuke.args4j.Argument;\n+import org.kohsuke.args4j.Option;\n+import org.spearce.jgit.errors.MissingObjectException;\n+import org.spearce.jgit.errors.ObjectWritingException;\n+import org.spearce.jgit.lib.Commit;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.LockFile;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ObjectWriter;\n+import org.spearce.jgit.lib.PersonIdent;\n+import org.spearce.jgit.lib.ProgressMonitor;\n+import org.spearce.jgit.lib.Ref;\n+import org.spearce.jgit.lib.RefUpdate;\n+import org.spearce.jgit.lib.RefWriter;\n+import org.spearce.jgit.lib.TextProgressMonitor;\n+import org.spearce.jgit.lib.Tree;\n+import org.spearce.jgit.pgm.TextBuiltin;\n+import org.spearce.jgit.revwalk.RevWalk;\n+\n+/**\n+ * Recreates a repository from another one's commit graph.\n+ * <p>\n+ * <b>Do not run this on a repository unless you want to destroy it.</b>\n+ * <p>\n+ * To create the input files, in the source repository use:\n+ * \n+ * <pre>\n+ * git for-each-ref &gt;in.refs\n+ * git log --all '--pretty=format:%H %ct %P' &gt;in.dag\n+ * </pre>\n+ * <p>\n+ * Run the rebuild in either an empty repository, or a clone of the source. Any\n+ * missing commits (which might be the entire graph) will be created. All refs\n+ * will be modified to match the input exactly, which means some refs may be\n+ * deleted from the current repository.\n+ * <p>\n+ */\n+class RebuildCommitGraph extends TextBuiltin {\n+\tprivate final String REALLY = \"--destroy-this-repository\";\n+\n+\t@Option(name = REALLY, usage = \"approve destruction of repository\")\n+\tboolean really;\n+\n+\t@Argument(index = 0, required = true, metaVar = \"REFS\", usage = \"for-each-ref output\")\n+\tFile refList;\n+\n+\t@Argument(index = 1, required = true, metaVar = \"DAG\", usage = \"log --all '--pretty=format:%H %ct %P' output\")\n+\tFile graph;\n+\n+\tprivate final ProgressMonitor pm = new TextProgressMonitor();\n+\n+\tprivate Map<ObjectId, ObjectId> rewrites = new HashMap<ObjectId, ObjectId>();\n+\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tif (!really && !db.getAllRefs().isEmpty()) {\n+\t\t\tfinal StringBuilder m = new StringBuilder();\n+\t\t\tm.append(\"fatal: \");\n+\t\t\tm.append(\"This program will destroy the repository:\");\n+\t\t\tm.append(\"\\n\");\n+\t\t\tm.append(\"fatal:\\n\");\n+\t\t\tm.append(\"fatal:    \");\n+\t\t\tm.append(db.getDirectory().getAbsolutePath());\n+\t\t\tm.append(\"\\n\");\n+\t\t\tm.append(\"fatal:\\n\");\n+\t\t\tm.append(\"fatal: \");\n+\t\t\tm.append(\"To continue, add \");\n+\t\t\tm.append(REALLY);\n+\t\t\tm.append(\" to the command line\");\n+\t\t\tm.append(\"\\n\");\n+\t\t\tm.append(\"fatal:\");\n+\t\t\tSystem.err.println(m);\n+\t\t\tthrow die(\"Need approval to destroy current repository\");\n+\t\t}\n+\t\tif (!refList.isFile())\n+\t\t\tthrow die(\"no such file: \" + refList.getPath());\n+\t\tif (!graph.isFile())\n+\t\t\tthrow die(\"no such file: \" + graph.getPath());\n+\n+\t\trecreateCommitGraph();\n+\t\tdetachHead();\n+\t\tdeleteAllRefs();\n+\t\trecreateRefs();\n+\t}\n+\n+\tprivate void recreateCommitGraph() throws IOException {\n+\t\tfinal RevWalk rw = new RevWalk(db);\n+\t\tfinal Map<ObjectId, ToRewrite> toRewrite = new HashMap<ObjectId, ToRewrite>();\n+\t\tList<ToRewrite> queue = new ArrayList<ToRewrite>();\n+\t\tfinal BufferedReader br = new BufferedReader(new InputStreamReader(\n+\t\t\t\tnew FileInputStream(graph), Constants.CHARSET));\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = br.readLine()) != null) {\n+\t\t\t\tfinal String[] parts = line.split(\"[ \\t]{1,}\");\n+\t\t\t\tfinal ObjectId oldId = ObjectId.fromString(parts[0]);\n+\t\t\t\ttry {\n+\t\t\t\t\trw.parseCommit(oldId);\n+\t\t\t\t\t// We have it already. Don't rewrite it.\n+\t\t\t\t\tcontinue;\n+\t\t\t\t} catch (MissingObjectException mue) {\n+\t\t\t\t\t// Fall through and rewrite it.\n+\t\t\t\t}\n+\n+\t\t\t\tfinal long time = Long.parseLong(parts[1]) * 1000L;\n+\t\t\t\tfinal ObjectId[] parents = new ObjectId[parts.length - 2];\n+\t\t\t\tfor (int i = 0; i < parents.length; i++) {\n+\t\t\t\t\tparents[i] = ObjectId.fromString(parts[2 + i]);\n+\t\t\t\t}\n+\n+\t\t\t\tfinal ToRewrite t = new ToRewrite(oldId, time, parents);\n+\t\t\t\ttoRewrite.put(oldId, t);\n+\t\t\t\tqueue.add(t);\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tbr.close();\n+\t\t}\n+\n+\t\tpm.beginTask(\"Rewriting commits\", queue.size());\n+\t\tfinal ObjectWriter ow = new ObjectWriter(db);\n+\t\tfinal ObjectId emptyTree = ow.writeTree(new Tree(db));\n+\t\tfinal PersonIdent me = new PersonIdent(\"jgit rebuild-commitgraph\",\n+\t\t\t\t\"rebuild-commitgraph@localhost\");\n+\t\twhile (!queue.isEmpty()) {\n+\t\t\tfinal ListIterator<ToRewrite> itr = queue\n+\t\t\t\t\t.listIterator(queue.size());\n+\t\t\tqueue = new ArrayList<ToRewrite>();\n+\t\t\tREWRITE: while (itr.hasPrevious()) {\n+\t\t\t\tfinal ToRewrite t = itr.previous();\n+\t\t\t\tfinal ObjectId[] newParents = new ObjectId[t.oldParents.length];\n+\t\t\t\tfor (int k = 0; k < t.oldParents.length; k++) {\n+\t\t\t\t\tfinal ToRewrite p = toRewrite.get(t.oldParents[k]);\n+\t\t\t\t\tif (p != null) {\n+\t\t\t\t\t\tif (p.newId == null) {\n+\t\t\t\t\t\t\t// Must defer until after the parent is rewritten.\n+\t\t\t\t\t\t\tqueue.add(t);\n+\t\t\t\t\t\t\tcontinue REWRITE;\n+\t\t\t\t\t\t} else {\n+\t\t\t\t\t\t\tnewParents[k] = p.newId;\n+\t\t\t\t\t\t}\n+\t\t\t\t\t} else {\n+\t\t\t\t\t\t// We have the old parent object. Use it.\n+\t\t\t\t\t\t//\n+\t\t\t\t\t\tnewParents[k] = t.oldParents[k];\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\n+\t\t\t\tfinal Commit newc = new Commit(db);\n+\t\t\t\tnewc.setTreeId(emptyTree);\n+\t\t\t\tnewc.setAuthor(new PersonIdent(me, new Date(t.commitTime)));\n+\t\t\t\tnewc.setCommitter(newc.getAuthor());\n+\t\t\t\tnewc.setParentIds(newParents);\n+\t\t\t\tnewc.setMessage(\"ORIGINAL \" + t.oldId.name() + \"\\n\");\n+\t\t\t\tt.newId = ow.writeCommit(newc);\n+\t\t\t\trewrites.put(t.oldId, t.newId);\n+\t\t\t\tpm.update(1);\n+\t\t\t}\n+\t\t}\n+\t\tpm.endTask();\n+\t}\n+\n+\tprivate static class ToRewrite {\n+\t\tfinal ObjectId oldId;\n+\n+\t\tfinal long commitTime;\n+\n+\t\tfinal ObjectId[] oldParents;\n+\n+\t\tObjectId newId;\n+\n+\t\tToRewrite(final ObjectId o, final long t, final ObjectId[] p) {\n+\t\t\toldId = o;\n+\t\t\tcommitTime = t;\n+\t\t\toldParents = p;\n+\t\t}\n+\t}\n+\n+\tprivate void detachHead() throws IOException {\n+\t\tfinal String head = db.getFullBranch();\n+\t\tfinal ObjectId id = db.resolve(Constants.HEAD);\n+\t\tif (!ObjectId.isId(head) && id != null) {\n+\t\t\tfinal LockFile lf;\n+\t\t\tlf = new LockFile(new File(db.getDirectory(), Constants.HEAD));\n+\t\t\tif (!lf.lock())\n+\t\t\t\tthrow new IOException(\"Cannot lock HEAD\");\n+\t\t\tlf.write(id);\n+\t\t\tif (!lf.commit())\n+\t\t\t\tthrow new IOException(\"Cannot deatch HEAD\");\n+\t\t}\n+\t}\n+\n+\tprivate void deleteAllRefs() throws Exception {\n+\t\tfinal RevWalk rw = new RevWalk(db);\n+\t\tfor (final Ref r : db.getAllRefs().values()) {\n+\t\t\tif (Constants.HEAD.equals(r.getName()))\n+\t\t\t\tcontinue;\n+\t\t\tfinal RefUpdate u = db.updateRef(r.getName());\n+\t\t\tu.setForceUpdate(true);\n+\t\t\tu.delete(rw);\n+\t\t}\n+\t}\n+\n+\tprivate void recreateRefs() throws Exception {\n+\t\tfinal Map<String, Ref> refs = computeNewRefs();\n+\t\tnew RefWriter(refs.values()) {\n+\t\t\t@Override\n+\t\t\tprotected void writeFile(final String name, final byte[] content)\n+\t\t\t\t\tthrows IOException {\n+\t\t\t\tfinal File file = new File(db.getDirectory(), name);\n+\t\t\t\tfinal LockFile lck = new LockFile(file);\n+\t\t\t\tif (!lck.lock())\n+\t\t\t\t\tthrow new ObjectWritingException(\"Can't write \" + file);\n+\t\t\t\ttry {\n+\t\t\t\t\tlck.write(content);\n+\t\t\t\t} catch (IOException ioe) {\n+\t\t\t\t\tthrow new ObjectWritingException(\"Can't write \" + file);\n+\t\t\t\t}\n+\t\t\t\tif (!lck.commit())\n+\t\t\t\t\tthrow new ObjectWritingException(\"Can't write \" + file);\n+\t\t\t}\n+\t\t}.writePackedRefs();\n+\t}\n+\n+\tprivate Map<String, Ref> computeNewRefs() throws IOException {\n+\t\tfinal RevWalk rw = new RevWalk(db);\n+\t\tfinal Map<String, Ref> refs = new HashMap<String, Ref>();\n+\t\tfinal BufferedReader br = new BufferedReader(new InputStreamReader(\n+\t\t\t\tnew FileInputStream(refList), Constants.CHARSET));\n+\t\ttry {\n+\t\t\tString line;\n+\t\t\twhile ((line = br.readLine()) != null) {\n+\t\t\t\tfinal String[] parts = line.split(\"[ \\t]{1,}\");\n+\t\t\t\tfinal ObjectId origId = ObjectId.fromString(parts[0]);\n+\t\t\t\tfinal String type = parts[1];\n+\t\t\t\tfinal String name = parts[2];\n+\n+\t\t\t\tObjectId id = rewrites.get(origId);\n+\t\t\t\tif (id == null)\n+\t\t\t\t\tid = origId;\n+\t\t\t\ttry {\n+\t\t\t\t\trw.parseAny(id);\n+\t\t\t\t} catch (MissingObjectException mue) {\n+\t\t\t\t\tif (!Constants.TYPE_COMMIT.equals(type)) {\n+\t\t\t\t\t\tSystem.err.println(\"skipping \" + type + \" \" + name);\n+\t\t\t\t\t\tcontinue;\n+\t\t\t\t\t}\n+\t\t\t\t\tthrow new MissingObjectException(id, type);\n+\t\t\t\t}\n+\t\t\t\trefs.put(name, new Ref(Ref.Storage.PACKED, name, id));\n+\t\t\t}\n+\t\t} finally {\n+\t\t\tbr.close();\n+\t\t}\n+\t\treturn refs;\n+\t}\n+}\n-- \n1.6.2.1.286.g8173\n"}]}