Git Mailing List Archive mirror
 help / color / mirror / Atom feed
* [PATCH 0/4] Eclipse (EGIT) Structured compare
@ 2007-03-18 22:15 Robin Rosenberg
  2007-03-18 22:15 ` [PATCH 1/4] Cache tree ObjectId's too Robin Rosenberg
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Robin Rosenberg @ 2007-03-18 22:15 UTC (permalink / raw
  To: git; +Cc: spearce

Now Eclipse can compare two git version at the project level. Thanks
to the SHA-1's the compare is very quick. There is some extra cost
for version managed archives (zip/jars) as Eclipse insists on comparing
the contents of those at the file level too. Not that I mind that. It
is useful and cool. (No, it doesn't compare open office documents in
any useful manner, unless you happen to have a plugin for it, in
which case it might actually work.)

Along with this some experiments with caching and a minor bug fix in
the commit reader.

If anyone besides me actually uses this, please send a comment.

-- robin

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH 1/4] Cache tree ObjectId's too
  2007-03-18 22:15 [PATCH 0/4] Eclipse (EGIT) Structured compare Robin Rosenberg
@ 2007-03-18 22:15 ` Robin Rosenberg
  2007-03-18 22:16 ` [PATCH 2/4] Rework tree/commit cache Robin Rosenberg
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Robin Rosenberg @ 2007-03-18 22:15 UTC (permalink / raw
  To: git; +Cc: spearce

The caching part is experimental. We explicitly leave it up to
the garbage collector when to recycle git objects. After trying
this practically we will se what is best. The upside is that
rescanning the history for a certain resource is very quick when
data is still in the cache (tens of milliseconds), which is very
good for interactivity. When memory is tight we go to disk and
fetch the data, which could take a couple of seconds on a large
repo.

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---

 .../src/org/spearce/jgit/lib/Repository.java       |   18 +++++++++++++-----
 1 files changed, 13 insertions(+), 5 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
index 8b1f42c..d3b2178 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -47,7 +47,8 @@ public class Repository {
 
 	private WindowCache windows;
 
-	private Map cache = new WeakHashMap(30000); 
+	private Map treeCache = new WeakHashMap(30000); 
+	private Map commitCache = new WeakHashMap(30000); 
 
 	public Repository(final File d) throws IOException {
 		gitDir = d.getAbsoluteFile();
@@ -189,7 +190,7 @@ public class Repository {
 	}
 
 	public Commit mapCommit(final ObjectId id) throws IOException {
-		Commit ret = (Commit)cache.get(id);
+		Commit ret = (Commit)commitCache.get(id);
 		if (ret != null)
 			return ret;
 
@@ -201,7 +202,7 @@ public class Repository {
 			ret = new Commit(this, id, raw);
 			// The key must not be the referenced strongly
 			// by the value in WeakHashMaps
-			cache.put(new ObjectId(id.getBytes()), ret);
+			commitCache.put(new ObjectId(id.getBytes()), ret);
 			return ret;
 		}
 		throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
@@ -213,12 +214,19 @@ public class Repository {
 	}
 
 	public Tree mapTree(final ObjectId id) throws IOException {
+		Tree ret = (Tree)treeCache.get(id);
+		if (ret != null)
+			return ret;
+
 		final ObjectLoader or = openObject(id);
 		if (or == null)
 			return null;
 		final byte[] raw = or.getBytes();
-		if (Constants.TYPE_TREE.equals(or.getType()))
-			return new Tree(this, id, raw);
+		if (Constants.TYPE_TREE.equals(or.getType())) {
+			ret = new Tree(this, id, raw);
+			treeCache.put(new ObjectId(id.getBytes()), ret);
+			return ret;
+		}
 		if (Constants.TYPE_COMMIT.equals(or.getType()))
 			return mapTree(ObjectId.fromString(raw, 5));
 		throw new IncorrectObjectTypeException(id, Constants.TYPE_TREE);

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 2/4] Rework tree/commit cache
  2007-03-18 22:15 [PATCH 0/4] Eclipse (EGIT) Structured compare Robin Rosenberg
  2007-03-18 22:15 ` [PATCH 1/4] Cache tree ObjectId's too Robin Rosenberg
