{"thread":{"id":"14948","subject":"[EGIT PATCH 00/26] New DirCache API","startedAt":"2008-08-12T01:07:47Z","lastAt":"2008-08-12T01:08:13Z","messageCount":26,"participants":["Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":26},"messages":[{"id":"86863","messageId":"1218503293-14057-1-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":null,"subject":"[EGIT PATCH 00/26] New DirCache API","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:47Z","receivedAt":"2008-08-12T01:07:47Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This is (finally) my long-promised DirCache API.  It has been evolving\nand growing since before April I think.  Its finally cleaned up enough\nfor public viewing and usage.\n\nI started this change because I didn't see an easy way to plug\nGitIndex into TreeWalk, I found GitIndex to be too slow, and it\nrequired direct access to the working directory in order to handle\ncertain operations.  DirCache has none of these issues.\n\nThe API is meant to replace GitIndex, as we can now merge-sort a 'DIRC'\n(aka .git/index) file against any other tree as part of any active\nTreeWalk instance.  This makes it really easy to do n-way diffs against\ncommits, one or more index files, and the working directory.\n\nThe implementation is leaner and meaner than GitIndex.  It avoids a\nlot of unnecessary conversion overheads, and it tries to reduce the\nper-entry memory usage as much as possible.\n\nWe almost support the 'TREE' cache extension.  Updates to the index\nthrough DirCacheBuilder or DirCacheEditor wind up throwing away the\nentire 'TREE' extension, instead of invalidating only those trees\nwe know to be dirty.  This is certainly suboptimal and is an area\nfor future improvement.\n\n>From here I'm going to start building 3 way merge support onto\nthis API, by taking advantage of TreeWalk to do the parallel\ntree unpacks necessary for the merge operation.\n\nYes, its ~4.5k lines (excluding some test vectors).  However it\nwill allow us to eventually remove about 2k lines from jgit alone:\n\n  AbstractIndexTreeVisitor  74\n  GitIndex                 912\n  IndexDiff                160\n  IndexTreeWalker          241\n  IndexTreeVisitor          97\n  TreeIterator             203\n  WorkDirCheckout          401\n  ----------------------------\n                          2088\n\nOther classes like GitResourceDecorator simplify I think as a result\nof this new API being available.  I have only started to port that\nover, but found I was getting side-tracked in porting the existing\nGitIndex users and I really want to focus on the merge code instead.\n\nSo overall we may see about a 2k line increase as a result of this\nAPI being used in the tree rather than GitIndex, but since it fits\ninto the overall TreeWalk architecture its better suited for reuse,\nso we may see more than 2k saved over time.\n\n\nShawn O. Pearce (26):\n  Force all source code to UTF-8 encoding by default\n  Protect WorkingTreeIterator's name encoding from weird ByteBuffers\n  Add Constants.encode as a utility for quick encoding in UTF-8\n  Rely upon Constants.CHARSET over Constants.CHARACTER_ENCODING\n  Allow AbstractTreeIterators to find out about StopWalkExceptions\n  Implement a new .git/index (aka dircache) read interface\n  Export the new DirCache API to Eclipse plugins using jgit\n  Support locking (and unlocking) a .git/index through DirCache\n  Support writing a .git/index through DirCache\n  Support the 'TREE' extension in .git/index through DirCache\n  Support using a DirCache within a TreeWalk\n  Support recreating a .git/index through DirCache\n  Support iterating and building a DirCache at the same time\n  Support creating a new DirCacheEntry for an arbitrary path\n  Support a simplified model of editing index entries\n  Support recursively getting all entries under a subtree path\n  Support copying meta fields from one DirCacheEntry to another\n  Add JUnit tests for new DirCache API\n  Add JUnit tests for DirCache compatibility with C Git\n  Allow the new DirCacheIterator in command line arguments\n  Add debugging commands to interact with the new DirCache code\n  Add a basic command line implementation of rm\n  Rewrite GitMoveDeleteHook to use DirCacheBuilder\n  Teach GitMoveDeleteHook how to move a folder recursively\n  Rewrite UntrackOperation to use DirCacheBuilder\n  Rewrite AssumeUnchangedOperation to use DirCache\n\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../org/spearce/egit/core/GitMoveDeleteHook.java   |  156 ++-\n .../egit/core/op/AssumeUnchangedOperation.java     |  133 +-\n .../org/spearce/egit/core/op/UntrackOperation.java |   98 +-\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    6 +\n .../src/org/spearce/jgit/pgm/Rm.java               |   92 ++\n .../org/spearce/jgit/pgm/debug/MakeCacheTree.java  |   67 +\n .../org/spearce/jgit/pgm/debug/ReadDirCache.java   |   53 +\n .../org/spearce/jgit/pgm/debug/ShowCacheTree.java  |   69 +\n .../org/spearce/jgit/pgm/debug/ShowDirCache.java   |   72 +\n .../org/spearce/jgit/pgm/debug/WriteDirCache.java  |   54 +\n .../jgit/pgm/opt/AbstractTreeIteratorHandler.java  |   13 +\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n .../spearce/jgit/dircache/DirCacheBasicTest.java   |  185 +++\n .../jgit/dircache/DirCacheBuilderIteratorTest.java |   91 ++\n .../spearce/jgit/dircache/DirCacheBuilderTest.java |  253 ++++\n .../dircache/DirCacheCGitCompatabilityTest.java    |  204 +++\n .../spearce/jgit/dircache/DirCacheFindTest.java    |   86 ++\n .../jgit/dircache/DirCacheIteratorTest.java        |  273 ++++\n .../spearce/jgit/dircache/DirCacheTreeTest.java    |  150 ++\n .../tst/org/spearce/jgit/dircache/gitgit.index     |  Bin 0 -> 134799 bytes\n .../tst/org/spearce/jgit/dircache/gitgit.lsfiles   | 1437 ++++++++++++++++++++\n .../tst/org/spearce/jgit/dircache/gitgit.lstree    |  331 +++++\n .../spearce/jgit/lib/ConstantsEncodingTest.java    |   89 ++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +\n org.spearce.jgit/META-INF/MANIFEST.MF              |    3 +-\n .../spearce/jgit/dircache/BaseDirCacheEditor.java  |  194 +++\n .../src/org/spearce/jgit/dircache/DirCache.java    |  681 +++++++++\n .../jgit/dircache/DirCacheBuildIterator.java       |  124 ++\n .../org/spearce/jgit/dircache/DirCacheBuilder.java |  175 +++\n .../org/spearce/jgit/dircache/DirCacheEditor.java  |  265 ++++\n .../org/spearce/jgit/dircache/DirCacheEntry.java   |  385 ++++++\n .../spearce/jgit/dircache/DirCacheIterator.java    |  205 +++\n .../org/spearce/jgit/dircache/DirCacheTree.java    |  453 ++++++\n .../src/org/spearce/jgit/lib/Constants.java        |   25 +\n .../src/org/spearce/jgit/lib/ObjectWriter.java     |    2 +-\n .../src/org/spearce/jgit/lib/RefDatabase.java      |    9 +-\n .../src/org/spearce/jgit/lib/RepositoryConfig.java |    4 +-\n .../org/spearce/jgit/transport/PacketLineIn.java   |    3 +-\n .../jgit/transport/SideBandInputStream.java        |    3 +-\n .../spearce/jgit/transport/TransportBundle.java    |    3 +-\n .../spearce/jgit/transport/WalkPushConnection.java |    4 +-\n .../jgit/transport/WalkRemoteObjectDatabase.java   |    5 +-\n .../jgit/treewalk/AbstractTreeIterator.java        |   11 +\n .../spearce/jgit/treewalk/EmptyTreeIterator.java   |    6 +\n .../src/org/spearce/jgit/treewalk/TreeWalk.java    |   12 +-\n .../spearce/jgit/treewalk/WorkingTreeIterator.java |    2 +-\n .../spearce/jgit/treewalk/filter/PathFilter.java   |   10 +-\n 54 files changed, 6333 insertions(+), 190 deletions(-)\n create mode 100644 org.spearce.egit-feature/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit-updatesite/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit.core.test/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit.core/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit.ui/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.jgit.pgm/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Rm.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/MakeCacheTree.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ReadDirCache.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCacheTree.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowDirCache.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/WriteDirCache.java\n create mode 100644 org.spearce.jgit.test/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBasicTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderIteratorTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheCGitCompatabilityTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheFindTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheIteratorTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheTreeTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/gitgit.index\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/gitgit.lsfiles\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/gitgit.lstree\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ConstantsEncodingTest.java\n create mode 100644 org.spearce.jgit/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/BaseDirCacheEditor.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuildIterator.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEditor.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheIterator.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java\n"},{"id":"86865","messageId":"1218503293-14057-2-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-1-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 01/26] Force all source code to UTF-8 encoding by default","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:48Z","receivedAt":"2008-08-12T01:07:48Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"To keep things simple we should use a single character encoding\nfor all of our source files, and UTF-8 is the best choice as it\nwill handle any possible Unicode character but the majority of\nour source is plain 'ole ASCII.\n\nWe really want to support full Unicode in our sources so we can\nsetup Unicode test vectors, have translations which are usually\nin the standard encoding, and correctly support author names in\nsource code copyright comments.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n .../.settings/org.eclipse.core.resources.prefs     |    3 +++\n 9 files changed, 27 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit-feature/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit-updatesite/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit.core.test/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit.core/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit.ui/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.egit/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.jgit.pgm/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.jgit.test/.settings/org.eclipse.core.resources.prefs\n create mode 100644 org.spearce.jgit/.settings/org.eclipse.core.resources.prefs\n\ndiff --git a/org.spearce.egit-feature/.settings/org.eclipse.core.resources.prefs b/org.spearce.egit-feature/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..8d266d9\n--- /dev/null\n+++ b/org.spearce.egit-feature/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:46:45 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.egit-updatesite/.settings/org.eclipse.core.resources.prefs b/org.spearce.egit-updatesite/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..4ec836b\n--- /dev/null\n+++ b/org.spearce.egit-updatesite/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:46:54 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.egit.core.test/.settings/org.eclipse.core.resources.prefs b/org.spearce.egit.core.test/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..84e44d8\n--- /dev/null\n+++ b/org.spearce.egit.core.test/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:47:12 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.egit.core/.settings/org.eclipse.core.resources.prefs b/org.spearce.egit.core/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..d4ff695\n--- /dev/null\n+++ b/org.spearce.egit.core/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:47:03 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.egit.ui/.settings/org.eclipse.core.resources.prefs b/org.spearce.egit.ui/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..2f242b3\n--- /dev/null\n+++ b/org.spearce.egit.ui/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:47:19 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.egit/.settings/org.eclipse.core.resources.prefs b/org.spearce.egit/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..78f271d\n--- /dev/null\n+++ b/org.spearce.egit/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:46:35 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.jgit.pgm/.settings/org.eclipse.core.resources.prefs b/org.spearce.jgit.pgm/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..759548b\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:46:23 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.jgit.test/.settings/org.eclipse.core.resources.prefs b/org.spearce.jgit.test/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..a2724ba\n--- /dev/null\n+++ b/org.spearce.jgit.test/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:05:15 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\ndiff --git a/org.spearce.jgit/.settings/org.eclipse.core.resources.prefs b/org.spearce.jgit/.settings/org.eclipse.core.resources.prefs\nnew file mode 100644\nindex 0000000..66ac15c\n--- /dev/null\n+++ b/org.spearce.jgit/.settings/org.eclipse.core.resources.prefs\n@@ -0,0 +1,3 @@\n+#Mon Aug 11 16:46:12 PDT 2008\n+eclipse.preferences.version=1\n+encoding/<project>=UTF-8\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86864","messageId":"1218503293-14057-3-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-2-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 02/26] Protect WorkingTreeIterator's name encoding from weird ByteBuffers","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:49Z","receivedAt":"2008-08-12T01:07:49Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If a ByteBuffer decides to be cute and give us an array but use\nan array offset that isn't zero we cannot use its array as our\nown encodedName array.  Instead we must create a copy of the array\nso we can safely assume where the path starts.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/treewalk/WorkingTreeIterator.java |    2 +-\n 1 files changed, 1 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/WorkingTreeIterator.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/WorkingTreeIterator.java\nindex 4ac711b..356222b 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/WorkingTreeIterator.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/WorkingTreeIterator.java\n@@ -415,7 +415,7 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {\n \t\t\t\tthrows CharacterCodingException {\n \t\t\tfinal ByteBuffer b = enc.encode(CharBuffer.wrap(getName()));\n \t\t\tencodedNameLen = b.limit();\n-\t\t\tif (b.hasArray())\n+\t\t\tif (b.hasArray() && b.arrayOffset() == 0)\n \t\t\t\tencodedName = b.array();\n \t\t\telse\n \t\t\t\tb.get(encodedName = new byte[encodedNameLen]);\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86866","messageId":"1218503293-14057-4-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-3-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 03/26] Add Constants.encode as a utility for quick encoding in UTF-8","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:50Z","receivedAt":"2008-08-12T01:07:50Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We often need to convert a string into a UTF-8 encoding, so that\nwe can use this string as a path filter in a TreeWalk or in some\nother suitable place where we assume a standard UTF-8 encoding is\nbeing used.  As we have already done the lookup for the CHARSET\nwe can reuse that same CHARSET reference during future encoding\ncalls, while allowing the CharSet implementation to cache and\nreuse the actual encoder instance.\n\nWhenever possible we try to avoid copying the result as most of\nthe time the returned ByteBuffer's internal array matches the\nresult array we need to return to our caller.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/lib/ConstantsEncodingTest.java    |   89 ++++++++++++++++++++\n .../src/org/spearce/jgit/lib/Constants.java        |   25 ++++++\n 2 files changed, 114 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/lib/ConstantsEncodingTest.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ConstantsEncodingTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ConstantsEncodingTest.java\nnew file mode 100644\nindex 0000000..7b3e5a0\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/lib/ConstantsEncodingTest.java\n@@ -0,0 +1,89 @@\n+/*\n+ * Copyright (C) 2008, 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.lib;\n+\n+import java.io.UnsupportedEncodingException;\n+import java.util.Arrays;\n+\n+import junit.framework.TestCase;\n+\n+public class ConstantsEncodingTest extends TestCase {\n+\tpublic void testEncodeASCII_SimpleASCII()\n+\t\t\tthrows UnsupportedEncodingException {\n+\t\tfinal String src = \"abc\";\n+\t\tfinal byte[] exp = { 'a', 'b', 'c' };\n+\t\tfinal byte[] res = Constants.encodeASCII(src);\n+\t\tassertTrue(Arrays.equals(exp, res));\n+\t\tassertEquals(src, new String(res, 0, res.length, \"UTF-8\"));\n+\t}\n+\n+\tpublic void testEncodeASCII_FailOnNonASCII() {\n+\t\tfinal String src = \"ÅªnÄ­cÅdeÌ½\";\n+\t\ttry {\n+\t\t\tConstants.encodeASCII(src);\n+\t\t\tfail(\"Incorrectly accepted a Unicode character\");\n+\t\t} catch (IllegalArgumentException err) {\n+\t\t\tassertEquals(\"Not ASCII string: \" + src, err.getMessage());\n+\t\t}\n+\t}\n+\n+\tpublic void testEncodeASCII_Number13() {\n+\t\tfinal long src = 13;\n+\t\tfinal byte[] exp = { '1', '3' };\n+\t\tfinal byte[] res = Constants.encodeASCII(src);\n+\t\tassertTrue(Arrays.equals(exp, res));\n+\t}\n+\n+\tpublic void testEncode_SimpleASCII() throws UnsupportedEncodingException {\n+\t\tfinal String src = \"abc\";\n+\t\tfinal byte[] exp = { 'a', 'b', 'c' };\n+\t\tfinal byte[] res = Constants.encode(src);\n+\t\tassertTrue(Arrays.equals(exp, res));\n+\t\tassertEquals(src, new String(res, 0, res.length, \"UTF-8\"));\n+\t}\n+\n+\tpublic void testEncode_Unicode() throws UnsupportedEncodingException {\n+\t\tfinal String src = \"ÅªnÄ­cÅdeÌ½\";\n+\t\tfinal byte[] exp = { (byte) 0xC5, (byte) 0xAA, 0x6E, (byte) 0xC4,\n+\t\t\t\t(byte) 0xAD, 0x63, (byte) 0xC5, (byte) 0x8D, 0x64, 0x65,\n+\t\t\t\t(byte) 0xCC, (byte) 0xBD };\n+\t\tfinal byte[] res = Constants.encode(src);\n+\t\tassertTrue(Arrays.equals(exp, res));\n+\t\tassertEquals(src, new String(res, 0, res.length, \"UTF-8\"));\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Constants.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Constants.java\nindex 7c2cef9..23ac3ac 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Constants.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Constants.java\n@@ -1,6 +1,7 @@\n /*\n  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Google Inc.\n  *\n  * All rights reserved.\n  *\n@@ -38,6 +39,7 @@\n \n package org.spearce.jgit.lib;\n \n+import java.nio.ByteBuffer;\n import java.nio.charset.Charset;\n import java.security.MessageDigest;\n import java.security.NoSuchAlgorithmException;\n@@ -387,6 +389,29 @@ public final class Constants {\n \t\treturn r;\n \t}\n \n+\t/**\n+\t * Convert a string to a byte array in the standard character encoding.\n+\t * \n+\t * @param str\n+\t *            the string to convert. May contain any Unicode characters.\n+\t * @return a byte array representing the requested string, encoded using the\n+\t *         default character encoding (UTF-8).\n+\t * @see #CHARACTER_ENCODING\n+\t */\n+\tpublic static byte[] encode(final String str) {\n+\t\tfinal ByteBuffer bb = Constants.CHARSET.encode(str);\n+\t\tfinal int len = bb.limit();\n+\t\tif (bb.hasArray() && bb.arrayOffset() == 0) {\n+\t\t\tfinal byte[] arr = bb.array();\n+\t\t\tif (arr.length == len)\n+\t\t\t\treturn arr;\n+\t\t}\n+\n+\t\tfinal byte[] arr = new byte[len];\n+\t\tbb.get(arr);\n+\t\treturn arr;\n+\t}\n+\n \tstatic {\n \t\tif (OBJECT_ID_LENGTH != newMessageDigest().getDigestLength())\n \t\t\tthrow new LinkageError(\"Incorrect OBJECT_ID_LENGTH.\");\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86871","messageId":"1218503293-14057-5-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-4-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 04/26] Rely upon Constants.CHARSET over Constants.CHARACTER_ENCODING","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:51Z","receivedAt":"2008-08-12T01:07:51Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This avoids the UnsupportedEncodingException error which we know\nmust not fail on a JRE that supports jgit.  We have to have the\nstock UTF-8 encoding available for basic operation.\n\nIn some cases we can also avoid an unnecessary copy.  Some code\npaths in the byte[]->String utility methods in the Sun JRE seem\nto copy the input byte[] prior to calling the character set for\nthe conversion.  This copy is unnecessary.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/lib/ObjectWriter.java     |    2 +-\n .../src/org/spearce/jgit/lib/RefDatabase.java      |    9 +++------\n .../src/org/spearce/jgit/lib/RepositoryConfig.java |    4 ++--\n .../org/spearce/jgit/transport/PacketLineIn.java   |    3 ++-\n .../jgit/transport/SideBandInputStream.java        |    3 ++-\n .../spearce/jgit/transport/TransportBundle.java    |    3 ++-\n .../spearce/jgit/transport/WalkPushConnection.java |    4 ++--\n .../jgit/transport/WalkRemoteObjectDatabase.java   |    5 ++---\n .../src/org/spearce/jgit/treewalk/TreeWalk.java    |   10 ++--------\n .../spearce/jgit/treewalk/filter/PathFilter.java   |   10 +---------\n 10 files changed, 19 insertions(+), 34 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectWriter.java b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectWriter.java\nindex be2e28c..6c2cd4f 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectWriter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/ObjectWriter.java\n@@ -236,7 +236,7 @@ public class ObjectWriter {\n \tpublic ObjectId writeTag(final Tag c) throws IOException {\n \t\tfinal ByteArrayOutputStream os = new ByteArrayOutputStream();\n \t\tfinal OutputStreamWriter w = new OutputStreamWriter(os,\n-\t\t\t\tConstants.CHARACTER_ENCODING);\n+\t\t\t\tConstants.CHARSET);\n \n \t\tw.write(\"object \");\n \t\tc.getObjId().copyTo(w);\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 3e68a8d..facd3e4 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@@ -45,7 +45,6 @@ import java.io.FileInputStream;\n import java.io.FileNotFoundException;\n import java.io.IOException;\n import java.io.InputStreamReader;\n-import java.io.UnsupportedEncodingException;\n import java.util.HashMap;\n import java.util.Map;\n \n@@ -54,8 +53,6 @@ import org.spearce.jgit.util.FS;\n import org.spearce.jgit.util.NB;\n \n class RefDatabase {\n-\tprivate static final String CHAR_ENC = Constants.CHARACTER_ENCODING;\n-\n \tprivate static final String REFS_SLASH = \"refs/\";\n \n \tprivate static final String HEADS_SLASH = Constants.HEADS_PREFIX + \"/\";\n@@ -157,7 +154,7 @@ class RefDatabase {\n \t * @throws IOException\n \t */\n \tvoid link(final String name, final String target) throws IOException {\n-\t\tfinal byte[] content = (\"ref: \" + target + \"\\n\").getBytes(CHAR_ENC);\n+\t\tfinal byte[] content = Constants.encode(\"ref: \" + target + \"\\n\");\n \t\tfinal LockFile lck = new LockFile(fileForRef(name));\n \t\tif (!lck.lock())\n \t\t\tthrow new ObjectWritingException(\"Unable to lock \" + name);\n@@ -438,9 +435,9 @@ class RefDatabase {\n \t}\n \n \tprivate static BufferedReader openReader(final File fileLocation)\n-\t\t\tthrows UnsupportedEncodingException, FileNotFoundException {\n+\t\t\tthrows FileNotFoundException {\n \t\treturn new BufferedReader(new InputStreamReader(new FileInputStream(\n-\t\t\t\tfileLocation), CHAR_ENC));\n+\t\t\t\tfileLocation), Constants.CHARSET));\n \t}\n \n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\nindex d1cd5fc..048940d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/RepositoryConfig.java\n@@ -517,7 +517,7 @@ public class RepositoryConfig {\n \t\t\t\t+ \".lock\");\n \t\tfinal PrintWriter r = new PrintWriter(new BufferedWriter(\n \t\t\t\tnew OutputStreamWriter(new FileOutputStream(tmp),\n-\t\t\t\t\t\tConstants.CHARACTER_ENCODING))) {\n+\t\t\t\t\t\tConstants.CHARSET))) {\n \t\t\t@Override\n \t\t\tpublic void println() {\n \t\t\t\tprint('\\n');\n@@ -585,7 +585,7 @@ public class RepositoryConfig {\n \t\tclear();\n \t\treadFile = true;\n \t\tfinal BufferedReader r = new BufferedReader(new InputStreamReader(\n-\t\t\t\tnew FileInputStream(configFile), Constants.CHARACTER_ENCODING));\n+\t\t\t\tnew FileInputStream(configFile), Constants.CHARSET));\n \t\ttry {\n \t\t\tEntry last = null;\n \t\t\tEntry e = new Entry();\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java\nindex f330638..f87517d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/PacketLineIn.java\n@@ -47,6 +47,7 @@ import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.MutableObjectId;\n import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n \n class PacketLineIn {\n \tprivate static final byte fromhex[];\n@@ -107,7 +108,7 @@ class PacketLineIn {\n \t\tfinal byte[] raw = new byte[len];\n \t\tNB.readFully(in, raw, 0, len);\n \t\treadLF();\n-\t\treturn new String(raw, 0, len, Constants.CHARACTER_ENCODING);\n+\t\treturn RawParseUtils.decode(Constants.CHARSET, raw, 0, len);\n \t}\n \n \tprivate void readLF() throws IOException {\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java\nindex 9ccf5ad..3ec9bff 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/SideBandInputStream.java\n@@ -48,6 +48,7 @@ import org.spearce.jgit.errors.TransportException;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * Unmultiplexes the data portion of a side-band channel.\n@@ -194,6 +195,6 @@ class SideBandInputStream extends InputStream {\n \tprivate String readString(final int len) throws IOException {\n \t\tfinal byte[] raw = new byte[len];\n \t\tNB.readFully(in, raw, 0, len);\n-\t\treturn new String(raw, 0, len, Constants.CHARACTER_ENCODING);\n+\t\treturn RawParseUtils.decode(Constants.CHARSET, raw, 0, len);\n \t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java\nindex 24d49eb..d8c2ba4 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/TransportBundle.java\n@@ -58,6 +58,7 @@ import org.spearce.jgit.lib.ProgressMonitor;\n import org.spearce.jgit.lib.Ref;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.util.FS;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * Supports fetching from a git bundle (sneaker-net object transport).\n@@ -193,7 +194,7 @@ class TransportBundle extends PackTransport {\n \t\t\tbin.skip(lf);\n \t\t\tif (lf < cnt && hdrbuf[lf] == '\\n')\n \t\t\t\tbin.skip(1);\n-\t\t\treturn new String(hdrbuf, 0, lf, Constants.CHARACTER_ENCODING);\n+\t\t\treturn RawParseUtils.decode(Constants.CHARSET, hdrbuf, 0, lf);\n \t\t}\n \n \t\t@Override\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java\nindex 904a699..85bbc14 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkPushConnection.java\n@@ -325,7 +325,7 @@ class WalkPushConnection extends BaseConnection implements PushConnection {\n \t\t\tthrows TransportException {\n \t\ttry {\n \t\t\tfinal String ref = \"ref: \" + pickHEAD(updates) + \"\\n\";\n-\t\t\tfinal byte[] bytes = ref.getBytes(Constants.CHARACTER_ENCODING);\n+\t\t\tfinal byte[] bytes = Constants.encode(ref);\n \t\t\tdest.writeFile(\"../HEAD\", bytes);\n \t\t} catch (IOException e) {\n \t\t\tthrow new TransportException(uri, \"cannot create HEAD\", e);\n@@ -334,7 +334,7 @@ class WalkPushConnection extends BaseConnection implements PushConnection {\n \t\ttry {\n \t\t\tfinal String config = \"[core]\\n\"\n \t\t\t\t\t+ \"\\trepositoryformatversion = 0\\n\";\n-\t\t\tfinal byte[] bytes = config.getBytes(Constants.CHARACTER_ENCODING);\n+\t\t\tfinal byte[] bytes = Constants.encode(config);\n \t\t\tdest.writeFile(\"../config\", bytes);\n \t\t} catch (IOException e) {\n \t\t\tthrow new TransportException(uri, \"cannot create config\", e);\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java\nindex c5a5199..ababc69 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/transport/WalkRemoteObjectDatabase.java\n@@ -67,8 +67,6 @@ import org.spearce.jgit.util.NB;\n  * independent {@link WalkFetchConnection}.\n  */\n abstract class WalkRemoteObjectDatabase {\n-\tstatic final String CHARENC = Constants.CHARACTER_ENCODING;\n-\n \tstatic final String INFO_PACKS = \"info/packs\";\n \n \tstatic final String INFO_ALTERNATES = \"info/alternates\";\n@@ -448,7 +446,8 @@ abstract class WalkRemoteObjectDatabase {\n \t *             stream could be created.\n \t */\n \tBufferedReader openReader(final String path) throws IOException {\n-\t\treturn new BufferedReader(new InputStreamReader(open(path).in, CHARENC));\n+\t\tfinal InputStream is = open(path).in;\n+\t\treturn new BufferedReader(new InputStreamReader(is, Constants.CHARSET));\n \t}\n \n \t/**\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\nindex 9f373e6..d4050ec 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\n@@ -38,7 +38,6 @@\n package org.spearce.jgit.treewalk;\n \n import java.io.IOException;\n-import java.io.UnsupportedEncodingException;\n import java.util.Collections;\n \n import org.spearce.jgit.errors.CorruptObjectException;\n@@ -52,6 +51,7 @@ import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.revwalk.RevTree;\n import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n import org.spearce.jgit.treewalk.filter.TreeFilter;\n+import org.spearce.jgit.util.RawParseUtils;\n \n /**\n  * Walks one or more {@link AbstractTreeIterator}s in parallel.\n@@ -685,12 +685,6 @@ public class TreeWalk {\n \t}\n \n \tprivate static String pathOf(final AbstractTreeIterator t) {\n-\t\ttry {\n-\t\t\treturn new String(t.path, 0, t.pathLen,\n-\t\t\t\t\tConstants.CHARACTER_ENCODING);\n-\t\t} catch (UnsupportedEncodingException uee) {\n-\t\t\tthrow new RuntimeException(\"JVM doesn't support \"\n-\t\t\t\t\t+ Constants.CHARACTER_ENCODING, uee);\n-\t\t}\n+\t\treturn RawParseUtils.decode(Constants.CHARSET, t.path, 0, t.pathLen);\n \t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/filter/PathFilter.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/filter/PathFilter.java\nindex 3aff145..56773a3 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/filter/PathFilter.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/filter/PathFilter.java\n@@ -37,8 +37,6 @@\n \n package org.spearce.jgit.treewalk.filter;\n \n-import java.io.UnsupportedEncodingException;\n-\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.treewalk.TreeWalk;\n \n@@ -81,13 +79,7 @@ public class PathFilter extends TreeFilter {\n \n \tprivate PathFilter(final String s) {\n \t\tpathStr = s;\n-\t\ttry {\n-\t\t\tpathRaw = pathStr.getBytes(Constants.CHARACTER_ENCODING);\n-\t\t} catch (UnsupportedEncodingException uee) {\n-\t\t\tthrow new RuntimeException(\"JVM doesn't support \"\n-\t\t\t\t\t+ Constants.CHARACTER_ENCODING\n-\t\t\t\t\t+ \" which is required for path filtering.\", uee);\n-\t\t}\n+\t\tpathRaw = Constants.encode(pathStr);\n \t}\n \n \t@Override\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86881","messageId":"1218503293-14057-6-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-5-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 05/26] Allow AbstractTreeIterators to find out about StopWalkExceptions","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:52Z","receivedAt":"2008-08-12T01:07:52Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"If a tree iterator has overridden skip() to specially handle entries\nwhich the TreeWalk has decided are not relevant then it might also\nneed to pay attention to a StopWalkException.  The SWE is thrown out\nof a filter and behaves much like skip, except it skips everything\nelse in the tree.  This is used by PathFilterGroup to break out of\na walk once we know that no additional records could ever match.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../jgit/treewalk/AbstractTreeIterator.java        |   11 +++++++++++\n .../spearce/jgit/treewalk/EmptyTreeIterator.java   |    6 ++++++\n .../src/org/spearce/jgit/treewalk/TreeWalk.java    |    2 ++\n 3 files changed, 19 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/AbstractTreeIterator.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/AbstractTreeIterator.java\nindex 6d7159c..98fec09 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/AbstractTreeIterator.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/AbstractTreeIterator.java\n@@ -391,4 +391,15 @@ public abstract class AbstractTreeIterator {\n \tpublic void skip() throws CorruptObjectException {\n \t\tnext();\n \t}\n+\n+\t/**\n+\t * Indicates to the iterator that no more entries will be read.\n+\t * <p>\n+\t * This is only invoked by TreeWalk when the iteration is aborted early due\n+\t * to a {@link org.spearce.jgit.errors.StopWalkException} being thrown from\n+\t * within a TreeFilter.\n+\t */\n+\tpublic void stopWalk() {\n+\t\t// Do nothing by default.  Most iterators do not care.\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/EmptyTreeIterator.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/EmptyTreeIterator.java\nindex 09d2bde..c5dc4ad 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/EmptyTreeIterator.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/EmptyTreeIterator.java\n@@ -87,4 +87,10 @@ public class EmptyTreeIterator extends AbstractTreeIterator {\n \tpublic void next() throws CorruptObjectException {\n \t\t// Do nothing.\n \t}\n+\n+\t@Override\n+\tpublic void stopWalk() {\n+\t\tif (parent != null)\n+\t\t\tparent.stopWalk();\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\nindex d4050ec..1d842bc 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/treewalk/TreeWalk.java\n@@ -408,6 +408,8 @@ public class TreeWalk {\n \t\t\t\treturn true;\n \t\t\t}\n \t\t} catch (StopWalkException stop) {\n+\t\t\tfor (final AbstractTreeIterator t : trees)\n+\t\t\t\tt.stopWalk();\n \t\t\treturn false;\n \t\t}\n \t}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86888","messageId":"1218503293-14057-7-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-6-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 06/26] Implement a new .git/index (aka dircache) read interface","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:53Z","receivedAt":"2008-08-12T01:07:53Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This is a smaller (and faster) .git/index file reader and it will be\nan eventual replacement for the current GitIndex class.\n\nWe silently skip optional extensions, which means we silently skip the\ncurrent 'TREE' extension.  We also fail out if we identify a required\nextension which we do not support.\n\nMost of each index entry is held in a shared byte[] as we do not need\nmost of the fields in jgit, due to Java's lack of a POSIX lstat like\nutility.  This makes it more efficient for us to read the C Git index\nformat as we can avoid expending a lot of CPU on decode calls during\nthe file read.\n\nLookup is handled by binary searching against the encoded paths, as\nthis lines up better with how TreeWalk and its TreeFilters operate.\nMost of the time when we are looking for something in the index it\nis a quick read-find+update-write cycle affecting only a handful of\npaths.  Avoiding any unnecessary byte[]->String->byte[] translations\nduring these operations performance.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/dircache/DirCache.java    |  385 ++++++++++++++++++++\n .../org/spearce/jgit/dircache/DirCacheEntry.java   |  270 ++++++++++++++\n 2 files changed, 655 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\nnew file mode 100644\nindex 0000000..f8ca60d\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n@@ -0,0 +1,385 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.io.BufferedInputStream;\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.nio.ByteBuffer;\n+import java.nio.channels.FileChannel;\n+import java.util.Comparator;\n+\n+import org.spearce.jgit.errors.CorruptObjectException;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.util.NB;\n+\n+/**\n+ * Support for the Git dircache (aka index file).\n+ * <p>\n+ * The index file keeps track of which objects are currently checked out in the\n+ * working directory, and the last modified time of those working files. Changes\n+ * in the working directory can be detected by comparing the modification times\n+ * to the cached modification time within the index file.\n+ * <p>\n+ * Index files are also used during merges, where the merge happens within the\n+ * index file first, and the working directory is updated as a post-merge step.\n+ * Conflicts are stored in the index file to allow tool (and human) based\n+ * resolutions to be easily performed.\n+ */\n+public class DirCache {\n+\tprivate static final byte[] SIG_DIRC = { 'D', 'I', 'R', 'C' };\n+\n+\tprivate static final int INFO_LEN = DirCacheEntry.INFO_LEN;\n+\n+\tprivate static final DirCacheEntry[] NO_ENTRIES = {};\n+\n+\tstatic final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {\n+\t\tpublic int compare(final DirCacheEntry o1, final DirCacheEntry o2) {\n+\t\t\tfinal int cr = cmp(o1, o2);\n+\t\t\tif (cr != 0)\n+\t\t\t\treturn cr;\n+\t\t\treturn o1.getStage() - o2.getStage();\n+\t\t}\n+\t};\n+\n+\tstatic int cmp(final DirCacheEntry a, final DirCacheEntry b) {\n+\t\treturn cmp(a.path, a.path.length, b);\n+\t}\n+\n+\tstatic int cmp(final byte[] aPath, final int aLen, final DirCacheEntry b) {\n+\t\treturn cmp(aPath, aLen, b.path, b.path.length);\n+\t}\n+\n+\tstatic int cmp(final byte[] aPath, final int aLen, final byte[] bPath,\n+\t\t\tfinal int bLen) {\n+\t\tfor (int cPos = 0; cPos < aLen && cPos < bLen; cPos++) {\n+\t\t\tfinal int cmp = (aPath[cPos] & 0xff) - (bPath[cPos] & 0xff);\n+\t\t\tif (cmp != 0)\n+\t\t\t\treturn cmp;\n+\t\t}\n+\t\treturn aLen - bLen;\n+\t}\n+\n+\t/**\n+\t * Create a new in-core index representation and read an index from disk.\n+\t * <p>\n+\t * The new index will be read before it is returned to the caller. Read\n+\t * failures are reported as exceptions and therefore prevent the method from\n+\t * returning a partially populated index.\n+\t * \n+\t * @param indexLocation\n+\t *            location of the index file on disk.\n+\t * @return a cache representing the contents of the specified index file (if\n+\t *         it exists) or an empty cache if the file does not exist.\n+\t * @throws IOException\n+\t *             the index file is present but could not be read.\n+\t * @throws CorruptObjectException\n+\t *             the index file is using a format or extension that this\n+\t *             library does not support.\n+\t */\n+\tpublic static DirCache read(final File indexLocation)\n+\t\t\tthrows CorruptObjectException, IOException {\n+\t\tfinal DirCache c = new DirCache(indexLocation);\n+\t\tc.read();\n+\t\treturn c;\n+\t}\n+\n+\t/**\n+\t * Create a new in-core index representation and read an index from disk.\n+\t * <p>\n+\t * The new index will be read before it is returned to the caller. Read\n+\t * failures are reported as exceptions and therefore prevent the method from\n+\t * returning a partially populated index.\n+\t * \n+\t * @param db\n+\t *            repository the caller wants to read the default index of.\n+\t * @return a cache representing the contents of the specified index file (if\n+\t *         it exists) or an empty cache if the file does not exist.\n+\t * @throws IOException\n+\t *             the index file is present but could not be read.\n+\t * @throws CorruptObjectException\n+\t *             the index file is using a format or extension that this\n+\t *             library does not support.\n+\t */\n+\tpublic static DirCache read(final Repository db)\n+\t\t\tthrows CorruptObjectException, IOException {\n+\t\treturn read(new File(db.getDirectory(), \"index\"));\n+\t}\n+\n+\t/** Location of the current version of the index file. */\n+\tprivate final File liveFile;\n+\n+\t/** Modification time of the file at the last read/write we did. */\n+\tprivate long lastModified;\n+\n+\t/** Individual file index entries, sorted by path name. */\n+\tprivate DirCacheEntry[] sortedEntries;\n+\n+\t/** Number of positions within {@link #sortedEntries} that are valid. */\n+\tprivate int entryCnt;\n+\n+\t/**\n+\t * Create a new in-core index representation.\n+\t * <p>\n+\t * The new index will be empty. Callers may wish to read from the on disk\n+\t * file first with {@link #read()}.\n+\t * \n+\t * @param indexLocation\n+\t *            location of the index file on disk.\n+\t */\n+\tpublic DirCache(final File indexLocation) {\n+\t\tliveFile = indexLocation;\n+\t\tclear();\n+\t}\n+\n+\t/**\n+\t * Read the index from disk, if it has changed on disk.\n+\t * <p>\n+\t * This method tries to avoid loading the index if it has not changed since\n+\t * the last time we consulted it. A missing index file will be treated as\n+\t * though it were present but had no file entries in it.\n+\t * \n+\t * @throws IOException\n+\t *             the index file is present but could not be read. This\n+\t *             DirCache instance may not be populated correctly.\n+\t * @throws CorruptObjectException\n+\t *             the index file is using a format or extension that this\n+\t *             library does not support.\n+\t */\n+\tpublic void read() throws IOException, CorruptObjectException {\n+\t\tif (!liveFile.exists())\n+\t\t\tclear();\n+\t\telse if (liveFile.lastModified() != lastModified) {\n+\t\t\ttry {\n+\t\t\t\tfinal FileInputStream inStream = new FileInputStream(liveFile);\n+\t\t\t\ttry {\n+\t\t\t\t\tclear();\n+\t\t\t\t\treadFrom(inStream);\n+\t\t\t\t} finally {\n+\t\t\t\t\ttry {\n+\t\t\t\t\t\tinStream.close();\n+\t\t\t\t\t} catch (IOException err2) {\n+\t\t\t\t\t\t// Ignore any close failures.\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t} catch (FileNotFoundException fnfe) {\n+\t\t\t\t// Someone must have deleted it between our exists test\n+\t\t\t\t// and actually opening the path. That's fine, its empty.\n+\t\t\t\t//\n+\t\t\t\tclear();\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/** Empty this index, removing all entries. */\n+\tpublic void clear() {\n+\t\tlastModified = 0;\n+\t\tsortedEntries = NO_ENTRIES;\n+\t\tentryCnt = 0;\n+\t}\n+\n+\tprivate void readFrom(final FileInputStream inStream) throws IOException,\n+\t\t\tCorruptObjectException {\n+\t\tfinal FileChannel fd = inStream.getChannel();\n+\t\tfinal long sizeOnDisk = fd.size();\n+\t\tfinal BufferedInputStream in = new BufferedInputStream(inStream);\n+\n+\t\t// Read the index header and verify we understand it.\n+\t\t//\n+\t\tfinal byte[] hdr = new byte[12];\n+\t\tNB.readFully(in, hdr, 0, 12);\n+\t\tif (!is_DIRC(hdr))\n+\t\t\tthrow new CorruptObjectException(\"Not a DIRC file.\");\n+\t\tfinal int ver = NB.decodeInt32(hdr, 4);\n+\t\tif (ver != 2)\n+\t\t\tthrow new CorruptObjectException(\"Unknown DIRC version \" + ver);\n+\t\tentryCnt = NB.decodeInt32(hdr, 8);\n+\t\tif (entryCnt < 0)\n+\t\t\tthrow new CorruptObjectException(\"DIRC has too many entries.\");\n+\n+\t\t// Load the individual file entries.\n+\t\t//\n+\t\tfinal byte[] infos = new byte[INFO_LEN * entryCnt];\n+\t\tsortedEntries = new DirCacheEntry[entryCnt];\n+\t\tfor (int i = 0; i < entryCnt; i++)\n+\t\t\tsortedEntries[i] = new DirCacheEntry(infos, i * INFO_LEN, in);\n+\t\tlastModified = liveFile.lastModified();\n+\n+\t\t// After the file entries are index extensions.\n+\t\t//\n+\t\twhile (fd.position() - in.available() < sizeOnDisk - 20) {\n+\t\t\tNB.readFully(in, hdr, 0, 8);\n+\t\t\tswitch (NB.decodeInt32(hdr, 0)) {\n+\t\t\tdefault:\n+\t\t\t\tif (hdr[0] >= 'A' && hdr[0] <= 'Z') {\n+\t\t\t\t\t// The extension is optional and is here only as\n+\t\t\t\t\t// a performance optimization. Since we do not\n+\t\t\t\t\t// understand it, we can safely skip past it.\n+\t\t\t\t\t//\n+\t\t\t\t\tin.skip(NB.decodeInt32(hdr, 4));\n+\t\t\t\t} else {\n+\t\t\t\t\t// The extension is not an optimization and is\n+\t\t\t\t\t// _required_ to understand this index format.\n+\t\t\t\t\t// Since we did not trap it above we must abort.\n+\t\t\t\t\t//\n+\t\t\t\t\tthrow new CorruptObjectException(\"DIRC extension '\"\n+\t\t\t\t\t\t\t+ Constants.CHARSET.decode(\n+\t\t\t\t\t\t\t\t\tByteBuffer.wrap(hdr, 0, 4)).toString()\n+\t\t\t\t\t\t\t+ \"' not supported by this version.\");\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate static boolean is_DIRC(final byte[] hdr) {\n+\t\tif (hdr.length < SIG_DIRC.length)\n+\t\t\treturn false;\n+\t\tfor (int i = 0; i < SIG_DIRC.length; i++)\n+\t\t\tif (hdr[i] != SIG_DIRC[i])\n+\t\t\t\treturn false;\n+\t\treturn true;\n+\t}\n+\n+\t/**\n+\t * Locate the position a path's entry is at in the index.\n+\t * <p>\n+\t * If there is at least one entry in the index for this path the position of\n+\t * the lowest stage is returned. Subsequent stages can be identified by\n+\t * testing consecutive entries until the path differs.\n+\t * <p>\n+\t * If no path matches the entry -(position+1) is returned, where position is\n+\t * the location it would have gone within the index.\n+\t * \n+\t * @param path\n+\t *            the path to search for.\n+\t * @return if >= 0 then the return value is the position of the entry in the\n+\t *         index; pass to {@link #getEntry(int)} to obtain the entry\n+\t *         information. If < 0 the entry does not exist in the index.\n+\t */\n+\tpublic int findEntry(final String path) {\n+\t\tif (entryCnt == 0)\n+\t\t\treturn -1;\n+\t\tfinal byte[] p = Constants.encode(path);\n+\t\treturn findEntry(p, p.length);\n+\t}\n+\n+\tint findEntry(final byte[] p, final int pLen) {\n+\t\tint low = 0;\n+\t\tint high = entryCnt;\n+\t\tdo {\n+\t\t\tint mid = (low + high) >> 1;\n+\t\t\tfinal int cmp = cmp(p, pLen, sortedEntries[mid]);\n+\t\t\tif (cmp < 0)\n+\t\t\t\thigh = mid;\n+\t\t\telse if (cmp == 0) {\n+\t\t\t\twhile (mid > 0 && cmp(p, pLen, sortedEntries[mid - 1]) == 0)\n+\t\t\t\t\tmid--;\n+\t\t\t\treturn mid;\n+\t\t\t} else\n+\t\t\t\tlow = mid + 1;\n+\t\t} while (low < high);\n+\t\treturn -(low + 1);\n+\t}\n+\n+\t/**\n+\t * Determine the next index position past all entries with the same name.\n+\t * <p>\n+\t * As index entries are sorted by path name, then stage number, this method\n+\t * advances the supplied position to the first position in the index whose\n+\t * path name does not match the path name of the supplied position's entry.\n+\t * \n+\t * @param position\n+\t *            entry position of the path that should be skipped.\n+\t * @return position of the next entry whose path is after the input.\n+\t */\n+\tpublic int nextEntry(final int position) {\n+\t\tDirCacheEntry last = sortedEntries[position];\n+\t\tint nextIdx = position + 1;\n+\t\twhile (nextIdx < entryCnt) {\n+\t\t\tfinal DirCacheEntry next = sortedEntries[nextIdx];\n+\t\t\tif (cmp(last, next) != 0)\n+\t\t\t\tbreak;\n+\t\t\tlast = next;\n+\t\t\tnextIdx++;\n+\t\t}\n+\t\treturn nextIdx;\n+\t}\n+\n+\t/**\n+\t * Total number of file entries stored in the index.\n+\t * <p>\n+\t * This count includes unmerged stages for a file entry if the file is\n+\t * currently conflicted in a merge. This means the total number of entries\n+\t * in the index may be up to 3 times larger than the number of files in the\n+\t * working directory.\n+\t * <p>\n+\t * Note that this value counts only <i>files</i>.\n+\t * \n+\t * @return number of entries available.\n+\t * @see #getEntry(int)\n+\t */\n+\tpublic int getEntryCount() {\n+\t\treturn entryCnt;\n+\t}\n+\n+\t/**\n+\t * Get a specific entry.\n+\t * \n+\t * @param i\n+\t *            position of the entry to get.\n+\t * @return the entry at position <code>i</code>.\n+\t */\n+\tpublic DirCacheEntry getEntry(final int i) {\n+\t\treturn sortedEntries[i];\n+\t}\n+\n+\t/**\n+\t * Get a specific entry.\n+\t * \n+\t * @param path\n+\t *            the path to search for.\n+\t * @return the entry at position <code>i</code>.\n+\t */\n+\tpublic DirCacheEntry getEntry(final String path) {\n+\t\tfinal int i = findEntry(path);\n+\t\treturn i < 0 ? null : sortedEntries[i];\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\nnew file mode 100644\nindex 0000000..7efab48\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n@@ -0,0 +1,270 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.io.IOException;\n+import java.io.InputStream;\n+import java.nio.ByteBuffer;\n+\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.util.NB;\n+\n+/**\n+ * A single file (or stage of a file) in a {@link DirCache}.\n+ * <p>\n+ * An entry represents exactly one stage of a file. If a file path is unmerged\n+ * then multiple DirCacheEntry instances may appear for the same path name.\n+ */\n+public class DirCacheEntry {\n+\t// private static final int P_CTIME = 0;\n+\n+\t// private static final int P_CTIME_NSEC = 4;\n+\n+\tprivate static final int P_MTIME = 8;\n+\n+\t// private static final int P_MTIME_NSEC = 12;\n+\n+\t// private static final int P_DEV = 16;\n+\n+\t// private static final int P_INO = 20;\n+\n+\tprivate static final int P_MODE = 24;\n+\n+\t// private static final int P_UID = 28;\n+\n+\t// private static final int P_GID = 32;\n+\n+\tprivate static final int P_SIZE = 36;\n+\n+\tprivate static final int P_OBJECTID = 40;\n+\n+\tprivate static final int P_FLAGS = 60;\n+\n+\tstatic final int INFO_LEN = 62;\n+\n+\tprivate static final int ASSUME_VALID = 0x80;\n+\n+\t/** (Possibly shared) header information storage. */\n+\tprivate final byte[] info;\n+\n+\t/** First location within {@link #info} where our header starts. */\n+\tprivate final int infoOffset;\n+\n+\t/** Our encoded path name, from the root of the repository. */\n+\tfinal byte[] path;\n+\n+\tDirCacheEntry(final byte[] sharedInfo, final int infoAt,\n+\t\t\tfinal InputStream in) throws IOException {\n+\t\tinfo = sharedInfo;\n+\t\tinfoOffset = infoAt;\n+\n+\t\tNB.readFully(in, info, infoOffset, INFO_LEN);\n+\n+\t\tint pathLen = NB.decodeUInt16(info, infoOffset + P_FLAGS) & 0xfff;\n+\t\tpath = new byte[pathLen];\n+\t\tNB.readFully(in, path, 0, pathLen);\n+\n+\t\t// Index records are padded out to the next 8 byte alignment\n+\t\t// for historical reasons related to how C Git read the files.\n+\t\t//\n+\t\tfinal int actLen = INFO_LEN + pathLen;\n+\t\tfinal int expLen = (actLen + 8) & ~7;\n+\t\tif (actLen != expLen)\n+\t\t\tin.skip(expLen - actLen);\n+\t}\n+\n+\tfinal byte[] idBuffer() {\n+\t\treturn info;\n+\t}\n+\n+\tfinal int idOffset() {\n+\t\treturn infoOffset + P_OBJECTID;\n+\t}\n+\n+\t/**\n+\t * Is this entry always thought to be unmodified?\n+\t * <p>\n+\t * Most entries in the index do not have this flag set. Users may however\n+\t * set them on if the file system stat() costs are too high on this working\n+\t * directory, such as on NFS or SMB volumes.\n+\t * \n+\t * @return true if we must assume the entry is unmodified.\n+\t */\n+\tpublic boolean isAssumeValid() {\n+\t\treturn (info[infoOffset + P_FLAGS] & ASSUME_VALID) != 0;\n+\t}\n+\n+\t/**\n+\t * Set the assume valid flag for this entry,\n+\t * \n+\t * @param assume\n+\t *            true to ignore apparent modifications; false to look at last\n+\t *            modified to detect file modifications.\n+\t */\n+\tpublic void setAssumeValid(final boolean assume) {\n+\t\tif (assume)\n+\t\t\tinfo[infoOffset + P_FLAGS] |= ASSUME_VALID;\n+\t\telse\n+\t\t\tinfo[infoOffset + P_FLAGS] &= ~ASSUME_VALID;\n+\t}\n+\n+\t/**\n+\t * Get the stage of this entry.\n+\t * <p>\n+\t * Entries have one of 4 possible stages: 0-3.\n+\t * \n+\t * @return the stage of this entry.\n+\t */\n+\tpublic int getStage() {\n+\t\treturn (info[infoOffset + P_FLAGS] >>> 4) & 0x3;\n+\t}\n+\n+\t/**\n+\t * Obtain the raw {@link FileMode} bits for this entry.\n+\t * \n+\t * @return mode bits for the entry.\n+\t * @see FileMode#fromBits(int)\n+\t */\n+\tpublic int getRawMode() {\n+\t\treturn NB.decodeInt32(info, infoOffset + P_MODE);\n+\t}\n+\n+\t/**\n+\t * Set the file mode for this entry.\n+\t * \n+\t * @param mode\n+\t *            the new mode constant.\n+\t */\n+\tpublic void setFileMode(final FileMode mode) {\n+\t\tNB.encodeInt32(info, infoOffset + P_MODE, mode.getBits());\n+\t}\n+\n+\t/**\n+\t * Get the cached last modification date of this file, in milliseconds.\n+\t * <p>\n+\t * One of the indicators that the file has been modified by an application\n+\t * changing the working tree is if the last modification time for the file\n+\t * differs from the time stored in this entry.\n+\t * \n+\t * @return last modification time of this file, in milliseconds since the\n+\t *         Java epoch (midnight Jan 1, 1970 UTC).\n+\t */\n+\tpublic long getLastModified() {\n+\t\treturn decodeTS(P_MTIME);\n+\t}\n+\n+\t/**\n+\t * Set the cached last modification date of this file, using milliseconds.\n+\t * \n+\t * @param when\n+\t *            new cached modification date of the file, in milliseconds.\n+\t */\n+\tpublic void setLastModified(final long when) {\n+\t\tencodeTS(P_MTIME, when);\n+\t}\n+\n+\t/**\n+\t * Get the cached size (in bytes) of this file.\n+\t * <p>\n+\t * One of the indicators that the file has been modified by an application\n+\t * changing the working tree is if the size of the file (in bytes) differs\n+\t * from the size stored in this entry.\n+\t * <p>\n+\t * Note that this is the length of the file in the working directory, which\n+\t * may differ from the size of the decompressed blob if work tree filters\n+\t * are being used, such as LF<->CRLF conversion.\n+\t * \n+\t * @return cached size of the working directory file, in bytes.\n+\t */\n+\tpublic int getLength() {\n+\t\treturn NB.decodeInt32(info, infoOffset + P_SIZE);\n+\t}\n+\n+\t/**\n+\t * Set the cached size (in bytes) of this file.\n+\t * \n+\t * @param sz\n+\t *            new cached size of the file, as bytes.\n+\t */\n+\tpublic void setLength(final int sz) {\n+\t\tNB.encodeInt32(info, infoOffset + P_SIZE, sz);\n+\t}\n+\n+\t/**\n+\t * Obtain the ObjectId for the entry.\n+\t * <p>\n+\t * Using this method to compare ObjectId values between entries is\n+\t * inefficient as it causes memory allocation.\n+\t * \n+\t * @return object identifier for the entry.\n+\t */\n+\tpublic ObjectId getObjectId() {\n+\t\treturn ObjectId.fromRaw(idBuffer(), idOffset());\n+\t}\n+\n+\t/**\n+\t * Get the entry's complete path.\n+\t * <p>\n+\t * This method is not very efficient and is primarily meant for debugging\n+\t * and final output generation. Applications should try to avoid calling it,\n+\t * and if invoked do so only once per interesting entry, where the name is\n+\t * absolutely required for correct function.\n+\t * \n+\t * @return complete path of the entry, from the root of the repository. If\n+\t *         the entry is in a subtree there will be at least one '/' in the\n+\t *         returned string.\n+\t */\n+\tpublic String getPathString() {\n+\t\treturn Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();\n+\t}\n+\n+\tprivate long decodeTS(final int pIdx) {\n+\t\tfinal int base = infoOffset + pIdx;\n+\t\tfinal int sec = NB.decodeInt32(info, base);\n+\t\tfinal int ms = NB.decodeInt32(info, base + 4) / 1000000;\n+\t\treturn 1000L * sec + ms;\n+\t}\n+\n+\tprivate void encodeTS(final int pIdx, final long when) {\n+\t\tfinal int base = infoOffset + pIdx;\n+\t\tNB.encodeInt32(info, base, (int) (when / 1000));\n+\t\tNB.encodeInt32(info, base + 4, ((int) (when % 1000)) * 1000000);\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86874","messageId":"1218503293-14057-8-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-7-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 07/26] Export the new DirCache API to Eclipse plugins using jgit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:54Z","receivedAt":"2008-08-12T01:07:54Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Signed-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n org.spearce.jgit/META-INF/MANIFEST.MF |    3 ++-\n 1 files changed, 2 insertions(+), 1 deletions(-)\n\ndiff --git a/org.spearce.jgit/META-INF/MANIFEST.MF b/org.spearce.jgit/META-INF/MANIFEST.MF\nindex 388dc18..36f92f2 100644\n--- a/org.spearce.jgit/META-INF/MANIFEST.MF\n+++ b/org.spearce.jgit/META-INF/MANIFEST.MF\n@@ -5,7 +5,8 @@ Bundle-SymbolicName: org.spearce.jgit\n Bundle-Version: 0.3.1.qualifier\n Bundle-Localization: plugin\n Bundle-Vendor: %provider_name\n-Export-Package: org.spearce.jgit.errors;uses:=\"org.spearce.jgit.lib\",\n+Export-Package: org.spearce.jgit.dircache,\n+ org.spearce.jgit.errors;uses:=\"org.spearce.jgit.lib\",\n  org.spearce.jgit.lib,\n  org.spearce.jgit.revplot,\n  org.spearce.jgit.revwalk,\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86869","messageId":"1218503293-14057-9-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-8-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 08/26] Support locking (and unlocking) a .git/index through DirCache","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:55Z","receivedAt":"2008-08-12T01:07:55Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"In order to safely perform an update of the .git/index file (or any\nother DirCache file) we need to lock the file prior to reading its\ncontents into memory.  This ensures that the contents will not be\nmodified between the time we start processing the entires and when\nthe update operation is completed.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/dircache/DirCache.java    |   86 ++++++++++++++++++++\n 1 files changed, 86 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\nindex f8ca60d..8f38a79 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n@@ -48,6 +48,7 @@ import java.util.Comparator;\n \n import org.spearce.jgit.errors.CorruptObjectException;\n import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.LockFile;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.util.NB;\n \n@@ -144,6 +145,56 @@ public class DirCache {\n \t\treturn read(new File(db.getDirectory(), \"index\"));\n \t}\n \n+\t/**\n+\t * Create a new in-core index representation, lock it, and read from disk.\n+\t * <p>\n+\t * The new index will be locked and then read before it is returned to the\n+\t * caller. Read failures are reported as exceptions and therefore prevent\n+\t * the method from returning a partially populated index.\n+\t * \n+\t * @param indexLocation\n+\t *            location of the index file on disk.\n+\t * @return a cache representing the contents of the specified index file (if\n+\t *         it exists) or an empty cache if the file does not exist.\n+\t * @throws IOException\n+\t *             the index file is present but could not be read, or the lock\n+\t *             could not be obtained.\n+\t * @throws CorruptObjectException\n+\t *             the index file is using a format or extension that this\n+\t *             library does not support.\n+\t */\n+\tpublic static DirCache lock(final File indexLocation)\n+\t\t\tthrows CorruptObjectException, IOException {\n+\t\tfinal DirCache c = new DirCache(indexLocation);\n+\t\tif (!c.lock())\n+\t\t\tthrow new IOException(\"Cannot lock \" + indexLocation);\n+\t\tc.read();\n+\t\treturn c;\n+\t}\n+\n+\t/**\n+\t * Create a new in-core index representation, lock it, and read from disk.\n+\t * <p>\n+\t * The new index will be locked and then read before it is returned to the\n+\t * caller. Read failures are reported as exceptions and therefore prevent\n+\t * the method from returning a partially populated index.\n+\t * \n+\t * @param db\n+\t *            repository the caller wants to read the default index of.\n+\t * @return a cache representing the contents of the specified index file (if\n+\t *         it exists) or an empty cache if the file does not exist.\n+\t * @throws IOException\n+\t *             the index file is present but could not be read, or the lock\n+\t *             could not be obtained.\n+\t * @throws CorruptObjectException\n+\t *             the index file is using a format or extension that this\n+\t *             library does not support.\n+\t */\n+\tpublic static DirCache lock(final Repository db)\n+\t\t\tthrows CorruptObjectException, IOException {\n+\t\treturn lock(new File(db.getDirectory(), \"index\"));\n+\t}\n+\n \t/** Location of the current version of the index file. */\n \tprivate final File liveFile;\n \n@@ -156,6 +207,9 @@ public class DirCache {\n \t/** Number of positions within {@link #sortedEntries} that are valid. */\n \tprivate int entryCnt;\n \n+\t/** Our active lock (if we hold it); null if we don't have it locked. */\n+\tprivate LockFile myLock;\n+\n \t/**\n \t * Create a new in-core index representation.\n \t * <p>\n@@ -279,6 +333,38 @@ public class DirCache {\n \t}\n \n \t/**\n+\t * Try to establish an update lock on the cache file.\n+\t * \n+\t * @return true if the lock is now held by the caller; false if it is held\n+\t *         by someone else.\n+\t * @throws IOException\n+\t *             the output file could not be created. The caller does not\n+\t *             hold the lock.\n+\t */\n+\tpublic boolean lock() throws IOException {\n+\t\tfinal LockFile tmp = new LockFile(liveFile);\n+\t\tif (tmp.lock()) {\n+\t\t\ttmp.setNeedStatInformation(true);\n+\t\t\tmyLock = tmp;\n+\t\t\treturn true;\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\t/**\n+\t * Unlock this file and abort this change.\n+\t * <p>\n+\t * The temporary file (if created) is deleted before returning.\n+\t */\n+\tpublic void unlock() {\n+\t\tfinal LockFile tmp = myLock;\n+\t\tif (tmp != null) {\n+\t\t\tmyLock = null;\n+\t\t\ttmp.unlock();\n+\t\t}\n+\t}\n+\n+\t/**\n \t * Locate the position a path's entry is at in the index.\n \t * <p>\n \t * If there is at least one entry in the index for this path the position of\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86868","messageId":"1218503293-14057-10-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-9-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 09/26] Support writing a .git/index through DirCache","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:56Z","receivedAt":"2008-08-12T01:07:56Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We now support writing back the same entires we read in, with maybe\nsome minor entry level edits such as to change the mode of an entry,\nor the assume valid flag.\n\nWe also include full support for smudging potentially 'racily clean'\nentries while writing an updated index file back to disk.  We differ\nfrom C Git in how we smudge an entry, but the effect should be the\nsame in that potentially racily clean entries will require C Git to\ncompare the content of the file, at least until Jan 19, 2038.  The\nsmudge rule needs to be different because we do not have access to\nthe working directory in order to validate a racily clean entry.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/dircache/DirCache.java    |   97 ++++++++++++++++++++\n .../org/spearce/jgit/dircache/DirCacheEntry.java   |   75 +++++++++++++++\n 2 files changed, 172 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\nindex 8f38a79..248e2a2 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n@@ -38,12 +38,16 @@\n package org.spearce.jgit.dircache;\n \n import java.io.BufferedInputStream;\n+import java.io.BufferedOutputStream;\n import java.io.File;\n import java.io.FileInputStream;\n import java.io.FileNotFoundException;\n import java.io.IOException;\n+import java.io.OutputStream;\n import java.nio.ByteBuffer;\n import java.nio.channels.FileChannel;\n+import java.security.DigestOutputStream;\n+import java.security.MessageDigest;\n import java.util.Comparator;\n \n import org.spearce.jgit.errors.CorruptObjectException;\n@@ -352,6 +356,99 @@ public class DirCache {\n \t}\n \n \t/**\n+\t * Write the entry records from memory to disk.\n+\t * <p>\n+\t * The cache must be locked first by calling {@link #lock()} and receiving\n+\t * true as the return value. Applications are encouraged to lock the index,\n+\t * then invoke {@link #read()} to ensure the in-memory data is current,\n+\t * prior to updating the in-memory entries.\n+\t * <p>\n+\t * Once written the lock is closed and must be either committed with\n+\t * {@link #commit()} or rolled back with {@link #unlock()}.\n+\t * \n+\t * @throws IOException\n+\t *             the output file could not be created. The caller no longer\n+\t *             holds the lock.\n+\t */\n+\tpublic void write() throws IOException {\n+\t\tfinal LockFile tmp = myLock;\n+\t\trequireLocked(tmp);\n+\t\ttry {\n+\t\t\twriteTo(new BufferedOutputStream(tmp.getOutputStream()));\n+\t\t} catch (IOException err) {\n+\t\t\ttmp.unlock();\n+\t\t\tthrow err;\n+\t\t} catch (RuntimeException err) {\n+\t\t\ttmp.unlock();\n+\t\t\tthrow err;\n+\t\t} catch (Error err) {\n+\t\t\ttmp.unlock();\n+\t\t\tthrow err;\n+\t\t}\n+\t}\n+\n+\tprivate void writeTo(final OutputStream os) throws IOException {\n+\t\tfinal MessageDigest foot = Constants.newMessageDigest();\n+\t\tfinal DigestOutputStream dos = new DigestOutputStream(os, foot);\n+\n+\t\t// Write the header.\n+\t\t//\n+\t\tfinal byte[] tmp = new byte[128];\n+\t\tSystem.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length);\n+\t\tNB.encodeInt32(tmp, 4, /* version */2);\n+\t\tNB.encodeInt32(tmp, 8, entryCnt);\n+\t\tdos.write(tmp, 0, 12);\n+\n+\t\t// Write the individual file entries.\n+\t\t//\n+\t\tif (lastModified <= 0) {\n+\t\t\t// Write a new index, as no entries require smudging.\n+\t\t\t//\n+\t\t\tfor (int i = 0; i < entryCnt; i++)\n+\t\t\t\tsortedEntries[i].write(dos);\n+\t\t} else {\n+\t\t\tfinal int smudge_s = (int) (lastModified / 1000);\n+\t\t\tfinal int smudge_ns = ((int) (lastModified % 1000)) * 1000000;\n+\t\t\tfor (int i = 0; i < entryCnt; i++) {\n+\t\t\t\tfinal DirCacheEntry e = sortedEntries[i];\n+\t\t\t\tif (e.mightBeRacilyClean(smudge_s, smudge_ns))\n+\t\t\t\t\te.smudgeRacilyClean();\n+\t\t\t\te.write(dos);\n+\t\t\t}\n+\t\t}\n+\n+\t\tos.write(foot.digest());\n+\t\tos.close();\n+\t}\n+\n+\t/**\n+\t * Commit this change and release the lock.\n+\t * <p>\n+\t * If this method fails (returns false) the lock is still released.\n+\t * \n+\t * @return true if the commit was successful and the file contains the new\n+\t *         data; false if the commit failed and the file remains with the\n+\t *         old data.\n+\t * @throws IllegalStateException\n+\t *             the lock is not held.\n+\t */\n+\tpublic boolean commit() {\n+\t\tfinal LockFile tmp = myLock;\n+\t\trequireLocked(tmp);\n+\t\tmyLock = null;\n+\t\tif (!tmp.commit())\n+\t\t\treturn false;\n+\t\tlastModified = tmp.getCommitLastModified();\n+\t\treturn true;\n+\t}\n+\n+\tprivate void requireLocked(final LockFile tmp) {\n+\t\tif (tmp == null)\n+\t\t\tthrow new IllegalStateException(\"DirCache \"\n+\t\t\t\t\t+ liveFile.getAbsolutePath() + \" not locked.\");\n+\t}\n+\n+\t/**\n \t * Unlock this file and abort this change.\n \t * <p>\n \t * The temporary file (if created) is deleted before returning.\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\nindex 7efab48..8ca8f22 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n@@ -39,7 +39,9 @@ package org.spearce.jgit.dircache;\n \n import java.io.IOException;\n import java.io.InputStream;\n+import java.io.OutputStream;\n import java.nio.ByteBuffer;\n+import java.util.Arrays;\n \n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.FileMode;\n@@ -53,6 +55,8 @@ import org.spearce.jgit.util.NB;\n  * then multiple DirCacheEntry instances may appear for the same path name.\n  */\n public class DirCacheEntry {\n+\tprivate static final byte[] nullpad = new byte[8];\n+\n \t// private static final int P_CTIME = 0;\n \n \t// private static final int P_CTIME_NSEC = 4;\n@@ -110,6 +114,77 @@ public class DirCacheEntry {\n \t\t\tin.skip(expLen - actLen);\n \t}\n \n+\tvoid write(final OutputStream os) throws IOException {\n+\t\tfinal int pathLen = path.length;\n+\t\tos.write(info, infoOffset, INFO_LEN);\n+\t\tos.write(path, 0, pathLen);\n+\n+\t\t// Index records are padded out to the next 8 byte alignment\n+\t\t// for historical reasons related to how C Git read the files.\n+\t\t//\n+\t\tfinal int actLen = INFO_LEN + pathLen;\n+\t\tfinal int expLen = (actLen + 8) & ~7;\n+\t\tif (actLen != expLen)\n+\t\t\tos.write(nullpad, 0, expLen - actLen);\n+\t}\n+\n+\t/**\n+\t * Is it possible for this entry to be accidently assumed clean?\n+\t * <p>\n+\t * The \"racy git\" problem happens when a work file can be updated faster\n+\t * than the filesystem records file modification timestamps. It is possible\n+\t * for an application to edit a work file, update the index, then edit it\n+\t * again before the filesystem will give the work file a new modification\n+\t * timestamp. This method tests to see if file was written out at the same\n+\t * time as the index.\n+\t * \n+\t * @param smudge_s\n+\t *            seconds component of the index's last modified time.\n+\t * @param smudge_ns\n+\t *            nanoseconds component of the index's last modified time.\n+\t * @return true if extra careful checks should be used.\n+\t */\n+\tfinal boolean mightBeRacilyClean(final int smudge_s, final int smudge_ns) {\n+\t\t// If the index has a modification time then it came from disk\n+\t\t// and was not generated from scratch in memory. In such cases\n+\t\t// the entry is 'racily clean' if the entry's cached modification\n+\t\t// time is equal to or later than the index modification time. In\n+\t\t// such cases the work file is too close to the index to tell if\n+\t\t// it is clean or not based on the modification time alone.\n+\t\t//\n+\t\tfinal int base = infoOffset + P_MTIME;\n+\t\tfinal int mtime = NB.decodeInt32(info, base);\n+\t\tif (smudge_s < mtime)\n+\t\t\treturn true;\n+\t\tif (smudge_s == mtime)\n+\t\t\treturn smudge_ns <= NB.decodeInt32(info, base + 4) / 1000000;\n+\t\treturn false;\n+\t}\n+\n+\t/**\n+\t * Force this entry to no longer match its working tree file.\n+\t * <p>\n+\t * This avoids the \"racy git\" problem by making this index entry no longer\n+\t * match the file in the working directory. Later git will be forced to\n+\t * compare the file content to ensure the file matches the working tree.\n+\t */\n+\tfinal void smudgeRacilyClean() {\n+\t\t// We don't use the same approach as C Git to smudge the entry,\n+\t\t// as we cannot compare the working tree file to our SHA-1 and\n+\t\t// thus cannot use the \"size to 0\" trick without accidentally\n+\t\t// thinking a zero length file is clean.\n+\t\t//\n+\t\t// Instead we force the mtime to the largest possible value, so\n+\t\t// it is certainly after the index's own modification time and\n+\t\t// on a future read will cause mightBeRacilyClean to say \"yes!\".\n+\t\t// It is also unlikely to match with the working tree file.\n+\t\t//\n+\t\t// I'll see you again before Jan 19, 2038, 03:14:07 AM GMT.\n+\t\t//\n+\t\tfinal int base = infoOffset + P_MTIME;\n+\t\tArrays.fill(info, base, base + 8, (byte) 127);\n+\t}\n+\n \tfinal byte[] idBuffer() {\n \t\treturn info;\n \t}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86867","messageId":"1218503293-14057-11-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-10-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 10/26] Support the 'TREE' extension in .git/index through DirCache","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:57Z","receivedAt":"2008-08-12T01:07:57Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We now support reading and writing the 'TREE' extension of the 'DIRC'\nfile format.  C Git uses this as an optional extension to cache the\nSHA-1 id of each tree object used by the index, permitting C Git to\nidentify subsections of the repository which have not been modified\nand to speed up the git-write-tree plumbing command prior to making\na commit object.\n\nOur support includes constructing the tree objects if they do not\nexist, but we don't write them to the object database.  This change\nonly builds the ranges within the index file and marks them invalid,\nso C Git knows that the tree must be generated and saved into the\nobject database before it can rely upon it in a commit.\n\nLike the DirCacheEntry objects we try to avoid unnecessary encode\nand decode operations during reads and writes as they would just\ndrag down the processor.\n\nOur sort ordering of the 'TREE' data is different from that of C Git.\nC Git sorts tree children according to lengths of path names, as the\nactual order was not relevant but Junio wanted a stable ordering.  We\nwant our tree children sorted by name so we can merge-sort them onto\nthe main index entries.  That means we may have to sort the children\nafter we read them from disk; at least that is true if C Git was the\nlast process to write the index file.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/dircache/DirCache.java    |   46 ++\n .../org/spearce/jgit/dircache/DirCacheTree.java    |  453 ++++++++++++++++++++\n 2 files changed, 499 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\nindex 248e2a2..b5fdd46 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n@@ -54,7 +54,9 @@ import org.spearce.jgit.errors.CorruptObjectException;\n import org.spearce.jgit.lib.Constants;\n import org.spearce.jgit.lib.LockFile;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.util.MutableInteger;\n import org.spearce.jgit.util.NB;\n+import org.spearce.jgit.util.TemporaryBuffer;\n \n /**\n  * Support for the Git dircache (aka index file).\n@@ -72,6 +74,8 @@ import org.spearce.jgit.util.NB;\n public class DirCache {\n \tprivate static final byte[] SIG_DIRC = { 'D', 'I', 'R', 'C' };\n \n+\tprivate static final int EXT_TREE = 0x54524545 /* 'TREE' */;\n+\n \tprivate static final int INFO_LEN = DirCacheEntry.INFO_LEN;\n \n \tprivate static final DirCacheEntry[] NO_ENTRIES = {};\n@@ -211,6 +215,9 @@ public class DirCache {\n \t/** Number of positions within {@link #sortedEntries} that are valid. */\n \tprivate int entryCnt;\n \n+\t/** Cache tree for this index; null if the cache tree is not available. */\n+\tprivate DirCacheTree tree;\n+\n \t/** Our active lock (if we hold it); null if we don't have it locked. */\n \tprivate LockFile myLock;\n \n@@ -272,6 +279,7 @@ public class DirCache {\n \t\tlastModified = 0;\n \t\tsortedEntries = NO_ENTRIES;\n \t\tentryCnt = 0;\n+\t\ttree = null;\n \t}\n \n \tprivate void readFrom(final FileInputStream inStream) throws IOException,\n@@ -306,6 +314,12 @@ public class DirCache {\n \t\twhile (fd.position() - in.available() < sizeOnDisk - 20) {\n \t\t\tNB.readFully(in, hdr, 0, 8);\n \t\t\tswitch (NB.decodeInt32(hdr, 0)) {\n+\t\t\tcase EXT_TREE: {\n+\t\t\t\tfinal byte[] raw = new byte[NB.decodeInt32(hdr, 4)];\n+\t\t\t\tNB.readFully(in, raw, 0, raw.length);\n+\t\t\t\ttree = new DirCacheTree(raw, new MutableInteger(), null);\n+\t\t\t\tbreak;\n+\t\t\t}\n \t\t\tdefault:\n \t\t\t\tif (hdr[0] >= 'A' && hdr[0] <= 'Z') {\n \t\t\t\t\t// The extension is optional and is here only as\n@@ -417,6 +431,17 @@ public class DirCache {\n \t\t\t}\n \t\t}\n \n+\t\tif (tree != null) {\n+\t\t\tfinal TemporaryBuffer bb = new TemporaryBuffer();\n+\t\t\ttree.write(tmp, bb);\n+\t\t\tbb.close();\n+\n+\t\t\tNB.encodeInt32(tmp, 0, EXT_TREE);\n+\t\t\tNB.encodeInt32(tmp, 4, (int) bb.length());\n+\t\t\tdos.write(tmp, 0, 8);\n+\t\t\tbb.writeTo(dos, null);\n+\t\t}\n+\n \t\tos.write(foot.digest());\n \t\tos.close();\n \t}\n@@ -565,4 +590,25 @@ public class DirCache {\n \t\tfinal int i = findEntry(path);\n \t\treturn i < 0 ? null : sortedEntries[i];\n \t}\n+\n+\t/**\n+\t * Obtain (or build) the current cache tree structure.\n+\t * <p>\n+\t * This method can optionally recreate the cache tree, without flushing the\n+\t * tree objects themselves to disk.\n+\t * \n+\t * @param build\n+\t *            if true and the cache tree is not present in the index it will\n+\t *            be generated and returned to the caller.\n+\t * @return the cache tree; null if there is no current cache tree available\n+\t *         and <code>build</code> was false.\n+\t */\n+\tpublic DirCacheTree getCacheTree(final boolean build) {\n+\t\tif (build) {\n+\t\t\tif (tree == null)\n+\t\t\t\ttree = new DirCacheTree();\n+\t\t\ttree.validate(sortedEntries, entryCnt, 0, 0);\n+\t\t}\n+\t\treturn tree;\n+\t}\n }\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java\nnew file mode 100644\nindex 0000000..d06be08\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheTree.java\n@@ -0,0 +1,453 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.io.IOException;\n+import java.io.OutputStream;\n+import java.nio.ByteBuffer;\n+import java.util.Arrays;\n+import java.util.Comparator;\n+\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.util.MutableInteger;\n+import org.spearce.jgit.util.RawParseUtils;\n+\n+/**\n+ * Single tree record from the 'TREE' {@link DirCache} extension.\n+ * <p>\n+ * A valid cache tree record contains the object id of a tree object and the\n+ * total number of {@link DirCacheEntry} instances (counted recursively) from\n+ * the DirCache contained within the tree. This information facilitates faster\n+ * traversal of the index and quicker generation of tree objects prior to\n+ * creating a new commit.\n+ * <p>\n+ * An invalid cache tree record indicates a known subtree whose file entries\n+ * have changed in ways that cause the tree to no longer have a known object id.\n+ * Invalid cache tree records must be revalidated prior to use.\n+ */\n+public class DirCacheTree {\n+\tprivate static final byte[] NO_NAME = {};\n+\n+\tprivate static final DirCacheTree[] NO_CHILDREN = {};\n+\n+\tprivate static final Comparator<DirCacheTree> TREE_CMP = new Comparator<DirCacheTree>() {\n+\t\tpublic int compare(final DirCacheTree o1, final DirCacheTree o2) {\n+\t\t\tfinal byte[] a = o1.encodedName;\n+\t\t\tfinal byte[] b = o2.encodedName;\n+\t\t\tfinal int aLen = a.length;\n+\t\t\tfinal int bLen = b.length;\n+\t\t\tint cPos;\n+\t\t\tfor (cPos = 0; cPos < aLen && cPos < bLen; cPos++) {\n+\t\t\t\tfinal int cmp = (a[cPos] & 0xff) - (b[cPos] & 0xff);\n+\t\t\t\tif (cmp != 0)\n+\t\t\t\t\treturn cmp;\n+\t\t\t}\n+\t\t\tif (aLen == bLen)\n+\t\t\t\treturn 0;\n+\t\t\tif (aLen < bLen)\n+\t\t\t\treturn '/' - (b[cPos] & 0xff);\n+\t\t\treturn (a[cPos] & 0xff) - '/';\n+\t\t}\n+\t};\n+\n+\t/** Tree this tree resides in; null if we are the root. */\n+\tprivate DirCacheTree parent;\n+\n+\t/** Name of this tree within its parent. */\n+\tprivate byte[] encodedName;\n+\n+\t/** Number of {@link DirCacheEntry} records that belong to this tree. */\n+\tprivate int entrySpan;\n+\n+\t/** Unique SHA-1 of this tree; null if invalid. */\n+\tprivate ObjectId id;\n+\n+\t/** Child trees, if any, sorted by {@link #encodedName}. */\n+\tprivate DirCacheTree[] children;\n+\n+\t/** Number of valid children in {@link #children}. */\n+\tprivate int childCnt;\n+\n+\tDirCacheTree() {\n+\t\tencodedName = NO_NAME;\n+\t\tchildren = NO_CHILDREN;\n+\t\tchildCnt = 0;\n+\t\tentrySpan = -1;\n+\t}\n+\n+\tprivate DirCacheTree(final DirCacheTree myParent, final byte[] path,\n+\t\t\tfinal int pathOff, final int pathLen) {\n+\t\tparent = myParent;\n+\t\tencodedName = new byte[pathLen];\n+\t\tSystem.arraycopy(path, pathOff, encodedName, 0, pathLen);\n+\t\tchildren = NO_CHILDREN;\n+\t\tchildCnt = 0;\n+\t\tentrySpan = -1;\n+\t}\n+\n+\tDirCacheTree(final byte[] in, final MutableInteger off,\n+\t\t\tfinal DirCacheTree myParent) {\n+\t\tparent = myParent;\n+\n+\t\tint ptr = RawParseUtils.next(in, off.value, '\\0');\n+\t\tfinal int nameLen = ptr - off.value - 1;\n+\t\tif (nameLen > 0) {\n+\t\t\tencodedName = new byte[nameLen];\n+\t\t\tSystem.arraycopy(in, off.value, encodedName, 0, nameLen);\n+\t\t} else\n+\t\t\tencodedName = NO_NAME;\n+\n+\t\tentrySpan = RawParseUtils.parseBase10(in, ptr, off);\n+\t\tfinal int subcnt = RawParseUtils.parseBase10(in, off.value, off);\n+\t\toff.value = RawParseUtils.next(in, off.value, '\\n');\n+\n+\t\tif (entrySpan >= 0) {\n+\t\t\t// Valid trees have a positive entry count and an id of a\n+\t\t\t// tree object that should exist in the object database.\n+\t\t\t//\n+\t\t\tid = ObjectId.fromRaw(in, off.value);\n+\t\t\toff.value += Constants.OBJECT_ID_LENGTH;\n+\t\t}\n+\n+\t\tif (subcnt > 0) {\n+\t\t\tboolean alreadySorted = true;\n+\t\t\tchildren = new DirCacheTree[subcnt];\n+\t\t\tfor (int i = 0; i < subcnt; i++) {\n+\t\t\t\tchildren[i] = new DirCacheTree(in, off, this);\n+\n+\t\t\t\t// C Git's ordering differs from our own; it prefers to\n+\t\t\t\t// sort by length first. This sometimes produces a sort\n+\t\t\t\t// we do not desire. On the other hand it may have been\n+\t\t\t\t// created by us, and be sorted the way we want.\n+\t\t\t\t//\n+\t\t\t\tif (alreadySorted && i > 0\n+\t\t\t\t\t\t&& TREE_CMP.compare(children[i - 1], children[i]) > 0)\n+\t\t\t\t\talreadySorted = false;\n+\t\t\t}\n+\t\t\tif (!alreadySorted)\n+\t\t\t\tArrays.sort(children, 0, subcnt, TREE_CMP);\n+\t\t} else {\n+\t\t\t// Leaf level trees have no children, only (file) entries.\n+\t\t\t//\n+\t\t\tchildren = NO_CHILDREN;\n+\t\t}\n+\t\tchildCnt = subcnt;\n+\t}\n+\n+\tvoid write(final byte[] tmp, final OutputStream os) throws IOException {\n+\t\tint ptr = tmp.length;\n+\t\ttmp[--ptr] = '\\n';\n+\t\tptr = RawParseUtils.formatBase10(tmp, ptr, childCnt);\n+\t\ttmp[--ptr] = ' ';\n+\t\tptr = RawParseUtils.formatBase10(tmp, ptr, isValid() ? entrySpan : -1);\n+\t\ttmp[--ptr] = 0;\n+\n+\t\tos.write(encodedName);\n+\t\tos.write(tmp, ptr, tmp.length - ptr);\n+\t\tif (isValid()) {\n+\t\t\tid.copyRawTo(tmp, 0);\n+\t\t\tos.write(tmp, 0, Constants.OBJECT_ID_LENGTH);\n+\t\t}\n+\t\tfor (int i = 0; i < childCnt; i++)\n+\t\t\tchildren[i].write(tmp, os);\n+\t}\n+\n+\t/**\n+\t * Determine if this cache is currently valid.\n+\t * <p>\n+\t * A valid cache tree knows how many {@link DirCacheEntry} instances from\n+\t * the parent {@link DirCache} reside within this tree (recursively\n+\t * enumerated). It also knows the object id of the tree, as the tree should\n+\t * be readily available from the repository's object database.\n+\t * \n+\t * @return true if this tree is knows key details about itself; false if the\n+\t *         tree needs to be regenerated.\n+\t */\n+\tpublic boolean isValid() {\n+\t\treturn id != null;\n+\t}\n+\n+\t/**\n+\t * Get the number of entries this tree spans within the DirCache.\n+\t * <p>\n+\t * If this tree is not valid (see {@link #isValid()}) this method's return\n+\t * value is always strictly negative (less than 0) but is otherwise an\n+\t * undefined result.\n+\t * \n+\t * @return total number of entries (recursively) contained within this tree.\n+\t */\n+\tpublic int getEntrySpan() {\n+\t\treturn entrySpan;\n+\t}\n+\n+\t/**\n+\t * Get the number of cached subtrees contained within this tree.\n+\t * \n+\t * @return number of child trees available through this tree.\n+\t */\n+\tpublic int getChildCount() {\n+\t\treturn childCnt;\n+\t}\n+\n+\t/**\n+\t * Get the i-th child cache tree.\n+\t * \n+\t * @param i\n+\t *            index of the child to obtain.\n+\t * @return the child tree.\n+\t */\n+\tpublic DirCacheTree getChild(final int i) {\n+\t\treturn children[i];\n+\t}\n+\n+\tObjectId getObjectId() {\n+\t\treturn id;\n+\t}\n+\n+\t/**\n+\t * Get the tree's name within its parent.\n+\t * <p>\n+\t * This method is not very efficient and is primarily meant for debugging\n+\t * and final output generation. Applications should try to avoid calling it,\n+\t * and if invoked do so only once per interesting entry, where the name is\n+\t * absolutely required for correct function.\n+\t * \n+\t * @return name of the tree. This does not contain any '/' characters.\n+\t */\n+\tpublic String getNameString() {\n+\t\tfinal ByteBuffer bb = ByteBuffer.wrap(encodedName);\n+\t\treturn Constants.CHARSET.decode(bb).toString();\n+\t}\n+\n+\t/**\n+\t * Get the tree's path within the repository.\n+\t * <p>\n+\t * This method is not very efficient and is primarily meant for debugging\n+\t * and final output generation. Applications should try to avoid calling it,\n+\t * and if invoked do so only once per interesting entry, where the name is\n+\t * absolutely required for correct function.\n+\t * \n+\t * @return path of the tree, relative to the repository root. If this is not\n+\t *         the root tree the path ends with '/'. The root tree's path string\n+\t *         is the empty string (\"\").\n+\t */\n+\tpublic String getPathString() {\n+\t\tfinal StringBuilder r = new StringBuilder();\n+\t\tappendName(r);\n+\t\treturn r.toString();\n+\t}\n+\n+\tprivate void appendName(final StringBuilder r) {\n+\t\tif (parent != null) {\n+\t\t\tparent.appendName(r);\n+\t\t\tr.append(getNameString());\n+\t\t\tr.append('/');\n+\t\t} else if (nameLength() > 0) {\n+\t\t\tr.append(getNameString());\n+\t\t\tr.append('/');\n+\t\t}\n+\t}\n+\n+\tfinal int nameLength() {\n+\t\treturn encodedName.length;\n+\t}\n+\n+\tfinal boolean contains(final byte[] a, int aOff, final int aLen) {\n+\t\tfinal byte[] e = encodedName;\n+\t\tfinal int eLen = e.length;\n+\t\tfor (int eOff = 0; eOff < eLen && aOff < aLen; eOff++, aOff++)\n+\t\t\tif (e[eOff] != a[aOff])\n+\t\t\t\treturn false;\n+\t\tif (aOff == aLen)\n+\t\t\treturn false;\n+\t\treturn a[aOff] == '/';\n+\t}\n+\n+\t/**\n+\t * Update (if necessary) this tree's entrySpan.\n+\t * \n+\t * @param cache\n+\t *            the complete cache from DirCache.\n+\t * @param cCnt\n+\t *            number of entries in <code>cache</code> that are valid for\n+\t *            iteration.\n+\t * @param cIdx\n+\t *            first position of <code>cache</code> that is a member of this\n+\t *            tree. The path of <code>cache[cacheIdx].path</code> for the\n+\t *            range <code>[0,pathOff-1)</code> matches the complete path of\n+\t *            this tree, from the root of the repository.\n+\t * @param pathOff\n+\t *            number of bytes of <code>cache[cacheIdx].path</code> that\n+\t *            matches this tree's path. The value at array position\n+\t *            <code>cache[cacheIdx].path[pathOff-1]</code> is always '/' if\n+\t *            <code>pathOff</code> is > 0.\n+\t */\n+\tvoid validate(final DirCacheEntry[] cache, final int cCnt, int cIdx,\n+\t\t\tfinal int pathOff) {\n+\t\tif (entrySpan >= 0) {\n+\t\t\t// If we are valid, our children are also valid.\n+\t\t\t// We have no need to validate them.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tentrySpan = 0;\n+\t\tif (cCnt == 0) {\n+\t\t\t// Special case of an empty index, and we are the root tree.\n+\t\t\t//\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal byte[] firstPath = cache[cIdx].path;\n+\t\tint stIdx = 0;\n+\t\twhile (cIdx < cCnt) {\n+\t\t\tfinal byte[] currPath = cache[cIdx].path;\n+\t\t\tif (pathOff > 0 && !peq(firstPath, currPath, pathOff)) {\n+\t\t\t\t// The current entry is no longer in this tree. Our\n+\t\t\t\t// span is updated and the remainder goes elsewhere.\n+\t\t\t\t//\n+\t\t\t\tbreak;\n+\t\t\t}\n+\n+\t\t\tDirCacheTree st = stIdx < childCnt ? children[stIdx] : null;\n+\t\t\tfinal int cc = namecmp(currPath, pathOff, st);\n+\t\t\tif (cc > 0) {\n+\t\t\t\t// This subtree is now empty.\n+\t\t\t\t//\n+\t\t\t\tremoveChild(stIdx);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tif (cc < 0) {\n+\t\t\t\tfinal int p = slash(currPath, pathOff);\n+\t\t\t\tif (p < 0) {\n+\t\t\t\t\t// The entry has no '/' and thus is directly in this\n+\t\t\t\t\t// tree. Count it as one of our own.\n+\t\t\t\t\t//\n+\t\t\t\t\tcIdx++;\n+\t\t\t\t\tentrySpan++;\n+\t\t\t\t\tcontinue;\n+\t\t\t\t}\n+\n+\t\t\t\t// Build a new subtree for this entry.\n+\t\t\t\t//\n+\t\t\t\tst = new DirCacheTree(this, currPath, pathOff, p - pathOff);\n+\t\t\t\tinsertChild(stIdx, st);\n+\t\t\t}\n+\n+\t\t\t// The entry is contained in this subtree.\n+\t\t\t//\n+\t\t\tst.validate(cache, cCnt, cIdx, pathOff + st.nameLength() + 1);\n+\t\t\tcIdx += st.entrySpan;\n+\t\t\tentrySpan += st.entrySpan;\n+\t\t\tstIdx++;\n+\t\t}\n+\n+\t\tif (stIdx < childCnt) {\n+\t\t\t// None of our remaining children can be in this tree\n+\t\t\t// as the current cache entry is after our own name.\n+\t\t\t//\n+\t\t\tfinal DirCacheTree[] dct = new DirCacheTree[stIdx];\n+\t\t\tSystem.arraycopy(children, 0, dct, 0, stIdx);\n+\t\t\tchildren = dct;\n+\t\t}\n+\t}\n+\n+\tprivate void insertChild(final int stIdx, final DirCacheTree st) {\n+\t\tfinal DirCacheTree[] c = children;\n+\t\tif (childCnt + 1 <= c.length) {\n+\t\t\tif (stIdx < childCnt)\n+\t\t\t\tSystem.arraycopy(c, stIdx, c, stIdx + 1, childCnt - stIdx);\n+\t\t\tc[stIdx] = st;\n+\t\t\tchildCnt++;\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal int n = c.length;\n+\t\tfinal DirCacheTree[] a = new DirCacheTree[n + 1];\n+\t\tif (stIdx > 0)\n+\t\t\tSystem.arraycopy(c, 0, a, 0, stIdx);\n+\t\ta[stIdx] = st;\n+\t\tif (stIdx < n)\n+\t\t\tSystem.arraycopy(c, stIdx, a, stIdx + 1, n - stIdx);\n+\t\tchildren = a;\n+\t\tchildCnt++;\n+\t}\n+\n+\tprivate void removeChild(final int stIdx) {\n+\t\tfinal int n = --childCnt;\n+\t\tif (stIdx < n)\n+\t\t\tSystem.arraycopy(children, stIdx + 1, children, stIdx, n - stIdx);\n+\t\tchildren[n] = null;\n+\t}\n+\n+\tstatic boolean peq(final byte[] a, final byte[] b, int aLen) {\n+\t\tif (b.length < aLen)\n+\t\t\treturn false;\n+\t\tfor (aLen--; aLen >= 0; aLen--)\n+\t\t\tif (a[aLen] != b[aLen])\n+\t\t\t\treturn false;\n+\t\treturn true;\n+\t}\n+\n+\tprivate static int namecmp(final byte[] a, int aPos, final DirCacheTree ct) {\n+\t\tif (ct == null)\n+\t\t\treturn -1;\n+\t\tfinal byte[] b = ct.encodedName;\n+\t\tfinal int aLen = a.length;\n+\t\tfinal int bLen = b.length;\n+\t\tint bPos = 0;\n+\t\tfor (; aPos < aLen && bPos < bLen; aPos++, bPos++) {\n+\t\t\tfinal int cmp = (a[aPos] & 0xff) - (b[bPos] & 0xff);\n+\t\t\tif (cmp != 0)\n+\t\t\t\treturn cmp;\n+\t\t}\n+\t\tif (bPos == bLen)\n+\t\t\treturn a[aPos] == '/' ? 0 : -1;\n+\t\treturn aLen - bLen;\n+\t}\n+\n+\tprivate static int slash(final byte[] a, int aPos) {\n+\t\tfinal int aLen = a.length;\n+\t\tfor (; aPos < aLen; aPos++)\n+\t\t\tif (a[aPos] == '/')\n+\t\t\t\treturn aPos;\n+\t\treturn -1;\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86884","messageId":"1218503293-14057-12-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-11-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 11/26] Support using a DirCache within a TreeWalk","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:58Z","receivedAt":"2008-08-12T01:07:58Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Being able to include the .git/index file as part of a parallel TreeWalk\nwhich also covers one or more canonical tree objects from the database\nand the working directory makes it much easier to handle differences or\nmerges between the various states.\n\nThis iterator implementation adapts a loaded DirCache instance into a\ntree structure that the TreeWalk can iterate.  Since TreeWalk wants to\nuse a hierarchical structure and the DirCache is flat we force the TREE\nextension to load (or generate) and work off that.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/dircache/DirCacheIterator.java    |  205 ++++++++++++++++++++\n 1 files changed, 205 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheIterator.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheIterator.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheIterator.java\nnew file mode 100644\nindex 0000000..f7ef15d\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheIterator.java\n@@ -0,0 +1,205 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.io.IOException;\n+import java.util.Arrays;\n+\n+import org.spearce.jgit.errors.CorruptObjectException;\n+import org.spearce.jgit.errors.IncorrectObjectTypeException;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.treewalk.AbstractTreeIterator;\n+\n+/**\n+ * Iterate a {@link DirCache} as part of a <code>TreeWalk</code>.\n+ * <p>\n+ * This is an iterator to adapt a loaded <code>DirCache</code> instance (such as\n+ * read from an existing <code>.git/index</code> file) to the tree structure\n+ * used by a <code>TreeWalk</code>, making it possible for applications to walk\n+ * over any combination of tree objects already in the object database, index\n+ * files, or working directories.\n+ * \n+ * @see org.spearce.jgit.treewalk.TreeWalk\n+ */\n+public class DirCacheIterator extends AbstractTreeIterator {\n+\t/** The cache this iterator was created to walk. */\n+\tprotected final DirCache cache;\n+\n+\t/** The tree this iterator is walking. */\n+\tprivate final DirCacheTree tree;\n+\n+\t/** Special buffer to hold the ObjectId of {@link #currentSubtree}. */\n+\tprivate final byte[] subtreeId;\n+\n+\t/** Index of entry within {@link #cache}. */\n+\tprotected int cachePos;\n+\n+\t/** Position of entry within {@link #tree}'s entry span. */\n+\tprivate int treePos;\n+\n+\t/** Next subtree to consider within {@link #tree}. */\n+\tprivate int subtreeIdx;\n+\n+\t/** The current file entry from {@link #cache}, matching {@link #cachePos}. */\n+\tprotected DirCacheEntry currentEntry;\n+\n+\t/** The subtree containing {@link #currentEntry} if this is first entry. */\n+\tprotected DirCacheTree currentSubtree;\n+\n+\t/**\n+\t * Create a new iterator for an already loaded DirCache instance.\n+\t * <p>\n+\t * The iterator implementation may copy part of the cache's data during\n+\t * construction, so the cache must be read in prior to creating the\n+\t * iterator.\n+\t * \n+\t * @param dc\n+\t *            the cache to walk. It must be already loaded into memory.\n+\t */\n+\tpublic DirCacheIterator(final DirCache dc) {\n+\t\tcache = dc;\n+\t\ttree = dc.getCacheTree(true);\n+\t\tsubtreeId = new byte[Constants.OBJECT_ID_LENGTH];\n+\t\tcachePos = -1;\n+\t\ttreePos = -1;\n+\t}\n+\n+\tprotected DirCacheIterator(final DirCacheIterator p, final DirCacheTree dct) {\n+\t\tsuper(p, p.path, p.pathLen + 1);\n+\t\tcache = p.cache;\n+\t\ttree = dct;\n+\t\tsubtreeId = p.subtreeId;\n+\t\tcachePos = p.cachePos - 1; // back up so first next() call enters it\n+\t\ttreePos = -1;\n+\t}\n+\n+\t@Override\n+\tpublic AbstractTreeIterator createSubtreeIterator(final Repository repo)\n+\t\t\tthrows IncorrectObjectTypeException, IOException {\n+\t\tif (currentSubtree == null)\n+\t\t\tthrow new IncorrectObjectTypeException(getEntryObjectId(),\n+\t\t\t\t\tConstants.TYPE_TREE);\n+\t\treturn new DirCacheIterator(this, currentSubtree);\n+\t}\n+\n+\t@Override\n+\tpublic byte[] idBuffer() {\n+\t\tif (currentSubtree != null)\n+\t\t\treturn subtreeId;\n+\t\tif (currentEntry != null)\n+\t\t\treturn currentEntry.idBuffer();\n+\t\treturn zeroid;\n+\t}\n+\n+\t@Override\n+\tpublic int idOffset() {\n+\t\tif (currentSubtree != null)\n+\t\t\treturn 0;\n+\t\tif (currentEntry != null)\n+\t\t\treturn currentEntry.idOffset();\n+\t\treturn 0;\n+\t}\n+\n+\t@Override\n+\tpublic boolean eof() {\n+\t\treturn treePos >= tree.getEntrySpan();\n+\t}\n+\n+\t@Override\n+\tpublic void next() throws CorruptObjectException {\n+\t\tif (currentSubtree != null) {\n+\t\t\t// If our last position was a subtree we need to skip over\n+\t\t\t// its entire span to get to the item after the subtree.\n+\t\t\t//\n+\t\t\tfinal int n = currentSubtree.getEntrySpan();\n+\t\t\tcachePos += n;\n+\t\t\ttreePos += n;\n+\t\t\tcurrentSubtree = null;\n+\t\t} else {\n+\t\t\t// Our last position was a file/symlink/gitlink, so we\n+\t\t\t// only skip the one entry.\n+\t\t\t//\n+\t\t\tcachePos++;\n+\t\t\ttreePos++;\n+\t\t}\n+\n+\t\tif (treePos >= tree.getEntrySpan())\n+\t\t\treturn; // this iterator is now at EOF.\n+\n+\t\tcurrentEntry = cache.getEntry(cachePos);\n+\t\tfinal byte[] cep = currentEntry.path;\n+\t\tif (subtreeIdx < tree.getChildCount()) {\n+\t\t\tfinal DirCacheTree s = tree.getChild(subtreeIdx);\n+\t\t\tif (s.contains(cep, pathOffset, cep.length)) {\n+\t\t\t\t// The current position is the first file of this subtree.\n+\t\t\t\t// Use the subtree instead as the current position.\n+\t\t\t\t//\n+\t\t\t\tcurrentSubtree = s;\n+\t\t\t\tsubtreeIdx++;\n+\n+\t\t\t\tif (s.isValid())\n+\t\t\t\t\ts.getObjectId().copyRawTo(subtreeId, 0);\n+\t\t\t\telse\n+\t\t\t\t\tArrays.fill(subtreeId, (byte) 0);\n+\t\t\t\tmode = FileMode.TREE.getBits();\n+\t\t\t\tpath = cep;\n+\t\t\t\tpathLen = pathOffset + s.nameLength();\n+\t\t\t\treturn;\n+\t\t\t}\n+\t\t}\n+\n+\t\t// The current position is a file/symlink/gitlink so we\n+\t\t// do not have a subtree located here.\n+\t\t//\n+\t\tmode = currentEntry.getRawMode();\n+\t\tpath = cep;\n+\t\tpathLen = cep.length;\n+\t}\n+\n+\t/**\n+\t * Get the DirCacheEntry for the current file.\n+\t * \n+\t * @return the current cache entry, if this iterator is positioned on a\n+\t *         non-tree.\n+\t */\n+\tpublic DirCacheEntry getDirCacheEntry() {\n+\t\treturn currentSubtree == null ? currentEntry : null;\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86875","messageId":"1218503293-14057-13-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-12-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 12/26] Support recreating a .git/index through DirCache","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:07:59Z","receivedAt":"2008-08-12T01:07:59Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The DirCacheBuilder is meant for building up a new DirCache content\nby iterating through the paths in proper Git sort order.  This is\nmostly useful during a TreeWalk, where the paths are being fed to\nthe application in the correct sort ordering.  Appending them into\nthe new entry table in order avoids sorting costs later on when we\ntry to write the file back to disk.\n\nThe API is designed for speed, not ease-of-use.  Performing updates\nin order and by copying large ranges of the existing index into the\nnew index is incredibly fast, but requires the application to work\nharder to construct the right sequence of calls.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/dircache/BaseDirCacheEditor.java  |  194 ++++++++++++++++++++\n .../src/org/spearce/jgit/dircache/DirCache.java    |   23 +++\n .../org/spearce/jgit/dircache/DirCacheBuilder.java |  173 +++++++++++++++++\n 3 files changed, 390 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/BaseDirCacheEditor.java\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/BaseDirCacheEditor.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/BaseDirCacheEditor.java\nnew file mode 100644\nindex 0000000..268c01e\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/BaseDirCacheEditor.java\n@@ -0,0 +1,194 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.io.IOException;\n+\n+/**\n+ * Generic update/editing support for {@link DirCache}.\n+ * <p>\n+ * The different update strategies extend this class to provide their own unique\n+ * services to applications.\n+ */\n+abstract class BaseDirCacheEditor {\n+\t/** The cache instance this editor updates during {@link #finish()}. */\n+\tprotected DirCache cache;\n+\n+\t/**\n+\t * Entry table this builder will eventually replace into {@link #cache}.\n+\t * <p>\n+\t * Use {@link #fastAdd(DirCacheEntry)} or {@link #fastKeep(int, int)} to\n+\t * make additions to this table. The table is automatically expanded if it\n+\t * is too small for a new addition.\n+\t * <p>\n+\t * Typically the entries in here are sorted by their path names, just like\n+\t * they are in the DirCache instance.\n+\t */\n+\tprotected DirCacheEntry[] entries;\n+\n+\t/** Total number of valid entries in {@link #entries}. */\n+\tprotected int entryCnt;\n+\n+\t/**\n+\t * Construct a new editor.\n+\t * \n+\t * @param dc\n+\t *            the cache this editor will eventually update.\n+\t * @param ecnt\n+\t *            estimated number of entries the editor will have upon\n+\t *            completion. This sizes the initial entry table.\n+\t */\n+\tprotected BaseDirCacheEditor(final DirCache dc, final int ecnt) {\n+\t\tcache = dc;\n+\t\tentries = new DirCacheEntry[ecnt];\n+\t}\n+\n+\t/**\n+\t * @return the cache we will update on {@link #finish()}.\n+\t */\n+\tpublic DirCache getDirCache() {\n+\t\treturn cache;\n+\t}\n+\n+\t/**\n+\t * Append one entry into the resulting entry list.\n+\t * <p>\n+\t * The entry is placed at the end of the entry list. The caller is\n+\t * responsible for making sure the final table is correctly sorted.\n+\t * <p>\n+\t * The {@link #entries} table is automatically expanded if there is\n+\t * insufficient space for the new addition.\n+\t * \n+\t * @param newEntry\n+\t *            the new entry to add.\n+\t */\n+\tprotected void fastAdd(final DirCacheEntry newEntry) {\n+\t\tif (entries.length == entryCnt) {\n+\t\t\tfinal DirCacheEntry[] n = new DirCacheEntry[(entryCnt + 16) * 3 / 2];\n+\t\t\tSystem.arraycopy(entries, 0, n, 0, entryCnt);\n+\t\t\tentries = n;\n+\t\t}\n+\t\tentries[entryCnt++] = newEntry;\n+\t}\n+\n+\t/**\n+\t * Add a range of existing entries from the destination cache.\n+\t * <p>\n+\t * The entries are placed at the end of the entry list, preserving their\n+\t * current order. The caller is responsible for making sure the final table\n+\t * is correctly sorted.\n+\t * <p>\n+\t * This method copies from the destination cache, which has not yet been\n+\t * updated with this editor's new table. So all offsets into the destination\n+\t * cache are not affected by any updates that may be currently taking place\n+\t * in this editor.\n+\t * <p>\n+\t * The {@link #entries} table is automatically expanded if there is\n+\t * insufficient space for the new additions.\n+\t * \n+\t * @param pos\n+\t *            first entry to copy from the destination cache.\n+\t * @param cnt\n+\t *            number of entries to copy.\n+\t */\n+\tprotected void fastKeep(final int pos, int cnt) {\n+\t\tif (entryCnt + cnt > entries.length) {\n+\t\t\tfinal int m1 = (entryCnt + 16) * 3 / 2;\n+\t\t\tfinal int m2 = entryCnt + cnt;\n+\t\t\tfinal DirCacheEntry[] n = new DirCacheEntry[Math.max(m1, m2)];\n+\t\t\tSystem.arraycopy(entries, 0, n, 0, entryCnt);\n+\t\t\tentries = n;\n+\t\t}\n+\n+\t\tcache.toArray(pos, entries, entryCnt, cnt);\n+\t\tentryCnt += cnt;\n+\t}\n+\n+\t/**\n+\t * Finish this builder and update the destination {@link DirCache}.\n+\t * <p>\n+\t * When this method completes this builder instance is no longer usable by\n+\t * the calling application. A new builder must be created to make additional\n+\t * changes to the index entries.\n+\t * <p>\n+\t * After completion the DirCache returned by {@link #getDirCache()} will\n+\t * contain all modifications.\n+\t * <p>\n+\t * <i>Note to implementors:</i> Make sure {@link #entries} is fully sorted\n+\t * then invoke {@link #replace()} to update the DirCache with the new table.\n+\t */\n+\tpublic abstract void finish();\n+\n+\t/**\n+\t * Update the DirCache with the contents of {@link #entries}.\n+\t * <p>\n+\t * This method should be invoked only during an implementation of\n+\t * {@link #finish()}, and only after {@link #entries} is sorted.\n+\t */\n+\tprotected void replace() {\n+\t\tif (entryCnt < entries.length / 2) {\n+\t\t\tfinal DirCacheEntry[] n = new DirCacheEntry[entryCnt];\n+\t\t\tSystem.arraycopy(entries, 0, n, 0, entryCnt);\n+\t\t\tentries = n;\n+\t\t}\n+\t\tcache.replace(entries, entryCnt);\n+\t}\n+\n+\t/**\n+\t * Finish, write, commit this change, and release the index lock.\n+\t * <p>\n+\t * If this method fails (returns false) the lock is still released.\n+\t * <p>\n+\t * This is a utility method for applications as the finish-write-commit\n+\t * pattern is very common after using a builder to update entries.\n+\t * \n+\t * @return true if the commit was successful and the file contains the new\n+\t *         data; false if the commit failed and the file remains with the\n+\t *         old data.\n+\t * @throws IllegalStateException\n+\t *             the lock is not held.\n+\t * @throws IOException\n+\t *             the output file could not be created. The caller no longer\n+\t *             holds the lock.\n+\t */\n+\tpublic boolean commit() throws IOException {\n+\t\tfinish();\n+\t\tcache.write();\n+\t\treturn cache.commit();\n+\t}\n+}\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\nindex b5fdd46..810b479 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n@@ -236,6 +236,24 @@ public class DirCache {\n \t}\n \n \t/**\n+\t * Create a new builder to update this cache.\n+\t * <p>\n+\t * Callers should add all entries to the builder, then use\n+\t * {@link DirCacheBuilder#finish()} to update this instance.\n+\t * \n+\t * @return a new builder instance for this cache.\n+\t */\n+\tpublic DirCacheBuilder builder() {\n+\t\treturn new DirCacheBuilder(this, entryCnt + 16);\n+\t}\n+\n+\tvoid replace(final DirCacheEntry[] e, final int cnt) {\n+\t\tsortedEntries = e;\n+\t\tentryCnt = cnt;\n+\t\ttree = null;\n+\t}\n+\n+\t/**\n \t * Read the index from disk, if it has changed on disk.\n \t * <p>\n \t * This method tries to avoid loading the index if it has not changed since\n@@ -591,6 +609,11 @@ public class DirCache {\n \t\treturn i < 0 ? null : sortedEntries[i];\n \t}\n \n+\tvoid toArray(final int i, final DirCacheEntry[] dst, final int off,\n+\t\t\tfinal int cnt) {\n+\t\tSystem.arraycopy(sortedEntries, i, dst, off, cnt);\n+\t}\n+\n \t/**\n \t * Obtain (or build) the current cache tree structure.\n \t * <p>\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java\nnew file mode 100644\nindex 0000000..e303b43\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java\n@@ -0,0 +1,173 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.util.Arrays;\n+\n+/**\n+ * Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s.\n+ * <p>\n+ * A builder always starts from a clean slate and appends in every single\n+ * <code>DirCacheEntry</code> which the final updated index must have to reflect\n+ * its new content.\n+ * <p>\n+ * For maximum performance applications should add entries in path name order.\n+ * Adding entries out of order is permitted, however a final sorting pass will\n+ * be implicitly performed during {@link #finish()} to correct any out-of-order\n+ * entries. Duplicate detection is also delayed until the sorting is complete.\n+ */\n+public class DirCacheBuilder extends BaseDirCacheEditor {\n+\tprivate boolean sorted;\n+\n+\t/**\n+\t * Construct a new builder.\n+\t * \n+\t * @param dc\n+\t *            the cache this builder will eventually update.\n+\t * @param ecnt\n+\t *            estimated number of entries the builder will have upon\n+\t *            completion. This sizes the initial entry table.\n+\t */\n+\tprotected DirCacheBuilder(final DirCache dc, final int ecnt) {\n+\t\tsuper(dc, ecnt);\n+\t}\n+\n+\t/**\n+\t * Append one entry into the resulting entry list.\n+\t * <p>\n+\t * The entry is placed at the end of the entry list. If the entry causes the\n+\t * list to now be incorrectly sorted a final sorting phase will be\n+\t * automatically enabled within {@link #finish()}.\n+\t * <p>\n+\t * The internal entry table is automatically expanded if there is\n+\t * insufficient space for the new addition.\n+\t * \n+\t * @param newEntry\n+\t *            the new entry to add.\n+\t */\n+\tpublic void add(final DirCacheEntry newEntry) {\n+\t\tbeforeAdd(newEntry);\n+\t\tfastAdd(newEntry);\n+\t}\n+\n+\t/**\n+\t * Add a range of existing entries from the destination cache.\n+\t * <p>\n+\t * The entries are placed at the end of the entry list. If any of the\n+\t * entries causes the list to now be incorrectly sorted a final sorting\n+\t * phase will be automatically enabled within {@link #finish()}.\n+\t * <p>\n+\t * This method copies from the destination cache, which has not yet been\n+\t * updated with this editor's new table. So all offsets into the destination\n+\t * cache are not affected by any updates that may be currently taking place\n+\t * in this editor.\n+\t * <p>\n+\t * The internal entry table is automatically expanded if there is\n+\t * insufficient space for the new additions.\n+\t * \n+\t * @param pos\n+\t *            first entry to copy from the destination cache.\n+\t * @param cnt\n+\t *            number of entries to copy.\n+\t */\n+\tpublic void keep(final int pos, int cnt) {\n+\t\tbeforeAdd(cache.getEntry(pos));\n+\t\tfastKeep(pos, cnt);\n+\t}\n+\n+\tpublic void finish() {\n+\t\tif (!sorted)\n+\t\t\tresort();\n+\t\treplace();\n+\t}\n+\n+\tprivate void beforeAdd(final DirCacheEntry newEntry) {\n+\t\tif (sorted && entryCnt > 0) {\n+\t\t\tfinal DirCacheEntry lastEntry = entries[entryCnt - 1];\n+\t\t\tfinal int cr = DirCache.cmp(lastEntry, newEntry);\n+\t\t\tif (cr > 0) {\n+\t\t\t\t// The new entry sorts before the old entry; we are\n+\t\t\t\t// no longer sorted correctly. We'll need to redo\n+\t\t\t\t// the sorting before we can close out the build.\n+\t\t\t\t//\n+\t\t\t\tsorted = false;\n+\t\t\t} else if (cr == 0) {\n+\t\t\t\t// Same file path; we can only insert this if the\n+\t\t\t\t// stages won't be violated.\n+\t\t\t\t//\n+\t\t\t\tfinal int peStage = lastEntry.getStage();\n+\t\t\t\tfinal int dceStage = newEntry.getStage();\n+\t\t\t\tif (peStage == dceStage)\n+\t\t\t\t\tthrow bad(newEntry, \"Duplicate stages not allowed\");\n+\t\t\t\tif (peStage == 0 || dceStage == 0)\n+\t\t\t\t\tthrow bad(newEntry, \"Mixed stages not allowed\");\n+\t\t\t\tif (peStage > dceStage)\n+\t\t\t\t\tsorted = false;\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\tprivate void resort() {\n+\t\tArrays.sort(entries, 0, entryCnt, DirCache.ENT_CMP);\n+\n+\t\tfor (int entryIdx = 1; entryIdx < entryCnt; entryIdx++) {\n+\t\t\tfinal DirCacheEntry pe = entries[entryIdx - 1];\n+\t\t\tfinal DirCacheEntry ce = entries[entryIdx];\n+\t\t\tfinal int cr = DirCache.cmp(pe, ce);\n+\t\t\tif (cr == 0) {\n+\t\t\t\t// Same file path; we can only allow this if the stages\n+\t\t\t\t// are 1-3 and no 0 exists.\n+\t\t\t\t//\n+\t\t\t\tfinal int peStage = pe.getStage();\n+\t\t\t\tfinal int ceStage = ce.getStage();\n+\t\t\t\tif (peStage == ceStage)\n+\t\t\t\t\tthrow bad(ce, \"Duplicate stages not allowed\");\n+\t\t\t\tif (peStage == 0 || ceStage == 0)\n+\t\t\t\t\tthrow bad(ce, \"Mixed stages not allowed\");\n+\t\t\t}\n+\t\t}\n+\n+\t\tsorted = true;\n+\t}\n+\n+\tprivate static IllegalStateException bad(final DirCacheEntry a,\n+\t\t\tfinal String msg) {\n+\t\treturn new IllegalStateException(msg + \": \" + a.getStage() + \" \"\n+\t\t\t\t+ a.getPathString());\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86880","messageId":"1218503293-14057-14-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-13-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 13/26] Support iterating and building a DirCache at the same time","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:00Z","receivedAt":"2008-08-12T01:08:00Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"This iterator combines the DirCacheBuilder add-in-order update strategy\nwith a suitable TreeWalk iterator, allowing entries which the TreeWalk\ndoes not return to the application to be automatically carried into the\nnew index, and the application to handle the entires which the TreeWalk\ndoes return to it.\n\nThis can be used to implement a simple \"rm\" function which removes one\nor more paths from the index, while leaving the rest of the index alone.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../jgit/dircache/DirCacheBuildIterator.java       |  124 ++++++++++++++++++++\n 1 files changed, 124 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuildIterator.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuildIterator.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuildIterator.java\nnew file mode 100644\nindex 0000000..ca934ec\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuildIterator.java\n@@ -0,0 +1,124 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.io.IOException;\n+\n+import org.spearce.jgit.errors.CorruptObjectException;\n+import org.spearce.jgit.errors.IncorrectObjectTypeException;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.treewalk.AbstractTreeIterator;\n+\n+/**\n+ * Iterate and update a {@link DirCache} as part of a <code>TreeWalk</code>.\n+ * <p>\n+ * Like {@link DirCacheIterator} this iterator allows a DirCache to be used in\n+ * parallel with other sorts of iterators in a TreeWalk. However any entry which\n+ * appears in the source DirCache and which is skipped by the TreeFilter is\n+ * automatically copied into {@link DirCacheBuilder}, thus retaining it in the\n+ * newly updated index.\n+ * <p>\n+ * This iterator is suitable for update processes, or even a simple delete\n+ * algorithm. For example deleting a path:\n+ * \n+ * <pre>\n+ * final DirCache dirc = DirCache.lock(db);\n+ * final DirCacheBuilder edit = dirc.builder();\n+ * \n+ * final TreeWalk walk = new TreeWalk(db);\n+ * walk.reset();\n+ * walk.setRecursive(true);\n+ * walk.setFilter(PathFilter.create(&quot;name/to/remove&quot;));\n+ * walk.addTree(new DirCacheBuildIterator(edit));\n+ * \n+ * while (walk.next())\n+ * \t; // do nothing on a match as we want to remove matches\n+ * edit.commit();\n+ * </pre>\n+ */\n+public class DirCacheBuildIterator extends DirCacheIterator {\n+\tprivate final DirCacheBuilder builder;\n+\n+\t/**\n+\t * Create a new iterator for an already loaded DirCache instance.\n+\t * <p>\n+\t * The iterator implementation may copy part of the cache's data during\n+\t * construction, so the cache must be read in prior to creating the\n+\t * iterator.\n+\t * \n+\t * @param dcb\n+\t *            the cache builder for the cache to walk. The cache must be\n+\t *            already loaded into memory.\n+\t */\n+\tpublic DirCacheBuildIterator(final DirCacheBuilder dcb) {\n+\t\tsuper(dcb.getDirCache());\n+\t\tbuilder = dcb;\n+\t}\n+\n+\tprotected DirCacheBuildIterator(final DirCacheBuildIterator p,\n+\t\t\tfinal DirCacheTree dct) {\n+\t\tsuper(p, dct);\n+\t\tbuilder = p.builder;\n+\t}\n+\n+\t@Override\n+\tpublic AbstractTreeIterator createSubtreeIterator(final Repository repo)\n+\t\t\tthrows IncorrectObjectTypeException, IOException {\n+\t\tif (currentSubtree == null)\n+\t\t\tthrow new IncorrectObjectTypeException(getEntryObjectId(),\n+\t\t\t\t\tConstants.TYPE_TREE);\n+\t\treturn new DirCacheBuildIterator(this, currentSubtree);\n+\t}\n+\n+\t@Override\n+\tpublic void skip() throws CorruptObjectException {\n+\t\tif (currentSubtree != null)\n+\t\t\tbuilder.keep(cachePos, currentSubtree.getEntrySpan());\n+\t\telse\n+\t\t\tbuilder.add(currentEntry);\n+\t\tnext();\n+\t}\n+\n+\t@Override\n+\tpublic void stopWalk() {\n+\t\tfinal int cnt = cache.getEntryCount();\n+\t\tif (cachePos < cnt)\n+\t\t\tbuilder.keep(cachePos, cnt - cachePos);\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86887","messageId":"1218503293-14057-15-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-14-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 14/26] Support creating a new DirCacheEntry for an arbitrary path","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:01Z","receivedAt":"2008-08-12T01:08:01Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Applications which use a DirCacheBuilder may need to build their own\nDirCacheEntry in order to insert new records into the index file.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../org/spearce/jgit/dircache/DirCacheEntry.java   |   24 ++++++++++++++++++++\n 1 files changed, 24 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\nindex 8ca8f22..eba2678 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n@@ -114,6 +114,30 @@ public class DirCacheEntry {\n \t\t\tin.skip(expLen - actLen);\n \t}\n \n+\t/**\n+\t * Create an empty entry.\n+\t * \n+\t * @param newPath\n+\t *            name of the cache entry.\n+\t */\n+\tpublic DirCacheEntry(final String newPath) {\n+\t\tthis(Constants.encode(newPath));\n+\t}\n+\n+\t/**\n+\t * Create an empty entry.\n+\t * \n+\t * @param newPath\n+\t *            name of the cache entry, in the standard encoding.\n+\t */\n+\tpublic DirCacheEntry(final byte[] newPath) {\n+\t\tinfo = new byte[INFO_LEN];\n+\t\tinfoOffset = 0;\n+\n+\t\tpath = newPath;\n+\t\tNB.encodeInt16(info, infoOffset + P_FLAGS, path.length);\n+\t}\n+\n \tvoid write(final OutputStream os) throws IOException {\n \t\tfinal int pathLen = path.length;\n \t\tos.write(info, infoOffset, INFO_LEN);\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86885","messageId":"1218503293-14057-16-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-15-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 15/26] Support a simplified model of editing index entries","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:02Z","receivedAt":"2008-08-12T01:08:02Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Some applications may find using DirCacheBuilder difficult and/or just\nimpossible, as it requires inserting entries in order to have any sort\nof good performance.\n\nDirCacheEditor tries to bridge that gap by allowing applications to\ninsert edit commands out-of-order, then merge-sorts those edits onto\nthe index entry list from the DirCache.  Since the edit list is likely\nto be shorter than the index entry list sorting the edit list and doing\na merge-sort onto the index entry list allows for large range copies in\nthe regions that are not modified.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/dircache/DirCache.java    |   22 ++\n .../org/spearce/jgit/dircache/DirCacheBuilder.java |    2 +\n .../org/spearce/jgit/dircache/DirCacheEditor.java  |  265 ++++++++++++++++++++\n 3 files changed, 289 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEditor.java\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\nindex 810b479..e48fdec 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n@@ -247,6 +247,18 @@ public class DirCache {\n \t\treturn new DirCacheBuilder(this, entryCnt + 16);\n \t}\n \n+\t/**\n+\t * Create a new editor to recreate this cache.\n+\t * <p>\n+\t * Callers should add commands to the editor, then use\n+\t * {@link DirCacheEditor#finish()} to update this instance.\n+\t * \n+\t * @return a new builder instance for this cache.\n+\t */\n+\tpublic DirCacheEditor editor() {\n+\t\treturn new DirCacheEditor(this, entryCnt + 16);\n+\t}\n+\n \tvoid replace(final DirCacheEntry[] e, final int cnt) {\n \t\tsortedEntries = e;\n \t\tentryCnt = cnt;\n@@ -569,6 +581,16 @@ public class DirCache {\n \t\treturn nextIdx;\n \t}\n \n+\tint nextEntry(final byte[] p, final int pLen, int nextIdx) {\n+\t\twhile (nextIdx < entryCnt) {\n+\t\t\tfinal DirCacheEntry next = sortedEntries[nextIdx];\n+\t\t\tif (!DirCacheTree.peq(p, next.path, pLen))\n+\t\t\t\tbreak;\n+\t\t\tnextIdx++;\n+\t\t}\n+\t\treturn nextIdx;\n+\t}\n+\n \t/**\n \t * Total number of file entries stored in the index.\n \t * <p>\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java\nindex e303b43..b1d0a1d 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheBuilder.java\n@@ -50,6 +50,8 @@ import java.util.Arrays;\n  * Adding entries out of order is permitted, however a final sorting pass will\n  * be implicitly performed during {@link #finish()} to correct any out-of-order\n  * entries. Duplicate detection is also delayed until the sorting is complete.\n+ * \n+ * @see DirCacheEditor\n  */\n public class DirCacheBuilder extends BaseDirCacheEditor {\n \tprivate boolean sorted;\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEditor.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEditor.java\nnew file mode 100644\nindex 0000000..e371d9e\n--- /dev/null\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEditor.java\n@@ -0,0 +1,265 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.dircache;\n+\n+import java.io.IOException;\n+import java.util.ArrayList;\n+import java.util.Collections;\n+import java.util.Comparator;\n+import java.util.List;\n+\n+import org.spearce.jgit.lib.Constants;\n+\n+/**\n+ * Updates a {@link DirCache} by supplying discrete edit commands.\n+ * <p>\n+ * An editor updates a DirCache by taking a list of {@link PathEdit} commands\n+ * and executing them against the entries of the destination cache to produce a\n+ * new cache. This edit style allows applications to insert a few commands and\n+ * then have the editor compute the proper entry indexes necessary to perform an\n+ * efficient in-order update of the index records. This can be easier to use\n+ * than {@link DirCacheBuilder}.\n+ * <p>\n+ * \n+ * @see DirCacheBuilder\n+ */\n+public class DirCacheEditor extends BaseDirCacheEditor {\n+\tprivate static final Comparator<PathEdit> EDIT_CMP = new Comparator<PathEdit>() {\n+\t\tpublic int compare(final PathEdit o1, final PathEdit o2) {\n+\t\t\tfinal byte[] a = o1.path;\n+\t\t\tfinal byte[] b = o2.path;\n+\t\t\treturn DirCache.cmp(a, a.length, b, b.length);\n+\t\t}\n+\t};\n+\n+\tprivate final List<PathEdit> edits;\n+\n+\t/**\n+\t * Construct a new editor.\n+\t * \n+\t * @param dc\n+\t *            the cache this editor will eventually update.\n+\t * @param ecnt\n+\t *            estimated number of entries the editor will have upon\n+\t *            completion. This sizes the initial entry table.\n+\t */\n+\tprotected DirCacheEditor(final DirCache dc, final int ecnt) {\n+\t\tsuper(dc, ecnt);\n+\t\tedits = new ArrayList<PathEdit>();\n+\t}\n+\n+\t/**\n+\t * Append one edit command to the list of commands to be applied.\n+\t * <p>\n+\t * Edit commands may be added in any order chosen by the application. They\n+\t * are automatically rearranged by the builder to provide the most efficient\n+\t * update possible.\n+\t * \n+\t * @param edit\n+\t *            another edit command.\n+\t */\n+\tpublic void add(final PathEdit edit) {\n+\t\tedits.add(edit);\n+\t}\n+\n+\t@Override\n+\tpublic boolean commit() throws IOException {\n+\t\tif (edits.isEmpty()) {\n+\t\t\t// No changes? Don't rewrite the index.\n+\t\t\t//\n+\t\t\tcache.unlock();\n+\t\t\treturn true;\n+\t\t}\n+\t\treturn super.commit();\n+\t}\n+\n+\tpublic void finish() {\n+\t\tif (!edits.isEmpty()) {\n+\t\t\tapplyEdits();\n+\t\t\treplace();\n+\t\t}\n+\t}\n+\n+\tprivate void applyEdits() {\n+\t\tCollections.sort(edits, EDIT_CMP);\n+\n+\t\tfinal int maxIdx = cache.getEntryCount();\n+\t\tint lastIdx = 0;\n+\t\tfor (final PathEdit e : edits) {\n+\t\t\tint eIdx = cache.findEntry(e.path, e.path.length);\n+\t\t\tfinal boolean missing = eIdx < 0;\n+\t\t\tif (eIdx < 0)\n+\t\t\t\teIdx = -(eIdx + 1);\n+\t\t\tfinal int cnt = Math.min(eIdx, maxIdx) - lastIdx;\n+\t\t\tif (cnt > 0)\n+\t\t\t\tfastKeep(lastIdx, cnt);\n+\t\t\tlastIdx = missing ? eIdx : cache.nextEntry(eIdx);\n+\n+\t\t\tif (e instanceof DeletePath)\n+\t\t\t\tcontinue;\n+\t\t\tif (e instanceof DeleteTree) {\n+\t\t\t\tlastIdx = cache.nextEntry(e.path, e.path.length, eIdx);\n+\t\t\t\tcontinue;\n+\t\t\t}\n+\n+\t\t\tfinal DirCacheEntry ent;\n+\t\t\tif (missing)\n+\t\t\t\tent = new DirCacheEntry(e.path);\n+\t\t\telse\n+\t\t\t\tent = cache.getEntry(eIdx);\n+\t\t\te.apply(ent);\n+\t\t\tfastAdd(ent);\n+\t\t}\n+\n+\t\tfinal int cnt = maxIdx - lastIdx;\n+\t\tif (cnt > 0)\n+\t\t\tfastKeep(lastIdx, cnt);\n+\t}\n+\n+\t/**\n+\t * Any index record update.\n+\t * <p>\n+\t * Applications should subclass and provide their own implementation for the\n+\t * {@link #apply(DirCacheEntry)} method. The editor will invoke apply once\n+\t * for each record in the index which matches the path name. If there are\n+\t * multiple records (for example in stages 1, 2 and 3), the edit instance\n+\t * will be called multiple times, once for each stage.\n+\t */\n+\tpublic abstract static class PathEdit {\n+\t\tfinal byte[] path;\n+\n+\t\t/**\n+\t\t * Create a new update command by path name.\n+\t\t * \n+\t\t * @param entryPath\n+\t\t *            path of the file within the repository.\n+\t\t */\n+\t\tpublic PathEdit(final String entryPath) {\n+\t\t\tpath = Constants.encode(entryPath);\n+\t\t}\n+\n+\t\t/**\n+\t\t * Create a new update command for an existing entry instance.\n+\t\t * \n+\t\t * @param ent\n+\t\t *            entry instance to match path of. Only the path of this\n+\t\t *            entry is actually considered during command evaluation.\n+\t\t */\n+\t\tpublic PathEdit(final DirCacheEntry ent) {\n+\t\t\tpath = ent.path;\n+\t\t}\n+\n+\t\t/**\n+\t\t * Apply the update to a single cache entry matching the path.\n+\t\t * <p>\n+\t\t * After apply is invoked the entry is added to the output table, and\n+\t\t * will be included in the new index.\n+\t\t * \n+\t\t * @param ent\n+\t\t *            the entry being processed. All fields are zeroed out if\n+\t\t *            the path is a new path in the index.\n+\t\t */\n+\t\tpublic abstract void apply(DirCacheEntry ent);\n+\t}\n+\n+\t/**\n+\t * Deletes a single file entry from the index.\n+\t * <p>\n+\t * This deletion command removes only a single file at the given location,\n+\t * but removes multiple stages (if present) for that path. To remove a\n+\t * complete subtree use {@link DeleteTree} instead.\n+\t * \n+\t * @see DeleteTree\n+\t */\n+\tpublic static final class DeletePath extends PathEdit {\n+\t\t/**\n+\t\t * Create a new deletion command by path name.\n+\t\t * \n+\t\t * @param entryPath\n+\t\t *            path of the file within the repository.\n+\t\t */\n+\t\tpublic DeletePath(final String entryPath) {\n+\t\t\tsuper(entryPath);\n+\t\t}\n+\n+\t\t/**\n+\t\t * Create a new deletion command for an existing entry instance.\n+\t\t * \n+\t\t * @param ent\n+\t\t *            entry instance to remove. Only the path of this entry is\n+\t\t *            actually considered during command evaluation.\n+\t\t */\n+\t\tpublic DeletePath(final DirCacheEntry ent) {\n+\t\t\tsuper(ent);\n+\t\t}\n+\n+\t\tpublic void apply(final DirCacheEntry ent) {\n+\t\t\tthrow new UnsupportedOperationException(\"No apply in delete\");\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * Recursively deletes all paths under a subtree.\n+\t * <p>\n+\t * This deletion command is more generic than {@link DeletePath} as it can\n+\t * remove all records which appear recursively under the same subtree.\n+\t * Multiple stages are removed (if present) for any deleted entry.\n+\t * <p>\n+\t * This command will not remove a single file entry. To remove a single file\n+\t * use {@link DeletePath}.\n+\t * \n+\t * @see DeletePath\n+\t */\n+\tpublic static final class DeleteTree extends PathEdit {\n+\t\t/**\n+\t\t * Create a new tree deletion command by path name.\n+\t\t * \n+\t\t * @param entryPath\n+\t\t *            path of the subtree within the repository. If the path\n+\t\t *            does not end with \"/\" a \"/\" is implicitly added to ensure\n+\t\t *            only the subtree's contents are matched by the command.\n+\t\t */\n+\t\tpublic DeleteTree(final String entryPath) {\n+\t\t\tsuper(entryPath.endsWith(\"/\") ? entryPath : entryPath + \"/\");\n+\t\t}\n+\n+\t\tpublic void apply(final DirCacheEntry ent) {\n+\t\t\tthrow new UnsupportedOperationException(\"No apply in delete\");\n+\t\t}\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86870","messageId":"1218503293-14057-17-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-16-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 16/26] Support recursively getting all entries under a subtree path","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:03Z","receivedAt":"2008-08-12T01:08:03Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Some applications need to process through all entries of a subtree.\nA good example is if the entries need to be recreated into another\nsubtree to support a \"cp -r a b\" or \"mv a b\" operation.\n\nThe getEntriesWithin method returns an array of all entries within\na single subtree path, allowing the aplication to process over that\narray in order.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../src/org/spearce/jgit/dircache/DirCache.java    |   22 ++++++++++++++++++++\n 1 files changed, 22 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\nindex e48fdec..302c514 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCache.java\n@@ -631,6 +631,28 @@ public class DirCache {\n \t\treturn i < 0 ? null : sortedEntries[i];\n \t}\n \n+\t/**\n+\t * Recursively get all entries within a subtree.\n+\t * \n+\t * @param path\n+\t *            the subtree path to get all entries within.\n+\t * @return all entries recursively contained within the subtree.\n+\t */\n+\tpublic DirCacheEntry[] getEntriesWithin(String path) {\n+\t\tif (!path.endsWith(\"/\"))\n+\t\t\tpath += \"/\";\n+\t\tfinal byte[] p = Constants.encode(path);\n+\t\tfinal int pLen = p.length;\n+\n+\t\tint eIdx = findEntry(p, pLen);\n+\t\tif (eIdx < 0)\n+\t\t\teIdx = -(eIdx + 1);\n+\t\tfinal int lastIdx = nextEntry(p, pLen, eIdx);\n+\t\tfinal DirCacheEntry[] r = new DirCacheEntry[lastIdx - eIdx];\n+\t\tSystem.arraycopy(sortedEntries, eIdx, r, 0, r.length);\n+\t\treturn r;\n+\t}\n+\n \tvoid toArray(final int i, final DirCacheEntry[] dst, final int off,\n \t\t\tfinal int cnt) {\n \t\tSystem.arraycopy(sortedEntries, i, dst, off, cnt);\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86872","messageId":"1218503293-14057-18-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-17-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 17/26] Support copying meta fields from one DirCacheEntry to another","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:04Z","receivedAt":"2008-08-12T01:08:04Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"When an application moves a DirCacheEntry record from one path to\nanother path in the index we need to recreate the entry object and\nalso copy the meta fields from the old entry to the new entry.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../org/spearce/jgit/dircache/DirCacheEntry.java   |   16 ++++++++++++++++\n 1 files changed, 16 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\nindex eba2678..2b5ce03 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/dircache/DirCacheEntry.java\n@@ -354,6 +354,22 @@ public class DirCacheEntry {\n \t\treturn Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();\n \t}\n \n+\t/**\n+\t * Copy the ObjectId and other meta fields from an existing entry.\n+\t * <p>\n+\t * This method copies everything except the path from one entry to another,\n+\t * supporting renaming.\n+\t * \n+\t * @param src\n+\t *            the entry to copy ObjectId and meta fields from.\n+\t */\n+\tpublic void copyMetaData(final DirCacheEntry src) {\n+\t\tfinal int pLen = NB.decodeUInt16(info, infoOffset + P_FLAGS) & 0xfff;\n+\t\tSystem.arraycopy(src.info, src.infoOffset, info, infoOffset, INFO_LEN);\n+\t\tNB.encodeInt16(info, infoOffset + P_FLAGS, pLen\n+\t\t\t\t| NB.decodeUInt16(info, infoOffset + P_FLAGS) & ~0xfff);\n+\t}\n+\n \tprivate long decodeTS(final int pIdx) {\n \t\tfinal int base = infoOffset + pIdx;\n \t\tfinal int sec = NB.decodeInt32(info, base);\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86873","messageId":"1218503293-14057-19-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-18-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 18/26] Add JUnit tests for new DirCache API","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:05Z","receivedAt":"2008-08-12T01:08:05Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"These tests excerise the file reading and writing routines, as well as\nthe two forms of editing the cache (through builder and editor).\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../spearce/jgit/dircache/DirCacheBasicTest.java   |  185 +++++++++++++\n .../jgit/dircache/DirCacheBuilderIteratorTest.java |   91 +++++++\n .../spearce/jgit/dircache/DirCacheBuilderTest.java |  253 ++++++++++++++++++\n .../spearce/jgit/dircache/DirCacheFindTest.java    |   86 ++++++\n .../jgit/dircache/DirCacheIteratorTest.java        |  273 ++++++++++++++++++++\n .../spearce/jgit/dircache/DirCacheTreeTest.java    |  150 +++++++++++\n 6 files changed, 1038 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBasicTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderIteratorTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheFindTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheIteratorTest.java\n create mode 100644 org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheTreeTest.java\n\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBasicTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBasicTest.java\nnew file mode 100644\nindex 0000000..b3097ac\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBasicTest.java\n@@ -0,0 +1,185 @@\n+/*\n+ * Copyright (C) 2008, 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.dircache;\n+\n+import java.io.File;\n+\n+import org.spearce.jgit.lib.RepositoryTestCase;\n+\n+public class DirCacheBasicTest extends RepositoryTestCase {\n+\tpublic void testReadMissing_RealIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"index\");\n+\t\tassertFalse(idx.exists());\n+\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\t\tassertNotNull(dc);\n+\t\tassertEquals(0, dc.getEntryCount());\n+\t}\n+\n+\tpublic void testReadMissing_TempIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"tmp_index\");\n+\t\tassertFalse(idx.exists());\n+\n+\t\tfinal DirCache dc = DirCache.read(idx);\n+\t\tassertNotNull(dc);\n+\t\tassertEquals(0, dc.getEntryCount());\n+\t}\n+\n+\tpublic void testLockMissing_RealIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"index\");\n+\t\tfinal File lck = new File(db.getDirectory(), \"index.lock\");\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\n+\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\tassertNotNull(dc);\n+\t\tassertFalse(idx.exists());\n+\t\tassertTrue(lck.exists());\n+\t\tassertEquals(0, dc.getEntryCount());\n+\n+\t\tdc.unlock();\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\t}\n+\n+\tpublic void testLockMissing_TempIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"tmp_index\");\n+\t\tfinal File lck = new File(db.getDirectory(), \"tmp_index.lock\");\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\n+\t\tfinal DirCache dc = DirCache.lock(idx);\n+\t\tassertNotNull(dc);\n+\t\tassertFalse(idx.exists());\n+\t\tassertTrue(lck.exists());\n+\t\tassertEquals(0, dc.getEntryCount());\n+\n+\t\tdc.unlock();\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\t}\n+\n+\tpublic void testWriteEmptyUnlock_RealIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"index\");\n+\t\tfinal File lck = new File(db.getDirectory(), \"index.lock\");\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\n+\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\tassertEquals(0, lck.length());\n+\t\tdc.write();\n+\t\tassertEquals(12 + 20, lck.length());\n+\n+\t\tdc.unlock();\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\t}\n+\n+\tpublic void testWriteEmptyCommit_RealIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"index\");\n+\t\tfinal File lck = new File(db.getDirectory(), \"index.lock\");\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\n+\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\tassertEquals(0, lck.length());\n+\t\tdc.write();\n+\t\tassertEquals(12 + 20, lck.length());\n+\n+\t\tassertTrue(dc.commit());\n+\t\tassertTrue(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\t\tassertEquals(12 + 20, idx.length());\n+\t}\n+\n+\tpublic void testWriteEmptyReadEmpty_RealIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"index\");\n+\t\tfinal File lck = new File(db.getDirectory(), \"index.lock\");\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\t\tdc.write();\n+\t\t\tassertTrue(dc.commit());\n+\t\t\tassertTrue(idx.exists());\n+\t\t}\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.read(db);\n+\t\t\tassertEquals(0, dc.getEntryCount());\n+\t\t}\n+\t}\n+\n+\tpublic void testWriteEmptyLockEmpty_RealIndex() throws Exception {\n+\t\tfinal File idx = new File(db.getDirectory(), \"index\");\n+\t\tfinal File lck = new File(db.getDirectory(), \"index.lock\");\n+\t\tassertFalse(idx.exists());\n+\t\tassertFalse(lck.exists());\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\t\tdc.write();\n+\t\t\tassertTrue(dc.commit());\n+\t\t\tassertTrue(idx.exists());\n+\t\t}\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\t\tassertEquals(0, dc.getEntryCount());\n+\t\t\tassertTrue(idx.exists());\n+\t\t\tassertTrue(lck.exists());\n+\t\t\tdc.unlock();\n+\t\t}\n+\t}\n+\n+\tpublic void testBuildThenClear() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a.b\", \"a/b\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tassertEquals(paths.length, dc.getEntryCount());\n+\t\tdc.clear();\n+\t\tassertEquals(0, dc.getEntryCount());\n+\t}\n+\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderIteratorTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderIteratorTest.java\nnew file mode 100644\nindex 0000000..cbcdeb5\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderIteratorTest.java\n@@ -0,0 +1,91 @@\n+/*\n+ * Copyright (C) 2008, 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.dircache;\n+\n+import java.util.Collections;\n+\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.RepositoryTestCase;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n+\n+public class DirCacheBuilderIteratorTest extends RepositoryTestCase {\n+\tpublic void testPathFilterGroup_DoesNotSkipTail() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal FileMode mode = FileMode.REGULAR_FILE;\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\t\tents[i].setFileMode(mode);\n+\t\t}\n+\t\t{\n+\t\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\t\tb.add(ents[i]);\n+\t\t\tb.finish();\n+\t\t}\n+\n+\t\tfinal int expIdx = 2;\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\ttw.reset();\n+\t\ttw.addTree(new DirCacheBuildIterator(b));\n+\t\ttw.setRecursive(true);\n+\t\ttw.setFilter(PathFilterGroup.createFromStrings(Collections\n+\t\t\t\t.singleton(paths[expIdx])));\n+\n+\t\tassertTrue(\"found \" + paths[expIdx], tw.next());\n+\t\tfinal DirCacheIterator c = tw.getTree(0, DirCacheIterator.class);\n+\t\tassertNotNull(c);\n+\t\tassertEquals(expIdx, c.cachePos);\n+\t\tassertSame(ents[expIdx], c.getDirCacheEntry());\n+\t\tassertEquals(paths[expIdx], tw.getPathString());\n+\t\tassertEquals(mode.getBits(), tw.getRawMode(0));\n+\t\tassertSame(mode, tw.getFileMode(0));\n+\t\tb.add(c.getDirCacheEntry());\n+\n+\t\tassertFalse(\"no more entries\", tw.next());\n+\n+\t\tb.finish();\n+\t\tassertEquals(ents.length, dc.getEntryCount());\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tassertSame(ents[i], dc.getEntry(i));\n+\t}\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderTest.java\nnew file mode 100644\nindex 0000000..2cf1d92\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheBuilderTest.java\n@@ -0,0 +1,253 @@\n+/*\n+ * Copyright (C) 2008, 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.dircache;\n+\n+import java.io.File;\n+\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.RepositoryTestCase;\n+\n+public class DirCacheBuilderTest extends RepositoryTestCase {\n+\tpublic void testBuildEmpty() throws Exception {\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\t\tassertNotNull(b);\n+\t\t\tb.finish();\n+\t\t\tdc.write();\n+\t\t\tassertTrue(dc.commit());\n+\t\t}\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.read(db);\n+\t\t\tassertEquals(0, dc.getEntryCount());\n+\t\t}\n+\t}\n+\n+\tpublic void testBuildOneFile_FinishWriteCommit() throws Exception {\n+\t\tfinal String path = \"a-file-path\";\n+\t\tfinal FileMode mode = FileMode.REGULAR_FILE;\n+\t\tfinal long lastModified = 1218123387057L;\n+\t\tfinal int length = 1342;\n+\t\tfinal DirCacheEntry entOrig;\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\t\tassertNotNull(b);\n+\n+\t\t\tentOrig = new DirCacheEntry(path);\n+\t\t\tentOrig.setFileMode(mode);\n+\t\t\tentOrig.setLastModified(lastModified);\n+\t\t\tentOrig.setLength(length);\n+\n+\t\t\tassertNotSame(path, entOrig.getPathString());\n+\t\t\tassertEquals(path, entOrig.getPathString());\n+\t\t\tassertEquals(ObjectId.zeroId(), entOrig.getObjectId());\n+\t\t\tassertEquals(mode.getBits(), entOrig.getRawMode());\n+\t\t\tassertEquals(0, entOrig.getStage());\n+\t\t\tassertEquals(lastModified, entOrig.getLastModified());\n+\t\t\tassertEquals(length, entOrig.getLength());\n+\t\t\tassertFalse(entOrig.isAssumeValid());\n+\t\t\tb.add(entOrig);\n+\n+\t\t\tb.finish();\n+\t\t\tassertEquals(1, dc.getEntryCount());\n+\t\t\tassertSame(entOrig, dc.getEntry(0));\n+\n+\t\t\tdc.write();\n+\t\t\tassertTrue(dc.commit());\n+\t\t}\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.read(db);\n+\t\t\tassertEquals(1, dc.getEntryCount());\n+\n+\t\t\tfinal DirCacheEntry entRead = dc.getEntry(0);\n+\t\t\tassertNotSame(entOrig, entRead);\n+\t\t\tassertEquals(path, entRead.getPathString());\n+\t\t\tassertEquals(ObjectId.zeroId(), entOrig.getObjectId());\n+\t\t\tassertEquals(mode.getBits(), entOrig.getRawMode());\n+\t\t\tassertEquals(0, entOrig.getStage());\n+\t\t\tassertEquals(lastModified, entOrig.getLastModified());\n+\t\t\tassertEquals(length, entOrig.getLength());\n+\t\t\tassertFalse(entOrig.isAssumeValid());\n+\t\t}\n+\t}\n+\n+\tpublic void testBuildOneFile_Commit() throws Exception {\n+\t\tfinal String path = \"a-file-path\";\n+\t\tfinal FileMode mode = FileMode.REGULAR_FILE;\n+\t\tfinal long lastModified = 1218123387057L;\n+\t\tfinal int length = 1342;\n+\t\tfinal DirCacheEntry entOrig;\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.lock(db);\n+\t\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\t\tassertNotNull(b);\n+\n+\t\t\tentOrig = new DirCacheEntry(path);\n+\t\t\tentOrig.setFileMode(mode);\n+\t\t\tentOrig.setLastModified(lastModified);\n+\t\t\tentOrig.setLength(length);\n+\n+\t\t\tassertNotSame(path, entOrig.getPathString());\n+\t\t\tassertEquals(path, entOrig.getPathString());\n+\t\t\tassertEquals(ObjectId.zeroId(), entOrig.getObjectId());\n+\t\t\tassertEquals(mode.getBits(), entOrig.getRawMode());\n+\t\t\tassertEquals(0, entOrig.getStage());\n+\t\t\tassertEquals(lastModified, entOrig.getLastModified());\n+\t\t\tassertEquals(length, entOrig.getLength());\n+\t\t\tassertFalse(entOrig.isAssumeValid());\n+\t\t\tb.add(entOrig);\n+\n+\t\t\tassertTrue(b.commit());\n+\t\t\tassertEquals(1, dc.getEntryCount());\n+\t\t\tassertSame(entOrig, dc.getEntry(0));\n+\t\t\tassertFalse(new File(db.getDirectory(), \"index.lock\").exists());\n+\t\t}\n+\t\t{\n+\t\t\tfinal DirCache dc = DirCache.read(db);\n+\t\t\tassertEquals(1, dc.getEntryCount());\n+\n+\t\t\tfinal DirCacheEntry entRead = dc.getEntry(0);\n+\t\t\tassertNotSame(entOrig, entRead);\n+\t\t\tassertEquals(path, entRead.getPathString());\n+\t\t\tassertEquals(ObjectId.zeroId(), entOrig.getObjectId());\n+\t\t\tassertEquals(mode.getBits(), entOrig.getRawMode());\n+\t\t\tassertEquals(0, entOrig.getStage());\n+\t\t\tassertEquals(lastModified, entOrig.getLastModified());\n+\t\t\tassertEquals(length, entOrig.getLength());\n+\t\t\tassertFalse(entOrig.isAssumeValid());\n+\t\t}\n+\t}\n+\n+\tpublic void testFindSingleFile() throws Exception {\n+\t\tfinal String path = \"a-file-path\";\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tassertNotNull(b);\n+\n+\t\tfinal DirCacheEntry entOrig = new DirCacheEntry(path);\n+\t\tassertNotSame(path, entOrig.getPathString());\n+\t\tassertEquals(path, entOrig.getPathString());\n+\t\tb.add(entOrig);\n+\t\tb.finish();\n+\n+\t\tassertEquals(1, dc.getEntryCount());\n+\t\tassertSame(entOrig, dc.getEntry(0));\n+\t\tassertEquals(0, dc.findEntry(path));\n+\n+\t\tassertEquals(-1, dc.findEntry(\"@@-before\"));\n+\t\tassertEquals(0, real(dc.findEntry(\"@@-before\")));\n+\n+\t\tassertEquals(-2, dc.findEntry(\"a-zoo\"));\n+\t\tassertEquals(1, real(dc.findEntry(\"a-zoo\")));\n+\n+\t\tassertSame(entOrig, dc.getEntry(path));\n+\t}\n+\n+\tpublic void testAdd_InGitSortOrder() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a.b\", \"a/b\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tassertEquals(paths.length, dc.getEntryCount());\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tassertSame(ents[i], dc.getEntry(i));\n+\t\t\tassertEquals(paths[i], dc.getEntry(i).getPathString());\n+\t\t\tassertEquals(i, dc.findEntry(paths[i]));\n+\t\t\tassertSame(ents[i], dc.getEntry(paths[i]));\n+\t\t}\n+\t}\n+\n+\tpublic void testAdd_ReverseGitSortOrder() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a.b\", \"a/b\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = ents.length - 1; i >= 0; i--)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tassertEquals(paths.length, dc.getEntryCount());\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tassertSame(ents[i], dc.getEntry(i));\n+\t\t\tassertEquals(paths[i], dc.getEntry(i).getPathString());\n+\t\t\tassertEquals(i, dc.findEntry(paths[i]));\n+\t\t\tassertSame(ents[i], dc.getEntry(paths[i]));\n+\t\t}\n+\t}\n+\n+\tpublic void testBuilderClear() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a.b\", \"a/b\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\t{\n+\t\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\t\tb.add(ents[i]);\n+\t\t\tb.finish();\n+\t\t}\n+\t\tassertEquals(paths.length, dc.getEntryCount());\n+\t\t{\n+\t\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\t\tb.finish();\n+\t\t}\n+\t\tassertEquals(0, dc.getEntryCount());\n+\t}\n+\n+\tprivate static int real(int eIdx) {\n+\t\tif (eIdx < 0)\n+\t\t\teIdx = -(eIdx + 1);\n+\t\treturn eIdx;\n+\t}\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheFindTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheFindTest.java\nnew file mode 100644\nindex 0000000..0eb0302\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheFindTest.java\n@@ -0,0 +1,86 @@\n+/*\n+ * Copyright (C) 2008, 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.dircache;\n+\n+import org.spearce.jgit.lib.RepositoryTestCase;\n+\n+public class DirCacheFindTest extends RepositoryTestCase {\n+\tpublic void testEntriesWithin() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\tfinal int aFirst = 1;\n+\t\tfinal int aLast = 3;\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tassertEquals(paths.length, dc.getEntryCount());\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tassertSame(ents[i], dc.getEntry(i));\n+\n+\t\t{\n+\t\t\tfinal DirCacheEntry[] aContents = dc.getEntriesWithin(\"a\");\n+\t\t\tassertNotNull(aContents);\n+\t\t\tassertEquals(aLast - aFirst + 1, aContents.length);\n+\t\t\tfor (int i = aFirst, j = 0; i <= aLast; i++, j++)\n+\t\t\t\tassertSame(ents[i], aContents[j]);\n+\t\t}\n+\t\t{\n+\t\t\tfinal DirCacheEntry[] aContents = dc.getEntriesWithin(\"a/\");\n+\t\t\tassertNotNull(aContents);\n+\t\t\tassertEquals(aLast - aFirst + 1, aContents.length);\n+\t\t\tfor (int i = aFirst, j = 0; i <= aLast; i++, j++)\n+\t\t\t\tassertSame(ents[i], aContents[j]);\n+\t\t}\n+\n+\t\tassertNotNull(dc.getEntriesWithin(\"a.\"));\n+\t\tassertEquals(0, dc.getEntriesWithin(\"a.\").length);\n+\n+\t\tassertNotNull(dc.getEntriesWithin(\"a0b\"));\n+\t\tassertEquals(0, dc.getEntriesWithin(\"a0b.\").length);\n+\n+\t\tassertNotNull(dc.getEntriesWithin(\"zoo\"));\n+\t\tassertEquals(0, dc.getEntriesWithin(\"zoo.\").length);\n+\t}\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheIteratorTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheIteratorTest.java\nnew file mode 100644\nindex 0000000..7d4e6bb\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheIteratorTest.java\n@@ -0,0 +1,273 @@\n+/*\n+ * Copyright (C) 2008, 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.dircache;\n+\n+import java.util.Collections;\n+\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.lib.RepositoryTestCase;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.filter.PathFilterGroup;\n+\n+public class DirCacheIteratorTest extends RepositoryTestCase {\n+\tpublic void testEmptyTree_NoTreeWalk() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\t\tassertEquals(0, dc.getEntryCount());\n+\n+\t\tfinal DirCacheIterator i = new DirCacheIterator(dc);\n+\t\ti.next();\n+\t\tassertTrue(i.eof());\n+\t}\n+\n+\tpublic void testEmptyTree_WithTreeWalk() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\t\tassertEquals(0, dc.getEntryCount());\n+\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\ttw.reset();\n+\t\ttw.addTree(new DirCacheIterator(dc));\n+\t\tassertFalse(tw.next());\n+\t}\n+\n+\tpublic void testNoSubtree_NoTreeWalk() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tfinal DirCacheIterator i = new DirCacheIterator(dc);\n+\t\tint pathIdx = 0;\n+\t\tfor (;;) {\n+\t\t\ti.next();\n+\t\t\tif (i.eof())\n+\t\t\t\tbreak;\n+\t\t\tassertEquals(pathIdx, i.cachePos);\n+\t\t\tassertSame(ents[pathIdx], i.getDirCacheEntry());\n+\t\t\tpathIdx++;\n+\t\t}\n+\t\tassertEquals(pathIdx, paths.length);\n+\t}\n+\n+\tpublic void testNoSubtree_WithTreeWalk() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a0b\" };\n+\t\tfinal FileMode[] modes = { FileMode.EXECUTABLE_FILE, FileMode.GITLINK };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\t\tents[i].setFileMode(modes[i]);\n+\t\t}\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tfinal DirCacheIterator i = new DirCacheIterator(dc);\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\ttw.reset();\n+\t\ttw.addTree(i);\n+\t\tint pathIdx = 0;\n+\t\twhile (tw.next()) {\n+\t\t\tassertSame(i, tw.getTree(0, DirCacheIterator.class));\n+\t\t\tassertEquals(pathIdx, i.cachePos);\n+\t\t\tassertSame(ents[pathIdx], i.getDirCacheEntry());\n+\t\t\tassertEquals(paths[pathIdx], tw.getPathString());\n+\t\t\tassertEquals(modes[pathIdx].getBits(), tw.getRawMode(0));\n+\t\t\tassertSame(modes[pathIdx], tw.getFileMode(0));\n+\t\t\tpathIdx++;\n+\t\t}\n+\t\tassertEquals(pathIdx, paths.length);\n+\t}\n+\n+\tpublic void testSingleSubtree_NoRecursion() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\t\tents[i].setFileMode(FileMode.REGULAR_FILE);\n+\t\t}\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tfinal String[] expPaths = { \"a.\", \"a\", \"a0b\" };\n+\t\tfinal FileMode[] expModes = { FileMode.REGULAR_FILE, FileMode.TREE,\n+\t\t\t\tFileMode.REGULAR_FILE };\n+\t\tfinal int expPos[] = { 0, -1, 4 };\n+\n+\t\tfinal DirCacheIterator i = new DirCacheIterator(dc);\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\ttw.reset();\n+\t\ttw.addTree(i);\n+\t\ttw.setRecursive(false);\n+\t\tint pathIdx = 0;\n+\t\twhile (tw.next()) {\n+\t\t\tassertSame(i, tw.getTree(0, DirCacheIterator.class));\n+\t\t\tassertEquals(expModes[pathIdx].getBits(), tw.getRawMode(0));\n+\t\t\tassertSame(expModes[pathIdx], tw.getFileMode(0));\n+\t\t\tassertEquals(expPaths[pathIdx], tw.getPathString());\n+\n+\t\t\tif (expPos[pathIdx] >= 0) {\n+\t\t\t\tassertEquals(expPos[pathIdx], i.cachePos);\n+\t\t\t\tassertSame(ents[expPos[pathIdx]], i.getDirCacheEntry());\n+\t\t\t} else {\n+\t\t\t\tassertSame(FileMode.TREE, tw.getFileMode(0));\n+\t\t\t}\n+\n+\t\t\tpathIdx++;\n+\t\t}\n+\t\tassertEquals(pathIdx, expPaths.length);\n+\t}\n+\n+\tpublic void testSingleSubtree_Recursive() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal FileMode mode = FileMode.REGULAR_FILE;\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\t\tents[i].setFileMode(mode);\n+\t\t}\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tfinal DirCacheIterator i = new DirCacheIterator(dc);\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\ttw.reset();\n+\t\ttw.addTree(i);\n+\t\ttw.setRecursive(true);\n+\t\tint pathIdx = 0;\n+\t\twhile (tw.next()) {\n+\t\t\tfinal DirCacheIterator c = tw.getTree(0, DirCacheIterator.class);\n+\t\t\tassertNotNull(c);\n+\t\t\tassertEquals(pathIdx, c.cachePos);\n+\t\t\tassertSame(ents[pathIdx], c.getDirCacheEntry());\n+\t\t\tassertEquals(paths[pathIdx], tw.getPathString());\n+\t\t\tassertEquals(mode.getBits(), tw.getRawMode(0));\n+\t\t\tassertSame(mode, tw.getFileMode(0));\n+\t\t\tpathIdx++;\n+\t\t}\n+\t\tassertEquals(pathIdx, paths.length);\n+\t}\n+\n+\tpublic void testTwoLevelSubtree_Recursive() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal FileMode mode = FileMode.REGULAR_FILE;\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c/e\", \"a/c/f\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\t\tents[i].setFileMode(mode);\n+\t\t}\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\ttw.reset();\n+\t\ttw.addTree(new DirCacheIterator(dc));\n+\t\ttw.setRecursive(true);\n+\t\tint pathIdx = 0;\n+\t\twhile (tw.next()) {\n+\t\t\tfinal DirCacheIterator c = tw.getTree(0, DirCacheIterator.class);\n+\t\t\tassertNotNull(c);\n+\t\t\tassertEquals(pathIdx, c.cachePos);\n+\t\t\tassertSame(ents[pathIdx], c.getDirCacheEntry());\n+\t\t\tassertEquals(paths[pathIdx], tw.getPathString());\n+\t\t\tassertEquals(mode.getBits(), tw.getRawMode(0));\n+\t\t\tassertSame(mode, tw.getFileMode(0));\n+\t\t\tpathIdx++;\n+\t\t}\n+\t\tassertEquals(pathIdx, paths.length);\n+\t}\n+\n+\tpublic void testTwoLevelSubtree_FilterPath() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal FileMode mode = FileMode.REGULAR_FILE;\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c/e\", \"a/c/f\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++) {\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\t\tents[i].setFileMode(mode);\n+\t\t}\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tfinal TreeWalk tw = new TreeWalk(db);\n+\t\tfor (int victimIdx = 0; victimIdx < paths.length; victimIdx++) {\n+\t\t\ttw.reset();\n+\t\t\ttw.addTree(new DirCacheIterator(dc));\n+\t\t\ttw.setFilter(PathFilterGroup.createFromStrings(Collections\n+\t\t\t\t\t.singleton(paths[victimIdx])));\n+\t\t\ttw.setRecursive(tw.getFilter().shouldBeRecursive());\n+\t\t\tassertTrue(tw.next());\n+\t\t\tfinal DirCacheIterator c = tw.getTree(0, DirCacheIterator.class);\n+\t\t\tassertNotNull(c);\n+\t\t\tassertEquals(victimIdx, c.cachePos);\n+\t\t\tassertSame(ents[victimIdx], c.getDirCacheEntry());\n+\t\t\tassertEquals(paths[victimIdx], tw.getPathString());\n+\t\t\tassertEquals(mode.getBits(), tw.getRawMode(0));\n+\t\t\tassertSame(mode, tw.getFileMode(0));\n+\t\t\tassertFalse(tw.next());\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheTreeTest.java b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheTreeTest.java\nnew file mode 100644\nindex 0000000..b37095d\n--- /dev/null\n+++ b/org.spearce.jgit.test/tst/org/spearce/jgit/dircache/DirCacheTreeTest.java\n@@ -0,0 +1,150 @@\n+/*\n+ * Copyright (C) 2008, 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.dircache;\n+\n+import org.spearce.jgit.lib.RepositoryTestCase;\n+\n+public class DirCacheTreeTest extends RepositoryTestCase {\n+\tpublic void testEmptyCache_NoCacheTree() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\t\tassertNull(dc.getCacheTree(false));\n+\t}\n+\n+\tpublic void testEmptyCache_CreateEmptyCacheTree() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\t\tfinal DirCacheTree tree = dc.getCacheTree(true);\n+\t\tassertNotNull(tree);\n+\t\tassertSame(tree, dc.getCacheTree(false));\n+\t\tassertSame(tree, dc.getCacheTree(true));\n+\t\tassertEquals(\"\", tree.getNameString());\n+\t\tassertEquals(\"\", tree.getPathString());\n+\t\tassertEquals(0, tree.getChildCount());\n+\t\tassertEquals(0, tree.getEntrySpan());\n+\t\tassertFalse(tree.isValid());\n+\t}\n+\n+\tpublic void testEmptyCache_Clear_NoCacheTree() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\t\tfinal DirCacheTree tree = dc.getCacheTree(true);\n+\t\tassertNotNull(tree);\n+\t\tdc.clear();\n+\t\tassertNull(dc.getCacheTree(false));\n+\t\tassertNotSame(tree, dc.getCacheTree(true));\n+\t}\n+\n+\tpublic void testSingleSubtree() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\tfinal int aFirst = 1;\n+\t\tfinal int aLast = 3;\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tassertNull(dc.getCacheTree(false));\n+\t\tfinal DirCacheTree root = dc.getCacheTree(true);\n+\t\tassertNotNull(root);\n+\t\tassertSame(root, dc.getCacheTree(true));\n+\t\tassertEquals(\"\", root.getNameString());\n+\t\tassertEquals(\"\", root.getPathString());\n+\t\tassertEquals(1, root.getChildCount());\n+\t\tassertEquals(dc.getEntryCount(), root.getEntrySpan());\n+\t\tassertFalse(root.isValid());\n+\n+\t\tfinal DirCacheTree aTree = root.getChild(0);\n+\t\tassertNotNull(aTree);\n+\t\tassertSame(aTree, root.getChild(0));\n+\t\tassertEquals(\"a\", aTree.getNameString());\n+\t\tassertEquals(\"a/\", aTree.getPathString());\n+\t\tassertEquals(0, aTree.getChildCount());\n+\t\tassertEquals(aLast - aFirst + 1, aTree.getEntrySpan());\n+\t\tassertFalse(aTree.isValid());\n+\t}\n+\n+\tpublic void testTwoLevelSubtree() throws Exception {\n+\t\tfinal DirCache dc = DirCache.read(db);\n+\n+\t\tfinal String[] paths = { \"a.\", \"a/b\", \"a/c/e\", \"a/c/f\", \"a/d\", \"a0b\" };\n+\t\tfinal DirCacheEntry[] ents = new DirCacheEntry[paths.length];\n+\t\tfor (int i = 0; i < paths.length; i++)\n+\t\t\tents[i] = new DirCacheEntry(paths[i]);\n+\t\tfinal int aFirst = 1;\n+\t\tfinal int aLast = 4;\n+\t\tfinal int acFirst = 2;\n+\t\tfinal int acLast = 3;\n+\n+\t\tfinal DirCacheBuilder b = dc.builder();\n+\t\tfor (int i = 0; i < ents.length; i++)\n+\t\t\tb.add(ents[i]);\n+\t\tb.finish();\n+\n+\t\tassertNull(dc.getCacheTree(false));\n+\t\tfinal DirCacheTree root = dc.getCacheTree(true);\n+\t\tassertNotNull(root);\n+\t\tassertSame(root, dc.getCacheTree(true));\n+\t\tassertEquals(\"\", root.getNameString());\n+\t\tassertEquals(\"\", root.getPathString());\n+\t\tassertEquals(1, root.getChildCount());\n+\t\tassertEquals(dc.getEntryCount(), root.getEntrySpan());\n+\t\tassertFalse(root.isValid());\n+\n+\t\tfinal DirCacheTree aTree = root.getChild(0);\n+\t\tassertNotNull(aTree);\n+\t\tassertSame(aTree, root.getChild(0));\n+\t\tassertEquals(\"a\", aTree.getNameString());\n+\t\tassertEquals(\"a/\", aTree.getPathString());\n+\t\tassertEquals(1, aTree.getChildCount());\n+\t\tassertEquals(aLast - aFirst + 1, aTree.getEntrySpan());\n+\t\tassertFalse(aTree.isValid());\n+\n+\t\tfinal DirCacheTree acTree = aTree.getChild(0);\n+\t\tassertNotNull(acTree);\n+\t\tassertSame(acTree, aTree.getChild(0));\n+\t\tassertEquals(\"c\", acTree.getNameString());\n+\t\tassertEquals(\"a/c/\", acTree.getPathString());\n+\t\tassertEquals(0, acTree.getChildCount());\n+\t\tassertEquals(acLast - acFirst + 1, acTree.getEntrySpan());\n+\t\tassertFalse(acTree.isValid());\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86879","messageId":"1218503293-14057-21-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-20-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 20/26] Allow the new DirCacheIterator in command line arguments","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:07Z","receivedAt":"2008-08-12T01:08:07Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"By assuming files passed where we want an AbtractTreeIterator to\nbe supplied are actually 'DIRC' files (aka .git/index) we can\nautomatically load them for the command line tool.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../jgit/pgm/opt/AbstractTreeIteratorHandler.java  |   13 +++++++++++++\n 1 files changed, 13 insertions(+), 0 deletions(-)\n\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/AbstractTreeIteratorHandler.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/AbstractTreeIteratorHandler.java\nindex 8e216c7..e439c87 100644\n--- a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/AbstractTreeIteratorHandler.java\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/opt/AbstractTreeIteratorHandler.java\n@@ -46,6 +46,8 @@ import org.kohsuke.args4j.OptionDef;\n import org.kohsuke.args4j.spi.OptionHandler;\n import org.kohsuke.args4j.spi.Parameters;\n import org.kohsuke.args4j.spi.Setter;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheIterator;\n import org.spearce.jgit.errors.IncorrectObjectTypeException;\n import org.spearce.jgit.errors.MissingObjectException;\n import org.spearce.jgit.lib.ObjectId;\n@@ -87,6 +89,17 @@ public class AbstractTreeIteratorHandler extends\n \t\t\treturn 1;\n \t\t}\n \n+\t\tif (new File(name).isFile()) {\n+\t\t\tfinal DirCache dirc;\n+\t\t\ttry {\n+\t\t\t\tdirc = DirCache.read(new File(name));\n+\t\t\t} catch (IOException e) {\n+\t\t\t\tthrow new CmdLineException(name + \" is not an index file\", e);\n+\t\t\t}\n+\t\t\tsetter.addValue(new DirCacheIterator(dirc));\n+\t\t\treturn 1;\n+\t\t}\n+\n \t\tfinal ObjectId id;\n \t\ttry {\n \t\t\tid = clp.getRepository().resolve(name);\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86878","messageId":"1218503293-14057-22-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-21-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 21/26] Add debugging commands to interact with the new DirCache code","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:08Z","receivedAt":"2008-08-12T01:08:08Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"These debug commands allow the command line user to read or write\nthe dircache, to exercise the code, and test it against C Git.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    5 ++\n .../org/spearce/jgit/pgm/debug/MakeCacheTree.java  |   67 ++++++++++++++++++\n .../org/spearce/jgit/pgm/debug/ReadDirCache.java   |   53 ++++++++++++++\n .../org/spearce/jgit/pgm/debug/ShowCacheTree.java  |   69 +++++++++++++++++++\n .../org/spearce/jgit/pgm/debug/ShowDirCache.java   |   72 ++++++++++++++++++++\n .../org/spearce/jgit/pgm/debug/WriteDirCache.java  |   54 +++++++++++++++\n 6 files changed, 320 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/MakeCacheTree.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ReadDirCache.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCacheTree.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowDirCache.java\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/WriteDirCache.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 734de3d..8ff815a 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@@ -12,4 +12,9 @@ org.spearce.jgit.pgm.RevList\n org.spearce.jgit.pgm.ShowRev\n org.spearce.jgit.pgm.Tag\n \n+org.spearce.jgit.pgm.debug.MakeCacheTree\n+org.spearce.jgit.pgm.debug.ReadDirCache\n+org.spearce.jgit.pgm.debug.ShowCacheTree\n org.spearce.jgit.pgm.debug.ShowCommands\n+org.spearce.jgit.pgm.debug.ShowDirCache\n+org.spearce.jgit.pgm.debug.WriteDirCache\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/MakeCacheTree.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/MakeCacheTree.java\nnew file mode 100644\nindex 0000000..a7d7438\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/MakeCacheTree.java\n@@ -0,0 +1,67 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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 org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheTree;\n+import org.spearce.jgit.pgm.TextBuiltin;\n+\n+class MakeCacheTree extends TextBuiltin {\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tfinal DirCache cache = DirCache.read(db);\n+\t\tfinal DirCacheTree tree = cache.getCacheTree(true);\n+\t\tshow(tree);\n+\t}\n+\n+\tprivate void show(final DirCacheTree tree) {\n+\t\tout.print(\"\\\"\");\n+\t\tout.print(tree.getPathString());\n+\t\tout.print(\"\\\"\");\n+\t\tout.print(\":  \");\n+\t\tout.print(tree.getEntrySpan());\n+\t\tout.print(\" entries\");\n+\t\tout.print(\", \");\n+\t\tout.print(tree.getChildCount());\n+\t\tout.print(\" children\");\n+\t\tout.println();\n+\n+\t\tfor (int i = 0; i < tree.getChildCount(); i++)\n+\t\t\tshow(tree.getChild(i));\n+\t}\n+}\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ReadDirCache.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ReadDirCache.java\nnew file mode 100644\nindex 0000000..d92a9fd\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ReadDirCache.java\n@@ -0,0 +1,53 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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 org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.pgm.TextBuiltin;\n+\n+class ReadDirCache extends TextBuiltin {\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tfinal int cnt = 100;\n+\t\tfinal long start = System.currentTimeMillis();\n+\t\tfor (int i = 0; i < cnt; i++)\n+\t\t\tDirCache.read(db);\n+\t\tfinal long end = System.currentTimeMillis();\n+\t\tout.println(\" average \" + ((end - start) / cnt) + \" ms/read\");\n+\t}\n+}\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCacheTree.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCacheTree.java\nnew file mode 100644\nindex 0000000..0683fa7\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowCacheTree.java\n@@ -0,0 +1,69 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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 org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheTree;\n+import org.spearce.jgit.pgm.TextBuiltin;\n+\n+class ShowCacheTree extends TextBuiltin {\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tfinal DirCache cache = DirCache.read(db);\n+\t\tfinal DirCacheTree tree = cache.getCacheTree(false);\n+\t\tif (tree == null)\n+\t\t\tthrow die(\"no 'TREE' section in index\");\n+\t\tshow(tree);\n+\t}\n+\n+\tprivate void show(final DirCacheTree tree) {\n+\t\tout.print(\"\\\"\");\n+\t\tout.print(tree.getPathString());\n+\t\tout.print(\"\\\"\");\n+\t\tout.print(\":  \");\n+\t\tout.print(tree.getEntrySpan());\n+\t\tout.print(\" entries\");\n+\t\tout.print(\", \");\n+\t\tout.print(tree.getChildCount());\n+\t\tout.print(\" children\");\n+\t\tout.println();\n+\n+\t\tfor (int i = 0; i < tree.getChildCount(); i++)\n+\t\t\tshow(tree.getChild(i));\n+\t}\n+}\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowDirCache.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowDirCache.java\nnew file mode 100644\nindex 0000000..0822503\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/ShowDirCache.java\n@@ -0,0 +1,72 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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.text.SimpleDateFormat;\n+import java.util.Date;\n+\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheEntry;\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.pgm.TextBuiltin;\n+\n+class ShowDirCache extends TextBuiltin {\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tfinal SimpleDateFormat fmt;\n+\t\tfmt = new SimpleDateFormat(\"yyyyMMdd,HHmmss.SSS\");\n+\n+\t\tfinal DirCache cache = DirCache.read(db);\n+\t\tfor (int i = 0; i < cache.getEntryCount(); i++) {\n+\t\t\tfinal DirCacheEntry ent = cache.getEntry(i);\n+\t\t\tfinal FileMode mode = FileMode.fromBits(ent.getRawMode());\n+\t\t\tfinal int len = ent.getLength();\n+\t\t\tfinal Date mtime = new Date(ent.getLastModified());\n+\n+\t\t\tout.print(mode);\n+\t\t\tout.format(\" %6d\", len);\n+\t\t\tout.print(' ');\n+\t\t\tout.print(fmt.format(mtime));\n+\t\t\tout.print(' ');\n+\t\t\tout.print(ent.getObjectId());\n+\t\t\tout.print('\\t');\n+\t\t\tout.print(ent.getPathString());\n+\t\t\tout.println();\n+\t\t}\n+\t}\n+}\ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/WriteDirCache.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/WriteDirCache.java\nnew file mode 100644\nindex 0000000..a613cbe\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/debug/WriteDirCache.java\n@@ -0,0 +1,54 @@\n+/*\n+ * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\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 org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.pgm.TextBuiltin;\n+\n+class WriteDirCache extends TextBuiltin {\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\tfinal DirCache cache = DirCache.read(db);\n+\t\tif (!cache.lock())\n+\t\t\tthrow die(\"failed to lock index\");\n+\t\tcache.read();\n+\t\tcache.write();\n+\t\tif (!cache.commit())\n+\t\t\tthrow die(\"failed to commit index\");\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86882","messageId":"1218503293-14057-23-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-22-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 22/26] Add a basic command line implementation of rm","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:09Z","receivedAt":"2008-08-12T01:08:09Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Removing entries from the index and from the working directory is a\nvery trivial operation now that we have DirCacheBuilder able to do\nan update of the index, automatically keeping the paths that were\nskipped over by the TreeWalk.  Therefore we only need to match the\npaths the user wants to remove, and do nothing with them.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../services/org.spearce.jgit.pgm.TextBuiltin      |    1 +\n .../src/org/spearce/jgit/pgm/Rm.java               |   92 ++++++++++++++++++++\n 2 files changed, 93 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Rm.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 8ff815a..39ae664 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@@ -9,6 +9,7 @@ org.spearce.jgit.pgm.LsTree\n org.spearce.jgit.pgm.MergeBase\n org.spearce.jgit.pgm.Push\n org.spearce.jgit.pgm.RevList\n+org.spearce.jgit.pgm.Rm\n org.spearce.jgit.pgm.ShowRev\n org.spearce.jgit.pgm.Tag\n \ndiff --git a/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Rm.java b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Rm.java\nnew file mode 100644\nindex 0000000..9a0d863\n--- /dev/null\n+++ b/org.spearce.jgit.pgm/src/org/spearce/jgit/pgm/Rm.java\n@@ -0,0 +1,92 @@\n+/*\n+ * Copyright (C) 2008, 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.File;\n+\n+import org.kohsuke.args4j.Argument;\n+import org.kohsuke.args4j.Option;\n+import org.kohsuke.args4j.spi.StopOptionHandler;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheBuildIterator;\n+import org.spearce.jgit.dircache.DirCacheBuilder;\n+import org.spearce.jgit.lib.Constants;\n+import org.spearce.jgit.lib.FileMode;\n+import org.spearce.jgit.pgm.opt.PathTreeFilterHandler;\n+import org.spearce.jgit.treewalk.TreeWalk;\n+import org.spearce.jgit.treewalk.filter.TreeFilter;\n+\n+@Command(usage = \"Stop tracking a file\", common = true)\n+class Rm extends TextBuiltin {\n+\t@Argument(metaVar = \"path\", usage = \"path\", multiValued = true, required = true, handler = PathTreeFilterHandler.class)\n+\t@Option(name = \"--\", handler = StopOptionHandler.class)\n+\tprivate TreeFilter paths;\n+\n+\tprivate File root;\n+\n+\t@Override\n+\tprotected void run() throws Exception {\n+\t\troot = db.getWorkDir();\n+\n+\t\tfinal DirCache dirc = DirCache.lock(db);\n+\t\tfinal DirCacheBuilder edit = dirc.builder();\n+\n+\t\tfinal TreeWalk walk = new TreeWalk(db);\n+\t\twalk.reset(); // drop the first empty tree, which we do not need here\n+\t\twalk.setRecursive(true);\n+\t\twalk.setFilter(paths);\n+\t\twalk.addTree(new DirCacheBuildIterator(edit));\n+\n+\t\twhile (walk.next()) {\n+\t\t\tfinal File path = new File(root, walk.getPathString());\n+\t\t\tfinal FileMode mode = walk.getFileMode(0);\n+\t\t\tif (mode.getObjectType() == Constants.OBJ_BLOB) {\n+\t\t\t\t// Deleting a blob is simply a matter of removing\n+\t\t\t\t// the file or symlink named by the tree entry.\n+\t\t\t\tdelete(path);\n+\t\t\t}\n+\t\t}\n+\n+\t\tedit.commit();\n+\t}\n+\n+\tprivate void delete(File p) {\n+\t\twhile (p != null && !p.equals(root) && p.delete())\n+\t\t\tp = p.getParentFile();\n+\t}\n+}\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86886","messageId":"1218503293-14057-24-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-23-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 23/26] Rewrite GitMoveDeleteHook to use DirCacheBuilder","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:10Z","receivedAt":"2008-08-12T01:08:10Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"The new DirCache API can read and write an index file faster than\nthe older GitIndex API can.  Its slightly more verbose to use but\noperations will execute more quickly, especially when we are forced\nto perform these single path updates by Eclipse.\n\nWhen we move a file we maintain its stage status.  If the file has\nbeen modified on disk but is not yet staged into the index (or it\nwas modified since staging) we leave the index alone, retaining\nthat staged state.\n\nIf we are moving within the same index file (same repository) we\nonly write the index out once.  This is a common case as most of\nthe renames performed by users will be within the same project,\nand the same Git repository.  It is rare for a rename to wind up\nspanning multiple repositories.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../org/spearce/egit/core/GitMoveDeleteHook.java   |  105 ++++++++++++--------\n 1 files changed, 63 insertions(+), 42 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java b/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java\nindex cc4059c..409f018 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java\n@@ -8,13 +8,13 @@\n  *******************************************************************************/\n package org.spearce.egit.core;\n \n-import java.io.File;\n import java.io.IOException;\n \n import org.eclipse.core.resources.IFile;\n import org.eclipse.core.resources.IFolder;\n import org.eclipse.core.resources.IProject;\n import org.eclipse.core.resources.IProjectDescription;\n+import org.eclipse.core.resources.IResource;\n import org.eclipse.core.resources.team.IMoveDeleteHook;\n import org.eclipse.core.resources.team.IResourceTree;\n import org.eclipse.core.runtime.Assert;\n@@ -23,8 +23,10 @@ import org.eclipse.core.runtime.IStatus;\n import org.eclipse.core.runtime.Status;\n import org.spearce.egit.core.project.GitProjectData;\n import org.spearce.egit.core.project.RepositoryMapping;\n-import org.spearce.jgit.lib.GitIndex;\n-import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheBuilder;\n+import org.spearce.jgit.dircache.DirCacheEditor;\n+import org.spearce.jgit.dircache.DirCacheEntry;\n \n class GitMoveDeleteHook implements IMoveDeleteHook {\n \tprivate static final boolean I_AM_DONE = true;\n@@ -40,21 +42,37 @@ class GitMoveDeleteHook implements IMoveDeleteHook {\n \n \tpublic boolean deleteFile(final IResourceTree tree, final IFile file,\n \t\t\tfinal int updateFlags, final IProgressMonitor monitor) {\n+\t\tfinal boolean force = (updateFlags & IResource.FORCE) == IResource.FORCE;\n+\t\tif (!force && !tree.isSynchronized(file, IResource.DEPTH_ZERO))\n+\t\t\treturn false;\n+\n+\t\tfinal RepositoryMapping map = RepositoryMapping.getMapping(file);\n+\t\tif (map == null)\n+\t\t\treturn false;\n+\n \t\ttry {\n-\t\t\tRepositoryMapping map = RepositoryMapping.getMapping(file);\n-\t\t\tif (map != null) {\n-\t\t\t\tRepository repository = map.getRepository();\n-\t\t\t\tGitIndex index = repository.getIndex();\n-\t\t\t\tif (index.remove(map.getWorkDir(), file.getLocation().toFile()))\n-\t\t\t\t\tindex.write();\n+\t\t\tfinal DirCache dirc = DirCache.lock(map.getRepository());\n+\t\t\tfinal int first = dirc.findEntry(map.getRepoRelativePath(file));\n+\t\t\tif (first < 0) {\n+\t\t\t\tdirc.unlock();\n+\t\t\t\treturn false;\n \t\t\t}\n-\t\t\treturn FINISH_FOR_ME;\n+\n+\t\t\tfinal DirCacheBuilder edit = dirc.builder();\n+\t\t\tif (first > 0)\n+\t\t\t\tedit.keep(0, first);\n+\t\t\tfinal int next = dirc.nextEntry(first);\n+\t\t\tif (next < dirc.getEntryCount())\n+\t\t\t\tedit.keep(next, dirc.getEntryCount() - next);\n+\t\t\tif (!edit.commit())\n+\t\t\t\ttree.failed(new Status(IStatus.ERROR, Activator.getPluginId(),\n+\t\t\t\t\t\t0, CoreText.MoveDeleteHook_operationError, null));\n+\t\t\ttree.standardDeleteFile(file, updateFlags, monitor);\n \t\t} catch (IOException e) {\n-\t\t\te.printStackTrace();\n \t\t\ttree.failed(new Status(IStatus.ERROR, Activator.getPluginId(), 0,\n \t\t\t\t\tCoreText.MoveDeleteHook_operationError, e));\n-\t\t\treturn I_AM_DONE;\n \t\t}\n+\t\treturn true;\n \t}\n \n \tpublic boolean deleteFolder(final IResourceTree tree, final IFolder folder,\n@@ -77,45 +95,48 @@ class GitMoveDeleteHook implements IMoveDeleteHook {\n \t\treturn FINISH_FOR_ME;\n \t}\n \n-\tpublic boolean moveFile(final IResourceTree tree, final IFile source,\n-\t\t\tfinal IFile destination, final int updateFlags,\n+\tpublic boolean moveFile(final IResourceTree tree, final IFile srcf,\n+\t\t\tfinal IFile dstf, final int updateFlags,\n \t\t\tfinal IProgressMonitor monitor) {\n+\t\tfinal boolean force = (updateFlags & IResource.FORCE) == IResource.FORCE;\n+\t\tif (!force && !tree.isSynchronized(srcf, IResource.DEPTH_ZERO))\n+\t\t\treturn false;\n+\n+\t\tfinal RepositoryMapping srcm = RepositoryMapping.getMapping(srcf);\n+\t\tif (srcm == null)\n+\t\t\treturn false;\n+\t\tfinal RepositoryMapping dstm = RepositoryMapping.getMapping(dstf);\n+\n \t\ttry {\n-\t\t\tfinal RepositoryMapping map1 = RepositoryMapping.getMapping(source);\n-\t\t\tif (map1 == null) {\n-\t\t\t\t// Source is not in a Git controlled project, fine\n-\t\t\t\treturn FINISH_FOR_ME;\n-\t\t\t}\n-\t\t\tfinal GitIndex index1 = map1.getRepository().getIndex();\n-\t\t\tfinal RepositoryMapping map2 = RepositoryMapping.getMapping(destination);\n-\t\t\tfinal File sourceFile = source.getLocation().toFile();\n-\t\t\tif (map2 == null) {\n-\t\t\t\tif (index1.getEntry(Repository.stripWorkDir(map1.getWorkDir(), sourceFile)) == null) {\n-\t\t\t\t\t// if the source resource is not tracked by Git that is ok too\n-\t\t\t\t\treturn FINISH_FOR_ME;\n-\t\t\t\t}\n-\t\t\t\ttree.failed(new Status(IStatus.ERROR, Activator.getPluginId(),\n-\t\t\t\t\t\t0, \"Destination not in a git versioned project\", null));\n-\t\t\t\treturn FINISH_FOR_ME;\n+\t\t\tfinal DirCache sCache = DirCache.lock(srcm.getRepository());\n+\t\t\tfinal String sPath = srcm.getRepoRelativePath(srcf);\n+\t\t\tfinal DirCacheEntry sEnt = sCache.getEntry(sPath);\n+\t\t\tif (sEnt == null) {\n+\t\t\t\tsCache.unlock();\n+\t\t\t\treturn false;\n \t\t\t}\n-\t\t\tGitIndex index2 = map2.getRepository().getIndex();\n-\t\t\ttree.standardMoveFile(source, destination, updateFlags, monitor);\n-\t\t\tif (index1.remove(map1.getWorkDir(), sourceFile)) {\n-\t\t\t\tindex2.add(map2.getWorkDir(), destination.getLocation().toFile());\n-\t\t\t\tindex1.write();\n-\t\t\t\tif (index2 != index1)\n-\t\t\t\t\tindex2.write();\n+\n+\t\t\tfinal DirCacheEditor sEdit = sCache.editor();\n+\t\t\tsEdit.add(new DirCacheEditor.DeletePath(sEnt));\n+\t\t\tif (dstm != null && dstm.getRepository() == srcm.getRepository()) {\n+\t\t\t\tfinal String dPath = srcm.getRepoRelativePath(dstf);\n+\t\t\t\tsEdit.add(new DirCacheEditor.PathEdit(dPath) {\n+\t\t\t\t\t@Override\n+\t\t\t\t\tpublic void apply(final DirCacheEntry dEnt) {\n+\t\t\t\t\t\tdEnt.copyMetaData(sEnt);\n+\t\t\t\t\t}\n+\t\t\t\t});\n \t\t\t}\n-\t\t\treturn I_AM_DONE;\n+\t\t\tif (!sEdit.commit())\n+\t\t\t\ttree.failed(new Status(IStatus.ERROR, Activator.getPluginId(),\n+\t\t\t\t\t\t0, CoreText.MoveDeleteHook_operationError, null));\n \n+\t\t\ttree.standardMoveFile(srcf, dstf, updateFlags, monitor);\n \t\t} catch (IOException e) {\n-\t\t\t// Recover properly!\n-\t\t\te.printStackTrace();\n \t\t\ttree.failed(new Status(IStatus.ERROR, Activator.getPluginId(), 0,\n \t\t\t\t\tCoreText.MoveDeleteHook_operationError, e));\n-\t\t\treturn I_AM_DONE;\n-\n \t\t}\n+\t\treturn true;\n \t}\n \n \tpublic boolean moveFolder(final IResourceTree tree, final IFolder source,\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86883","messageId":"1218503293-14057-25-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-24-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 24/26] Teach GitMoveDeleteHook how to move a folder recursively","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:11Z","receivedAt":"2008-08-12T01:08:11Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"When the user renames or moves a folder Eclipse passes us one event\nto move the entire container and its contents.  So we need to move\nall of the files contained within that container in our index.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../org/spearce/egit/core/GitMoveDeleteHook.java   |   51 ++++++++++++++++++--\n 1 files changed, 46 insertions(+), 5 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java b/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java\nindex 409f018..2cdff7d 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/GitMoveDeleteHook.java\n@@ -1,6 +1,7 @@\n /*******************************************************************************\n  * Copyright (C) 2008, Robin Rosenberg <robin.rosenberg@dewire.com>\n  * Copyright (C) 2007, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Google Inc.\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n@@ -139,12 +140,52 @@ class GitMoveDeleteHook implements IMoveDeleteHook {\n \t\treturn true;\n \t}\n \n-\tpublic boolean moveFolder(final IResourceTree tree, final IFolder source,\n-\t\t\tfinal IFolder destination, final int updateFlags,\n+\tpublic boolean moveFolder(final IResourceTree tree, final IFolder srcf,\n+\t\t\tfinal IFolder dstf, final int updateFlags,\n \t\t\tfinal IProgressMonitor monitor) {\n-\t\t// TODO: Implement this. Should be relatively easy, but consider that\n-\t\t// Eclipse thinks folders are real thinsgs, while Git does not care.\n-\t\treturn FINISH_FOR_ME;\n+\t\tfinal boolean force = (updateFlags & IResource.FORCE) == IResource.FORCE;\n+\t\tif (!force && !tree.isSynchronized(srcf, IResource.DEPTH_ZERO))\n+\t\t\treturn false;\n+\n+\t\tfinal RepositoryMapping srcm = RepositoryMapping.getMapping(srcf);\n+\t\tif (srcm == null)\n+\t\t\treturn false;\n+\t\tfinal RepositoryMapping dstm = RepositoryMapping.getMapping(dstf);\n+\n+\t\ttry {\n+\t\t\tfinal DirCache sCache = DirCache.lock(srcm.getRepository());\n+\t\t\tfinal String sPath = srcm.getRepoRelativePath(srcf);\n+\t\t\tfinal DirCacheEntry[] sEnt = sCache.getEntriesWithin(sPath);\n+\t\t\tif (sEnt.length == 0) {\n+\t\t\t\tsCache.unlock();\n+\t\t\t\treturn false;\n+\t\t\t}\n+\n+\t\t\tfinal DirCacheEditor sEdit = sCache.editor();\n+\t\t\tsEdit.add(new DirCacheEditor.DeleteTree(sPath));\n+\t\t\tif (dstm != null && dstm.getRepository() == srcm.getRepository()) {\n+\t\t\t\tfinal String dPath = srcm.getRepoRelativePath(dstf) + \"/\";\n+\t\t\t\tfinal int sPathLen = sPath.length() + 1;\n+\t\t\t\tfor (final DirCacheEntry se : sEnt) {\n+\t\t\t\t\tfinal String p = se.getPathString().substring(sPathLen);\n+\t\t\t\t\tsEdit.add(new DirCacheEditor.PathEdit(dPath + p) {\n+\t\t\t\t\t\t@Override\n+\t\t\t\t\t\tpublic void apply(final DirCacheEntry dEnt) {\n+\t\t\t\t\t\t\tdEnt.copyMetaData(se);\n+\t\t\t\t\t\t}\n+\t\t\t\t\t});\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tif (!sEdit.commit())\n+\t\t\t\ttree.failed(new Status(IStatus.ERROR, Activator.getPluginId(),\n+\t\t\t\t\t\t0, CoreText.MoveDeleteHook_operationError, null));\n+\n+\t\t\ttree.standardMoveFolder(srcf, dstf, updateFlags, monitor);\n+\t\t} catch (IOException e) {\n+\t\t\ttree.failed(new Status(IStatus.ERROR, Activator.getPluginId(), 0,\n+\t\t\t\t\tCoreText.MoveDeleteHook_operationError, e));\n+\t\t}\n+\t\treturn true;\n \t}\n \n \tpublic boolean moveProject(final IResourceTree tree, final IProject source,\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86876","messageId":"1218503293-14057-26-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-25-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 25/26] Rewrite UntrackOperation to use DirCacheBuilder","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:12Z","receivedAt":"2008-08-12T01:08:12Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"We can more efficiently untrack single files and entire directories using\nthe DirCacheBuilder API as that API is able to perform range edits on\nthe index data structure.  Its also able to perform faster disk IO, due\nto less conversion overheads.\n\nAlthough performance of untrack is not a critical feature it is a good\nidea to move it over to the newer API, as we will eventually get rid of\nthe GitIndex class entirely.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../org/spearce/egit/core/op/UntrackOperation.java |   98 +++++++++++---------\n 1 files changed, 55 insertions(+), 43 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/UntrackOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/UntrackOperation.java\nindex b491e6d..4656552 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/UntrackOperation.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/UntrackOperation.java\n@@ -1,6 +1,7 @@\n /*******************************************************************************\n  * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Google Inc.\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n@@ -8,15 +9,14 @@\n  *******************************************************************************/\n package org.spearce.egit.core.op;\n \n-import java.io.File;\n import java.io.IOException;\n import java.util.Collection;\n import java.util.IdentityHashMap;\n-import java.util.Iterator;\n+import java.util.Map;\n \n import org.eclipse.core.resources.IContainer;\n+import org.eclipse.core.resources.IProject;\n import org.eclipse.core.resources.IResource;\n-import org.eclipse.core.resources.IResourceVisitor;\n import org.eclipse.core.resources.IWorkspaceRunnable;\n import org.eclipse.core.runtime.CoreException;\n import org.eclipse.core.runtime.IAdaptable;\n@@ -26,7 +26,9 @@ import org.spearce.egit.core.Activator;\n import org.spearce.egit.core.CoreText;\n import org.spearce.egit.core.project.GitProjectData;\n import org.spearce.egit.core.project.RepositoryMapping;\n-import org.spearce.jgit.lib.GitIndex;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheEditor;\n+import org.spearce.jgit.lib.Repository;\n \n /**\n  * Remove one or more existing files/folders from the Git repository.\n@@ -43,6 +45,10 @@ import org.spearce.jgit.lib.GitIndex;\n public class UntrackOperation implements IWorkspaceRunnable {\n \tprivate final Collection rsrcList;\n \n+\tprivate final IdentityHashMap<Repository, DirCacheEditor> edits;\n+\n+\tprivate final IdentityHashMap<RepositoryMapping, Object> mappings;\n+\n \t/**\n \t * Create a new operation to stop tracking existing files/folders.\n \t * \n@@ -52,63 +58,69 @@ public class UntrackOperation implements IWorkspaceRunnable {\n \t */\n \tpublic UntrackOperation(final Collection rsrcs) {\n \t\trsrcList = rsrcs;\n+\t\tedits = new IdentityHashMap<Repository, DirCacheEditor>();\n+\t\tmappings = new IdentityHashMap<RepositoryMapping, Object>();\n \t}\n \n \tpublic void run(IProgressMonitor m) throws CoreException {\n-\t\tif (m == null) {\n+\t\tif (m == null)\n \t\t\tm = new NullProgressMonitor();\n-\t\t}\n \n-\t\tfinal IdentityHashMap<RepositoryMapping, Boolean> tomerge = new IdentityHashMap<RepositoryMapping, Boolean>();\n+\t\tedits.clear();\n+\t\tmappings.clear();\n+\n \t\tm.beginTask(CoreText.AddOperation_adding, rsrcList.size() * 200);\n \t\ttry {\n \t\t\tfor (Object obj : rsrcList) {\n-\t\t\t\tobj = ((IAdaptable)obj).getAdapter(IResource.class);\n-\t\t\t\tif (obj instanceof IResource) {\n-\t\t\t\t\tfinal IResource toRemove = (IResource)obj;\n-\t\t\t\t\tfinal GitProjectData pd = GitProjectData.get(toRemove.getProject());\n-\t\t\t\t\tfinal RepositoryMapping rm = pd.getRepositoryMapping(toRemove);\n-\t\t\t\t\tfinal GitIndex index = rm.getRepository().getIndex();\n-\t\t\t\t\ttomerge.put(rm, Boolean.TRUE);\n-\t\t\t\t\tif (toRemove instanceof IContainer) {\n-\t\t\t\t\t\t((IContainer)toRemove).accept(new IResourceVisitor() {\n-\t\t\t\t\t\t\tpublic boolean visit(IResource resource) throws CoreException {\n-\t\t\t\t\t\t\t\tif (resource.getType() == IResource.FILE) {\n-\t\t\t\t\t\t\t\t\tindex.remove(rm.getWorkDir(), new File(rm.getWorkDir(),rm.getRepoRelativePath(resource)));\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\treturn true;\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t},IResource.DEPTH_INFINITE, IContainer.EXCLUDE_DERIVED);\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tindex.remove(rm.getWorkDir(), new File(rm.getWorkDir(),rm.getRepoRelativePath(toRemove)));\n-\t\t\t\t\t}\n-\t\t\t\t}\n+\t\t\t\tobj = ((IAdaptable) obj).getAdapter(IResource.class);\n+\t\t\t\tif (obj instanceof IResource)\n+\t\t\t\t\tremove((IResource) obj);\n \t\t\t\tm.worked(200);\n \t\t\t}\n-\t\t\tfor (RepositoryMapping rm : tomerge.keySet()) {\n-\t\t\t\tm.setTaskName(\"Writing index for \"+rm.getRepository().getDirectory());\n-\t\t\t\trm.getRepository().getIndex().write();\n+\n+\t\t\tfor (Map.Entry<Repository, DirCacheEditor> e : edits.entrySet()) {\n+\t\t\t\tfinal Repository db = e.getKey();\n+\t\t\t\tfinal DirCacheEditor editor = e.getValue();\n+\t\t\t\tm.setTaskName(\"Writing index for \" + db.getDirectory());\n+\t\t\t\teditor.commit();\n \t\t\t}\n \t\t} catch (RuntimeException e) {\n-\t\t\te.printStackTrace();\n \t\t\tthrow Activator.error(CoreText.UntrackOperation_failed, e);\n \t\t} catch (IOException e) {\n-\t\t\te.printStackTrace();\n \t\t\tthrow Activator.error(CoreText.UntrackOperation_failed, e);\n \t\t} finally {\n+\t\t\tfor (final RepositoryMapping rm : mappings.keySet())\n+\t\t\t\trm.fireRepositoryChanged();\n+\t\t\tedits.clear();\n+\t\t\tmappings.clear();\n+\t\t\tm.done();\n+\t\t}\n+\t}\n+\n+\tprivate void remove(final IResource path) throws CoreException {\n+\t\tfinal IProject proj = path.getProject();\n+\t\tfinal GitProjectData pd = GitProjectData.get(proj);\n+\t\tif (pd == null)\n+\t\t\treturn;\n+\t\tfinal RepositoryMapping rm = pd.getRepositoryMapping(path);\n+\t\tif (rm == null)\n+\t\t\treturn;\n+\t\tfinal Repository db = rm.getRepository();\n+\n+\t\tDirCacheEditor e = edits.get(db);\n+\t\tif (e == null) {\n \t\t\ttry {\n-\t\t\t\tfinal Iterator i = tomerge.keySet().iterator();\n-\t\t\t\twhile (i.hasNext()) {\n-\t\t\t\t\tfinal RepositoryMapping r = (RepositoryMapping) i.next();\n-\t\t\t\t\tr.getRepository().getIndex().read();\n-\t\t\t\t\tr.fireRepositoryChanged();\n-\t\t\t\t}\n-\t\t\t} catch (IOException e) {\n-\t\t\t\te.printStackTrace();\n-\t\t\t} finally {\n-\t\t\t\tm.done();\n+\t\t\t\te = DirCache.lock(db).editor();\n+\t\t\t} catch (IOException err) {\n+\t\t\t\tthrow Activator.error(CoreText.UntrackOperation_failed, err);\n \t\t\t}\n+\t\t\tedits.put(db, e);\n+\t\t\tmappings.put(rm, rm);\n \t\t}\n-\t}\n \n+\t\tif (path instanceof IContainer)\n+\t\t\te.add(new DirCacheEditor.DeleteTree(rm.getRepoRelativePath(path)));\n+\t\telse\n+\t\t\te.add(new DirCacheEditor.DeletePath(rm.getRepoRelativePath(path)));\n+\t}\n }\n-- \n1.6.0.rc2.22.g71b99\n"},{"id":"86877","messageId":"1218503293-14057-27-git-send-email-spearce@spearce.org","threadId":"14948","inReplyTo":"1218503293-14057-26-git-send-email-spearce@spearce.org","subject":"[EGIT PATCH 26/26] Rewrite AssumeUnchangedOperation to use DirCache","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2008-08-12T01:08:13Z","receivedAt":"2008-08-12T01:08:13Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Its faster to use the DirCache and run over the range of entries\nthan to use GitIndex and probe the hash many times as we are in\na loop over the resources in the workspace.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n .../egit/core/op/AssumeUnchangedOperation.java     |  133 ++++++++++----------\n 1 files changed, 69 insertions(+), 64 deletions(-)\n\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/AssumeUnchangedOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/AssumeUnchangedOperation.java\nindex 78a84bb..fa70b6c 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/AssumeUnchangedOperation.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/AssumeUnchangedOperation.java\n@@ -1,6 +1,7 @@\n /*******************************************************************************\n  * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>\n  * Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>\n+ * Copyright (C) 2008, Google Inc.\n  *\n  * All rights reserved. This program and the accompanying materials\n  * are made available under the terms of the Eclipse Public License v1.0\n@@ -11,11 +12,11 @@ package org.spearce.egit.core.op;\n import java.io.IOException;\n import java.util.Collection;\n import java.util.IdentityHashMap;\n-import java.util.Iterator;\n+import java.util.Map;\n \n import org.eclipse.core.resources.IContainer;\n+import org.eclipse.core.resources.IProject;\n import org.eclipse.core.resources.IResource;\n-import org.eclipse.core.resources.IResourceVisitor;\n import org.eclipse.core.resources.IWorkspaceRunnable;\n import org.eclipse.core.runtime.CoreException;\n import org.eclipse.core.runtime.IAdaptable;\n@@ -23,9 +24,11 @@ import org.eclipse.core.runtime.IProgressMonitor;\n import org.eclipse.core.runtime.NullProgressMonitor;\n import org.spearce.egit.core.Activator;\n import org.spearce.egit.core.CoreText;\n+import org.spearce.egit.core.project.GitProjectData;\n import org.spearce.egit.core.project.RepositoryMapping;\n-import org.spearce.jgit.lib.GitIndex;\n-import org.spearce.jgit.lib.GitIndex.Entry;\n+import org.spearce.jgit.dircache.DirCache;\n+import org.spearce.jgit.dircache.DirCacheEntry;\n+import org.spearce.jgit.lib.Repository;\n \n /**\n  * Tell JGit to ignore changes in selected files\n@@ -33,87 +36,89 @@ import org.spearce.jgit.lib.GitIndex.Entry;\n public class AssumeUnchangedOperation implements IWorkspaceRunnable {\n \tprivate final Collection rsrcList;\n \n+\tprivate final IdentityHashMap<Repository, DirCache> caches;\n+\n+\tprivate final IdentityHashMap<RepositoryMapping, Object> mappings;\n+\n \t/**\n \t * Create a new operation to ignore changes in tracked files\n-\t *\n+\t * \n \t * @param rsrcs\n-\t *            collection of {@link IResource}s which should be \n-\t *            ignored when looking for changes or committing.\n+\t *            collection of {@link IResource}s which should be ignored when\n+\t *            looking for changes or committing.\n \t */\n \tpublic AssumeUnchangedOperation(final Collection rsrcs) {\n \t\trsrcList = rsrcs;\n+\t\tcaches = new IdentityHashMap<Repository, DirCache>();\n+\t\tmappings = new IdentityHashMap<RepositoryMapping, Object>();\n \t}\n \n \tpublic void run(IProgressMonitor m) throws CoreException {\n-\t\tif (m == null) {\n+\t\tif (m == null)\n \t\t\tm = new NullProgressMonitor();\n-\t\t}\n \n-\t\tfinal IdentityHashMap<RepositoryMapping, Boolean> tomerge = new IdentityHashMap<RepositoryMapping, Boolean>();\n-\t\tm.beginTask(CoreText.AssumeUnchangedOperation_adding, rsrcList.size() * 200);\n+\t\tcaches.clear();\n+\t\tmappings.clear();\n+\n+\t\tm.beginTask(CoreText.AssumeUnchangedOperation_adding,\n+\t\t\t\trsrcList.size() * 200);\n \t\ttry {\n \t\t\tfor (Object obj : rsrcList) {\n-\t\t\t\tobj = ((IAdaptable)obj).getAdapter(IResource.class);\n-\t\t\t\tif (obj instanceof IResource) {\n-\t\t\t\t\tfinal IResource toAssumeValid = (IResource)obj;\n-\t\t\t\t\tfinal RepositoryMapping rm = RepositoryMapping.getMapping(toAssumeValid);\n-\t\t\t\t\tfinal GitIndex index = rm.getRepository().getIndex();\n-\n-\t\t\t\t\tif (toAssumeValid instanceof IContainer) {\n-\t\t\t\t\t\t((IContainer)toAssumeValid).accept(new IResourceVisitor() {\n-\t\t\t\t\t\t\tpublic boolean visit(IResource resource) throws CoreException {\n-\t\t\t\t\t\t\t\ttry {\n-\t\t\t\t\t\t\t\t\tString repoPath = rm.getRepoRelativePath(resource);\n-\t\t\t\t\t\t\t\t\tEntry entry = index.getEntry(repoPath);\n-\t\t\t\t\t\t\t\t\tif (entry != null) {\n-\t\t\t\t\t\t\t\t\t\tif (!entry.isAssumedValid()) {\n-\t\t\t\t\t\t\t\t\t\t\tentry.setAssumeValid(true);\n-\t\t\t\t\t\t\t\t\t\t\ttomerge.put(rm, Boolean.TRUE);\n-\t\t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t} catch (IOException e) {\n-\t\t\t\t\t\t\t\t\te.printStackTrace();\n-\t\t\t\t\t\t\t\t\tthrow Activator.error(CoreText.AssumeUnchangedOperation_failed, e);\n-\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\treturn true;\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t},IResource.DEPTH_INFINITE, IContainer.EXCLUDE_DERIVED);\n-\t\t\t\t\t} else {\n-\t\t\t\t\t\tString repoPath = rm.getRepoRelativePath((IResource) obj);\n-\t\t\t\t\t\tEntry entry = index.getEntry(repoPath);\n-\t\t\t\t\t\tif (entry != null) {\n-\t\t\t\t\t\t\tif (!entry.isAssumedValid()) {\n-\t\t\t\t\t\t\t\tentry.setAssumeValid(true);\n-\t\t\t\t\t\t\t\ttomerge.put(rm, Boolean.TRUE);\n-\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t}\n-\t\t\t\t\t}\n-\t\t\t\t}\n+\t\t\t\tobj = ((IAdaptable) obj).getAdapter(IResource.class);\n+\t\t\t\tif (obj instanceof IResource)\n+\t\t\t\t\tassumeValid((IResource) obj);\n \t\t\t\tm.worked(200);\n \t\t\t}\n-\t\t\tfor (RepositoryMapping rm : tomerge.keySet()) {\n-\t\t\t\tm.setTaskName(\"Writing index for \"+rm.getRepository().getDirectory());\n-\t\t\t\trm.getRepository().getIndex().write();\n+\n+\t\t\tfor (Map.Entry<Repository, DirCache> e : caches.entrySet()) {\n+\t\t\t\tfinal Repository db = e.getKey();\n+\t\t\t\tfinal DirCache editor = e.getValue();\n+\t\t\t\tm.setTaskName(\"Writing index for \" + db.getDirectory());\n+\t\t\t\teditor.write();\n+\t\t\t\teditor.commit();\n \t\t\t}\n \t\t} catch (RuntimeException e) {\n-\t\t\te.printStackTrace();\n-\t\t\tthrow Activator.error(CoreText.AssumeUnchangedOperation_failed, e);\n+\t\t\tthrow Activator.error(CoreText.UntrackOperation_failed, e);\n \t\t} catch (IOException e) {\n-\t\t\te.printStackTrace();\n-\t\t\tthrow Activator.error(CoreText.AssumeUnchangedOperation_failed, e);\n+\t\t\tthrow Activator.error(CoreText.UntrackOperation_failed, e);\n \t\t} finally {\n+\t\t\tfor (final RepositoryMapping rm : mappings.keySet())\n+\t\t\t\trm.fireRepositoryChanged();\n+\t\t\tcaches.clear();\n+\t\t\tmappings.clear();\n+\t\t\tm.done();\n+\t\t}\n+\t}\n+\n+\tprivate void assumeValid(final IResource resource) throws CoreException {\n+\t\tfinal IProject proj = resource.getProject();\n+\t\tfinal GitProjectData pd = GitProjectData.get(proj);\n+\t\tif (pd == null)\n+\t\t\treturn;\n+\t\tfinal RepositoryMapping rm = pd.getRepositoryMapping(resource);\n+\t\tif (rm == null)\n+\t\t\treturn;\n+\t\tfinal Repository db = rm.getRepository();\n+\n+\t\tDirCache cache = caches.get(db);\n+\t\tif (cache == null) {\n \t\t\ttry {\n-\t\t\t\tfinal Iterator i = tomerge.keySet().iterator();\n-\t\t\t\twhile (i.hasNext()) {\n-\t\t\t\t\tfinal RepositoryMapping r = (RepositoryMapping) i.next();\n-\t\t\t\t\tr.getRepository().getIndex().read();\n-\t\t\t\t\tr.fireRepositoryChanged();\n-\t\t\t\t}\n-\t\t\t} catch (IOException e) {\n-\t\t\t\te.printStackTrace();\n-\t\t\t} finally {\n-\t\t\t\tm.done();\n+\t\t\t\tcache = DirCache.lock(db);\n+\t\t\t} catch (IOException err) {\n+\t\t\t\tthrow Activator.error(CoreText.UntrackOperation_failed, err);\n \t\t\t}\n+\t\t\tcaches.put(db, cache);\n+\t\t\tmappings.put(rm, rm);\n+\t\t}\n+\n+\t\tfinal String path = rm.getRepoRelativePath(resource);\n+\t\tif (resource instanceof IContainer) {\n+\t\t\tfor (final DirCacheEntry ent : cache.getEntriesWithin(path))\n+\t\t\t\tent.setAssumeValid(true);\n+\t\t} else {\n+\t\t\tfinal DirCacheEntry ent = cache.getEntry(path);\n+\t\t\tif (ent != null)\n+\t\t\t\tent.setAssumeValid(true);\n \t\t}\n \t}\n }\n-- \n1.6.0.rc2.22.g71b99\n"}]}