Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProviderTest.java194
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriberTest.java357
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/ResourceVariantTest.java301
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProviderTest.java98
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/VariantsTestCase.java101
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java4
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties1
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProvider.java129
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitCachedResourceVariantTree.java67
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantCache.java91
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantFileRevision.java60
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeProvider.java64
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriber.java164
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitSyncInfoToDiffConverter.java191
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProvider.java167
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/AbstractGitResourceVariant.java112
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexResourceVariant.java53
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/TreeParserResourceVariant.java57
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java23
19 files changed, 2233 insertions, 1 deletions
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProviderTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProviderTest.java
new file mode 100644
index 0000000000..37929acd17
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProviderTest.java
@@ -0,0 +1,194 @@
+/*******************************************************************************
+ * Copyright (C) 2015 Obeo 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.op.MergeOperation;
+import org.junit.Test;
+
+public class DirCacheResourceVariantTreeProviderTest extends VariantsTestCase {
+ @Test
+ public void testDirCacheAddToIndex() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ testRepo.appendFileContent(file1, INITIAL_CONTENT_1);
+
+ // untracked file : not part of the index
+ DirCacheResourceVariantTreeProvider treeProvider = new DirCacheResourceVariantTreeProvider(
+ repo);
+ assertTrue(treeProvider.getKnownResources().isEmpty());
+ assertFalse(treeProvider.getBaseTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getSourceTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getRemoteTree().hasResourceVariant(iFile1));
+
+ testRepo.addToIndex(iFile1);
+
+ // We now have a stage 0, but this isn't represented in the resource
+ // variant tree provider
+ treeProvider = new DirCacheResourceVariantTreeProvider(repo);
+ assertTrue(treeProvider.getKnownResources().isEmpty());
+ assertFalse(treeProvider.getBaseTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getSourceTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getRemoteTree().hasResourceVariant(iFile1));
+ }
+
+ @Test
+ public void testDirCacheTreesNoConflict() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ File file2 = testRepo.createFile(iProject, "file2");
+
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "first file - initial commit");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2,
+ "second file - initial commit");
+
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+ IFile iFile2 = testRepo.getIFile(iProject, file2);
+
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+
+ final String branchChanges = "branch changes\n";
+ setContentsAndCommit(testRepo, iFile2, branchChanges
+ + INITIAL_CONTENT_2, "branch commit");
+
+ testRepo.checkoutBranch(MASTER);
+
+ final String masterChanges = "\nsome changes";
+ setContentsAndCommit(testRepo, iFile1, INITIAL_CONTENT_1
+ + masterChanges, "master commit");
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+ // end setup
+
+ // try and merge the branch into master
+ new MergeOperation(repo, BRANCH).execute(null);
+
+ // no conflict on either file : nothing in the trees
+ DirCacheResourceVariantTreeProvider treeProvider = new DirCacheResourceVariantTreeProvider(
+ repo);
+ assertTrue(treeProvider.getKnownResources().isEmpty());
+
+ assertFalse(treeProvider.getBaseTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getBaseTree().hasResourceVariant(iFile2));
+
+ assertFalse(treeProvider.getSourceTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getSourceTree().hasResourceVariant(iFile2));
+
+ assertFalse(treeProvider.getRemoteTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getRemoteTree().hasResourceVariant(iFile2));
+ }
+
+ @Test
+ public void testDirCacheTreesConflictOnOne() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ File file2 = testRepo.createFile(iProject, "file2");
+
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "first file - initial commit");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2,
+ "second file - initial commit");
+
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+ IFile iFile2 = testRepo.getIFile(iProject, file2);
+
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+
+ final String branchChanges = "branch changes\n";
+ setContentsAndCommit(testRepo, iFile1, branchChanges
+ + INITIAL_CONTENT_1, "branch commit");
+ setContentsAndCommit(testRepo, iFile2, branchChanges
+ + INITIAL_CONTENT_2, "branch commit");
+
+ testRepo.checkoutBranch(MASTER);
+
+ final String masterChanges = "\nsome changes";
+ setContentsAndCommit(testRepo, iFile1, INITIAL_CONTENT_1
+ + masterChanges, "master commit");
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+ // end setup
+
+ // try and merge the branch into master
+ new MergeOperation(repo, BRANCH).execute(null);
+
+ // conflict on file 1 : present in all three trees
+ // no conflict on file 2 : not present in any tree
+ DirCacheResourceVariantTreeProvider treeProvider = new DirCacheResourceVariantTreeProvider(
+ repo);
+ assertTrue(treeProvider.getKnownResources().contains(iFile1));
+ assertFalse(treeProvider.getKnownResources().contains(iFile2));
+
+ assertTrue(treeProvider.getBaseTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getBaseTree().hasResourceVariant(iFile2));
+
+ assertTrue(treeProvider.getSourceTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getSourceTree().hasResourceVariant(iFile2));
+
+ assertTrue(treeProvider.getRemoteTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getRemoteTree().hasResourceVariant(iFile2));
+ }
+
+ @Test
+ public void testDirCacheTreesConflict() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ File file2 = testRepo.createFile(iProject, "file2");
+
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "first file - initial commit");
+
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+
+ final String branchChanges = "branch changes\n";
+ setContentsAndCommit(testRepo, iFile1, branchChanges
+ + INITIAL_CONTENT_1, "branch commit");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2
+ + "branch", "second file - initial commit - branch");
+
+ testRepo.checkoutBranch(MASTER);
+
+ final String masterChanges = "some changes\n";
+ setContentsAndCommit(testRepo, iFile1, INITIAL_CONTENT_1
+ + masterChanges, "master commit - file1");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2
+ + "master", "second file - initial commit - master");
+ IFile iFile2 = testRepo.getIFile(iProject, file2);
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+ // end setup
+
+ // try and merge the branch into master
+ new MergeOperation(repo, BRANCH).execute(null);
+
+ // conflict on file 1 : file 1 has three stages.
+ // conflict on file 2, but was not in the base : only stage 2 and 3
+ DirCacheResourceVariantTreeProvider treeProvider = new DirCacheResourceVariantTreeProvider(
+ repo);
+ assertTrue(treeProvider.getKnownResources().contains(iFile1));
+ assertTrue(treeProvider.getKnownResources().contains(iFile2));
+
+ assertTrue(treeProvider.getBaseTree().hasResourceVariant(iFile1));
+ assertFalse(treeProvider.getBaseTree().hasResourceVariant(iFile2));
+
+ assertTrue(treeProvider.getSourceTree().hasResourceVariant(iFile1));
+ assertTrue(treeProvider.getSourceTree().hasResourceVariant(iFile2));
+
+ assertTrue(treeProvider.getRemoteTree().hasResourceVariant(iFile1));
+ assertTrue(treeProvider.getRemoteTree().hasResourceVariant(iFile2));
+ }
+}
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriberTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriberTest.java
new file mode 100644
index 0000000000..11046f2985
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriberTest.java
@@ -0,0 +1,357 @@
+/*******************************************************************************
+ * Copyright (C) 2015 Obeo 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.junit.Assert.assertSame;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.team.core.diff.IDiff;
+import org.eclipse.team.core.diff.IThreeWayDiff;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.mapping.provider.ResourceDiff;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.junit.Before;
+import org.junit.Test;
+
+public class GitResourceVariantTreeSubscriberTest extends VariantsTestCase {
+ private static final String BRANCH_CHANGES = "branch changes\n";
+
+ private static final String MASTER_CHANGES = "\nsome changes";
+
+ private static final String BASE = "base";
+
+ private File file1;
+
+ private File file2;
+
+ private IFile iFile1;
+
+ private IFile iFile2;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+
+ file1 = testRepo.createFile(iProject, "file1");
+ file2 = testRepo.createFile(iProject, "file2");
+
+ iFile1 = testRepo.getIFile(iProject, file1);
+ iFile2 = testRepo.getIFile(iProject, file2);
+ }
+
+ @Test
+ public void testSubscriber() throws Exception {
+ GitResourceVariantTreeProvider provider = createTreeProvider();
+ GitResourceVariantTreeSubscriber subscriber = new GitResourceVariantTreeSubscriber(
+ provider);
+
+ assertTrue(subscriber.isSupervised(iProject));
+ assertTrue(subscriber.isSupervised(iFile1));
+ assertTrue(subscriber.isSupervised(iFile2));
+
+ assertSame(provider.getBaseTree(), subscriber.getBaseTree());
+ assertSame(provider.getRemoteTree(), subscriber.getRemoteTree());
+ assertSame(provider.getSourceTree(), subscriber.getSourceTree());
+
+ assertNotNull(subscriber.getDiff(iProject));
+ assertNotNull(subscriber.getDiff(iFile1));
+ assertNotNull(subscriber.getDiff(iFile2));
+
+ assertNotNull(subscriber.getSyncInfo(iProject));
+ assertNotNull(subscriber.getSyncInfo(iFile1));
+ assertNotNull(subscriber.getSyncInfo(iFile2));
+
+ }
+
+ @Test
+ public void testSyncInfo() throws Exception {
+ GitResourceVariantTreeProvider provider = createTreeProvider();
+ GitResourceVariantTreeSubscriber subscriber = new GitResourceVariantTreeSubscriber(
+ provider);
+
+ final SyncInfo projectInfo = subscriber.getSyncInfo(iProject);
+ assertNotNull(projectInfo);
+ assertEquals(SyncInfo.CONFLICTING | SyncInfo.CHANGE,
+ projectInfo.getKind());
+
+ final SyncInfo syncInfo1 = subscriber.getSyncInfo(iFile1);
+ assertNotNull(syncInfo1);
+ assertEquals(SyncInfo.OUTGOING | SyncInfo.CHANGE, syncInfo1.getKind());
+ IResourceVariant baseVariant1 = syncInfo1.getBase();
+ IResourceVariant remoteVariant1 = syncInfo1.getRemote();
+ assertContentEquals(baseVariant1, INITIAL_CONTENT_1);
+ assertContentEquals(remoteVariant1, INITIAL_CONTENT_1);
+
+ final SyncInfo syncInfo2 = subscriber.getSyncInfo(iFile2);
+ assertNotNull(syncInfo2);
+ assertEquals(SyncInfo.INCOMING | SyncInfo.CHANGE, syncInfo2.getKind());
+ IResourceVariant baseVariant2 = syncInfo2.getBase();
+ IResourceVariant remoteVariant2 = syncInfo2.getRemote();
+ assertContentEquals(baseVariant2, INITIAL_CONTENT_2);
+ assertContentEquals(remoteVariant2, BRANCH_CHANGES + INITIAL_CONTENT_2);
+ }
+
+ @Test
+ public void testDiff() throws Exception {
+ GitResourceVariantTreeProvider provider = createTreeProvider();
+ GitResourceVariantTreeSubscriber subscriber = new GitResourceVariantTreeSubscriber(
+ provider);
+
+ final IDiff diff1 = subscriber.getDiff(iFile1);
+ assertTrue(diff1 instanceof IThreeWayDiff);
+ assertEquals(IDiff.CHANGE, diff1.getKind());
+ assertEquals(IThreeWayDiff.OUTGOING,
+ ((IThreeWayDiff) diff1).getDirection());
+ final IDiff localDiff1 = ((IThreeWayDiff) diff1).getLocalChange();
+ final IDiff remoteDiff1 = ((IThreeWayDiff) diff1).getRemoteChange();
+ assertNull(remoteDiff1);
+ assertTrue(localDiff1 instanceof ResourceDiff);
+ final IFileRevision localState1 = ((ResourceDiff) localDiff1)
+ .getAfterState();
+ final IFileRevision baseState1 = ((ResourceDiff) localDiff1)
+ .getBeforeState();
+ assertNotNull(localState1);
+ assertNotNull(baseState1);
+ assertTrue(iFile1.getName().equals(localState1.getName()));
+ assertTrue(iFile1.getName().equals(baseState1.getName()));
+ final IStorage localStorage1 = localState1
+ .getStorage(new NullProgressMonitor());
+ final IStorage baseStorage1 = baseState1
+ .getStorage(new NullProgressMonitor());
+ assertContentEquals(localStorage1, INITIAL_CONTENT_1 + MASTER_CHANGES);
+ assertContentEquals(baseStorage1, INITIAL_CONTENT_1);
+
+ final IDiff diff2 = subscriber.getDiff(iFile2);
+ assertTrue(diff2 instanceof IThreeWayDiff);
+ assertEquals(IDiff.CHANGE, diff2.getKind());
+ assertEquals(IThreeWayDiff.INCOMING,
+ ((IThreeWayDiff) diff2).getDirection());
+ final IDiff localDiff2 = ((IThreeWayDiff) diff2).getLocalChange();
+ final IDiff remoteDiff2 = ((IThreeWayDiff) diff2).getRemoteChange();
+ assertTrue(remoteDiff2 instanceof ResourceDiff);
+ assertNull(localDiff2);
+ final IFileRevision remoteState2 = ((ResourceDiff) remoteDiff2)
+ .getAfterState();
+ final IFileRevision ancestorState2 = ((ResourceDiff) remoteDiff2)
+ .getBeforeState();
+ assertTrue(iFile2.getName().equals(ancestorState2.getName()));
+ assertTrue(iFile2.getName().equals(remoteState2.getName()));
+ final IStorage ancestorStorage2 = ancestorState2
+ .getStorage(new NullProgressMonitor());
+ final IStorage remoteStorage2 = remoteState2
+ .getStorage(new NullProgressMonitor());
+ assertContentEquals(ancestorStorage2, INITIAL_CONTENT_2);
+ assertContentEquals(remoteStorage2, BRANCH_CHANGES + INITIAL_CONTENT_2);
+ }
+
+ @Test
+ public void testAddLocalAndRemote() throws Exception {
+ GitResourceVariantTreeProvider provider = createTreeProviderWithAdditions();
+ GitResourceVariantTreeSubscriber subscriber = new GitResourceVariantTreeSubscriber(
+ provider);
+
+ final IDiff diff1 = subscriber.getDiff(iFile1);
+ assertTrue(diff1 instanceof IThreeWayDiff);
+ assertEquals(IDiff.ADD, diff1.getKind());
+ assertEquals(IThreeWayDiff.OUTGOING,
+ ((IThreeWayDiff) diff1).getDirection());
+ final IDiff localDiff1 = ((IThreeWayDiff) diff1).getLocalChange();
+ final IDiff remoteDiff1 = ((IThreeWayDiff) diff1).getRemoteChange();
+ assertTrue(localDiff1 instanceof ResourceDiff);
+ assertNull(remoteDiff1);
+ final IFileRevision ancestorState1 = ((ResourceDiff) localDiff1)
+ .getBeforeState();
+ final IFileRevision localState1 = ((ResourceDiff) localDiff1)
+ .getAfterState();
+ assertTrue(iFile1.getName().equals(localState1.getName()));
+ assertNull(ancestorState1);
+ final IStorage localStorage1 = localState1
+ .getStorage(new NullProgressMonitor());
+ assertContentEquals(localStorage1, INITIAL_CONTENT_1);
+
+ final IDiff diff2 = subscriber.getDiff(iFile2);
+ assertTrue(diff2 instanceof IThreeWayDiff);
+ assertEquals(IDiff.ADD, diff2.getKind());
+ assertEquals(IThreeWayDiff.INCOMING,
+ ((IThreeWayDiff) diff2).getDirection());
+ final IDiff localDiff2 = ((IThreeWayDiff) diff2).getLocalChange();
+ final IDiff remoteDiff2 = ((IThreeWayDiff) diff2).getRemoteChange();
+ assertTrue(remoteDiff2 instanceof ResourceDiff);
+ assertNull(localDiff2);
+ final IFileRevision ancestorState2 = ((ResourceDiff) remoteDiff2)
+ .getBeforeState();
+ final IFileRevision remoteState2 = ((ResourceDiff) remoteDiff2)
+ .getAfterState();
+ assertNull(ancestorState2);
+ assertTrue(iFile2.getName().equals(remoteState2.getName()));
+ final IStorage remoteStorage2 = remoteState2
+ .getStorage(new NullProgressMonitor());
+ assertContentEquals(remoteStorage2, INITIAL_CONTENT_2);
+ }
+
+ @Test
+ public void testRemoveLocalAndRemote() throws Exception {
+ GitResourceVariantTreeProvider provider = createTreeProviderWithDeletions();
+ GitResourceVariantTreeSubscriber subscriber = new GitResourceVariantTreeSubscriber(
+ provider);
+
+ // file1 has been removed locally
+ final IDiff diff1 = subscriber.getDiff(iFile1);
+ assertTrue(diff1 instanceof IThreeWayDiff);
+ assertEquals(IDiff.REMOVE, diff1.getKind());
+ assertEquals(IThreeWayDiff.OUTGOING,
+ ((IThreeWayDiff) diff1).getDirection());
+ final IDiff localDiff1 = ((IThreeWayDiff) diff1).getLocalChange();
+ final IDiff remoteDiff1 = ((IThreeWayDiff) diff1).getRemoteChange();
+ assertTrue(localDiff1 instanceof ResourceDiff);
+ assertNull(remoteDiff1);
+ final IFileRevision ancestorState1 = ((ResourceDiff) localDiff1)
+ .getBeforeState();
+ final IFileRevision localState1 = ((ResourceDiff) localDiff1)
+ .getAfterState();
+ assertTrue(iFile1.getName().equals(ancestorState1.getName()));
+ assertNull(localState1);
+ final IStorage ancestorStorage1 = ancestorState1
+ .getStorage(new NullProgressMonitor());
+ assertContentEquals(ancestorStorage1, INITIAL_CONTENT_1);
+
+ // file2 has been removed remotely
+ final IDiff diff2 = subscriber.getDiff(iFile2);
+ assertTrue(diff2 instanceof IThreeWayDiff);
+ assertEquals(IDiff.REMOVE, diff2.getKind());
+ assertEquals(IThreeWayDiff.INCOMING,
+ ((IThreeWayDiff) diff2).getDirection());
+ final IDiff localDiff2 = ((IThreeWayDiff) diff2).getLocalChange();
+ final IDiff remoteDiff2 = ((IThreeWayDiff) diff2).getRemoteChange();
+ assertTrue(remoteDiff2 instanceof ResourceDiff);
+ assertNull(localDiff2);
+ final IFileRevision ancestorState2 = ((ResourceDiff) remoteDiff2)
+ .getBeforeState();
+ final IFileRevision remoteState2 = ((ResourceDiff) remoteDiff2)
+ .getAfterState();
+ assertTrue(iFile2.getName().equals(ancestorState2.getName()));
+ assertNull(remoteState2);
+ final IStorage rancestorStorage2 = ancestorState2
+ .getStorage(new NullProgressMonitor());
+ assertContentEquals(rancestorStorage2, INITIAL_CONTENT_2);
+ }
+
+ private GitResourceVariantTreeProvider createTreeProvider()
+ throws Exception {
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "first file - initial commit");
+ testRepo.appendContentAndCommit(iProject, file2,
+ INITIAL_CONTENT_2, "second file - initial commit");
+ testRepo.createBranch(MASTER, BASE);
+
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+
+ setContentsAndCommit(testRepo, iFile2, BRANCH_CHANGES
+ + INITIAL_CONTENT_2, "branch commit");
+
+ testRepo.checkoutBranch(MASTER);
+
+ setContentsAndCommit(testRepo, iFile1, INITIAL_CONTENT_1
+ + MASTER_CHANGES, "master commit");
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+
+ // as if we tried to merge branch into master
+ try (RevWalk walk = new RevWalk(repo)) {
+ RevTree baseTree = walk.parseTree(repo.resolve(BASE));
+ RevTree sourceTree = walk.parseTree(repo.resolve(MASTER));
+ RevTree remoteTree = walk.parseTree(repo.resolve(BRANCH));
+ TreeWalk treeWalk = new NameConflictTreeWalk(repo);
+ treeWalk.addTree(baseTree);
+ treeWalk.addTree(sourceTree);
+ treeWalk.addTree(remoteTree);
+ return new TreeWalkResourceVariantTreeProvider(repo, treeWalk, 0,
+ 1, 2);
+ }
+ }
+
+ private GitResourceVariantTreeProvider createTreeProviderWithAdditions()
+ throws Exception {
+ testRepo.createBranch(MASTER, BASE);
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+ file2 = testRepo.createFile(iProject, "file2");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2,
+ "Creation of file2 in branch2.");
+
+ testRepo.checkoutBranch(MASTER);
+ file1 = testRepo.createFile(iProject, "file1");
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "Creation of file1 in branch1.");
+
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+
+ // as if we tried to merge branch3 into branch2
+ try (RevWalk walk = new RevWalk(repo)) {
+ RevTree baseTree = walk.parseTree(repo.resolve(BASE));
+ RevTree sourceTree = walk.parseTree(repo.resolve(MASTER));
+ RevTree remoteTree = walk.parseTree(repo.resolve(BRANCH));
+ TreeWalk treeWalk = new NameConflictTreeWalk(repo);
+ treeWalk.addTree(baseTree);
+ treeWalk.addTree(sourceTree);
+ treeWalk.addTree(remoteTree);
+ return new TreeWalkResourceVariantTreeProvider(repo, treeWalk, 0,
+ 1, 2);
+ }
+ }
+
+ private GitResourceVariantTreeProvider createTreeProviderWithDeletions()
+ throws Exception {
+ file1 = testRepo.createFile(iProject, "file1");
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "Creation of file1 in branch1.");
+ file2 = testRepo.createFile(iProject, "file2");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2,
+ "Creation of file2 in branch2.");
+ testRepo.createBranch(MASTER, BASE);
+
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+ testRepo.untrack(file2);
+ testRepo.commit("Removed file2 in branch.");
+
+ testRepo.checkoutBranch(MASTER);
+ testRepo.untrack(file1);
+ testRepo.commit("Removed file1 in master.");
+
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+
+ // as if we tried to merge branch3 into branch2
+ try (RevWalk walk = new RevWalk(repo)) {
+ RevTree baseTree = walk.parseTree(repo.resolve(BASE));
+ RevTree sourceTree = walk.parseTree(repo.resolve(MASTER));
+ RevTree remoteTree = walk.parseTree(repo.resolve(BRANCH));
+ TreeWalk treeWalk = new NameConflictTreeWalk(repo);
+ treeWalk.addTree(baseTree);
+ treeWalk.addTree(sourceTree);
+ treeWalk.addTree(remoteTree);
+ return new TreeWalkResourceVariantTreeProvider(repo, treeWalk, 0,
+ 1, 2);
+ }
+ }
+}
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/ResourceVariantTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/ResourceVariantTest.java
new file mode 100644
index 0000000000..ede07fa3d3
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/ResourceVariantTest.java
@@ -0,0 +1,301 @@
+/*******************************************************************************
+ * Copyright (C) 2015 Obeo 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+import static org.junit.Assert.fail;
+
+import java.io.File;
+import java.util.Arrays;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.internal.storage.AbstractGitResourceVariant;
+import org.eclipse.egit.core.internal.storage.IndexResourceVariant;
+import org.eclipse.egit.core.internal.storage.TreeParserResourceVariant;
+import org.eclipse.egit.core.op.MergeOperation;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.junit.Test;
+
+public class ResourceVariantTest extends VariantsTestCase {
+ private final static String BASE_BRANCH = "base";
+
+ private final static String BRANCH_CHANGE = "branch changes\n";
+
+ private final static String MASTER_CHANGE = "master changes\n";
+
+ @Test
+ public void testIndexVariants() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ File file2 = testRepo.createFile(iProject, "file2");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+ IFile iFile2 = testRepo.getIFile(iProject, file2);
+
+ setupUnconflictingBranches();
+
+ List<String> possibleNames = Arrays.asList(iFile1.getName(),
+ iFile2.getName());
+ DirCache cache = repo.readDirCache();
+ for (int i = 0; i < cache.getEntryCount(); i++) {
+ final DirCacheEntry entry = cache.getEntry(i);
+
+ AbstractGitResourceVariant variant = IndexResourceVariant.create(
+ repo, entry);
+
+ assertEquals(entry.getObjectId().getName(),
+ variant.getContentIdentifier());
+ assertTrue(possibleNames.contains(variant.getName()));
+ assertEquals(entry.getObjectId(), variant.getObjectId());
+ assertEquals(entry.getRawMode(), variant.getRawMode());
+ if (iFile1.getName().equals(variant.getName())) {
+ assertContentEquals(variant, INITIAL_CONTENT_1 + MASTER_CHANGE);
+ } else {
+ assertContentEquals(variant, INITIAL_CONTENT_2 + MASTER_CHANGE);
+ }
+ }
+
+ testRepo.checkoutBranch(BRANCH);
+
+ cache = repo.readDirCache();
+ for (int i = 0; i < cache.getEntryCount(); i++) {
+ final DirCacheEntry entry = cache.getEntry(i);
+
+ AbstractGitResourceVariant variant = IndexResourceVariant.create(
+ repo, entry);
+ assertEquals(entry.getObjectId().getName(),
+ variant.getContentIdentifier());
+ assertTrue(possibleNames.contains(variant.getName()));
+ assertEquals(entry.getObjectId(), variant.getObjectId());
+ assertEquals(entry.getRawMode(), variant.getRawMode());
+ if (iFile1.getName().equals(variant.getName())) {
+ assertContentEquals(variant, BRANCH_CHANGE + INITIAL_CONTENT_1);
+ } else {
+ assertContentEquals(variant, BRANCH_CHANGE + INITIAL_CONTENT_2);
+ }
+ }
+ }
+
+ @Test
+ public void testIndexVariantsConflict() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ setupConflictingBranches();
+ // end setup
+
+ // create a conflict to force multiple stages
+ new MergeOperation(repo, BRANCH).execute(null);
+
+ DirCache cache = repo.readDirCache();
+ // 3 stages for file 1, 2 stages for file 2
+ assertEquals(5, cache.getEntryCount());
+ for (int i = 0; i < cache.getEntryCount(); i++) {
+ final DirCacheEntry entry = cache.getEntry(i);
+
+ AbstractGitResourceVariant variant = IndexResourceVariant.create(
+ repo, entry);
+ assertEquals(entry.getObjectId().getName(),
+ variant.getContentIdentifier());
+ assertEquals(entry.getObjectId(), variant.getObjectId());
+ assertEquals(entry.getRawMode(), variant.getRawMode());
+ if (iFile1.getName().equals(variant.getName())) {
+ switch (entry.getStage()) {
+ case DirCacheEntry.STAGE_1:
+ assertContentEquals(variant, INITIAL_CONTENT_1);
+ break;
+ case DirCacheEntry.STAGE_2:
+ assertContentEquals(variant, INITIAL_CONTENT_1
+ + MASTER_CHANGE);
+ break;
+ case DirCacheEntry.STAGE_3:
+ assertContentEquals(variant, BRANCH_CHANGE
+ + INITIAL_CONTENT_1);
+ break;
+ case DirCacheEntry.STAGE_0:
+ default:
+ fail("Unexpected entry stage " + entry.getStage()
+ + " in the index for file " + entry.getPathString());
+ break;
+ }
+ } else {
+ switch (entry.getStage()) {
+ case DirCacheEntry.STAGE_2:
+ assertContentEquals(variant, INITIAL_CONTENT_2
+ + MASTER_CHANGE);
+ break;
+ case DirCacheEntry.STAGE_3:
+ assertContentEquals(variant, BRANCH_CHANGE
+ + INITIAL_CONTENT_2);
+ break;
+ case DirCacheEntry.STAGE_0:
+ case DirCacheEntry.STAGE_1:
+ default:
+ fail("Unexpected entry stage " + entry.getStage()
+ + " in the index for file " + entry.getPathString());
+ break;
+ }
+ }
+ }
+ }
+
+ @Test
+ public void testTreeWalkBranchVariants() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ setupUnconflictingBranches();
+
+ ObjectId baseId = repo.resolve(BRANCH);
+ RevWalk walk = new RevWalk(repo);
+ TreeWalk tw = new TreeWalk(repo);
+ tw.addTree(walk.parseTree(baseId));
+
+ while (tw.next()) {
+ AbstractGitResourceVariant variant = TreeParserResourceVariant
+ .create(repo, tw.getTree(0, CanonicalTreeParser.class));
+
+ assertEquals(tw.getObjectId(0).getName(),
+ variant.getContentIdentifier());
+ assertEquals(tw.getObjectId(0), variant.getObjectId());
+ assertEquals(tw.getRawMode(0), variant.getRawMode());
+ if (iFile1.getName().equals(variant.getName())) {
+ assertContentEquals(variant, BRANCH_CHANGE + INITIAL_CONTENT_1);
+ } else if (!tw.isSubtree()) {
+ assertContentEquals(variant, BRANCH_CHANGE + INITIAL_CONTENT_2);
+ }
+
+ if (tw.isSubtree()) {
+ tw.enterSubtree();
+ }
+ }
+ }
+
+ @Test
+ public void testTreeWalkMasterVariants() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ setupUnconflictingBranches();
+
+ ObjectId baseId = repo.resolve(MASTER);
+ RevWalk walk = new RevWalk(repo);
+ TreeWalk tw = new TreeWalk(repo);
+ tw.addTree(walk.parseTree(baseId));
+
+ while (tw.next()) {
+ AbstractGitResourceVariant variant = TreeParserResourceVariant
+ .create(repo, tw.getTree(0, CanonicalTreeParser.class));
+
+ assertEquals(tw.getObjectId(0).getName(),
+ variant.getContentIdentifier());
+ assertEquals(tw.getObjectId(0), variant.getObjectId());
+ assertEquals(tw.getRawMode(0), variant.getRawMode());
+ if (iFile1.getName().equals(variant.getName())) {
+ assertContentEquals(variant, INITIAL_CONTENT_1 + MASTER_CHANGE);
+ } else if (!tw.isSubtree()) {
+ assertContentEquals(variant, INITIAL_CONTENT_2 + MASTER_CHANGE);
+ }
+
+ if (tw.isSubtree()) {
+ tw.enterSubtree();
+ }
+ }
+ }
+
+ @Test
+ public void testTreeWalkBaseVariants() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ setupUnconflictingBranches();
+
+ ObjectId baseId = repo.resolve(BASE_BRANCH);
+ RevWalk walk = new RevWalk(repo);
+ TreeWalk tw = new TreeWalk(repo);
+ tw.addTree(walk.parseTree(baseId));
+
+ while (tw.next()) {
+ AbstractGitResourceVariant variant = TreeParserResourceVariant
+ .create(repo, tw.getTree(0, CanonicalTreeParser.class));
+
+ assertEquals(tw.getObjectId(0).getName(),
+ variant.getContentIdentifier());
+ assertEquals(tw.getObjectId(0), variant.getObjectId());
+ assertEquals(tw.getRawMode(0), variant.getRawMode());
+ if (iFile1.getName().equals(variant.getName())) {
+ assertContentEquals(variant, INITIAL_CONTENT_1);
+ } else if (!tw.isSubtree()) {
+ fail("file2 shouldn't exist in our base.");
+ }
+
+ if (tw.isSubtree()) {
+ tw.enterSubtree();
+ }
+ }
+ }
+
+ private void setupUnconflictingBranches() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ File file2 = testRepo.createFile(iProject, "file2");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "first file - initial commit");
+
+ testRepo.createBranch(MASTER, BASE_BRANCH);
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+
+ setContentsAndCommit(testRepo, iFile1, BRANCH_CHANGE
+ + INITIAL_CONTENT_1, "branch commit");
+ testRepo.appendContentAndCommit(iProject, file2, BRANCH_CHANGE
+ + INITIAL_CONTENT_2, "second file - initial commit - branch");
+
+ testRepo.checkoutBranch(MASTER);
+
+ setContentsAndCommit(testRepo, iFile1, INITIAL_CONTENT_1
+ + MASTER_CHANGE, "master commit - file1");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2
+ + MASTER_CHANGE, "second file - initial commit - master");
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+ }
+
+ private void setupConflictingBranches() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ File file2 = testRepo.createFile(iProject, "file2");
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "first file - initial commit");
+
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+
+ setContentsAndCommit(testRepo, iFile1, BRANCH_CHANGE
+ + INITIAL_CONTENT_1, "branch commit");
+ testRepo.appendContentAndCommit(iProject, file2, BRANCH_CHANGE
+ + INITIAL_CONTENT_2, "second file - initial commit - branch");
+
+ testRepo.checkoutBranch(MASTER);
+
+ setContentsAndCommit(testRepo, iFile1, INITIAL_CONTENT_1
+ + MASTER_CHANGE, "master commit - file1");
+ testRepo.appendContentAndCommit(iProject, file2, INITIAL_CONTENT_2
+ + MASTER_CHANGE, "second file - initial commit - master");
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+ }
+}
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProviderTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProviderTest.java
new file mode 100644
index 0000000000..d1508c728a
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProviderTest.java
@@ -0,0 +1,98 @@
+/*******************************************************************************
+ * Copyright (C) 2015 Obeo 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevTree;
+import org.eclipse.jgit.revwalk.RevWalk;
+import org.eclipse.jgit.treewalk.NameConflictTreeWalk;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.junit.Test;
+
+public class TreeWalkResourceVariantTreeProviderTest extends VariantsTestCase {
+ @Test
+ public void testTreeWalkTrees() throws Exception {
+ File file1 = testRepo.createFile(iProject, "file1");
+ File file2 = testRepo.createFile(iProject, "file2");
+
+ testRepo.appendContentAndCommit(iProject, file1, INITIAL_CONTENT_1,
+ "first file - initial commit");
+ RevCommit baseCommit = testRepo.appendContentAndCommit(iProject, file2,
+ INITIAL_CONTENT_2, "second file - initial commit");
+
+ IFile iFile1 = testRepo.getIFile(iProject, file1);
+ IFile iFile2 = testRepo.getIFile(iProject, file2);
+
+ testRepo.createAndCheckoutBranch(MASTER, BRANCH);
+
+ final String branchChanges = "branch changes\n";
+ setContentsAndCommit(testRepo, iFile2, branchChanges
+ + INITIAL_CONTENT_2, "branch commit");
+
+ testRepo.checkoutBranch(MASTER);
+
+ final String masterChanges = "\nsome changes";
+ setContentsAndCommit(testRepo, iFile1, INITIAL_CONTENT_1
+ + masterChanges, "master commit");
+ iProject.refreshLocal(IResource.DEPTH_INFINITE,
+ new NullProgressMonitor());
+ // end setup
+
+ // as if we tried to merge branch into master
+ try (RevWalk walk = new RevWalk(repo)) {
+ RevTree baseTree = walk.parseTree(baseCommit.getId());
+ RevTree sourceTree = walk.parseTree(repo.resolve(MASTER));
+ RevTree remoteTree = walk.parseTree(repo.resolve(BRANCH));
+ TreeWalk treeWalk = new NameConflictTreeWalk(repo);
+ treeWalk.addTree(baseTree);
+ treeWalk.addTree(sourceTree);
+ treeWalk.addTree(remoteTree);
+ TreeWalkResourceVariantTreeProvider treeProvider = new TreeWalkResourceVariantTreeProvider(
+ repo, treeWalk, 0, 1, 2);
+
+ assertEquals(1, treeProvider.getRoots().size());
+ assertTrue(treeProvider.getRoots().contains(iProject));
+
+ assertTrue(treeProvider.getKnownResources().contains(iFile1));
+ assertTrue(treeProvider.getKnownResources().contains(iFile2));
+
+ IResourceVariant file1BaseVariant = treeProvider.getBaseTree()
+ .getResourceVariant(iFile1);
+ IResourceVariant file2BaseVariant = treeProvider.getBaseTree()
+ .getResourceVariant(iFile2);
+ assertContentEquals(file1BaseVariant, INITIAL_CONTENT_1);
+ assertContentEquals(file2BaseVariant, INITIAL_CONTENT_2);
+
+ IResourceVariant file1TheirsVariant = treeProvider.getRemoteTree()
+ .getResourceVariant(iFile1);
+ IResourceVariant file2TheirsVariant = treeProvider.getRemoteTree()
+ .getResourceVariant(iFile2);
+ assertContentEquals(file1TheirsVariant, INITIAL_CONTENT_1);
+ assertContentEquals(file2TheirsVariant, branchChanges
+ + INITIAL_CONTENT_2);
+
+ IResourceVariant file1OursVariant = treeProvider.getSourceTree()
+ .getResourceVariant(iFile1);
+ IResourceVariant file2OursVariant = treeProvider.getSourceTree()
+ .getResourceVariant(iFile2);
+ assertContentEquals(file1OursVariant, INITIAL_CONTENT_1
+ + masterChanges);
+ assertContentEquals(file2OursVariant, INITIAL_CONTENT_2);
+ }
+ }
+}
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/VariantsTestCase.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/VariantsTestCase.java
new file mode 100644
index 0000000000..2e2a9e7cb8
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/merge/VariantsTestCase.java
@@ -0,0 +1,101 @@
+/*******************************************************************************
+ * Copyright (C) 2015 Obeo 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import static org.junit.Assert.assertEquals;
+
+import java.io.ByteArrayInputStream;
+import java.util.Scanner;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.egit.core.test.GitTestCase;
+import org.eclipse.egit.core.test.TestRepository;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.junit.After;
+import org.junit.Before;
+
+public abstract class VariantsTestCase extends GitTestCase {
+ protected final String INITIAL_CONTENT_1 = "some content for the first file";
+
+ protected final String INITIAL_CONTENT_2 = "some content for the second file";
+
+ protected static final String MASTER = Constants.R_HEADS + Constants.MASTER;
+
+ protected static final String BRANCH = Constants.R_HEADS + "branch";
+
+ protected Repository repo;
+
+ protected IProject iProject;
+
+ protected TestRepository testRepo;
+
+ @Before
+ @Override
+ public void setUp() throws Exception {
+ super.setUp();
+
+ iProject = project.project;
+ testRepo = new TestRepository(gitDir);
+ testRepo.connect(iProject);
+ repo = RepositoryMapping.getMapping(iProject).getRepository();
+
+ // make initial commit
+ try (Git git = new Git(repo)) {
+ git.commit().setAuthor("JUnit", "junit@jgit.org")
+ .setMessage("Initial commit").call();
+ }
+ }
+
+ @After
+ @Override
+ public void tearDown() throws Exception {
+ testRepo.disconnect(iProject);
+ testRepo.dispose();
+ repo = null;
+
+ super.tearDown();
+ }
+
+ protected RevCommit setContentsAndCommit(TestRepository testRepository,
+ IFile targetFile, String newContents, String commitMessage)
+ throws Exception {
+ targetFile.setContents(
+ new ByteArrayInputStream(newContents.getBytes()),
+ IResource.FORCE, new NullProgressMonitor());
+ testRepository.addToIndex(targetFile);
+ return testRepository.commit(commitMessage);
+ }
+
+ protected void assertContentEquals(IResourceVariant variant,
+ String expectedContents) throws Exception {
+ assertContentEquals(variant.getStorage(new NullProgressMonitor()),
+ expectedContents);
+ }
+
+ protected void assertContentEquals(IStorage storage, String expectedContents)
+ throws Exception {
+ try (Scanner scanner = new Scanner(storage.getContents())) {
+ scanner.useDelimiter("\\A");
+ String fileContent = "";
+ if (scanner.hasNext()) {
+ fileContent = scanner.next();
+ }
+ assertEquals(expectedContents, fileContent);
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java
index e92b70be61..297a43729c 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java
@@ -5,6 +5,7 @@
* Copyright (C) 2012, Markus Duft <markus.duft@salomon.at>
* Copyright (C) 2013, Matthias Sohn <matthias.sohn@sap.com>
* Copyright (C) 2013, Daniel Megert <daniel_megert@ch.ibm.com>
+ * Copyright (C) 2015, Obeo.
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -391,6 +392,9 @@ public class CoreText extends NLS {
public static String GitProjectData_UnmappingGoneResourceFailed;
/** */
+ public static String GitResourceVariantTreeSubscriber_name;
+
+ /** */
public static String GitResourceVariantTreeSubscriber_fetchTaskName;
/** */
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties
index 003a867d02..0ce50d79e9 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties
@@ -156,6 +156,7 @@ GitProjectData_repositoryChangedTaskName=Git repository changed
GitProjectData_UnmapJobName=Disconnecting project {0} from Git repository
GitProjectData_UnmappingGoneResourceFailed=Unmapping gone mapped resource {0} failed
+GitResourceVariantTreeSubscriber_name = Git Resource Variant Tree Subscriber
GitResourceVariantTreeSubscriber_fetchTaskName=Fetching data from git repositories
GitResourceVariantTreeSubscriber_CouldNotFindSourceVariant=Could not find source variant for resource: {0}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProvider.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProvider.java
new file mode 100644
index 0000000000..95bb42a719
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/DirCacheResourceVariantTreeProvider.java
@@ -0,0 +1,129 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.egit.core.internal.storage.IndexResourceVariant;
+import org.eclipse.egit.core.internal.util.ResourceUtil;
+import org.eclipse.jgit.dircache.DirCache;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.team.core.variants.IResourceVariantTree;
+
+/**
+ * This will populate its three {@link IResourceVariantTree} by looking up
+ * information within the repository's DirCache.
+ * <p>
+ * Files that are not located within the workspace will be ignored and thus will
+ * not be accessible through the trees created by this provider.
+ * </p>
+ */
+public class DirCacheResourceVariantTreeProvider implements
+ GitResourceVariantTreeProvider {
+ private final IResourceVariantTree baseTree;
+
+ private final IResourceVariantTree sourceTree;
+
+ private final IResourceVariantTree remoteTree;
+
+ private final Set<IResource> roots;
+
+ private final Set<IResource> knownResources;
+
+ /**
+ * Constructs the resource variant trees by iterating over the given
+ * repository's DirCache entries.
+ *
+ * @param repository
+ * The repository which DirCache info we need to cache as
+ * IResourceVariantTrees.
+ * @throws IOException
+ * if we somehow cannot read the DirCache.
+ */
+ public DirCacheResourceVariantTreeProvider(Repository repository)
+ throws IOException {
+ final DirCache cache = repository.readDirCache();
+ final GitResourceVariantCache baseCache = new GitResourceVariantCache();
+ final GitResourceVariantCache sourceCache = new GitResourceVariantCache();
+ final GitResourceVariantCache remoteCache = new GitResourceVariantCache();
+
+ for (int i = 0; i < cache.getEntryCount(); i++) {
+ final DirCacheEntry entry = cache.getEntry(i);
+ final IPath path = new Path(entry.getPathString());
+ final IResource resource = ResourceUtil
+ .getResourceHandleForLocation(path);
+ // Resource variants only make sense for IResources. Do not consider
+ // files outside of the workspace or otherwise non accessible.
+ if (resource == null || resource.getProject() == null
+ || !resource.getProject().isAccessible()) {
+ continue;
+ }
+ switch (entry.getStage()) {
+ case DirCacheEntry.STAGE_0:
+ // Skipped on purpose (no conflict)
+ break;
+ case DirCacheEntry.STAGE_1:
+ baseCache.setVariant(resource,
+ IndexResourceVariant.create(repository, entry));
+ break;
+ case DirCacheEntry.STAGE_2:
+ sourceCache.setVariant(resource,
+ IndexResourceVariant.create(repository, entry));
+ break;
+ case DirCacheEntry.STAGE_3:
+ remoteCache.setVariant(resource,
+ IndexResourceVariant.create(repository, entry));
+ break;
+ default:
+ throw new IllegalStateException(
+ "Invalid stage: " + entry.getStage()); //$NON-NLS-1$
+ }
+ }
+
+ baseTree = new GitCachedResourceVariantTree(baseCache);
+ sourceTree = new GitCachedResourceVariantTree(sourceCache);
+ remoteTree = new GitCachedResourceVariantTree(remoteCache);
+
+ roots = new LinkedHashSet<IResource>();
+ roots.addAll(baseCache.getRoots());
+ roots.addAll(sourceCache.getRoots());
+ roots.addAll(remoteCache.getRoots());
+
+ knownResources = new LinkedHashSet<IResource>();
+ knownResources.addAll(baseCache.getKnownResources());
+ knownResources.addAll(sourceCache.getKnownResources());
+ knownResources.addAll(remoteCache.getKnownResources());
+ }
+
+ public IResourceVariantTree getBaseTree() {
+ return baseTree;
+ }
+
+ public IResourceVariantTree getRemoteTree() {
+ return remoteTree;
+ }
+
+ public IResourceVariantTree getSourceTree() {
+ return sourceTree;
+ }
+
+ public Set<IResource> getKnownResources() {
+ return knownResources;
+ }
+
+ public Set<IResource> getRoots() {
+ return roots;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitCachedResourceVariantTree.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitCachedResourceVariantTree.java
new file mode 100644
index 0000000000..3a0110c05e
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitCachedResourceVariantTree.java
@@ -0,0 +1,67 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.core.variants.IResourceVariantTree;
+
+/**
+ * An immutable resource variant tree backed by a
+ * {@link GitResourceVariantCache}. This will never contact the server.
+ * <p>
+ * This will not react to refreshing calls and shouldn't be used for
+ * synchronization purposes.
+ * </p>
+ */
+/*
+ * Illegal implementation of IResourceVariantTree : we could also extend the
+ * AbstractResourceVariantTree... but since we don't react to refreshing calls
+ * anyway, we do not need the extra logic it provides.
+ */
+class GitCachedResourceVariantTree implements IResourceVariantTree {
+ private final GitResourceVariantCache cache;
+
+ public GitCachedResourceVariantTree(GitResourceVariantCache cache) {
+ this.cache = cache;
+ }
+
+ public IResource[] roots() {
+ final Set<IResource> roots = cache.getRoots();
+ return roots.toArray(new IResource[roots.size()]);
+ }
+
+ public IResource[] members(IResource resource) throws TeamException {
+ return cache.members(resource);
+ }
+
+ public IResourceVariant getResourceVariant(IResource resource)
+ throws TeamException {
+ return cache.getVariant(resource);
+ }
+
+ public boolean hasResourceVariant(IResource resource) throws TeamException {
+ return cache.getVariant(resource) != null;
+ }
+
+ public IResource[] refresh(IResource[] resources, int depth,
+ IProgressMonitor monitor) throws TeamException {
+ // This does not react to refresh calls
+ return new IResource[0];
+ }
+
+ public void flushVariants(IResource resource, int depth)
+ throws TeamException {
+ // Empty implementation
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantCache.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantCache.java
new file mode 100644
index 0000000000..523f0d195a
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantCache.java
@@ -0,0 +1,91 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import java.util.Collections;
+import java.util.LinkedHashMap;
+import java.util.LinkedHashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.team.core.variants.IResourceVariant;
+
+/**
+ * Caches the resource variants corresponding to local IResources.
+ */
+class GitResourceVariantCache {
+ private final Map<IResource, IResourceVariant> cache = new LinkedHashMap<IResource, IResourceVariant>();
+
+ private final Map<IResource, Set<IResource>> members = new LinkedHashMap<IResource, Set<IResource>>();
+
+ private final Set<IResource> roots = new LinkedHashSet<IResource>();
+
+ /**
+ * Sets the variant associated with the given resource in this cache.
+ *
+ * @param resource
+ * The resource for which we need to cache a variant.
+ * @param variant
+ * Variant for the resource.
+ */
+ public void setVariant(IResource resource, IResourceVariant variant) {
+ cache.put(resource, variant);
+
+ IProject project = resource.getProject();
+ roots.add(project);
+
+ members.put(resource, new LinkedHashSet<IResource>());
+
+ final IResource parent = resource.getParent();
+ Set<IResource> parentMembers = members.get(parent);
+ if (parentMembers == null) {
+ parentMembers = new LinkedHashSet<IResource>();
+ members.put(parent, parentMembers);
+ }
+ parentMembers.add(resource);
+ }
+
+ /**
+ * @param resource
+ * The resource which variant we need.
+ * @return The variant associated with this resource in this cache.
+ */
+ public IResourceVariant getVariant(IResource resource) {
+ return cache.get(resource);
+ }
+
+ /**
+ * @return The known roots of the tree we were populated from.
+ */
+ public Set<IResource> getRoots() {
+ return Collections.unmodifiableSet(roots);
+ }
+
+ /**
+ * @return All resources for which this cache holds variants.
+ */
+ public Set<IResource> getKnownResources() {
+ return Collections.unmodifiableSet(cache.keySet());
+ }
+
+ /**
+ * Returns all members of the given resource for which we hold variants.
+ *
+ * @param resource
+ * The resource which members we need.
+ * @return All members of the given resource for which we hold variants; an
+ * empty array if none.
+ */
+ public IResource[] members(IResource resource) {
+ final Set<IResource> children = members.get(resource);
+ return children.toArray(new IResource[children.size()]);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantFileRevision.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantFileRevision.java
new file mode 100644
index 0000000000..5c2b815b66
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantFileRevision.java
@@ -0,0 +1,60 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import org.eclipse.egit.core.synchronize.GitRemoteResource;
+import org.eclipse.jgit.lib.PersonIdent;
+import org.eclipse.team.internal.core.mapping.ResourceVariantFileRevision;
+
+/**
+ * The default implementation of ResourceVariantFileRevision has no author,
+ * comment, timestamp... or any information that could be provided by the Git
+ * resource variant. This implementation uses the variant's information.
+ */
+class GitResourceVariantFileRevision extends ResourceVariantFileRevision {
+
+ public GitResourceVariantFileRevision(GitRemoteResource variant) {
+ super(variant);
+ }
+
+ @Override
+ public GitRemoteResource getVariant() {
+ return (GitRemoteResource) super.getVariant();
+ }
+
+ @Override
+ public String getContentIdentifier() {
+ // Use the same ID as would CommitFileRevision
+ return getVariant().getCommitId().getId().getName();
+ }
+
+ @Override
+ public long getTimestamp() {
+ final PersonIdent author = getVariant().getCommitId().getAuthorIdent();
+ if (author != null) {
+ return author.getWhen().getTime();
+ }
+ return super.getTimestamp();
+ }
+
+ @Override
+ public String getAuthor() {
+ final PersonIdent author = getVariant().getCommitId().getAuthorIdent();
+ if (author != null) {
+ return author.getName();
+ }
+ return super.getAuthor();
+ }
+
+ @Override
+ public String getComment() {
+ return getVariant().getCommitId().getFullMessage();
+
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeProvider.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeProvider.java
new file mode 100644
index 0000000000..04e30379fc
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeProvider.java
@@ -0,0 +1,64 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.team.core.variants.IResourceVariantTree;
+
+/**
+ * Resource variant trees are in charge of providing the
+ * {@link org.eclipse.team.core.subscribers.Subscriber Subscribers} with
+ * resource variants, allowing them to retrieve the content of a given file in
+ * different states (remote, local, index, workspace...).
+ */
+public interface GitResourceVariantTreeProvider {
+ /**
+ * Returns the base resource variant tree. This should provide access to the
+ * common ancestor of the "source" and "remote" resource variants.
+ *
+ * @return The base resource variant tree.
+ */
+ IResourceVariantTree getBaseTree();
+
+ /**
+ * Returns the remote resource variant tree. This is traditionally the
+ * remote data, or 'right' side of a comparison. In git terms, this is the
+ * "theirs" side.
+ *
+ * @return The remote resource variant tree.
+ */
+ IResourceVariantTree getRemoteTree();
+
+ /**
+ * Returns the source resource variant tree. This is traditionally the local
+ * data, or 'left' side of a comparison. In git terms, this is the "ours"
+ * side.
+ *
+ * @return The source resource variant tree.
+ */
+ IResourceVariantTree getSourceTree();
+
+ /**
+ * @return The list of root resources for which this provider's trees may
+ * hold variants.
+ */
+ Set<IResource> getRoots();
+
+ /**
+ * Returns the whole set of resources for which this provider's trees hold
+ * variants. The returned resources may not necessarily exist in all three
+ * underlying trees.
+ *
+ * @return The whole set of resources for which this provider's trees hold
+ * variants.
+ */
+ Set<IResource> getKnownResources();
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriber.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriber.java
new file mode 100644
index 0000000000..30368af4b6
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitResourceVariantTreeSubscriber.java
@@ -0,0 +1,164 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import java.util.Arrays;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.egit.core.internal.CoreText;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.diff.IDiff;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.core.variants.IResourceVariantComparator;
+import org.eclipse.team.core.variants.IResourceVariantTree;
+import org.eclipse.team.core.variants.ResourceVariantTreeSubscriber;
+import org.eclipse.team.internal.core.mapping.SyncInfoToDiffConverter;
+
+/**
+ * This implementation of a {@link ResourceVariantTreeSubscriber} takes its
+ * input from a {@link GitResourceVariantTreeProvider}.
+ * <p>
+ * This allows us to hijack all calls from the default subscriber for "local"
+ * resources to our actual source tree, which could be the local working
+ * directory as well as it could be a branch.
+ * </p>
+ */
+public class GitResourceVariantTreeSubscriber extends
+ ResourceVariantTreeSubscriber {
+ private GitResourceVariantTreeProvider variantTreeProvider;
+
+ private final SyncInfoToDiffConverter syncInfoConverter;
+
+ private final IResourceVariantComparator comparator;
+
+ /**
+ * @param variantTreeProvider
+ * The instance that will provide the base, source and remote
+ * trees to this subscriber.
+ */
+ public GitResourceVariantTreeSubscriber(
+ GitResourceVariantTreeProvider variantTreeProvider) {
+ this.variantTreeProvider = variantTreeProvider;
+ syncInfoConverter = new GitSyncInfoToDiffConverter(variantTreeProvider);
+ comparator = new GitVariantComparator(
+ variantTreeProvider.getSourceTree());
+ }
+
+ @Override
+ protected IResourceVariantTree getBaseTree() {
+ return variantTreeProvider.getBaseTree();
+ }
+
+ @Override
+ protected IResourceVariantTree getRemoteTree() {
+ return variantTreeProvider.getRemoteTree();
+ }
+
+ /**
+ * @return the source resource variant tree.
+ */
+ protected IResourceVariantTree getSourceTree() {
+ return variantTreeProvider.getSourceTree();
+ }
+
+ @Override
+ public IDiff getDiff(IResource resource) throws CoreException {
+ final SyncInfo info = getSyncInfo(resource);
+ if (info == null || info.getKind() == SyncInfo.IN_SYNC)
+ return null;
+ return syncInfoConverter.getDeltaFor(info);
+ }
+
+ @Override
+ public SyncInfo getSyncInfo(IResource resource) throws TeamException {
+ // Overridden here to properly catch and re-throw the forwarded
+ // TeamException
+ try {
+ return super.getSyncInfo(resource);
+ } catch (ForwardedTeamException e) {
+ throw (TeamException) e.getCause();
+ }
+ }
+
+ @Override
+ public String getName() {
+ return CoreText.GitResourceVariantTreeSubscriber_name;
+ }
+
+ @Override
+ public boolean isSupervised(IResource resource) throws TeamException {
+ return variantTreeProvider.getKnownResources().contains(resource);
+ }
+
+ @Override
+ public IResource[] roots() {
+ final Set<IResource> roots = variantTreeProvider.getRoots();
+ return roots.toArray(new IResource[roots.size()]);
+ }
+
+ @Override
+ public IResourceVariantComparator getResourceComparator() {
+ return comparator;
+ }
+
+ /**
+ * We have a source tree whereas Team only knows about "local" files. This
+ * will always use said {@link #oursTree source tree} when comparing
+ * variants.
+ */
+ private static class GitVariantComparator implements
+ IResourceVariantComparator {
+ private final IResourceVariantTree oursTree;
+
+ public GitVariantComparator(IResourceVariantTree oursTree) {
+ this.oursTree = oursTree;
+ }
+
+ public boolean compare(IResource local, IResourceVariant remote) {
+ try {
+ final IResourceVariant oursVariant = oursTree
+ .getResourceVariant(local);
+ if (oursVariant == null)
+ return remote == null;
+ return compare(oursVariant, remote);
+ } catch (TeamException e) {
+ // We can't throw the TeamException from here, but we can't let
+ // the comparison go through either.
+ // This is only called from "getSyncInfo", we'll forward this
+ // exception and rethrow it from there.
+ throw new ForwardedTeamException(e);
+ }
+ }
+
+ public boolean compare(IResourceVariant base, IResourceVariant remote) {
+ return Arrays.equals(base.asBytes(), remote.asBytes());
+ }
+
+ public boolean isThreeWay() {
+ return true;
+ }
+ }
+
+ /**
+ * This should never be thrown outside of this class. The only purpose of
+ * this exception is to encapsulate a TeamException where it cannot be
+ * thrown.
+ */
+ private static class ForwardedTeamException extends RuntimeException {
+ /** Generated SUID. */
+ private static final long serialVersionUID = 4074010396155542178L;
+
+ public ForwardedTeamException(TeamException e) {
+ super(e);
+ }
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitSyncInfoToDiffConverter.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitSyncInfoToDiffConverter.java
new file mode 100644
index 0000000000..b82e9a7b60
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/GitSyncInfoToDiffConverter.java
@@ -0,0 +1,191 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.internal.CoreText;
+import org.eclipse.egit.core.internal.storage.WorkspaceFileRevision;
+import org.eclipse.egit.core.synchronize.GitRemoteResource;
+import org.eclipse.osgi.util.NLS;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.core.diff.IDiff;
+import org.eclipse.team.core.diff.ITwoWayDiff;
+import org.eclipse.team.core.diff.provider.ThreeWayDiff;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.mapping.provider.ResourceDiff;
+import org.eclipse.team.core.synchronize.SyncInfo;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.team.internal.core.mapping.ResourceVariantFileRevision;
+import org.eclipse.team.internal.core.mapping.SyncInfoToDiffConverter;
+
+/**
+ * The default implementation of SyncInfoToDiffConverter uses inaccurate
+ * information with regards to some of EGit features.
+ * <p>
+ * SyncInfoToDiffConverter#asFileRevision(IResourceVariant) is called when a
+ * user double-clicks a revision from the synchronize view (among others).
+ * However, the default implementation returns an IFileRevision with no comment,
+ * author or timestamp information.
+ * </p>
+ * <p>
+ * SyncInfoToDiffConverter#getDeltaFor(SyncInfo) had been originally thought by
+ * Team to be used for synchronizations that considered local changes. This is
+ * not always the case with EGit. For example, a user might try and compare two
+ * refs together from the Git repository explorer (right click > synchronize
+ * with each other). In such a case, the local files must not be taken into
+ * account.
+ * </p>
+ * <p>
+ * Most of the private methods here were copy/pasted from the super
+ * implementation.
+ * </p>
+ */
+public class GitSyncInfoToDiffConverter extends SyncInfoToDiffConverter {
+ private GitResourceVariantTreeProvider variantTreeProvider;
+
+ /**
+ * Creates our diff converter given the provider of our variant trees.
+ *
+ * @param variantTreeProvider
+ * Provides the resource variant trees that should be used to
+ * query file revisions.
+ */
+ public GitSyncInfoToDiffConverter(
+ GitResourceVariantTreeProvider variantTreeProvider) {
+ this.variantTreeProvider = variantTreeProvider;
+ }
+
+ @Override
+ public IDiff getDeltaFor(SyncInfo info) {
+ if (info.getComparator().isThreeWay()) {
+ ITwoWayDiff local = getLocalDelta(info);
+ ITwoWayDiff remote = getRemoteDelta(info);
+ return new ThreeWayDiff(local, remote);
+ } else {
+ if (info.getKind() != SyncInfo.IN_SYNC) {
+ IResourceVariant remote = info.getRemote();
+ IResource local = info.getLocal();
+
+ int kind;
+ if (remote == null) {
+ kind = IDiff.REMOVE;
+ } else if (!local.exists()) {
+ kind = IDiff.ADD;
+ } else {
+ kind = IDiff.CHANGE;
+ }
+ if (local.getType() == IResource.FILE) {
+ IFileRevision after = asFileState(remote);
+ IFileRevision before = getLocalFileRevision((IFile) local);
+ return new ResourceDiff(info.getLocal(), kind, 0, before,
+ after);
+ }
+ // For folders, we don't need file states
+ return new ResourceDiff(info.getLocal(), kind);
+ }
+ return null;
+ }
+ }
+
+ private ITwoWayDiff getLocalDelta(SyncInfo info) {
+ int direction = SyncInfo.getDirection(info.getKind());
+ if (direction == SyncInfo.OUTGOING || direction == SyncInfo.CONFLICTING) {
+ IResourceVariant ancestor = info.getBase();
+ IResource local = info.getLocal();
+
+ int kind;
+ if (ancestor == null) {
+ kind = IDiff.ADD;
+ } else if (!local.exists()) {
+ kind = IDiff.REMOVE;
+ } else {
+ kind = IDiff.CHANGE;
+ }
+ if (local.getType() == IResource.FILE) {
+ IFileRevision before = asFileState(ancestor);
+ IFileRevision after = getLocalFileRevision((IFile) local);
+ return new ResourceDiff(info.getLocal(), kind, 0, before, after);
+ }
+ // For folders, we don't need file states
+ return new ResourceDiff(info.getLocal(), kind);
+ }
+ return null;
+ }
+
+ /**
+ * Returns a file revision from the source tree for this local file.
+ *
+ * @param local
+ * The local file.
+ * @return The file revision that should be considered for the local (left)
+ * side of a delta
+ */
+ public IFileRevision getLocalFileRevision(IFile local) {
+ try {
+ return asFileState(variantTreeProvider.getSourceTree()
+ .getResourceVariant(local));
+ } catch (TeamException e) {
+ String error = NLS
+ .bind(CoreText.GitResourceVariantTreeSubscriber_CouldNotFindSourceVariant,
+ local.getName());
+ Activator.logError(error, e);
+ // fall back to the working tree version
+ return new WorkspaceFileRevision(local);
+ }
+ }
+
+ /*
+ * copied from the private implementation in SyncInfoToDiffConverter
+ */
+ private ITwoWayDiff getRemoteDelta(SyncInfo info) {
+ int direction = SyncInfo.getDirection(info.getKind());
+ if (direction == SyncInfo.INCOMING || direction == SyncInfo.CONFLICTING) {
+ IResourceVariant ancestor = info.getBase();
+ IResourceVariant remote = info.getRemote();
+
+ int kind;
+ if (ancestor == null)
+ kind = IDiff.ADD;
+ else if (remote == null)
+ kind = IDiff.REMOVE;
+ else
+ kind = IDiff.CHANGE;
+
+ // For folders, we don't need file states
+ if (info.getLocal().getType() == IResource.FILE) {
+ IFileRevision before = asFileState(ancestor);
+ IFileRevision after = asFileState(remote);
+ return new ResourceDiff(info.getLocal(), kind, 0, before, after);
+ }
+
+ return new ResourceDiff(info.getLocal(), kind);
+ }
+ return null;
+ }
+
+ /*
+ * copied from the private implementation in SyncInfoToDiffConverter
+ */
+ private IFileRevision asFileState(final IResourceVariant variant) {
+ if (variant == null)
+ return null;
+ return asFileRevision(variant);
+ }
+
+ @Override
+ protected ResourceVariantFileRevision asFileRevision(
+ IResourceVariant variant) {
+ if (variant instanceof GitRemoteResource)
+ return new GitResourceVariantFileRevision(
+ (GitRemoteResource) variant);
+ return new ResourceVariantFileRevision(variant);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProvider.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProvider.java
new file mode 100644
index 0000000000..24c1b86aa2
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/merge/TreeWalkResourceVariantTreeProvider.java
@@ -0,0 +1,167 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.merge;
+
+import java.io.IOException;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.egit.core.internal.storage.TreeParserResourceVariant;
+import org.eclipse.egit.core.internal.util.ResourceUtil;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.AbstractTreeIterator;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.jgit.treewalk.TreeWalk;
+import org.eclipse.team.core.variants.IResourceVariantTree;
+
+/**
+ * This will populate its three {@link IResourceVariantTree} by walking over a
+ * tree walk and caching the IResources it spans.
+ * <p>
+ * Files that are not located within the workspace will be ignored and thus will
+ * not be accessible through the trees created by this provider.
+ * </p>
+ */
+public class TreeWalkResourceVariantTreeProvider implements
+ GitResourceVariantTreeProvider {
+ private final IResourceVariantTree baseTree;
+
+ private final IResourceVariantTree oursTree;
+
+ private final IResourceVariantTree theirsTree;
+
+ private final Set<IResource> roots;
+
+ private final Set<IResource> knownResources;
+
+ /**
+ * Constructs the resource variant trees by iterating over the given tree
+ * walk. This TreeWalk must contain at least three trees corresponding to
+ * the three "sides" we need.
+ * <p>
+ * The tree walk will be reset to its initial state when we are done with
+ * the iteration.
+ * </p>
+ *
+ * @param repository
+ * The repository this tree walk has been created for.
+ * @param treeWalk
+ * The tree walk to iterate over.
+ * @param baseIndex
+ * Index of the ancestor tree in the given TreeWalk (value
+ * returned by {@link TreeWalk#addTree(AbstractTreeIterator)})
+ * @param ourIndex
+ * Index of our tree in the given TreeWalk (value returned by
+ * {@link TreeWalk#addTree(AbstractTreeIterator)})
+ * @param theirIndex
+ * Index of their tree in the given TreeWalk (value returned by
+ * {@link TreeWalk#addTree(AbstractTreeIterator)})
+ * @throws IOException
+ * if we somehow cannot iterate over the treewalk.
+ */
+ public TreeWalkResourceVariantTreeProvider(Repository repository,
+ TreeWalk treeWalk, int baseIndex, int ourIndex, int theirIndex)
+ throws IOException {
+ // Record the initial state of this tree walk before iterating
+ final AbstractTreeIterator[] initialTrees = new AbstractTreeIterator[treeWalk
+ .getTreeCount()];
+ for (int i = 0; i < treeWalk.getTreeCount(); i++) {
+ initialTrees[i] = treeWalk.getTree(i, AbstractTreeIterator.class);
+ }
+
+ final GitResourceVariantCache baseCache = new GitResourceVariantCache();
+ final GitResourceVariantCache theirsCache = new GitResourceVariantCache();
+ final GitResourceVariantCache oursCache = new GitResourceVariantCache();
+
+ while (treeWalk.next()) {
+ final int modeBase = treeWalk.getRawMode(baseIndex);
+ final int modeOurs = treeWalk.getRawMode(ourIndex);
+ final int modeTheirs = treeWalk.getRawMode(theirIndex);
+ if (modeBase == 0 && modeOurs == 0 && modeTheirs == 0) {
+ // untracked
+ continue;
+ }
+
+ final CanonicalTreeParser base = treeWalk.getTree(baseIndex,
+ CanonicalTreeParser.class);
+ final CanonicalTreeParser ours = treeWalk.getTree(ourIndex,
+ CanonicalTreeParser.class);
+ final CanonicalTreeParser theirs = treeWalk.getTree(theirIndex,
+ CanonicalTreeParser.class);
+
+ final IPath path = new Path(treeWalk.getPathString());
+ final IResource resource = ResourceUtil
+ .getResourceHandleForLocation(path);
+ // Resource variants only make sense for IResources. Do not consider
+ // files outside of the workspace or otherwise non accessible.
+ if (resource != null && resource.getProject().isAccessible()) {
+ if (modeBase != 0) {
+ baseCache.setVariant(resource,
+ TreeParserResourceVariant.create(repository, base));
+ }
+ if (modeOurs != 0) {
+ oursCache.setVariant(resource,
+ TreeParserResourceVariant.create(repository, ours));
+ }
+ if (modeTheirs != 0) {
+ theirsCache.setVariant(resource,
+ TreeParserResourceVariant.create(repository, theirs));
+ }
+ }
+
+ if (treeWalk.isSubtree()) {
+ treeWalk.enterSubtree();
+ }
+ }
+
+ // TODO any better way to reset the tree walk after an iteration?
+ treeWalk.reset();
+ for (int i = 0; i < initialTrees.length; i++) {
+ initialTrees[i].reset();
+ treeWalk.addTree(initialTrees[i]);
+ }
+
+ baseTree = new GitCachedResourceVariantTree(baseCache);
+ theirsTree = new GitCachedResourceVariantTree(theirsCache);
+ oursTree = new GitCachedResourceVariantTree(oursCache);
+
+ roots = new LinkedHashSet<IResource>();
+ roots.addAll(baseCache.getRoots());
+ roots.addAll(oursCache.getRoots());
+ roots.addAll(theirsCache.getRoots());
+
+ knownResources = new LinkedHashSet<IResource>();
+ knownResources.addAll(baseCache.getKnownResources());
+ knownResources.addAll(oursCache.getKnownResources());
+ knownResources.addAll(theirsCache.getKnownResources());
+ }
+
+ public IResourceVariantTree getBaseTree() {
+ return baseTree;
+ }
+
+ public IResourceVariantTree getRemoteTree() {
+ return theirsTree;
+ }
+
+ public IResourceVariantTree getSourceTree() {
+ return oursTree;
+ }
+
+ public Set<IResource> getKnownResources() {
+ return knownResources;
+ }
+
+ public Set<IResource> getRoots() {
+ return roots;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/AbstractGitResourceVariant.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/AbstractGitResourceVariant.java
new file mode 100644
index 0000000000..22effd8371
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/AbstractGitResourceVariant.java
@@ -0,0 +1,112 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.team.core.variants.IResourceVariant;
+
+/**
+ * Base class of the git-related resource variants.
+ */
+public abstract class AbstractGitResourceVariant implements IResourceVariant {
+ /** Repository in which this variant's content will be accessed. */
+ protected final Repository repository;
+
+ /** Repository-relative path of this resource. */
+ protected final String path;
+
+ /**
+ * Whether this resource is a container or not in this particular variant.
+ * This may be different than the local resource's state (if there is a
+ * file/folder conflict for example).
+ */
+ protected final boolean isContainer;
+
+ /** Object id of this variant in its repository. */
+ protected final ObjectId objectId;
+
+ /** Raw mode bits of this variant. */
+ protected final int rawMode;
+
+ /**
+ * @param repository
+ * Repository in which this variant's content will be accessed.
+ * @param path
+ * Repository-relative path of this resource.
+ * @param isContainer
+ * Whether this resource is a container or not in this particular
+ * variant.
+ * @param objectId
+ * Object id of this variant in its repository.
+ * @param rawMode
+ * Raw mode bits of this variant.
+ */
+ protected AbstractGitResourceVariant(Repository repository, String path,
+ boolean isContainer, ObjectId objectId, int rawMode) {
+ this.repository = repository;
+ this.path = path;
+ this.isContainer = isContainer;
+ this.objectId = objectId;
+ this.rawMode = rawMode;
+ }
+
+ public String getName() {
+ int lastSeparator = path.lastIndexOf('/');
+ return path.substring(lastSeparator + 1);
+ }
+
+ public boolean isContainer() {
+ return isContainer;
+ }
+
+ public String getContentIdentifier() {
+ return objectId.name();
+ }
+
+ public byte[] asBytes() {
+ return objectId.name().getBytes();
+ }
+
+ /**
+ * @return the object id of this variant in its backing repository.
+ */
+ public ObjectId getObjectId() {
+ return objectId;
+ }
+
+ /**
+ * @return the raw mode of this variant.
+ */
+ public int getRawMode() {
+ return rawMode;
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (obj == this)
+ return true;
+ if (obj instanceof AbstractGitResourceVariant) {
+ AbstractGitResourceVariant other = (AbstractGitResourceVariant) obj;
+ return this.path.equals(other.path)
+ && this.repository.equals(other.repository)
+ && this.objectId.equals(other.objectId);
+ }
+ return false;
+ }
+
+ @Override
+ public int hashCode() {
+ int hash = 37;
+ hash = 37 * hash + (path != null ? path.hashCode() : 0);
+ hash = 37 * hash + (repository != null ? repository.hashCode() : 0);
+ hash = 37 * hash + (objectId != null ? objectId.hashCode() : 0);
+ return hash;
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexResourceVariant.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexResourceVariant.java
new file mode 100644
index 0000000000..14b3b0be44
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/IndexResourceVariant.java
@@ -0,0 +1,53 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.jgit.dircache.DirCacheEntry;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.team.core.TeamException;
+
+/**
+ * Implementation of a resource variant populated through a Repository's
+ * DirCache information.
+ */
+public class IndexResourceVariant extends AbstractGitResourceVariant {
+ private IndexResourceVariant(Repository repository, String path,
+ boolean isContainer, ObjectId objectId, int rawMode) {
+ super(repository, path, isContainer, objectId, rawMode);
+ }
+
+ /**
+ * Constructs a resource variant corresponding to the given DirCache entry.
+ *
+ * @param repository
+ * Repository from which this DirCacheEntry was extracted.
+ * @param entry
+ * The DirCacheEntry for which content we need an
+ * IResourceVariant.
+ * @return The created variant.
+ */
+ public static IndexResourceVariant create(Repository repository,
+ DirCacheEntry entry) {
+ final String path = entry.getPathString();
+ final boolean isContainer = FileMode.TREE.equals(entry.getFileMode());
+ final ObjectId objectId = entry.getObjectId();
+ final int rawMode = entry.getRawMode();
+
+ return new IndexResourceVariant(repository, path, isContainer,
+ objectId, rawMode);
+ }
+
+ public IStorage getStorage(IProgressMonitor monitor) throws TeamException {
+ return new IndexBlobStorage(repository, path, objectId);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/TreeParserResourceVariant.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/TreeParserResourceVariant.java
new file mode 100644
index 0000000000..772ae80aee
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/TreeParserResourceVariant.java
@@ -0,0 +1,57 @@
+/*******************************************************************************
+ * Copyright (C) 2015, Obeo.
+ *
+ * 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
+ *******************************************************************************/
+package org.eclipse.egit.core.internal.storage;
+
+import org.eclipse.core.resources.IStorage;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.egit.core.storage.GitBlobStorage;
+import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.treewalk.CanonicalTreeParser;
+import org.eclipse.team.core.TeamException;
+
+/**
+ * Implementation of a resource variant populated through a CanonicalTreeParser.
+ * This can provide access to local resources as well as their remote variants.
+ */
+public class TreeParserResourceVariant extends AbstractGitResourceVariant {
+ private TreeParserResourceVariant(Repository repository, String path,
+ boolean isContainer, ObjectId objectId, int rawMode) {
+ super(repository, path, isContainer, objectId, rawMode);
+ }
+
+ /**
+ * Constructs a resource variant corresponding to the current entry of the
+ * given CanonicalTreeParser.
+ *
+ * @param repository
+ * Repository from which this CanonicalTreeParser was created.
+ * @param treeParser
+ * A CanonicalTreeParser to retrieve information from. This will
+ * only read information about the current entry on which this
+ * parser is positioned and will not change its state.
+ * @return The created variant.
+ */
+ public static TreeParserResourceVariant create(Repository repository,
+ CanonicalTreeParser treeParser) {
+ final String path = treeParser.getEntryPathString();
+ final boolean isContainer = FileMode.TREE.equals(treeParser
+ .getEntryFileMode());
+ final ObjectId objectId = treeParser.getEntryObjectId();
+ final int rawMode = treeParser.getEntryRawMode();
+
+ return new TreeParserResourceVariant(repository, path, isContainer,
+ objectId, rawMode);
+ }
+
+ public IStorage getStorage(IProgressMonitor monitor) throws TeamException {
+ return new GitBlobStorage(repository, path, objectId);
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java
index 1ac64f5f73..ef0b49b965 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ResourceUtil.java
@@ -1,7 +1,7 @@
/*******************************************************************************
* Copyright (C) 2011, Jens Baumgart <jens.baumgart@sap.com>
* Copyright (C) 2012, 2013 Robin Stocker <robin@nibor.org>
- * Copyright (C) 2012, 2013 Laurent Goubet <laurent.goubet@obeo.fr>
+ * Copyright (C) 2012, 2015 Laurent Goubet <laurent.goubet@obeo.fr>
* Copyright (C) 2012, Gunnar Wagenknecht <gunnar@wagenknecht.org>
*
* All rights reserved. This program and the accompanying materials
@@ -198,6 +198,27 @@ public class ResourceUtil {
}
/**
+ * Returns a resource handle for this path in the workspace. Note that
+ * neither the resource nor the result need exist in the workspace : this
+ * may return inexistant or otherwise non-accessible IResources.
+ *
+ * @param path
+ * Path for which we need a resource handle.
+ * @return The resource handle for the given path in the workspace.
+ */
+ public static IResource getResourceHandleForLocation(IPath path) {
+ final IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace()
+ .getRoot();
+
+ final IResource resource;
+ if (path.segmentCount() > 1)
+ resource = workspaceRoot.getFile(path);
+ else
+ resource = workspaceRoot.getProject(path.toString());
+ return resource;
+ }
+
+ /**
* The method splits the given resources by their repository. For each
* occurring repository a list is built containing the repository relative
* paths of the related resources.

Back to the top