@ 2007-03-18 22:16 ` Robin Rosenberg
  2007-03-18 22:16 ` [PATCH 3/4] Decode message when encoding line is present Robin Rosenberg
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Robin Rosenberg @ 2007-03-18 22:16 UTC (permalink / raw
  To: git; +Cc: spearce

We now use SoftRefernces to hold the value objects and reuse
the object id's as cache keys. Seems to work much better, i.e.
the gc only throws out stuff from the cache when memory is
tight, rather than at the first opportunity.

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---

 .../src/org/spearce/jgit/lib/Repository.java       |   30 +++++++++++++++--------
 1 files changed, 20 insertions(+), 10 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
index d3b2178..c7c47ae 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Repository.java
@@ -23,6 +23,8 @@ import java.io.FileNotFoundException;
 import java.io.FileReader;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.lang.ref.Reference;
+import java.lang.ref.SoftReference;
 import java.util.ArrayList;
 import java.util.Collection;
 import java.util.Map;
@@ -190,19 +192,24 @@ public class Repository {
 	}
 
 	public Commit mapCommit(final ObjectId id) throws IOException {
-		Commit ret = (Commit)commitCache.get(id);
-		if (ret != null)
-			return ret;
+//		System.out.println("commitcache.size="+commitCache.size());
+		Reference retr = (Reference)commitCache.get(id);
+		if (retr != null) {
+			Commit ret = (Commit)retr.get();
+			if (ret != null)
+				return ret;
+			System.out.println("Found a null id, size was "+commitCache.size());
+		}
 
 		final ObjectLoader or = openObject(id);
 		if (or == null)
 			return null;
 		final byte[] raw = or.getBytes();
 		if (Constants.TYPE_COMMIT.equals(or.getType())) {
-			ret = new Commit(this, id, raw);
+			Commit ret = new Commit(this, id, raw);
 			// The key must not be the referenced strongly
 			// by the value in WeakHashMaps
-			commitCache.put(new ObjectId(id.getBytes()), ret);
+			commitCache.put(id, new SoftReference(ret));
 			return ret;
 		}
 		throw new IncorrectObjectTypeException(id, Constants.TYPE_COMMIT);
@@ -214,17 +221,20 @@ public class Repository {
 	}
 
 	public Tree mapTree(final ObjectId id) throws IOException {
-		Tree ret = (Tree)treeCache.get(id);
-		if (ret != null)
-			return ret;
+		Reference wret = (Reference)treeCache.get(id);
+		if (wret != null) {
+			Tree ret = (Tree)wret.get();
+			if (ret != null)
+				return ret;
+		}
 
 		final ObjectLoader or = openObject(id);
 		if (or == null)
 			return null;
 		final byte[] raw = or.getBytes();
 		if (Constants.TYPE_TREE.equals(or.getType())) {
-			ret = new Tree(this, id, raw);
-			treeCache.put(new ObjectId(id.getBytes()), ret);
+			Tree ret = new Tree(this, id, raw);
+			treeCache.put(id, new SoftReference(ret));
 			return ret;
 		}
 		if (Constants.TYPE_COMMIT.equals(or.getType()))

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 3/4] Decode message when encoding line is present
  2007-03-18 22:15 [PATCH 0/4] Eclipse (EGIT) Structured compare Robin Rosenberg
  2007-03-18 22:15 ` [PATCH 1/4] Cache tree ObjectId's too Robin Rosenberg
  2007-03-18 22:16 ` [PATCH 2/4] Rework tree/commit cache Robin Rosenberg
@ 2007-03-18 22:16 ` Robin Rosenberg
  2007-03-18 22:16 ` [PATCH 4/4] Add support for structured comparison Robin Rosenberg
  2007-03-19  2:21 ` [PATCH 0/4] Eclipse (EGIT) Structured compare Shawn O. Pearce
  4 siblings, 0 replies; 6+ messages in thread
From: Robin Rosenberg @ 2007-03-18 22:16 UTC (permalink / raw
  To: git; +Cc: spearce

Seems we left the newline between the encoding and the rest
of the message

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---

 .../src/org/spearce/jgit/lib/Commit.java           |    6 ++++--
 1 files changed, 4 insertions(+), 2 deletions(-)

diff --git a/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java b/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java
index 4356149..c5c65c4 100644
--- a/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java
+++ b/org.spearce.jgit/src/org/spearce/jgit/lib/Commit.java
@@ -161,16 +161,18 @@ public class Commit implements Treeish {
 				}
 				byte[] readBuf = new byte[br.available()]; // in-memory stream so this is all bytes left
 				br.read(readBuf);
+				int msgstart = readBuf[0] == '\n' ? 1 : 0;
+					
 				if (encoding != null) {
 					// TODO: this isn't reliable so we need to guess the encoding from the actual content
 					author = new PersonIdent(new String(rawAuthor.getBytes(),encoding.name()));
 					committer = new PersonIdent(new String(rawCommitter.getBytes(),encoding.name()));
-					message = new String(readBuf,encoding.name());
+					message = new String(readBuf,msgstart, readBuf.length-msgstart, encoding.name());
 				} else {
 					// TODO: use config setting / platform / ascii / iso-latin
 					author = new PersonIdent(new String(rawAuthor.getBytes()));
 					committer = new PersonIdent(new String(rawCommitter.getBytes()));
-					message = new String(readBuf);
+					message = new String(readBuf, msgstart, readBuf.length-msgstart);
 				}
 			} catch (IOException e) {
 				e.printStackTrace();

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH 4/4] Add support for structured comparison.
  2007-03-18 22:15 [PATCH 0/4] Eclipse (EGIT) Structured compare Robin Rosenberg
                   ` (2 preceding siblings ...)
  2007-03-18 22:16 ` [PATCH 3/4] Decode message when encoding line is present Robin Rosenberg
@ 2007-03-18 22:16 ` Robin Rosenberg
  2007-03-19  2:21 ` [PATCH 0/4] Eclipse (EGIT) Structured compare Shawn O. Pearce
  4 siblings, 0 replies; 6+ messages in thread
From: Robin Rosenberg @ 2007-03-18 22:16 UTC (permalink / raw
  To: git; +Cc: spearce

With this code we can now compare revisions at project and folder
levels, rather than only file level. We use the compare api's that
eclipse provide to do the display and detailed compare work, while
we do the high level comparisons at the SHA-1 level.

A possible downside is that the API:s we use are internal, which
could make us version dependent. This code works with both 3.2.x and
3.3M5 versions though.

Signed-off-by: Robin Rosenberg <robin.rosenberg@dewire.com>
---

 org.spearce.egit.core/META-INF/MANIFEST.MF         |    6 
 .../src/org/spearce/egit/core/GitBlobStorage.java  |  108 +++++
 .../src/org/spearce/egit/core/GitTreeStorage.java  |  484 +++++++++++++++++++++++
 .../egit/core/internal/mapping/GitFileHistory.java |    8 
 .../core/internal/mapping/GitFileRevision.java     |   25 +
 .../core/internal/mapping/GitResourceNode.java     |   97 +++++
 org.spearce.egit.ui/plugin.xml                     |   16 -
 .../src/org/spearce/egit/ui/GitHistoryPage.java    |   11 -
 .../GitCompareFileRevisionEditorInput.java         |  305 ++++++++++++++
 .../internal/actions/GitCompareRevisionAction.java |  228 +++++++++++
 10 files changed, 1268 insertions(+), 20 deletions(-)

diff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF
index f4c96cc..8dc62d6 100644
--- a/org.spearce.egit.core/META-INF/MANIFEST.MF
+++ b/org.spearce.egit.core/META-INF/MANIFEST.MF
@@ -10,8 +10,12 @@ Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.team.core,
  org.eclipse.core.resources,
  org.spearce.jgit,
- org.eclipse.core.filesystem
+ org.eclipse.core.filesystem,
+ org.eclipse.compare,
+ org.eclipse.swt
 Export-Package: org.spearce.egit.core.internal.mapping;x-friends:="org.spearce.egit.ui",
+ org.spearce.egit.core,
+ org.spearce.egit.core.internal.mapping,
  org.spearce.egit.core.op,
  org.spearce.egit.core.project
 Eclipse-LazyStart: true
diff --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
new file mode 100644
index 0000000..c947e2b
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/GitBlobStorage.java
@@ -0,0 +1,108 @@
+/*
+ *  Copyright (C) 2006  Robin Rosenberg
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License, version 2.1, as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ */
+package org.spearce.egit.core;
+
+import java.io.ByteArrayInputStream;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.core.internal.resources.ResourceException;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceStatus;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.team.core.RepositoryProvider;
+import org.spearce.egit.core.project.RepositoryMapping;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.ObjectLoader;
+import org.spearce.jgit.lib.Tree;
+import org.spearce.jgit.lib.TreeEntry;
+
+public class GitBlobStorage implements IStorage {
+
+	private final IResource resource;
+
+	private TreeEntry entry;
+
+	public GitBlobStorage(ObjectId treeId, IResource resource, String relativeResourceName) {
+		this.resource = resource;
+		if (treeId == null)
+			return;
+
+		GitProvider provider = (GitProvider) RepositoryProvider
+				.getProvider(resource.getProject());
+		RepositoryMapping repositoryMapping = provider.getData()
+				.getRepositoryMapping(resource.getProject());
+		Tree tree;
+		try {
+			tree = repositoryMapping.getRepository().mapTree(treeId);
+			String prefix = repositoryMapping.getSubset();
+			if (prefix != null)
+				prefix = prefix + "/";
+			else
+				prefix = "";
+			String name = prefix + resource.getProjectRelativePath().toString();
+			entry = tree.findMember(name);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public InputStream getContents() throws CoreException {
+		try {
+			if (entry == null) {
+				return ((IFile) resource).getContents();
+			} else {
+				ObjectId id = entry.getId();
+				ObjectLoader reader = entry.getRepository().openBlob(id);
+				byte[] bytes = reader.getBytes();
+				return new ByteArrayInputStream(bytes);
+			}
+		} catch (FileNotFoundException e) {
+			throw new ResourceException(IResourceStatus.FAILED_READ_LOCAL,
+					resource.getFullPath(), "Could not read file", e);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+			throw new ResourceException(33, resource.getFullPath(), e
+					.getMessage(), e);
+		}
+	}
+
+	public IPath getFullPath() {
+		return resource.getFullPath();
+	}
+
+	public String getName() {
+		return resource.getName();
+	}
+
+	public boolean isReadOnly() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public Object getAdapter(Class adapter) {
+		System.out.println("GitStorage("+adapter.getName()+")");
+		return null;
+	}
+
+}
diff --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
new file mode 100644
index 0000000..cd27363
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/GitTreeStorage.java
@@ -0,0 +1,484 @@
+/*
+ *  Copyright (C) 2006  Robin Rosenberg
+ *
+ *  This library is free software; you can redistribute it and/or
+ *  modify it under the terms of the GNU Lesser General Public
+ *  License, version 2.1, as published by the Free Software Foundation.
+ *
+ *  This library is distributed in the hope that it will be useful,
+ *  but WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  Lesser General Public License for more details.
+ *
+ *  You should have received a copy of the GNU Lesser General Public
+ *  License along with this library; if not, write to the Free Software
+ *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301
+ */
+package org.spearce.egit.core;
+
+import java.io.IOException;
+import java.net.URI;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.eclipse.team.core.RepositoryProvider;
+import org.spearce.egit.core.project.RepositoryMapping;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.Tree;
+
+public class GitTreeStorage implements IContainer {
+
+	private Tree entry;
+
+	public GitTreeStorage(ObjectId treeId, IResource resource, String relativeResourceName) {
+		if (treeId == null)
+			return;
+
+		GitProvider provider = (GitProvider) RepositoryProvider
+				.getProvider(resource.getProject());
+		RepositoryMapping repositoryMapping = provider.getData()
+				.getRepositoryMapping(resource.getProject());
+		Tree tree;
+		try {
+			tree = repositoryMapping.getRepository().mapTree(treeId);
+			String prefix = repositoryMapping.getSubset();
+			if (prefix != null)
+				prefix = prefix + "/";
+			else
+				prefix = "";
+			String name = prefix + resource.getProjectRelativePath().toString();
+			entry = (Tree)tree.findMember(name);
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+	}
+
+	public boolean exists(IPath path) {
+		try {
+			return entry.findMember(path.toString()) != null;
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return false;
+	}
+
+	public IFile[] findDeletedMembersWithHistory(int depth,
+			IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResource findMember(String name) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResource findMember(IPath path) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResource findMember(String name, boolean includePhantoms) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResource findMember(IPath path, boolean includePhantoms) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public String getDefaultCharset() throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public String getDefaultCharset(boolean checkImplicit) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IFile getFile(IPath path) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IFolder getFolder(IPath path) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResource[] members() throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResource[] members(boolean includePhantoms) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResource[] members(int memberFlags) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public void setDefaultCharset(String charset) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void setDefaultCharset(String charset, IProgressMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void accept(IResourceVisitor visitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void accept(IResourceProxyVisitor visitor, int memberFlags)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void accept(IResourceVisitor visitor, int depth,
+			boolean includePhantoms) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void accept(IResourceVisitor visitor, int depth, int memberFlags)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void clearHistory(IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void copy(IPath destination, boolean force, IProgressMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void copy(IPath destination, int updateFlags,
+			IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void copy(IProjectDescription description, boolean force,
+			IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void copy(IProjectDescription description, int updateFlags,
+			IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public IMarker createMarker(String type) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IResourceProxy createProxy() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public void delete(boolean force, IProgressMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void delete(int updateFlags, IProgressMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void deleteMarkers(String type, boolean includeSubtypes, int depth)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public boolean exists() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public IMarker findMarker(long id) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IMarker[] findMarkers(String type, boolean includeSubtypes, int depth)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public String getFileExtension() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IPath getFullPath() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public long getLocalTimeStamp() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public IPath getLocation() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public URI getLocationURI() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IMarker getMarker(long id) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public long getModificationStamp() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public String getName() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IContainer getParent() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public String getPersistentProperty(QualifiedName key) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IProject getProject() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IPath getProjectRelativePath() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public IPath getRawLocation() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public URI getRawLocationURI() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public ResourceAttributes getResourceAttributes() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public Object getSessionProperty(QualifiedName key) throws CoreException {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public int getType() {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public IWorkspace getWorkspace() {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public boolean isAccessible() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isDerived() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isLinked() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isLinked(int options) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isLocal(int depth) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isPhantom() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isReadOnly() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isSynchronized(int depth) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isTeamPrivateMember() {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public void move(IPath destination, boolean force, IProgressMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void move(IPath destination, int updateFlags,
+			IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void move(IProjectDescription description, int updateFlags,
+			IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void move(IProjectDescription description, boolean force,
+			boolean keepHistory, IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void refreshLocal(int depth, IProgressMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void revertModificationStamp(long value) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void setDerived(boolean isDerived) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void setLocal(boolean flag, int depth, IProgressMonitor monitor)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public long setLocalTimeStamp(long value) throws CoreException {
+		// TODO Auto-generated method stub
+		return 0;
+	}
+
+	public void setPersistentProperty(QualifiedName key, String value)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void setReadOnly(boolean readOnly) {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void setResourceAttributes(ResourceAttributes attributes)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void setSessionProperty(QualifiedName key, Object value)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void setTeamPrivateMember(boolean isTeamPrivate)
+			throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public void touch(IProgressMonitor monitor) throws CoreException {
+		// TODO Auto-generated method stub
+		
+	}
+
+	public Object getAdapter(Class adapter) {
+		// TODO Auto-generated method stub
+		return null;
+	}
+
+	public boolean contains(ISchedulingRule rule) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+
+	public boolean isConflicting(ISchedulingRule rule) {
+		// TODO Auto-generated method stub
+		return false;
+	}
+}
diff --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
index 78a2e68..51109fb 100644
--- 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
@@ -28,6 +28,7 @@ import java.util.List;
 
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.team.core.RepositoryProvider;
 import org.eclipse.team.core.history.IFileHistoryProvider;
 import org.eclipse.team.core.history.IFileRevision;
@@ -42,7 +43,7 @@ import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.lib.Tree;
 import org.spearce.jgit.lib.TreeEntry;
 
-public class GitFileHistory extends FileHistory {
+public class GitFileHistory extends FileHistory implements IAdaptable {
 
 	private final IResource resource;
 
@@ -341,4 +342,9 @@ public class GitFileHistory extends FileHistory {
 		return (IFileRevision[]) ret.toArray(new IFileRevision[ret.size()]);
 	}
 
+	public Object getAdapter(Class adapter) {
+		System.out.println("GitFileHistory.getAdapter "+adapter.getName());
+		return null;
+	}
+
 }
diff --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
index ad15cc0..7b726f7 100644
--- 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
@@ -33,10 +33,13 @@ import org.eclipse.team.core.history.provider.FileRevision;
 import org.spearce.egit.core.GitProvider;
 import org.spearce.egit.core.GitStorage;
 import org.spearce.egit.core.GitTag;
+import org.spearce.egit.core.project.RepositoryMapping;
 import org.spearce.jgit.lib.Commit;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.PersonIdent;
 import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.Tree;
+import org.spearce.jgit.lib.TreeEntry;
 
 public class GitFileRevision extends FileRevision {
 
@@ -136,4 +139,26 @@ public class GitFileRevision extends FileRevision {
 	public int getCount() {
 		return count;
 	}
+
+	public TreeEntry getTreeEntry() {
+		GitProvider provider = (GitProvider) RepositoryProvider
+				.getProvider(resource.getProject());
+		RepositoryMapping repositoryMapping = provider.getData()
+				.getRepositoryMapping(resource.getProject());
+		Tree tree;
+		try {
+			tree = repositoryMapping.getRepository().mapTree(getCommit().getTreeId());
+			String prefix = repositoryMapping.getSubset();
+			if (prefix != null) {
+				prefix = prefix + "/";
+				String name = prefix + resource.getProjectRelativePath().toString();
+				return tree.findMember(name);
+			} else
+				return tree;
+		} catch (IOException e) {
+			// TODO Auto-generated catch block
+			e.printStackTrace();
+		}
+		return null;
+	}
 }
diff --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
new file mode 100644
index 0000000..168d736
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java
@@ -0,0 +1,97 @@
+package org.spearce.egit.core.internal.mapping;
+
+import java.io.ByteArrayInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+import org.eclipse.compare.BufferedContent;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.structuremergeviewer.IStructureComparator;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.team.core.history.IFileRevision;
+import org.spearce.jgit.lib.FileTreeEntry;
+import org.spearce.jgit.lib.ObjectId;
+import org.spearce.jgit.lib.ObjectLoader;
+import org.spearce.jgit.lib.Tree;
+import org.spearce.jgit.lib.TreeEntry;
+
+public class GitResourceNode extends BufferedContent implements IStructureComparator, ITypedElement {
+	TreeEntry entry;
+	GitResourceNode[] children;
+
+	public GitResourceNode(TreeEntry e) {
+		entry = e;
+	}
+
+	public GitResourceNode(IFileRevision file) {
+		this(((GitFileRevision)file).getTreeEntry());
+	}
+
+	public Object[] getChildren() {
+		if (children != null)
+			return children;
+		if (entry instanceof Tree) {
+			try {
+				Tree t = (Tree)entry;
+				children = new GitResourceNode[t.memberCount()];
+				for (int i=0; i<children.length; ++i) {
+					children[i] = new GitResourceNode(t.members()[i]);
+				}
+			} catch (IOException e) {
+				// TODO: eclipse error handling
+				e.printStackTrace();
+				children = new GitResourceNode[0];
+			}
+		}
+		return children;
+	}
+	
+	public boolean equals(Object obj) {
+		return entry.getId().equals(((GitResourceNode)obj).entry.getId());
+	}
+
+	protected InputStream createStream() throws CoreException {
+		if (entry instanceof FileTreeEntry) {
+			try {
+				ObjectId id = entry.getId();
+				ObjectLoader reader = entry.getRepository().openBlob(id);
+				byte[] bytes = reader.getBytes();
+				return new ByteArrayInputStream(bytes);
+			} catch (IOException e) {
+				// TODO: eclipse error handling
+				e.printStackTrace();
+				return null;
+			}
+		}
+		return null;
+	}
+	
+	public String getName() {
+		return entry.getFullName();
+	}
+
+	public Image getImage() {
+		return CompareUI.getImage(getType());
+	}
+	
+	public String getType() {
+		if (entry instanceof Tree)
+			return ITypedElement.FOLDER_TYPE;
+		else {
+			String name = entry.getName();
+			if (name != null) {
+				int index = name.lastIndexOf('.');
+				if (index == -1)
+					return ""; //$NON-NLS-1$
+				if (index == (name.length() - 1))
+					return ""; //$NON-NLS-1$
+				return name.substring(index + 1);
+			}
+			return "";
+		}
+	}
+
+}
+
diff --git a/org.spearce.egit.ui/plugin.xml b/org.spearce.egit.ui/plugin.xml
index d81a0b6..3febce8 100644
--- a/org.spearce.egit.ui/plugin.xml
+++ b/org.spearce.egit.ui/plugin.xml
@@ -56,12 +56,6 @@
                label="%ShowResourceInHistoryAction_label"
                menubarPath="team.main/group1"
                tooltip="%ShowResourceInHistoryAction_tooltip"/>
-		<action
-		       class="org.spearce.egit.ui.internal.actions.GitCompareRevisionAction"
-        	   id="org.spearce.egit.ui.internal.actions.GitCompareAction"
-               menubarPath="team.main/group1"
-        	   label="Structured Git Compare">
-		</action>
  	  </objectContribution>
       <objectContribution
             objectClass="org.eclipse.core.resources.IFile"
@@ -70,16 +64,6 @@
          <filter name="projectPersistentProperty"
                value="org.eclipse.team.core.repository=org.spearce.egit.core.GitProvider">
          </filter>
-         <action
-               class="org.spearce.egit.ui.internal.actions.CompareWithRevisionAction"
-               definitionId="org.spearce.egit.ui.compareWithRevision"
-               helpContextId="org.spearce.egit.ui.compare_with_revision_action_context"
-               id="org.spearce.egit.ui.compareWithRevision"
-               label="%CompareWithRevisionAction_label"
-               menubarPath="compareWithMenu/compareWithGroup"
-               overrideActionId="org.eclipse.team.ui.compareLocalHistory"
-               tooltip="%CompareWithRevisionAction_tooltip">
-         </action>
       </objectContribution>
    </extension>
 
diff --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
index 54635f5..3057344 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/GitHistoryPage.java
@@ -63,6 +63,7 @@ import org.eclipse.team.ui.history.HistoryPage;
 import org.eclipse.team.ui.history.IHistoryCompareAdapter;
 import org.eclipse.team.ui.history.IHistoryPageSite;
 import org.spearce.egit.core.internal.mapping.GitFileRevision;
+import org.spearce.egit.ui.internal.actions.GitCompareRevisionAction;
 
 public class GitHistoryPage extends HistoryPage implements IAdaptable,
 		IHistoryCompareAdapter {
@@ -104,6 +105,8 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,
 
 		final CompareRevisionAction compareAction = new CompareRevisionAction(
 				"Compare");
+		final GitCompareRevisionAction compareActionNG = new GitCompareRevisionAction(
+				"Compare NG");
 		tree.addSelectionListener(new SelectionAdapter() {
 			public void widgetSelected(SelectionEvent e) {
 				// update the current
@@ -116,16 +119,20 @@ public class GitHistoryPage extends HistoryPage implements IAdaptable,
 				compareAction.setCurrentFileRevision(fileRevisions[0]);
 				compareAction.selectionChanged(new StructuredSelection(
 						selection2));
-				
-				compareAction.setEnabled(selection!=null && selection[0]!=null  && ((GitFileRevision)fileRevisions[0]).getResource().getType() == IResource.FILE);
+
+				compareActionNG.setCurrentFileRevision(fileRevisions[0]);
+				compareActionNG.selectionChanged(new StructuredSelection(
+						selection2));
 			}
 		});
 		compareAction.setPage(this);
+		compareActionNG.setPage(this);
 		MenuManager menuMgr = new MenuManager();
 		Menu menu = menuMgr.createContextMenu(tree);
 		menuMgr.addMenuListener(new IMenuListener() {
 			public void menuAboutToShow(IMenuManager menuMgr) {
 				menuMgr.add(compareAction);
+				menuMgr.add(compareActionNG);
 			}
 		});
 		menuMgr.setRemoveAllWhenShown(true);
diff --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
new file mode 100644
index 0000000..9c8bd4f
--- /dev/null
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitCompareFileRevisionEditorInput.java
@@ -0,0 +1,305 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Robin Rosenberg - Git interface
+ *******************************************************************************/
+package org.spearce.egit.ui.internal;
+
+import java.lang.reflect.InvocationTargetException;
+
+import org.eclipse.compare.CompareConfiguration;
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.IEditableContent;
+import org.eclipse.compare.IResourceProvider;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.compare.structuremergeviewer.DiffNode;
+import org.eclipse.compare.structuremergeviewer.Differencer;
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.compare.structuremergeviewer.IStructureComparator;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFileState;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.internal.core.history.LocalFileRevision;
+import org.eclipse.team.internal.ui.TeamUIMessages;
+import org.eclipse.team.internal.ui.TeamUIPlugin;
+import org.eclipse.team.internal.ui.Utils;
+import org.eclipse.team.internal.ui.history.FileRevisionTypedElement;
+import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement;
+import org.eclipse.ui.IWorkbenchPage;
+
+public class GitCompareFileRevisionEditorInput extends CompareEditorInput {
+	
+	private ITypedElement left;
+	private ITypedElement right;
+	
+	/**
+	 * Creates a new CompareFileRevisionEditorInput.
+	 * @param left 
+	 * @param right 
+	 * @param page 
+	 */
+	public GitCompareFileRevisionEditorInput(ITypedElement left, ITypedElement right, IWorkbenchPage page) {
+		super(new CompareConfiguration());
+		this.left = left;
+		this.right = right;
+	}
+
+	protected FileRevisionTypedElement getRightRevision() {
+		if (right instanceof FileRevisionTypedElement) {
+			return (FileRevisionTypedElement) right;
+		}
+		return null;
+	}
+
+	protected FileRevisionTypedElement getLeftRevision() {
+		if (left instanceof FileRevisionTypedElement) {
+			return (FileRevisionTypedElement) left;
+		}
+		return null;
+	}
+
+	private static void ensureContentsCached(FileRevisionTypedElement left, FileRevisionTypedElement right,
+			IProgressMonitor monitor) {
+		if (left != null) {
+			try {
+				left.cacheContents(monitor);
+			} catch (CoreException e) {
+				TeamUIPlugin.log(e);
+			}
+		}
+		if (right != null) {
+			try {
+				right.cacheContents(monitor);
+			} catch (CoreException e) {
+				TeamUIPlugin.log(e);
+			}
+		}
+	}
+	
+	private boolean isLeftEditable(ICompareInput input) {
+		Object left = input.getLeft();
+		if (left instanceof IEditableContent) {
+			return ((IEditableContent) left).isEditable();
+		}
+		return false;
+	}
+
+	private IResource getResource(ICompareInput input) {
+		if (getLocalElement() != null) {
+			return ((IResourceProvider) getLocalElement()).getResource();
+		}
+		return null;
+	}
+
+	private ICompareInput createCompareInput() {
+		return compare(left, right);
+	}
+	
+	private DiffNode compare(ITypedElement left, ITypedElement right) {
+		if (left.getType().equals(ITypedElement.FOLDER_TYPE)) {
+			//			return new MyDiffContainer(null, left,right);
+			DiffNode diffNode = new DiffNode(null,Differencer.CHANGE,null,left,right);
+			ITypedElement[] lc = (ITypedElement[])((IStructureComparator)left).getChildren();
+			ITypedElement[] rc = (ITypedElement[])((IStructureComparator)right).getChildren();
+			int li=0;
+			int ri=0;
+			while (li<lc.length && ri<rc.length) {
+				ITypedElement ln = lc[li];
+				ITypedElement rn = rc[ri];
+				// TODO: Git ordering!
+				if (ln.getName().compareTo(rn.getName()) < 0) {
+					diffNode.add(new DiffNode(Differencer.ADDITION,null, ln, null));
+					++li;
+				}
+				if (ln.getName().compareTo(rn.getName()) > 0) {
+					diffNode.add(new DiffNode(Differencer.DELETION,null, null, rn));
+					++ri;
+				}
+				if (ln.getName().compareTo(rn.getName()) == 0) {
+					if (!ln.equals(rn))
+						diffNode.add(compare(ln,rn));
+					++li;
+					++ri;
+				}
+			}
+			while (li<lc.length) {
+				ITypedElement ln = lc[li];
+				diffNode.add(new DiffNode(Differencer.ADDITION,null, ln, null));
+				++li;
+			}
+			while (ri<rc.length) {
+				ITypedElement rn = rc[ri];
+				diffNode.add(new DiffNode(Differencer.ADDITION,null, null, rn));
+				++ri;
+			}
+			return diffNode;
+		} else {
+			return new DiffNode(left, right);
+		}
+	}
+
+	private void initLabels(ICompareInput input) {
+		CompareConfiguration cc = getCompareConfiguration();
+		if (getLeftRevision() != null) {
+			String leftLabel = getFileRevisionLabel(getLeftRevision());
+			cc.setLeftLabel(leftLabel);
+		} else if (getResource(input) != null) {
+			String label = NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_workspace, new Object[]{ input.getLeft().getName() });
+			cc.setLeftLabel(label);
+		}
+		if (getRightRevision() != null) {
+			String rightLabel = getFileRevisionLabel(getRightRevision());
+			cc.setRightLabel(rightLabel);
+		}
+	}
+
+	private String getFileRevisionLabel(FileRevisionTypedElement element) {
+		Object fileObject = element.getFileRevision();
+		if (fileObject instanceof LocalFileRevision){
+			return NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_localRevision, new Object[]{element.getName(), element.getTimestamp()});
+		} else {
+			return NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_repository, new Object[]{ element.getName(), element.getContentIdentifier()});
+		}
+	}
+
+	/* (non-Javadoc)
+	 * @see org.eclipse.compare.CompareEditorInput#getToolTipText()
+	 */
+	public String getToolTipText() {
+		Object[] titleObject = new Object[3];
+		titleObject[0] = getLongName(left);
+		titleObject[1] = getContentIdentifier(getLeftRevision());
+		titleObject[2] = getContentIdentifier(getRightRevision());
+		return NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_compareResourceAndVersions, titleObject);	 
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.compare.CompareEditorInput#getTitle()
+	 */
+	public String getTitle() {
+		Object[] titleObject = new Object[3];
+		titleObject[0] = getShortName(left);
+		titleObject[1] = getContentIdentifier(getLeftRevision());
+		titleObject[2] = getContentIdentifier(getRightRevision());
+		return NLS.bind(TeamUIMessages.CompareFileRevisionEditorInput_compareResourceAndVersions, titleObject);	 
+	}
+	
+	/* (non-Javadoc)
+	 * @see org.eclipse.compare.CompareEditorInput#getAdapter(java.lang.Class)
+	 */
+	public Object getAdapter(Class adapter) {
+		if (adapter == IFile.class || adapter == IResource.class) {
+			if (getLocalElement() != null) {
+				return getLocalElement().getResource();
+			}
+			return null;
+		}
+		return super.getAdapter(adapter);
+	}
+	
+	private String getShortName(ITypedElement element) {
+		if (element instanceof FileRevisionTypedElement){
+			FileRevisionTypedElement fileRevisionElement = (FileRevisionTypedElement) element;
+			return fileRevisionElement.getName();
+		}
+		else if (element instanceof LocalResourceTypedElement){
+			LocalResourceTypedElement typedContent = (LocalResourceTypedElement) element;
+			return typedContent.getResource().getName();
+		}
+		return element.getName();
+	}
+	
+	private String getLongName(ITypedElement element) {
+		if (element instanceof FileRevisionTypedElement){
+			FileRevisionTypedElement fileRevisionElement = (FileRevisionTypedElement) element;
+			return fileRevisionElement.getPath();
+		}
+		else if (element instanceof LocalResourceTypedElement){
+			LocalResourceTypedElement typedContent = (LocalResourceTypedElement) element;
+			return typedContent.getResource().getFullPath().toString();
+		}
+		return element.getName();
+	}
+	
+	private String getContentIdentifier(ITypedElement element){
+		if (element instanceof FileRevisionTypedElement){
+			FileRevisionTypedElement fileRevisionElement = (FileRevisionTypedElement) element;
+			Object fileObject = fileRevisionElement.getFileRevision();
+			if (fileObject instanceof LocalFileRevision){
+				try {
+					IStorage storage = ((LocalFileRevision) fileObject).getStorage(new NullProgressMonitor());
+					if (Utils.getAdapter(storage, IFileState.class) != null){
+						//local revision
+						return TeamUIMessages.CompareFileRevisionEditorInput_0;
+					} else if (Utils.getAdapter(storage, IFile.class) != null) {
+						//current revision
+						return TeamUIMessages.CompareFileRevisionEditorInput_1;
+					}
+				} catch (CoreException e) {
+				}
+			} else {
+				return fileRevisionElement.getContentIdentifier();
+			}
+		}
+		return TeamUIMessages.CompareFileRevisionEditorInput_2;
+	}
+
+//	/* (non-Javadoc)
+//	 * @see org.eclipse.team.ui.synchronize.LocalResourceCompareEditorInput#fireInputChange()
+//	 */
+//	protected void fireInputChange() {
+//		((DiffNode)getCompareResult()).fireChange();
+//	}
+//
+//	/* (non-Javadoc)
+//	 * @see org.eclipse.team.ui.synchronize.SaveableCompareEditorInput#contentsCreated()
+//	 */
+//	protected void contentsCreated() {
+//		super.contentsCreated();
+//		notifier.initialize();
+//	}
+//	
+//	/* (non-Javadoc)
+//	 * @see org.eclipse.team.ui.synchronize.SaveableCompareEditorInput#handleDispose()
+//	 */
+//	protected void handleDispose() {
+//		super.handleDispose();
+//		notifier.dispose();
+//		if (getLocalElement() != null) {
+//			getLocalElement().discardBuffer();
+//		}
+//	}
+//
+	public LocalResourceTypedElement getLocalElement() {
+		if (left instanceof LocalResourceTypedElement) {
+			return (LocalResourceTypedElement) left;
+		}
+		return null;
+	}
+
+	public ITypedElement getLeft() {
+		return left;
+	}
+
+	protected Object prepareInput(IProgressMonitor monitor) throws InvocationTargetException, InterruptedException {
+		ICompareInput input = createCompareInput();
+		getCompareConfiguration().setLeftEditable(isLeftEditable(input));
+		getCompareConfiguration().setRightEditable(false);
+		ensureContentsCached(getLeftRevision(), getRightRevision(), monitor);
+		initLabels(input);
+		setTitle(NLS.bind(TeamUIMessages.SyncInfoCompareInput_title, new String[] { input.getName() }));
+		return input;
+	}
+
+}
diff --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
new file mode 100644
index 0000000..be840f9
--- /dev/null
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/actions/GitCompareRevisionAction.java
@@ -0,0 +1,228 @@
+/*******************************************************************************
+ * Copyright (c) 2006 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ * 
+ * Contributors:
+ *     IBM Corporation - initial API and implementation
+ *     Robin Rosenberg - Git interface
+ *******************************************************************************/
+package org.spearce.egit.ui.internal.actions;
+
+import org.eclipse.compare.CompareEditorInput;
+import org.eclipse.compare.CompareUI;
+import org.eclipse.compare.ITypedElement;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.jface.dialogs.MessageDialog;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.history.provider.FileRevision;
+import org.eclipse.team.internal.core.history.LocalFileRevision;
+import org.eclipse.team.internal.ui.TeamUIMessages;
+import org.eclipse.team.internal.ui.history.AbstractHistoryCategory;
+import org.eclipse.team.internal.ui.synchronize.LocalResourceTypedElement;
+import org.eclipse.team.ui.history.HistoryPage;
+import org.eclipse.ui.IEditorInput;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IEditorReference;
+import org.eclipse.ui.IReusableEditor;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.actions.BaseSelectionListenerAction;
+import org.spearce.egit.core.GitWorkspaceFileRevision;
+import org.spearce.egit.core.internal.mapping.GitResourceNode;
+import org.spearce.egit.ui.internal.GitCompareFileRevisionEditorInput;
+
+/**
+ * Action to invoke a Git based compare on selected revivsions in the history window.
+ * 
+ * This class i almost a verbatim copy of @{link org.eclipse.team.internal.ui.actions.CompareRevisionAction}
+ * since it could not be subclassed to invoke the EGit methods.
+ */
+public class GitCompareRevisionAction extends BaseSelectionListenerAction {
+
+	HistoryPage page;
+	IStructuredSelection selection;
+	IFileRevision currentFileRevision;
+
+	public GitCompareRevisionAction(String text) {
+		super(text);
+	}
+
+	public GitCompareRevisionAction() {
+		this(TeamUIMessages.LocalHistoryPage_CompareAction);
+	}
+
+	public void run() {
+		IStructuredSelection structSel = selection;
+		Object[] objArray = structSel.toArray();
+
+		IFileRevision file1 = null;
+		IFileRevision file2 = null;
+
+		switch (structSel.size()){
+			case 1:
+				file1 = getCurrentFileRevision();
+				Object tempRevision = objArray[0];
+				if (tempRevision instanceof IFileRevision)
+					file2 = (IFileRevision) tempRevision;
+				else 
+					return;
+			break;
+
+			case 2:
+				Object tempRevision2 = objArray[0];
+				Object tempRevision3 = objArray[1];
+				
+				if (tempRevision2 instanceof IFileRevision &&
+					tempRevision3 instanceof IFileRevision){
+					file1 = (IFileRevision) objArray[0];
+					file2 = (IFileRevision) objArray[1];
+				} else 
+					return;
+			break;
+		}
+
+		if (file1 == null || file2 == null ||
+		   !file1.exists() || !file2.exists()){
+			MessageDialog.openError(page.getSite().getShell(),
+					TeamUIMessages.OpenRevisionAction_DeletedRevTitle,
+					TeamUIMessages.CompareRevisionAction_DeleteCompareMessage);
+			return;
+		}
+		
+		IResource resource = getResource(file2);
+		if (resource != null) {
+			IFileRevision temp = file1;
+			file1 = file2;
+			file2 = temp;
+		}
+		ITypedElement left;
+		resource = getResource(file1);
+		if (resource != null) {
+			left = new LocalResourceTypedElement(resource);
+		} else {
+			left = new GitResourceNode(file1);
+		}
+		ITypedElement right = new GitResourceNode(file2);
+		
+	    openInCompare(left, right);
+	}
+
+	private void openInCompare(ITypedElement left, ITypedElement right) {
+		CompareEditorInput input = createCompareEditorInput(left, right, page.getSite().getPage());
+		IWorkbenchPage workBenchPage = page.getSite().getPage();
+		IEditorPart editor = findReusableCompareEditor(workBenchPage);
+		if (editor != null) {
+			IEditorInput otherInput = editor.getEditorInput();
+			if (otherInput.equals(input)) {
+				// simply provide focus to editor
+				workBenchPage.activate(editor);
+			} else {
+				// if editor is currently not open on that input either re-use
+				// existing
+				CompareUI.reuseCompareEditor(input, (IReusableEditor) editor);
+				workBenchPage.activate(editor);
+			}
+		} else {
+			CompareUI.openCompareEditor(input);
+		}
+	}
+
+	protected GitCompareFileRevisionEditorInput createCompareEditorInput(
+			ITypedElement left, ITypedElement right, IWorkbenchPage page) {
+		return new GitCompareFileRevisionEditorInput(left, right, page);
+	}
+
+	private IResource getResource(IFileRevision revision) {
+		if (revision instanceof GitWorkspaceFileRevision) {
+			GitWorkspaceFileRevision local = (GitWorkspaceFileRevision) revision;
+			return local.getResource();
+		}
+		return null;
+	}
+
+	private IFileRevision getCurrentFileRevision() {
+		return currentFileRevision;
+	}
+
+	public void setCurrentFileRevision(IFileRevision fileRevision){
+		this.currentFileRevision = fileRevision;
+	}
+
+	/**
+	 * Returns an editor that can be re-used. An open compare editor that
+	 * has un-saved changes cannot be re-used.
+	 * @param page 
+	 * @return an EditorPart or <code>null</code> if none can be found
+	 */
+	public static IEditorPart findReusableCompareEditor(IWorkbenchPage page) {
+		IEditorReference[] editorRefs = page.getEditorReferences();	
+		for (int i = 0; i < editorRefs.length; i++) {
+			IEditorPart part = editorRefs[i].getEditor(false);
+			if(part != null 
+					&& (part.getEditorInput() instanceof GitCompareFileRevisionEditorInput) 
+					&& part instanceof IReusableEditor) {
+				if(! part.isDirty()) {	
+					return part;	
+				}
+			}
+		}
+		return null;
+	}
+	
+	protected boolean updateSelection(IStructuredSelection selection) {
+		this.selection = selection;
+		if (selection.size() == 1){
+			Object el = selection.getFirstElement();
+			if (el instanceof LocalFileRevision)
+				this.setText(TeamUIMessages.CompareRevisionAction_Local);
+			else if (el instanceof FileRevision){
+				FileRevision tempFileRevision = (FileRevision) el;
+				this.setText(NLS.bind(TeamUIMessages.CompareRevisionAction_Revision, new String[]{tempFileRevision.getContentIdentifier()}));
+			} 
+			else
+				this.setText(TeamUIMessages.CompareRevisionAction_CompareWithCurrent);
+			return shouldShow();
+		}
+		else if (selection.size() == 2){
+			this.setText(TeamUIMessages.CompareRevisionAction_CompareWithOther+" NG");	
+			return shouldShow();
+		}
+
+		return false;
+	}
+
+	public void setPage(HistoryPage page) {
+		this.page = page;
+	}
+
+	private boolean shouldShow() {
+		IStructuredSelection structSel = selection;
+		Object[] objArray = structSel.toArray();
+
+		if (objArray.length == 0)
+			return false;
+
+		// Comparing the workspace revision with itself is just stupid
+		if (objArray.length == 1 && objArray[0] instanceof GitWorkspaceFileRevision)
+			return false;
+
+		for (int i = 0; i < objArray.length; i++) {
+
+			// Don't bother showing if this a category
+			if (objArray[i] instanceof AbstractHistoryCategory)
+				return false;
+
+			IFileRevision revision = (IFileRevision) objArray[i];
+			//check to see if any of the selected revisions are deleted revisions
+			if (revision != null && !revision.exists())
+				return false;
+		}
+
+		return true;
+	}
+
+}

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* Re: [PATCH 0/4] Eclipse (EGIT) Structured compare
  2007-03-18 22:15 [PATCH 0/4] Eclipse (EGIT) Structured compare Robin Rosenberg
                   ` (3 preceding siblings ...)
  2007-03-18 22:16 ` [PATCH 4/4] Add support for structured comparison Robin Rosenberg
@ 2007-03-19  2:21 ` Shawn O. Pearce
  4 siblings, 0 replies; 6+ messages in thread
From: Shawn O. Pearce @ 2007-03-19  2:21 UTC (permalink / raw
  To: Robin Rosenberg; +Cc: git

Robin Rosenberg <robin.rosenberg@dewire.com> wrote:
> Now Eclipse can compare two git version at the project level. Thanks
> to the SHA-1's the compare is very quick. There is some extra cost
> for version managed archives (zip/jars) as Eclipse insists on comparing
> the contents of those at the file level too. Not that I mind that. It
> is useful and cool. (No, it doesn't compare open office documents in
> any useful manner, unless you happen to have a plugin for it, in
> which case it might actually work.)
> 
> Along with this some experiments with caching and a minor bug fix in
> the commit reader.

Thanks for this series.  I have applied it and pushed it out, along
with a small fix on top.

It works nicely, at least the "NG" variant when comparing trees.
;-)

The non-NG variant shows the raw tree diff, which is uh, a little
uninteresting, unless you are a true core Git hacker...

I tested it on a small-ish repository. The history browser works
quite nicely once the cache starts to get filled.  I don't know
how well it works on bigger repositories.  ;-)

--'da small fix--
From 6c402714c5a1850b871877cefe465a6a40ccdd0f Mon Sep 17 00:00:00 2001
From: Shawn O. Pearce <spearce@spearce.org>
Date: Sun, 18 Mar 2007 22:15:14 -0400
Subject: [PATCH] Move GitResourceNode to ui plugin

Because I am trying to keep the core plugin UI free, allowing it
to be used in headless workbenches, such as from an Eclipse based
build system, we don't want to depend on the org.eclipse.swt plugin.
Relocating GitResourceNode to the UI plugin lets us continue to avoid
that dependency.

Signed-off-by: Shawn O. Pearce <spearce@spearce.org>
---
 org.spearce.egit.core/META-INF/MANIFEST.MF         |    4 +---
 .../spearce/egit/ui/internal}/GitResourceNode.java |    3 ++-
 .../internal/actions/GitCompareRevisionAction.java |    2 +-
 3 files changed, 4 insertions(+), 5 deletions(-)
 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%)

