{"thread":{"id":"7300","subject":"[PATCH 0/4] Eclipse (EGIT) Structured compare","startedAt":"2007-03-18T22:15:50Z","lastAt":"2007-03-19T02:21:02Z","messageCount":6,"participants":["Robin Rosenberg","Shawn O. Pearce"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"37410","messageId":"20070318220711.24742.90943.stgit@lathund.dewire.com","threadId":"7300","inReplyTo":null,"subject":"[PATCH 0/4] Eclipse (EGIT) Structured compare","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2007-03-18T22:15:50Z","receivedAt":"2007-03-18T22:15:50Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Now Eclipse can compare two git version at the project level. Thanks\nto the SHA-1's the compare is very quick. There is some extra cost\nfor version managed archives (zip/jars) as Eclipse insists on comparing\nthe contents of those at the file level too. Not that I mind that. It\nis useful and cool. (No, it doesn't compare open office documents in\nany useful manner, unless you happen to have a plugin for it, in\nwhich case it might actually work.)\n\nAlong with this some experiments with caching and a minor bug fix in\nthe commit reader.\n\nIf anyone besides me actually uses this, please send a comment.\n\n-- robin\n"},{"id":"37411","messageId":"20070318221555.24742.93993.stgit@lathund.dewire.com","threadId":"7300","inReplyTo":"20070318220711.24742.90943.stgit@lathund.dewire.com","subject":"[PATCH 1/4] Cache tree ObjectId's too","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2007-03-18T22:15:55Z","receivedAt":"2007-03-18T22:15:55Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"The caching part is experimental. We explicitly leave it up to\nthe garbage collector when to recycle git objects. After trying\nthis practically we will se what is best. The upside is that\nrescanning the history for a certain resource is very quick when\ndata is still in the cache (tens of milliseconds), which is very\ngood for interactivity. When memory is tight we go to disk and\nfetch the data, which could take a couple of seconds on a large\nrepo.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n\n .../src/org/spearce/jgit/lib/Repository.java       |   18 +++++++++++++-----\n 1 files changed, 13 insertions(+), 5 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex 8b1f42c..d3b2178 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -47,7 +47,8 @@ public class Repository {\n \n \tprivate WindowCache windows;\n \n-\tprivate Map cache = new WeakHashMap(30000); \n+\tprivate Map treeCache = new WeakHashMap(30000); \n+\tprivate Map commitCache = new WeakHashMap(30000); \n \n \tpublic Repository(final File d) throws IOException {\n \t\tgitDir = d.getAbsoluteFile();\n@@ -189,7 +190,7 @@ public class Repository {\n \t}\n \n \tpublic Commit mapCommit(final ObjectId id) throws IOException {\n-\t\tCommit ret = (Commit)cache.get(id);\n+\t\tCommit ret = (Commit)commitCache.get(id);\n \t\tif (ret != null)\n \t\t\treturn ret;\n \n@@ -201,7 +202,7 @@ public class Repository {\n \t\t\tret = new Commit(this, id, raw);\n \t\t\t// The key must not be the referenced strongly\n \t\t\t// by the value in WeakHashMaps\n-\t\t\tcache.put(new ObjectId(id.getBytes()), ret);\n+\t\t\tcommitCache.put(new ObjectId(id.getBytes()), ret);\n \t\t\treturn ret;\n \t\t}\n \t\tthrow new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);\n@@ -213,12 +214,19 @@ public class Repository {\n \t}\n \n \tpublic Tree mapTree(final ObjectId id) throws IOException {\n+\t\tTree ret = (Tree)treeCache.get(id);\n+\t\tif (ret != null)\n+\t\t\treturn ret;\n+\n \t\tfinal ObjectLoader or = openObject(id);\n \t\tif (or == null)\n \t\t\treturn null;\n \t\tfinal byte[] raw = or.getBytes();\n-\t\tif (Constants.TYPE_TREE.equals(or.getType()))\n-\t\t\treturn new Tree(this, id, raw);\n+\t\tif (Constants.TYPE_TREE.equals(or.getType())) {\n+\t\t\tret = new Tree(this, id, raw);\n+\t\t\ttreeCache.put(new ObjectId(id.getBytes()), ret);\n+\t\t\treturn ret;\n+\t\t}\n \t\tif (Constants.TYPE_COMMIT.equals(or.getType()))\n \t\t\treturn mapTree(ObjectId.fromString(raw, 5));\n \t\tthrow new IncorrectObjectTypeException(id, Constants.TYPE_TREE);\n"},{"id":"37413","messageId":"20070318221601.24742.14256.stgit@lathund.dewire.com","threadId":"7300","inReplyTo":"20070318220711.24742.90943.stgit@lathund.dewire.com","subject":"[PATCH 2/4] Rework tree/commit cache","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2007-03-18T22:16:01Z","receivedAt":"2007-03-18T22:16:01Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"We now use SoftRefernces to hold the value objects and reuse\nthe object id's as cache keys. Seems to work much better, i.e.\nthe gc only throws out stuff from the cache when memory is\ntight, rather than at the first opportunity.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n\n .../src/org/spearce/jgit/lib/Repository.java       |   30 +++++++++++++++--------\n 1 files changed, 20 insertions(+), 10 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\nindex d3b2178..c7c47ae 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java\n@@ -23,6 +23,8 @@ import java.io.FileNotFoundException;\n import java.io.FileReader;\n import java.io.FileWriter;\n import java.io.IOException;\n+import java.lang.ref.Reference;\n+import java.lang.ref.SoftReference;\n import java.util.ArrayList;\n import java.util.Collection;\n import java.util.Map;\n@@ -190,19 +192,24 @@ public class Repository {\n \t}\n \n \tpublic Commit mapCommit(final ObjectId id) throws IOException {\n-\t\tCommit ret = (Commit)commitCache.get(id);\n-\t\tif (ret != null)\n-\t\t\treturn ret;\n+//\t\tSystem.out.println(\"commitcache.size=\"+commitCache.size());\n+\t\tReference retr = (Reference)commitCache.get(id);\n+\t\tif (retr != null) {\n+\t\t\tCommit ret = (Commit)retr.get();\n+\t\t\tif (ret != null)\n+\t\t\t\treturn ret;\n+\t\t\tSystem.out.println(\"Found a null id, size was \"+commitCache.size());\n+\t\t}\n \n \t\tfinal ObjectLoader or = openObject(id);\n \t\tif (or == null)\n \t\t\treturn null;\n \t\tfinal byte[] raw = or.getBytes();\n \t\tif (Constants.TYPE_COMMIT.equals(or.getType())) {\n-\t\t\tret = new Commit(this, id, raw);\n+\t\t\tCommit ret = new Commit(this, id, raw);\n \t\t\t// The key must not be the referenced strongly\n \t\t\t// by the value in WeakHashMaps\n-\t\t\tcommitCache.put(new ObjectId(id.getBytes()), ret);\n+\t\t\tcommitCache.put(id, new SoftReference(ret));\n \t\t\treturn ret;\n \t\t}\n \t\tthrow new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);\n@@ -214,17 +221,20 @@ public class Repository {\n \t}\n \n \tpublic Tree mapTree(final ObjectId id) throws IOException {\n-\t\tTree ret = (Tree)treeCache.get(id);\n-\t\tif (ret != null)\n-\t\t\treturn ret;\n+\t\tReference wret = (Reference)treeCache.get(id);\n+\t\tif (wret != null) {\n+\t\t\tTree ret = (Tree)wret.get();\n+\t\t\tif (ret != null)\n+\t\t\t\treturn ret;\n+\t\t}\n \n \t\tfinal ObjectLoader or = openObject(id);\n \t\tif (or == null)\n \t\t\treturn null;\n \t\tfinal byte[] raw = or.getBytes();\n \t\tif (Constants.TYPE_TREE.equals(or.getType())) {\n-\t\t\tret = new Tree(this, id, raw);\n-\t\t\ttreeCache.put(new ObjectId(id.getBytes()), ret);\n+\t\t\tTree ret = new Tree(this, id, raw);\n+\t\t\ttreeCache.put(id, new SoftReference(ret));\n \t\t\treturn ret;\n \t\t}\n \t\tif (Constants.TYPE_COMMIT.equals(or.getType()))\n"},{"id":"37412","messageId":"20070318221607.24742.44802.stgit@lathund.dewire.com","threadId":"7300","inReplyTo":"20070318220711.24742.90943.stgit@lathund.dewire.com","subject":"[PATCH 3/4] Decode message when encoding line is present","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2007-03-18T22:16:07Z","receivedAt":"2007-03-18T22:16:07Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"Seems we left the newline between the encoding and the rest\nof the message\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n\n .../src/org/spearce/jgit/lib/Commit.java           |    6 ++++--\n 1 files changed, 4 insertions(+), 2 deletions(-)\n\ndiff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java\nindex 4356149..c5c65c4 100644\n--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java\n+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java\n@@ -161,16 +161,18 @@ public class Commit implements Treeish {\n \t\t\t\t}\n \t\t\t\tbyte[] readBuf = new byte[br.available()]; // in-memory stream so this is all bytes left\n \t\t\t\tbr.read(readBuf);\n+\t\t\t\tint msgstart = readBuf[0] == '\\n' ? 1 : 0;\n+\t\t\t\t\t\n \t\t\t\tif (encoding != null) {\n \t\t\t\t\t// TODO: this isn't reliable so we need to guess the encoding from the actual content\n \t\t\t\t\tauthor = new PersonIdent(new String(rawAuthor.getBytes(),encoding.name()));\n \t\t\t\t\tcommitter = new PersonIdent(new String(rawCommitter.getBytes(),encoding.name()));\n-\t\t\t\t\tmessage = new String(readBuf,encoding.name());\n+\t\t\t\t\tmessage = new String(readBuf,msgstart, readBuf.length-msgstart, encoding.name());\n \t\t\t\t} else {\n \t\t\t\t\t// TODO: use config setting / platform / ascii / iso-latin\n \t\t\t\t\tauthor = new PersonIdent(new String(rawAuthor.getBytes()));\n \t\t\t\t\tcommitter = new PersonIdent(new String(rawCommitter.getBytes()));\n-\t\t\t\t\tmessage = new String(readBuf);\n+\t\t\t\t\tmessage = new String(readBuf, msgstart, readBuf.length-msgstart);\n \t\t\t\t}\n \t\t\t} catch (IOException e) {\n \t\t\t\te.printStackTrace();\n"},{"id":"37414","messageId":"20070318221612.24742.40025.stgit@lathund.dewire.com","threadId":"7300","inReplyTo":"20070318220711.24742.90943.stgit@lathund.dewire.com","subject":"[PATCH 4/4] Add support for structured comparison.","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2007-03-18T22:16:12Z","receivedAt":"2007-03-18T22:16:12Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"With this code we can now compare revisions at project and folder\nlevels, rather than only file level. We use the compare api's that\neclipse provide to do the display and detailed compare work, while\nwe do the high level comparisons at the SHA-1 level.\n\nA possible downside is that the API:s we use are internal, which\ncould make us version dependent. This code works with both 3.2.x and\n3.3M5 versions though.\n\nSigned-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>\n---\n\n org.spearce.egit.core/META-INF/MANIFEST.MF         |    6 \n .../src/org/spearce/egit/core/GitBlobStorage.java  |  108 +++++\n .../src/org/spearce/egit/core/GitTreeStorage.java  |  484 +++++++++++++++++++++++\n .../egit/core/internal/mapping/GitFileHistory.java |    8 \n .../core/internal/mapping/GitFileRevision.java     |   25 +\n .../core/internal/mapping/GitResourceNode.java     |   97 +++++\n org.spearce.egit.ui/plugin.xml                     |   16 -\n .../src/org/spearce/egit/ui/GitHistoryPage.java    |   11 -\n .../GitCompareFileRevisionEditorInput.java         |  305 ++++++++++++++\n .../internal/actions/GitCompareRevisionAction.java |  228 +++++++++++\n 10 files changed, 1268 insertions(+), 20 deletions(-)\n\ndiff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF\nindex f4c96cc..8dc62d6 100644\n--- a/org.spearce.egit.core/META-INF/MANIFEST.MF\n+++ b/org.spearce.egit.core/META-INF/MANIFEST.MF\n@@ -10,8 +10,12 @@ Require-Bundle: org.eclipse.core.runtime,\n  org.eclipse.team.core,\n  org.eclipse.core.resources,\n  org.spearce.jgit,\n- org.eclipse.core.filesystem\n+ org.eclipse.core.filesystem,\n+ org.eclipse.compare,\n+ org.eclipse.swt\n Export-Package: org.spearce.egit.core.internal.mapping;x-friends:=\"org.spearce.egit.ui\",\n+ org.spearce.egit.core,\n+ org.spearce.egit.core.internal.mapping,\n  org.spearce.egit.core.op,\n  org.spearce.egit.core.project\n Eclipse-LazyStart: true\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/GitBlobStorage.java b/org.spearce.egit.core/src/org/spearce/egit/core/GitBlobStorage.java\nnew file mode 100644\nindex 0000000..c947e2b\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/GitBlobStorage.java\n@@ -0,0 +1,108 @@\n+/*\n+ *  Copyright (C) 2006  Robin Rosenberg\n+ *\n+ *  This library is free software; you can redistribute it and/or\n+ *  modify it under the terms of the GNU Lesser General Public\n+ *  License, version 2.1, as published by the Free Software Foundation.\n+ *\n+ *  This library is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ *  Lesser General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU Lesser General Public\n+ *  License along with this library; if not, write to the Free Software\n+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\n+ */\n+package org.spearce.egit.core;\n+\n+import java.io.ByteArrayInputStream;\n+import java.io.FileNotFoundException;\n+import java.io.IOException;\n+import java.io.InputStream;\n+\n+import org.eclipse.core.internal.resources.ResourceException;\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.resources.IResourceStatus;\n+import org.eclipse.core.resources.IStorage;\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IPath;\n+import org.eclipse.team.core.RepositoryProvider;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ObjectLoader;\n+import org.spearce.jgit.lib.Tree;\n+import org.spearce.jgit.lib.TreeEntry;\n+\n+public class GitBlobStorage implements IStorage {\n+\n+\tprivate final IResource resource;\n+\n+\tprivate TreeEntry entry;\n+\n+\tpublic GitBlobStorage(ObjectId treeId, IResource resource, String relativeResourceName) {\n+\t\tthis.resource = resource;\n+\t\tif (treeId == null)\n+\t\t\treturn;\n+\n+\t\tGitProvider provider = (GitProvider) RepositoryProvider\n+\t\t\t\t.getProvider(resource.getProject());\n+\t\tRepositoryMapping repositoryMapping = provider.getData()\n+\t\t\t\t.getRepositoryMapping(resource.getProject());\n+\t\tTree tree;\n+\t\ttry {\n+\t\t\ttree = repositoryMapping.getRepository().mapTree(treeId);\n+\t\t\tString prefix = repositoryMapping.getSubset();\n+\t\t\tif (prefix != null)\n+\t\t\t\tprefix = prefix + \"/\";\n+\t\t\telse\n+\t\t\t\tprefix = \"\";\n+\t\t\tString name = prefix + resource.getProjectRelativePath().toString();\n+\t\t\tentry = tree.findMember(name);\n+\t\t} catch (IOException e) {\n+\t\t\t// TODO Auto-generated catch block\n+\t\t\te.printStackTrace();\n+\t\t}\n+\t}\n+\n+\tpublic InputStream getContents() throws CoreException {\n+\t\ttry {\n+\t\t\tif (entry == null) {\n+\t\t\t\treturn ((IFile) resource).getContents();\n+\t\t\t} else {\n+\t\t\t\tObjectId id = entry.getId();\n+\t\t\t\tObjectLoader reader = entry.getRepository().openBlob(id);\n+\t\t\t\tbyte[] bytes = reader.getBytes();\n+\t\t\t\treturn new ByteArrayInputStream(bytes);\n+\t\t\t}\n+\t\t} catch (FileNotFoundException e) {\n+\t\t\tthrow new ResourceException(IResourceStatus.FAILED_READ_LOCAL,\n+\t\t\t\t\tresource.getFullPath(), \"Could not read file\", e);\n+\t\t} catch (IOException e) {\n+\t\t\t// TODO Auto-generated catch block\n+\t\t\te.printStackTrace();\n+\t\t\tthrow new ResourceException(33, resource.getFullPath(), e\n+\t\t\t\t\t.getMessage(), e);\n+\t\t}\n+\t}\n+\n+\tpublic IPath getFullPath() {\n+\t\treturn resource.getFullPath();\n+\t}\n+\n+\tpublic String getName() {\n+\t\treturn resource.getName();\n+\t}\n+\n+\tpublic boolean isReadOnly() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic Object getAdapter(Class adapter) {\n+\t\tSystem.out.println(\"GitStorage(\"+adapter.getName()+\")\");\n+\t\treturn null;\n+\t}\n+\n+}\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/GitTreeStorage.java b/org.spearce.egit.core/src/org/spearce/egit/core/GitTreeStorage.java\nnew file mode 100644\nindex 0000000..cd27363\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/GitTreeStorage.java\n@@ -0,0 +1,484 @@\n+/*\n+ *  Copyright (C) 2006  Robin Rosenberg\n+ *\n+ *  This library is free software; you can redistribute it and/or\n+ *  modify it under the terms of the GNU Lesser General Public\n+ *  License, version 2.1, as published by the Free Software Foundation.\n+ *\n+ *  This library is distributed in the hope that it will be useful,\n+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of\n+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n+ *  Lesser General Public License for more details.\n+ *\n+ *  You should have received a copy of the GNU Lesser General Public\n+ *  License along with this library; if not, write to the Free Software\n+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301\n+ */\n+package org.spearce.egit.core;\n+\n+import java.io.IOException;\n+import java.net.URI;\n+\n+import org.eclipse.core.resources.IContainer;\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IFolder;\n+import org.eclipse.core.resources.IMarker;\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.IResourceProxy;\n+import org.eclipse.core.resources.IResourceProxyVisitor;\n+import org.eclipse.core.resources.IResourceVisitor;\n+import org.eclipse.core.resources.IWorkspace;\n+import org.eclipse.core.resources.ResourceAttributes;\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IPath;\n+import org.eclipse.core.runtime.IProgressMonitor;\n+import org.eclipse.core.runtime.QualifiedName;\n+import org.eclipse.core.runtime.jobs.ISchedulingRule;\n+import org.eclipse.team.core.RepositoryProvider;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.Tree;\n+\n+public class GitTreeStorage implements IContainer {\n+\n+\tprivate Tree entry;\n+\n+\tpublic GitTreeStorage(ObjectId treeId, IResource resource, String relativeResourceName) {\n+\t\tif (treeId == null)\n+\t\t\treturn;\n+\n+\t\tGitProvider provider = (GitProvider) RepositoryProvider\n+\t\t\t\t.getProvider(resource.getProject());\n+\t\tRepositoryMapping repositoryMapping = provider.getData()\n+\t\t\t\t.getRepositoryMapping(resource.getProject());\n+\t\tTree tree;\n+\t\ttry {\n+\t\t\ttree = repositoryMapping.getRepository().mapTree(treeId);\n+\t\t\tString prefix = repositoryMapping.getSubset();\n+\t\t\tif (prefix != null)\n+\t\t\t\tprefix = prefix + \"/\";\n+\t\t\telse\n+\t\t\t\tprefix = \"\";\n+\t\t\tString name = prefix + resource.getProjectRelativePath().toString();\n+\t\t\tentry = (Tree)tree.findMember(name);\n+\t\t} catch (IOException e) {\n+\t\t\t// TODO Auto-generated catch block\n+\t\t\te.printStackTrace();\n+\t\t}\n+\t}\n+\n+\tpublic boolean exists(IPath path) {\n+\t\ttry {\n+\t\t\treturn entry.findMember(path.toString()) != null;\n+\t\t} catch (IOException e) {\n+\t\t\t// TODO Auto-generated catch block\n+\t\t\te.printStackTrace();\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\tpublic IFile[] findDeletedMembersWithHistory(int depth,\n+\t\t\tIProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResource findMember(String name) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResource findMember(IPath path) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResource findMember(String name, boolean includePhantoms) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResource findMember(IPath path, boolean includePhantoms) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic String getDefaultCharset() throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic String getDefaultCharset(boolean checkImplicit) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IFile getFile(IPath path) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IFolder getFolder(IPath path) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResource[] members() throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResource[] members(boolean includePhantoms) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResource[] members(int memberFlags) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic void setDefaultCharset(String charset) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void setDefaultCharset(String charset, IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void accept(IResourceVisitor visitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void accept(IResourceProxyVisitor visitor, int memberFlags)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void accept(IResourceVisitor visitor, int depth,\n+\t\t\tboolean includePhantoms) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void accept(IResourceVisitor visitor, int depth, int memberFlags)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void clearHistory(IProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void copy(IPath destination, boolean force, IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void copy(IPath destination, int updateFlags,\n+\t\t\tIProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void copy(IProjectDescription description, boolean force,\n+\t\t\tIProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void copy(IProjectDescription description, int updateFlags,\n+\t\t\tIProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic IMarker createMarker(String type) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResourceProxy createProxy() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic void delete(boolean force, IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void delete(int updateFlags, IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void deleteMarkers(String type, boolean includeSubtypes, int depth)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic boolean exists() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic IMarker findMarker(long id) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IMarker[] findMarkers(String type, boolean includeSubtypes, int depth)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic String getFileExtension() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IPath getFullPath() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic long getLocalTimeStamp() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn 0;\n+\t}\n+\n+\tpublic IPath getLocation() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic URI getLocationURI() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IMarker getMarker(long id) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic long getModificationStamp() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn 0;\n+\t}\n+\n+\tpublic String getName() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IContainer getParent() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic String getPersistentProperty(QualifiedName key) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IProject getProject() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IPath getProjectRelativePath() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic IPath getRawLocation() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic URI getRawLocationURI() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic ResourceAttributes getResourceAttributes() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic Object getSessionProperty(QualifiedName key) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic int getType() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn 0;\n+\t}\n+\n+\tpublic IWorkspace getWorkspace() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic boolean isAccessible() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isDerived() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isLinked() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isLinked(int options) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isLocal(int depth) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isPhantom() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isReadOnly() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isSynchronized(int depth) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isTeamPrivateMember() {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic void move(IPath destination, boolean force, IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void move(IPath destination, int updateFlags,\n+\t\t\tIProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void move(IProjectDescription description, int updateFlags,\n+\t\t\tIProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void move(IProjectDescription description, boolean force,\n+\t\t\tboolean keepHistory, IProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void refreshLocal(int depth, IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void revertModificationStamp(long value) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void setDerived(boolean isDerived) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void setLocal(boolean flag, int depth, IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic long setLocalTimeStamp(long value) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn 0;\n+\t}\n+\n+\tpublic void setPersistentProperty(QualifiedName key, String value)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void setReadOnly(boolean readOnly) {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void setResourceAttributes(ResourceAttributes attributes)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void setSessionProperty(QualifiedName key, Object value)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void setTeamPrivateMember(boolean isTeamPrivate)\n+\t\t\tthrows CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic void touch(IProgressMonitor monitor) throws CoreException {\n+\t\t// TODO Auto-generated method stub\n+\t\t\n+\t}\n+\n+\tpublic Object getAdapter(Class adapter) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn null;\n+\t}\n+\n+\tpublic boolean contains(ISchedulingRule rule) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isConflicting(ISchedulingRule rule) {\n+\t\t// TODO Auto-generated method stub\n+\t\treturn false;\n+\t}\n+}\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileHistory.java b/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileHistory.java\nindex 78a2e68..51109fb 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileHistory.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileHistory.java\n@@ -28,6 +28,7 @@ import java.util.List;\n \n import org.eclipse.core.resources.IResource;\n import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IAdaptable;\n import org.eclipse.team.core.RepositoryProvider;\n import org.eclipse.team.core.history.IFileHistoryProvider;\n import org.eclipse.team.core.history.IFileRevision;\n@@ -42,7 +43,7 @@ import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.Tree;\n import org.spearce.jgit.lib.TreeEntry;\n \n-public class GitFileHistory extends FileHistory {\n+public class GitFileHistory extends FileHistory implements IAdaptable {\n \n \tprivate final IResource resource;\n \n@@ -341,4 +342,9 @@ public class GitFileHistory extends FileHistory {\n \t\treturn (IFileRevision[]) ret.toArray(new IFileRevision[ret.size()]);\n \t}\n \n+\tpublic Object getAdapter(Class adapter) {\n+\t\tSystem.out.println(\"GitFileHistory.getAdapter \"+adapter.getName());\n+\t\treturn null;\n+\t}\n+\n }\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileRevision.java b/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileRevision.java\nindex ad15cc0..7b726f7 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileRevision.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitFileRevision.java\n@@ -33,10 +33,13 @@ import org.eclipse.team.core.history.provider.FileRevision;\n import org.spearce.egit.core.GitProvider;\n import org.spearce.egit.core.GitStorage;\n import org.spearce.egit.core.GitTag;\n+import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.jgit.lib.Commit;\n import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.PersonIdent;\n import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.Tree;\n+import org.spearce.jgit.lib.TreeEntry;\n \n public class GitFileRevision extends FileRevision {\n \n@@ -136,4 +139,26 @@ public class GitFileRevision extends FileRevision {\n \tpublic int getCount() {\n \t\treturn count;\n \t}\n+\n+\tpublic TreeEntry getTreeEntry() {\n+\t\tGitProvider provider = (GitProvider) RepositoryProvider\n+\t\t\t\t.getProvider(resource.getProject());\n+\t\tRepositoryMapping repositoryMapping = provider.getData()\n+\t\t\t\t.getRepositoryMapping(resource.getProject());\n+\t\tTree tree;\n+\t\ttry {\n+\t\t\ttree = repositoryMapping.getRepository().mapTree(getCommit().getTreeId());\n+\t\t\tString prefix = repositoryMapping.getSubset();\n+\t\t\tif (prefix != null) {\n+\t\t\t\tprefix = prefix + \"/\";\n+\t\t\t\tString name = prefix + resource.getProjectRelativePath().toString();\n+\t\t\t\treturn tree.findMember(name);\n+\t\t\t} else\n+\t\t\t\treturn tree;\n+\t\t} catch (IOException e) {\n+\t\t\t// TODO Auto-generated catch block\n+\t\t\te.printStackTrace();\n+\t\t}\n+\t\treturn null;\n+\t}\n }\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java b/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java\nnew file mode 100644\nindex 0000000..168d736\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java\n@@ -0,0 +1,97 @@\n+package org.spearce.egit.core.internal.mapping;\n+\n+import java.io.ByteArrayInputStream;\n+import java.io.IOException;\n+import java.io.InputStream;\n+\n+import org.eclipse.compare.BufferedContent;\n+import org.eclipse.compare.CompareUI;\n+import org.eclipse.compare.ITypedElement;\n+import org.eclipse.compare.structuremergeviewer.IStructureComparator;\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.swt.graphics.Image;\n+import org.eclipse.team.core.history.IFileRevision;\n+import org.spearce.jgit.lib.FileTreeEntry;\n+import org.spearce.jgit.lib.ObjectId;\n+import org.spearce.jgit.lib.ObjectLoader;\n+import org.spearce.jgit.lib.Tree;\n+import org.spearce.jgit.lib.TreeEntry;\n+\n+public class GitResourceNode extends BufferedContent implements IStructureComparator, ITypedElement {\n+\tTreeEntry entry;\n+\tGitResourceNode[] children;\n+\n+\tpublic GitResourceNode(TreeEntry e) {\n+\t\tentry = e;\n+\t}\n+\n+\tpublic GitResourceNode(IFileRevision file) {\n+\t\tthis(((GitFileRevision)file).getTreeEntry());\n+\t}\n+\n+\tpublic Object[] getChildren() {\n+\t\tif (children != null)\n+\t\t\treturn children;\n+\t\tif (entry instanceof Tree) {\n+\t\t\ttry {\n+\t\t\t\tTree t = (Tree)entry;\n+\t\t\t\tchildren = new GitResourceNode[t.memberCount()];\n+\t\t\t\tfor (int i=0; i<children.length; ++i) {\n+\t\t\t\t\tchildren[i] = new GitResourceNode(t.members()[i]);\n+\t\t\t\t}\n+\t\t\t} catch (IOException e) {\n+\t\t\t\t// TODO: eclipse error handling\n+\t\t\t\te.printStackTrace();\n+\t\t\t\tchildren = new GitResourceNode[0];\n+\t\t\t}\n+\t\t}\n+\t\treturn children;\n+\t}\n+\t\n+\tpublic boolean equals(Object obj) {\n+\t\treturn entry.getId().equals(((GitResourceNode)obj).entry.getId());\n+\t}\n+\n+\tprotected InputStream createStream() throws CoreException {\n+\t\tif (entry instanceof FileTreeEntry) {\n+\t\t\ttry {\n+\t\t\t\tObjectId id = entry.getId();\n+\t\t\t\tObjectLoader reader = entry.getRepository().openBlob(id);\n+\t\t\t\tbyte[] bytes = reader.getBytes();\n+\t\t\t\treturn new ByteArrayInputStream(bytes);\n+\t\t\t} catch (IOException e) {\n+\t\t\t\t// TODO: eclipse error handling\n+\t\t\t\te.printStackTrace();\n+\t\t\t\treturn null;\n+\t\t\t}\n+\t\t}\n+\t\treturn null;\n+\t}\n+\t\n+\tpublic String getName() {\n+\t\treturn entry.getFullName();\n+\t}\n+\n+\tpublic Image getImage() {\n+\t\treturn CompareUI.getImage(getType());\n+\t}\n+\t\n+\tpublic String getType() {\n+\t\tif (entry instanceof Tree)\n+\t\t\treturn ITypedElement.FOLDER_TYPE;\n+\t\telse {\n+\t\t\tString name = entry.getName();\n+\t\t\tif (name != null) {\n+\t\t\t\tint index = name.lastIndexOf('.');\n+\t\t\t\tif (index == -1)\n+\t\t\t\t\treturn \"\"; //$NON-NLS-1$\n+\t\t\t\tif (index == (name.length() - 1))\n+\t\t\t\t\treturn \"\"; //$NON-NLS-1$\n+\t\t\t\treturn name.substring(index + 1);\n+\t\t\t}\n+\t\t\treturn \"\";\n+\t\t}\n+\t}\n+\n+}\n+\ndiff --git a/org.spearce.egit.ui/plugin.xml b/org.spearce.egit.ui/plugin.xml\nindex d81a0b6..3febce8 100644\n--- a/org.spearce.egit.ui/plugin.xml\n+++ b/org.spearce.egit.ui/plugin.xml\n@@ -56,12 +56,6 @@\n                label=\"%ShowResourceInHistoryAction_label\"\n                menubarPath=\"team.main/group1\"\n                tooltip=\"%ShowResourceInHistoryAction_tooltip\"/>\n-\t\t<action\n-\t\t       class=\"org.spearce.egit.ui.internal.actions.GitCompareRevisionAction\"\n-        \t   id=\"org.spearce.egit.ui.internal.actions.GitCompareAction\"\n-               menubarPath=\"team.main/group1\"\n-        \t   label=\"Structured Git Compare\">\n-\t\t</action>\n  \t  </objectContribution>\n       <objectContribution\n             objectClass=\"org.eclipse.core.resources.IFile\"\n@@ -70,16 +64,6 @@\n          <filter name=\"projectPersistentProperty\"\n                value=\"org.eclipse.team.core.repository=org.spearce.egit.core.GitProvider\">\n          </filter>\n-         <action\n-               class=\"org.spearce.egit.ui.internal.actions.CompareWithRevisionAction\"\n-               definitionId=\"org.spearce.egit.ui.compareWithRevision\"\n-               helpContextId=\"org.spearce.egit.ui.compare_with_revision_action_context\"\n-               id=\"org.spearce.egit.ui.compareWithRevision\"\n-               label=\"%CompareWithRevisionAction_label\"\n-               menubarPath=\"compareWithMenu/compareWithGroup\"\n-               overrideActionId=\"org.eclipse.team.ui.compareLocalHistory\"\n-               tooltip=\"%CompareWithRevisionAction_tooltip\">\n-         </action>\n       </objectContribution>\n    </extension>\n \ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java\nindex 54635f5..3057344 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java\n@@ -63,6 +63,7 @@ import org.eclipse.team.ui.history.HistoryPage;\n import org.eclipse.team.ui.history.IHistoryCompareAdapter;\n import org.eclipse.team.ui.history.IHistoryPageSite;\n import org.spearce.egit.core.internal.mapping.GitFileRevision;\n+import org.spearce.egit.ui.internal.actions.GitCompareRevisionAction;\n \n public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\tIHistoryCompareAdapter {\n@@ -104,6 +105,8 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \n \t\tfinal CompareRevisionAction compareAction = new CompareRevisionAction(\n \t\t\t\t\"Compare\");\n+\t\tfinal GitCompareRevisionAction compareActionNG = new GitCompareRevisionAction(\n+\t\t\t\t\"Compare NG\");\n \t\ttree.addSelectionListener(new SelectionAdapter() {\n \t\t\tpublic void widgetSelected(SelectionEvent e) {\n \t\t\t\t// update the current\n@@ -116,16 +119,20 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,\n \t\t\t\tcompareAction.setCurrentFileRevision(fileRevisions[0]);\n \t\t\t\tcompareAction.selectionChanged(new StructuredSelection(\n \t\t\t\t\t\tselection2));\n-\t\t\t\t\n-\t\t\t\tcompareAction.setEnabled(selection!=null && selection[0]!=null  && ((GitFileRevision)fileRevisions[0]).getResource().getType() == IResource.FILE);\n+\n+\t\t\t\tcompareActionNG.setCurrentFileRevision(fileRevisions[0]);\n+\t\t\t\tcompareActionNG.selectionChanged(new StructuredSelection(\n+\t\t\t\t\t\tselection2));\n \t\t\t}\n \t\t});\n \t\tcompareAction.setPage(this);\n+\t\tcompareActionNG.setPage(this);\n \t\tMenuManager menuMgr = new MenuManager();\n \t\tMenu menu = menuMgr.createContextMenu(tree);\n \t\tmenuMgr.addMenuListener(new IMenuListener() {\n \t\t\tpublic void menuAboutToShow(IMenuManager menuMgr) {\n \t\t\t\tmenuMgr.add(compareAction);\n+\t\t\t\tmenuMgr.add(compareActionNG);\n \t\t\t}\n \t\t});\n \t\tmenuMgr.setRemoveAllWhenShown(true);\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitCompareFileRevisionEditorInput.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitCompareFileRevisionEditorInput.java\nnew file mode 100644\nindex 0000000..9c8bd4f\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitCompareFileRevisionEditorInput.java\n@@ -0,0 +1,305 @@\n+/*******************************************************************************\n+ * Copyright (c) 2006 IBM Corporation and others.\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+ * which accompanies this distribution, and is available at\n+ * http://www.eclipse.org/legal/epl-v10.html\n+ * \n+ * Contributors:\n+ *     IBM Corporation - initial API and implementation\n+ *     Robin Rosenberg - Git interface\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal;\n+\n+import java.lang.reflect.InvocationTargetException;\n+\n+import org.eclipse.compare.CompareConfiguration;\n+import org.eclipse.compare.CompareEditorInput;\n+import org.eclipse.compare.IEditableContent;\n+import org.eclipse.compare.IResourceProvider;\n+import org.eclipse.compare.ITypedElement;\n+import org.eclipse.compare.structuremergeviewer.DiffNode;\n+import org.eclipse.compare.structuremergeviewer.Differencer;\n+import org.eclipse.compare.structuremergeviewer.ICompareInput;\n+import org.eclipse.compare.structuremergeviewer.IStructureComparator;\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IFileState;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.resources.IStorage;\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IProgressMonitor;\n+import org.eclipse.core.runtime.NullProgressMonitor;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.team.internal.core.history.LocalFileRevision;\n+import org.eclipse.team.internal.ui.TeamUIMessages;\n+import org.eclipse.team.internal.ui.TeamUIPlugin;\n+import org.eclipse.team.internal.ui.Utils;\n+import org.eclipse.team.internal.ui.history.FileRevisionTypedElement;\n+import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement;\n+import org.eclipse.ui.IWorkbenchPage;\n+\n+public class GitCompareFileRevisionEditorInput extends CompareEditorInput {\n+\t\n+\tprivate ITypedElement left;\n+\tprivate ITypedElement right;\n+\t\n+\t/**\n+\t * Creates a new CompareFileRevisionEditorInput.\n+\t * @param left \n+\t * @param right \n+\t * @param page \n+\t */\n+\tpublic GitCompareFileRevisionEditorInput(ITypedElement left, ITypedElement right, IWorkbenchPage page) {\n+\t\tsuper(new CompareConfiguration());\n+\t\tthis.left = left;\n+\t\tthis.right = right;\n+\t}\n+\n+\tprotected FileRevisionTypedElement getRightRevision() {\n+\t\tif (right instanceof FileRevisionTypedElement) {\n+\t\t\treturn (FileRevisionTypedElement) right;\n+\t\t}\n+\t\treturn null;\n+\t}\n+\n+\tprotected FileRevisionTypedElement getLeftRevision() {\n+\t\tif (left instanceof FileRevisionTypedElement) {\n+\t\t\treturn (FileRevisionTypedElement) left;\n+\t\t}\n+\t\treturn null;\n+\t}\n+\n+\tprivate static void ensureContentsCached(FileRevisionTypedElement left, FileRevisionTypedElement right,\n+\t\t\tIProgressMonitor monitor) {\n+\t\tif (left != null) {\n+\t\t\ttry {\n+\t\t\t\tleft.cacheContents(monitor);\n+\t\t\t} catch (CoreException e) {\n+\t\t\t\tTeamUIPlugin.log(e);\n+\t\t\t}\n+\t\t}\n+\t\tif (right != null) {\n+\t\t\ttry {\n+\t\t\t\tright.cacheContents(monitor);\n+\t\t\t} catch (CoreException e) {\n+\t\t\t\tTeamUIPlugin.log(e);\n+\t\t\t}\n+\t\t}\n+\t}\n+\t\n+\tprivate boolean isLeftEditable(ICompareInput input) {\n+\t\tObject left = input.getLeft();\n+\t\tif (left instanceof IEditableContent) {\n+\t\t\treturn ((IEditableContent) left).isEditable();\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\tprivate IResource getResource(ICompareInput input) {\n+\t\tif (getLocalElement() != null) {\n+\t\t\treturn ((IResourceProvider) getLocalElement()).getResource();\n+\t\t}\n+\t\treturn null;\n+\t}\n+\n+\tprivate ICompareInput createCompareInput() {\n+\t\treturn compare(left, right);\n+\t}\n+\t\n+\tprivate DiffNode compare(ITypedElement left, ITypedElement right) {\n+\t\tif (left.getType().equals(ITypedElement.FOLDER_TYPE)) {\n+\t\t\t//\t\t\treturn new MyDiffContainer(null, left,right);\n+\t\t\tDiffNode diffNode = new DiffNode(null,Differencer.CHANGE,null,left,right);\n+\t\t\tITypedElement[] lc = (ITypedElement[])((IStructureComparator)left).getChildren();\n+\t\t\tITypedElement[] rc = (ITypedElement[])((IStructureComparator)right).getChildren();\n+\t\t\tint li=0;\n+\t\t\tint ri=0;\n+\t\t\twhile (li<lc.length && ri<rc.length) {\n+\t\t\t\tITypedElement ln = lc[li];\n+\t\t\t\tITypedElement rn = rc[ri];\n+\t\t\t\t// TODO: Git ordering!\n+\t\t\t\tif (ln.getName().compareTo(rn.getName()) < 0) {\n+\t\t\t\t\tdiffNode.add(new DiffNode(Differencer.ADDITION,null, ln, null));\n+\t\t\t\t\t++li;\n+\t\t\t\t}\n+\t\t\t\tif (ln.getName().compareTo(rn.getName()) > 0) {\n+\t\t\t\t\tdiffNode.add(new DiffNode(Differencer.DELETION,null, null, rn));\n+\t\t\t\t\t++ri;\n+\t\t\t\t}\n+\t\t\t\tif (ln.getName().compareTo(rn.getName()) == 0) {\n+\t\t\t\t\tif (!ln.equals(rn))\n+\t\t\t\t\t\tdiffNode.add(compare(ln,rn));\n+\t\t\t\t\t++li;\n+\t\t\t\t\t++ri;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\twhile (li<lc.length) {\n+\t\t\t\tITypedElement ln = lc[li];\n+\t\t\t\tdiffNode.add(new DiffNode(Differencer.ADDITION,null, ln, null));\n+\t\t\t\t++li;\n+\t\t\t}\n+\t\t\twhile (ri<rc.length) {\n+\t\t\t\tITypedElement rn = rc[ri];\n+\t\t\t\tdiffNode.add(new DiffNode(Differencer.ADDITION,null, null, rn));\n+\t\t\t\t++ri;\n+\t\t\t}\n+\t\t\treturn diffNode;\n+\t\t} else {\n+\t\t\treturn new DiffNode(left, right);\n+\t\t}\n+\t}\n+\n+\tprivate void initLabels(ICompareInput input) {\n+\t\tCompareConfiguration cc = getCompareConfiguration();\n+\t\tif (getLeftRevision() != null) {\n+\t\t\tString leftLabel = getFileRevisionLabel(getLeftRevision());\n+\t\t\tcc.setLeftLabel(leftLabel);\n+\t\t} else if (getResource(input) != null) {\n+\t\t\tString label = NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_workspace, new Object[]{ input.getLeft().getName() });\n+\t\t\tcc.setLeftLabel(label);\n+\t\t}\n+\t\tif (getRightRevision() != null) {\n+\t\t\tString rightLabel = getFileRevisionLabel(getRightRevision());\n+\t\t\tcc.setRightLabel(rightLabel);\n+\t\t}\n+\t}\n+\n+\tprivate String getFileRevisionLabel(FileRevisionTypedElement element) {\n+\t\tObject fileObject = element.getFileRevision();\n+\t\tif (fileObject instanceof LocalFileRevision){\n+\t\t\treturn NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_localRevision, new Object[]{element.getName(), element.getTimestamp()});\n+\t\t} else {\n+\t\t\treturn NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_repository, new Object[]{ element.getName(), element.getContentIdentifier()});\n+\t\t}\n+\t}\n+\n+\t/* (non-Javadoc)\n+\t * @see org.eclipse.compare.CompareEditorInput#getToolTipText()\n+\t */\n+\tpublic String getToolTipText() {\n+\t\tObject[] titleObject = new Object[3];\n+\t\ttitleObject[0] = getLongName(left);\n+\t\ttitleObject[1] = getContentIdentifier(getLeftRevision());\n+\t\ttitleObject[2] = getContentIdentifier(getRightRevision());\n+\t\treturn NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_compareResourceAndVersions, titleObject);\t \n+\t}\n+\t\n+\t/* (non-Javadoc)\n+\t * @see org.eclipse.compare.CompareEditorInput#getTitle()\n+\t */\n+\tpublic String getTitle() {\n+\t\tObject[] titleObject = new Object[3];\n+\t\ttitleObject[0] = getShortName(left);\n+\t\ttitleObject[1] = getContentIdentifier(getLeftRevision());\n+\t\ttitleObject[2] = getContentIdentifier(getRightRevision());\n+\t\treturn NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_compareResourceAndVersions, titleObject);\t \n+\t}\n+\t\n+\t/* (non-Javadoc)\n+\t * @see org.eclipse.compare.CompareEditorInput#getAdapter(java.lang.Class)\n+\t */\n+\tpublic Object getAdapter(Class adapter) {\n+\t\tif (adapter == IFile.class || adapter == IResource.class) {\n+\t\t\tif (getLocalElement() != null) {\n+\t\t\t\treturn getLocalElement().getResource();\n+\t\t\t}\n+\t\t\treturn null;\n+\t\t}\n+\t\treturn super.getAdapter(adapter);\n+\t}\n+\t\n+\tprivate String getShortName(ITypedElement element) {\n+\t\tif (element instanceof FileRevisionTypedElement){\n+\t\t\tFileRevisionTypedElement fileRevisionElement = (FileRevisionTypedElement) element;\n+\t\t\treturn fileRevisionElement.getName();\n+\t\t}\n+\t\telse if (element instanceof LocalResourceTypedElement){\n+\t\t\tLocalResourceTypedElement typedContent = (LocalResourceTypedElement) element;\n+\t\t\treturn typedContent.getResource().getName();\n+\t\t}\n+\t\treturn element.getName();\n+\t}\n+\t\n+\tprivate String getLongName(ITypedElement element) {\n+\t\tif (element instanceof FileRevisionTypedElement){\n+\t\t\tFileRevisionTypedElement fileRevisionElement = (FileRevisionTypedElement) element;\n+\t\t\treturn fileRevisionElement.getPath();\n+\t\t}\n+\t\telse if (element instanceof LocalResourceTypedElement){\n+\t\t\tLocalResourceTypedElement typedContent = (LocalResourceTypedElement) element;\n+\t\t\treturn typedContent.getResource().getFullPath().toString();\n+\t\t}\n+\t\treturn element.getName();\n+\t}\n+\t\n+\tprivate String getContentIdentifier(ITypedElement element){\n+\t\tif (element instanceof FileRevisionTypedElement){\n+\t\t\tFileRevisionTypedElement fileRevisionElement = (FileRevisionTypedElement) element;\n+\t\t\tObject fileObject = fileRevisionElement.getFileRevision();\n+\t\t\tif (fileObject instanceof LocalFileRevision){\n+\t\t\t\ttry {\n+\t\t\t\t\tIStorage storage = ((LocalFileRevision) fileObject).getStorage(new NullProgressMonitor());\n+\t\t\t\t\tif (Utils.getAdapter(storage, IFileState.class) != null){\n+\t\t\t\t\t\t//local revision\n+\t\t\t\t\t\treturn TeamUIMessages.CompareFileRevisionEditorInput_0;\n+\t\t\t\t\t} else if (Utils.getAdapter(storage, IFile.class) != null) {\n+\t\t\t\t\t\t//current revision\n+\t\t\t\t\t\treturn TeamUIMessages.CompareFileRevisionEditorInput_1;\n+\t\t\t\t\t}\n+\t\t\t\t} catch (CoreException e) {\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\treturn fileRevisionElement.getContentIdentifier();\n+\t\t\t}\n+\t\t}\n+\t\treturn TeamUIMessages.CompareFileRevisionEditorInput_2;\n+\t}\n+\n+//\t/* (non-Javadoc)\n+//\t * @see org.eclipse.team.ui.synchronize.LocalResourceCompareEditorInput#fireInputChange()\n+//\t */\n+//\tprotected void fireInputChange() {\n+//\t\t((DiffNode)getCompareResult()).fireChange();\n+//\t}\n+//\n+//\t/* (non-Javadoc)\n+//\t * @see org.eclipse.team.ui.synchronize.SaveableCompareEditorInput#contentsCreated()\n+//\t */\n+//\tprotected void contentsCreated() {\n+//\t\tsuper.contentsCreated();\n+//\t\tnotifier.initialize();\n+//\t}\n+//\t\n+//\t/* (non-Javadoc)\n+//\t * @see org.eclipse.team.ui.synchronize.SaveableCompareEditorInput#handleDispose()\n+//\t */\n+//\tprotected void handleDispose() {\n+//\t\tsuper.handleDispose();\n+//\t\tnotifier.dispose();\n+//\t\tif (getLocalElement() != null) {\n+//\t\t\tgetLocalElement().discardBuffer();\n+//\t\t}\n+//\t}\n+//\n+\tpublic LocalResourceTypedElement getLocalElement() {\n+\t\tif (left instanceof LocalResourceTypedElement) {\n+\t\t\treturn (LocalResourceTypedElement) left;\n+\t\t}\n+\t\treturn null;\n+\t}\n+\n+\tpublic ITypedElement getLeft() {\n+\t\treturn left;\n+\t}\n+\n+\tprotected Object prepareInput(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {\n+\t\tICompareInput input = createCompareInput();\n+\t\tgetCompareConfiguration().setLeftEditable(isLeftEditable(input));\n+\t\tgetCompareConfiguration().setRightEditable(false);\n+\t\tensureContentsCached(getLeftRevision(), getRightRevision(), monitor);\n+\t\tinitLabels(input);\n+\t\tsetTitle(NLS.bind(TeamUIMessages.SyncInfoCompareInput_title, new String[] { input.getName() }));\n+\t\treturn input;\n+\t}\n+\n+}\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java\nnew file mode 100644\nindex 0000000..be840f9\n--- /dev/null\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java\n@@ -0,0 +1,228 @@\n+/*******************************************************************************\n+ * Copyright (c) 2006 IBM Corporation and others.\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+ * which accompanies this distribution, and is available at\n+ * http://www.eclipse.org/legal/epl-v10.html\n+ * \n+ * Contributors:\n+ *     IBM Corporation - initial API and implementation\n+ *     Robin Rosenberg - Git interface\n+ *******************************************************************************/\n+package org.spearce.egit.ui.internal.actions;\n+\n+import org.eclipse.compare.CompareEditorInput;\n+import org.eclipse.compare.CompareUI;\n+import org.eclipse.compare.ITypedElement;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.jface.dialogs.MessageDialog;\n+import org.eclipse.jface.viewers.IStructuredSelection;\n+import org.eclipse.osgi.util.NLS;\n+import org.eclipse.team.core.history.IFileRevision;\n+import org.eclipse.team.core.history.provider.FileRevision;\n+import org.eclipse.team.internal.core.history.LocalFileRevision;\n+import org.eclipse.team.internal.ui.TeamUIMessages;\n+import org.eclipse.team.internal.ui.history.AbstractHistoryCategory;\n+import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement;\n+import org.eclipse.team.ui.history.HistoryPage;\n+import org.eclipse.ui.IEditorInput;\n+import org.eclipse.ui.IEditorPart;\n+import org.eclipse.ui.IEditorReference;\n+import org.eclipse.ui.IReusableEditor;\n+import org.eclipse.ui.IWorkbenchPage;\n+import org.eclipse.ui.actions.BaseSelectionListenerAction;\n+import org.spearce.egit.core.GitWorkspaceFileRevision;\n+import org.spearce.egit.core.internal.mapping.GitResourceNode;\n+import org.spearce.egit.ui.internal.GitCompareFileRevisionEditorInput;\n+\n+/**\n+ * Action to invoke a Git based compare on selected revivsions in the history window.\n+ * \n+ * This class i almost a verbatim copy of @{link org.eclipse.team.internal.ui.actions.CompareRevisionAction}\n+ * since it could not be subclassed to invoke the EGit methods.\n+ */\n+public class GitCompareRevisionAction extends BaseSelectionListenerAction {\n+\n+\tHistoryPage page;\n+\tIStructuredSelection selection;\n+\tIFileRevision currentFileRevision;\n+\n+\tpublic GitCompareRevisionAction(String text) {\n+\t\tsuper(text);\n+\t}\n+\n+\tpublic GitCompareRevisionAction() {\n+\t\tthis(TeamUIMessages.LocalHistoryPage_CompareAction);\n+\t}\n+\n+\tpublic void run() {\n+\t\tIStructuredSelection structSel = selection;\n+\t\tObject[] objArray = structSel.toArray();\n+\n+\t\tIFileRevision file1 = null;\n+\t\tIFileRevision file2 = null;\n+\n+\t\tswitch (structSel.size()){\n+\t\t\tcase 1:\n+\t\t\t\tfile1 = getCurrentFileRevision();\n+\t\t\t\tObject tempRevision = objArray[0];\n+\t\t\t\tif (tempRevision instanceof IFileRevision)\n+\t\t\t\t\tfile2 = (IFileRevision) tempRevision;\n+\t\t\t\telse \n+\t\t\t\t\treturn;\n+\t\t\tbreak;\n+\n+\t\t\tcase 2:\n+\t\t\t\tObject tempRevision2 = objArray[0];\n+\t\t\t\tObject tempRevision3 = objArray[1];\n+\t\t\t\t\n+\t\t\t\tif (tempRevision2 instanceof IFileRevision &&\n+\t\t\t\t\ttempRevision3 instanceof IFileRevision){\n+\t\t\t\t\tfile1 = (IFileRevision) objArray[0];\n+\t\t\t\t\tfile2 = (IFileRevision) objArray[1];\n+\t\t\t\t} else \n+\t\t\t\t\treturn;\n+\t\t\tbreak;\n+\t\t}\n+\n+\t\tif (file1 == null || file2 == null ||\n+\t\t   !file1.exists() || !file2.exists()){\n+\t\t\tMessageDialog.openError(page.getSite().getShell(),\n+\t\t\t\t\tTeamUIMessages.OpenRevisionAction_DeletedRevTitle,\n+\t\t\t\t\tTeamUIMessages.CompareRevisionAction_DeleteCompareMessage);\n+\t\t\treturn;\n+\t\t}\n+\t\t\n+\t\tIResource resource = getResource(file2);\n+\t\tif (resource != null) {\n+\t\t\tIFileRevision temp = file1;\n+\t\t\tfile1 = file2;\n+\t\t\tfile2 = temp;\n+\t\t}\n+\t\tITypedElement left;\n+\t\tresource = getResource(file1);\n+\t\tif (resource != null) {\n+\t\t\tleft = new LocalResourceTypedElement(resource);\n+\t\t} else {\n+\t\t\tleft = new GitResourceNode(file1);\n+\t\t}\n+\t\tITypedElement right = new GitResourceNode(file2);\n+\t\t\n+\t    openInCompare(left, right);\n+\t}\n+\n+\tprivate void openInCompare(ITypedElement left, ITypedElement right) {\n+\t\tCompareEditorInput input = createCompareEditorInput(left, right, page.getSite().getPage());\n+\t\tIWorkbenchPage workBenchPage = page.getSite().getPage();\n+\t\tIEditorPart editor = findReusableCompareEditor(workBenchPage);\n+\t\tif (editor != null) {\n+\t\t\tIEditorInput otherInput = editor.getEditorInput();\n+\t\t\tif (otherInput.equals(input)) {\n+\t\t\t\t// simply provide focus to editor\n+\t\t\t\tworkBenchPage.activate(editor);\n+\t\t\t} else {\n+\t\t\t\t// if editor is currently not open on that input either re-use\n+\t\t\t\t// existing\n+\t\t\t\tCompareUI.reuseCompareEditor(input, (IReusableEditor) editor);\n+\t\t\t\tworkBenchPage.activate(editor);\n+\t\t\t}\n+\t\t} else {\n+\t\t\tCompareUI.openCompareEditor(input);\n+\t\t}\n+\t}\n+\n+\tprotected GitCompareFileRevisionEditorInput createCompareEditorInput(\n+\t\t\tITypedElement left, ITypedElement right, IWorkbenchPage page) {\n+\t\treturn new GitCompareFileRevisionEditorInput(left, right, page);\n+\t}\n+\n+\tprivate IResource getResource(IFileRevision revision) {\n+\t\tif (revision instanceof GitWorkspaceFileRevision) {\n+\t\t\tGitWorkspaceFileRevision local = (GitWorkspaceFileRevision) revision;\n+\t\t\treturn local.getResource();\n+\t\t}\n+\t\treturn null;\n+\t}\n+\n+\tprivate IFileRevision getCurrentFileRevision() {\n+\t\treturn currentFileRevision;\n+\t}\n+\n+\tpublic void setCurrentFileRevision(IFileRevision fileRevision){\n+\t\tthis.currentFileRevision = fileRevision;\n+\t}\n+\n+\t/**\n+\t * Returns an editor that can be re-used. An open compare editor that\n+\t * has un-saved changes cannot be re-used.\n+\t * @param page \n+\t * @return an EditorPart or <code>null</code> if none can be found\n+\t */\n+\tpublic static IEditorPart findReusableCompareEditor(IWorkbenchPage page) {\n+\t\tIEditorReference[] editorRefs = page.getEditorReferences();\t\n+\t\tfor (int i = 0; i < editorRefs.length; i++) {\n+\t\t\tIEditorPart part = editorRefs[i].getEditor(false);\n+\t\t\tif(part != null \n+\t\t\t\t\t&& (part.getEditorInput() instanceof GitCompareFileRevisionEditorInput) \n+\t\t\t\t\t&& part instanceof IReusableEditor) {\n+\t\t\t\tif(! part.isDirty()) {\t\n+\t\t\t\t\treturn part;\t\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\treturn null;\n+\t}\n+\t\n+\tprotected boolean updateSelection(IStructuredSelection selection) {\n+\t\tthis.selection = selection;\n+\t\tif (selection.size() == 1){\n+\t\t\tObject el = selection.getFirstElement();\n+\t\t\tif (el instanceof LocalFileRevision)\n+\t\t\t\tthis.setText(TeamUIMessages.CompareRevisionAction_Local);\n+\t\t\telse if (el instanceof FileRevision){\n+\t\t\t\tFileRevision tempFileRevision = (FileRevision) el;\n+\t\t\t\tthis.setText(NLS.bind(TeamUIMessages.CompareRevisionAction_Revision, new String[]{tempFileRevision.getContentIdentifier()}));\n+\t\t\t} \n+\t\t\telse\n+\t\t\t\tthis.setText(TeamUIMessages.CompareRevisionAction_CompareWithCurrent);\n+\t\t\treturn shouldShow();\n+\t\t}\n+\t\telse if (selection.size() == 2){\n+\t\t\tthis.setText(TeamUIMessages.CompareRevisionAction_CompareWithOther+\" NG\");\t\n+\t\t\treturn shouldShow();\n+\t\t}\n+\n+\t\treturn false;\n+\t}\n+\n+\tpublic void setPage(HistoryPage page) {\n+\t\tthis.page = page;\n+\t}\n+\n+\tprivate boolean shouldShow() {\n+\t\tIStructuredSelection structSel = selection;\n+\t\tObject[] objArray = structSel.toArray();\n+\n+\t\tif (objArray.length == 0)\n+\t\t\treturn false;\n+\n+\t\t// Comparing the workspace revision with itself is just stupid\n+\t\tif (objArray.length == 1 && objArray[0] instanceof GitWorkspaceFileRevision)\n+\t\t\treturn false;\n+\n+\t\tfor (int i = 0; i < objArray.length; i++) {\n+\n+\t\t\t// Don't bother showing if this a category\n+\t\t\tif (objArray[i] instanceof AbstractHistoryCategory)\n+\t\t\t\treturn false;\n+\n+\t\t\tIFileRevision revision = (IFileRevision) objArray[i];\n+\t\t\t//check to see if any of the selected revisions are deleted revisions\n+\t\t\tif (revision != null && !revision.exists())\n+\t\t\t\treturn false;\n+\t\t}\n+\n+\t\treturn true;\n+\t}\n+\n+}\n"},{"id":"37441","messageId":"20070319022102.GE20658@spearce.org","threadId":"7300","inReplyTo":"20070318220711.24742.90943.stgit@lathund.dewire.com","subject":"Re: [PATCH 0/4] Eclipse (EGIT) Structured compare","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2007-03-19T02:21:02Z","receivedAt":"2007-03-19T02:21:02Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Robin Rosenberg <robin.rosenberg@dewire.com> wrote:\n> Now Eclipse can compare two git version at the project level. Thanks\n> to the SHA-1's the compare is very quick. There is some extra cost\n> for version managed archives (zip/jars) as Eclipse insists on comparing\n> the contents of those at the file level too. Not that I mind that. It\n> is useful and cool. (No, it doesn't compare open office documents in\n> any useful manner, unless you happen to have a plugin for it, in\n> which case it might actually work.)\n> \n> Along with this some experiments with caching and a minor bug fix in\n> the commit reader.\n\nThanks for this series.  I have applied it and pushed it out, along\nwith a small fix on top.\n\nIt works nicely, at least the \"NG\" variant when comparing trees.\n;-)\n\nThe non-NG variant shows the raw tree diff, which is uh, a little\nuninteresting, unless you are a true core Git hacker...\n\nI tested it on a small-ish repository. The history browser works\nquite nicely once the cache starts to get filled.  I don't know\nhow well it works on bigger repositories.  ;-)\n\n--'da small fix--\nFrom 6c402714c5a1850b871877cefe465a6a40ccdd0f Mon Sep 17 00:00:00 2001\nFrom: Shawn O. Pearce <spearce@spearce.org>\nDate: Sun, 18 Mar 2007 22:15:14 -0400\nSubject: [PATCH] Move GitResourceNode to ui plugin\n\nBecause I am trying to keep the core plugin UI free, allowing it\nto be used in headless workbenches, such as from an Eclipse based\nbuild system, we don't want to depend on the org.eclipse.swt plugin.\nRelocating GitResourceNode to the UI plugin lets us continue to avoid\nthat dependency.\n\nSigned-off-by: Shawn O. Pearce <spearce@spearce.org>\n---\n org.spearce.egit.core/META-INF/MANIFEST.MF         |    4 +---\n .../spearce/egit/ui/internal}/GitResourceNode.java |    3 ++-\n .../internal/actions/GitCompareRevisionAction.java |    2 +-\n 3 files changed, 4 insertions(+), 5 deletions(-)\n rename {org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping => org.spearce.egit.ui/src/org/spearce/egit/ui/internal}/GitResourceNode.java (96%)\n\ndiff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF\nindex 8dc62d6..fd39ec6 100644\n--- a/org.spearce.egit.core/META-INF/MANIFEST.MF\n+++ b/org.spearce.egit.core/META-INF/MANIFEST.MF\n@@ -10,9 +10,7 @@ Require-Bundle: org.eclipse.core.runtime,\n  org.eclipse.team.core,\n  org.eclipse.core.resources,\n  org.spearce.jgit,\n- org.eclipse.core.filesystem,\n- org.eclipse.compare,\n- org.eclipse.swt\n+ org.eclipse.core.filesystem\n Export-Package: org.spearce.egit.core.internal.mapping;x-friends:=\"org.spearce.egit.ui\",\n  org.spearce.egit.core,\n  org.spearce.egit.core.internal.mapping,\ndiff --git a/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitResourceNode.java\nsimilarity index 96%\nrename from org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java\nrename to org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitResourceNode.java\nindex 0bba684..bdf8902 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitResourceNode.java\n@@ -1,4 +1,4 @@\n-package org.spearce.egit.core.internal.mapping;\n+package org.spearce.egit.ui.internal;\n \n import java.io.ByteArrayInputStream;\n import java.io.IOException;\n@@ -11,6 +11,7 @@ import org.eclipse.compare.structuremergeviewer.IStructureComparator;\n import org.eclipse.core.runtime.CoreException;\n import org.eclipse.swt.graphics.Image;\n import org.eclipse.team.core.history.IFileRevision;\n+import org.spearce.egit.core.internal.mapping.GitFileRevision;\n import org.spearce.jgit.lib.FileTreeEntry;\n import org.spearce.jgit.lib.ObjectId;\n import org.spearce.jgit.lib.ObjectLoader;\ndiff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java\nindex 84eb506..9a0b378 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java\n@@ -32,8 +32,8 @@ import org.eclipse.ui.IReusableEditor;\n import org.eclipse.ui.IWorkbenchPage;\n import org.eclipse.ui.actions.BaseSelectionListenerAction;\n import org.spearce.egit.core.GitWorkspaceFileRevision;\n-import org.spearce.egit.core.internal.mapping.GitResourceNode;\n import org.spearce.egit.ui.internal.GitCompareFileRevisionEditorInput;\n+import org.spearce.egit.ui.internal.GitResourceNode;\n \n /**\n  * Action to invoke a Git based compare on selected revivsions in the history window.\n-- \n1.5.0.4.1023.gb3ae\n\n-- \nShawn.\n"}]}