diff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF
index 8dc62d6..fd39ec6 100644
--- a/org.spearce.egit.core/META-INF/MANIFEST.MF
+++ b/org.spearce.egit.core/META-INF/MANIFEST.MF
@@ -10,9 +10,7 @@ Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.team.core,
  org.eclipse.core.resources,
  org.spearce.jgit,
- org.eclipse.core.filesystem,
- org.eclipse.compare,
- org.eclipse.swt
+ org.eclipse.core.filesystem
 Export-Package: org.spearce.egit.core.internal.mapping;x-friends:="org.spearce.egit.ui",
  org.spearce.egit.core,
  org.spearce.egit.core.internal.mapping,
diff --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
similarity index 96%
rename from org.spearce.egit.core/src/org/spearce/egit/core/internal/mapping/GitResourceNode.java
rename to org.spearce.egit.ui/src/org/spearce/egit/ui/internal/GitResourceNode.java
index 0bba684..bdf8902 100644
--- 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
@@ -1,4 +1,4 @@
-package org.spearce.egit.core.internal.mapping;
+package org.spearce.egit.ui.internal;
 
 import java.io.ByteArrayInputStream;
 import java.io.IOException;
@@ -11,6 +11,7 @@ import org.eclipse.compare.structuremergeviewer.IStructureComparator;
 import org.eclipse.core.runtime.CoreException;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.team.core.history.IFileRevision;
+import org.spearce.egit.core.internal.mapping.GitFileRevision;
 import org.spearce.jgit.lib.FileTreeEntry;
 import org.spearce.jgit.lib.ObjectId;
 import org.spearce.jgit.lib.ObjectLoader;
diff --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
index 84eb506..9a0b378 100644
--- 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
@@ -32,8 +32,8 @@ import org.eclipse.ui.IReusableEditor;
 import org.eclipse.ui.IWorkbenchPage;
 import org.eclipse.ui.actions.BaseSelectionListenerAction;
 import org.spearce.egit.core.GitWorkspaceFileRevision;
-import org.spearce.egit.core.internal.mapping.GitResourceNode;
 import org.spearce.egit.ui.internal.GitCompareFileRevisionEditorInput;
+import org.spearce.egit.ui.internal.GitResourceNode;
 
 /**
  * Action to invoke a Git based compare on selected revivsions in the history window.
-- 
1.5.0.4.1023.gb3ae

-- 
Shawn.

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2007-03-19  2:21 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2007-03-18 22:15 [PATCH 0/4] Eclipse (EGIT) Structured compare Robin Rosenberg
2007-03-18 22:15 ` [PATCH 1/4] Cache tree ObjectId's too Robin Rosenberg
2007-03-18 22:16 ` [PATCH 2/4] Rework tree/commit cache Robin Rosenberg
2007-03-18 22:16 ` [PATCH 3/4] Decode message when encoding line is present Robin Rosenberg
2007-03-18 22:16 ` [PATCH 4/4] Add support for structured comparison Robin Rosenberg
2007-03-19  2:21 ` [PATCH 0/4] Eclipse (EGIT) Structured compare Shawn O. Pearce

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).