aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLaurent Goubet2013-03-20 06:14:13 (EDT)
committerMatthias Sohn2013-04-13 19:07:35 (EDT)
commit2eb5e8b7b7ffb5f1fb9991714d113f8a4b782e36 (patch)
treec73dcb359483ced26855f87a5ccfdd0fbc1c53de
parentf2d4ffcfc3fc57e81f7629df4267b91574761aea (diff)
downloadegit-2eb5e8b7b7ffb5f1fb9991714d113f8a4b782e36.zip
egit-2eb5e8b7b7ffb5f1fb9991714d113f8a4b782e36.tar.gz
egit-2eb5e8b7b7ffb5f1fb9991714d113f8a4b782e36.tar.bz2
GitFileHistory should return the full history of its target.refs/changes/33/11333/6
When using GitFileHistoryProvider.getFileHistoryFor(IResource,...), a user expects the full history of the target resource to be returned, except if the SINGLE_REVISION or SINGLE_LINE_OF_DESCENT flags are positioned. The SINGLE_REVISION flag is properly handled, however when no flags are set, EGit previously returned only the commits descending from HEAD, without considering the history that exists on other branches or references. This adds specific tests to make sure of the behavior when either no flag or the SINGLE_REVISION flag are set. The history's "getTarget" and "getContributors" were rewritten to allow for some of Team quirks: when using the synchronize view, Team will use its own file revisions instead of what was originally fed to it. We thus need to adapt those revisions to types known to us. A specific test demonstrating this use case has been added. CQ: 7176 Bug: 398982 Change-Id: I67720110a47048fb85074a1cbbcb3d4205618a67 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/HistoryTest.java462
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java3
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties1
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java120
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResource.java15
5 files changed, 573 insertions, 28 deletions
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/HistoryTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/HistoryTest.java
new file mode 100644
index 0000000..fb185c3
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/HistoryTest.java
@@ -0,0 +1,462 @@
+/*******************************************************************************
+ * Copyright (C) 2013, Laurent Goubet <laurent.goubet@obeo.fr>
+ *
+ * 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.test;
+
+import static junit.framework.Assert.assertNotNull;
+import static junit.framework.Assert.assertEquals;
+import static junit.framework.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Iterator;
+import java.util.List;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.egit.core.synchronize.GitResourceVariantTreeSubscriber;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeData;
+import org.eclipse.egit.core.synchronize.dto.GitSynchronizeDataSet;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.team.core.RepositoryProvider;
+import org.eclipse.team.core.diff.IDiff;
+import org.eclipse.team.core.diff.IThreeWayDiff;
+import org.eclipse.team.core.history.IFileHistory;
+import org.eclipse.team.core.history.IFileHistoryProvider;
+import org.eclipse.team.core.history.IFileRevision;
+import org.eclipse.team.core.mapping.IResourceDiff;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class HistoryTest extends GitTestCase {
+ private static final String MASTER = Constants.R_HEADS + Constants.MASTER;
+
+ private static final String BRANCH = Constants.R_HEADS + "branch";
+
+ private TestRepository testRepository;
+
+ private IFile iFile1;
+
+ private IFile iFile2;
+
+ private final List<RevCommit> commits = new ArrayList<RevCommit>();
+
+ private RevCommit masterCommit1;
+
+ private RevCommit masterCommit2;
+
+ private RevCommit masterCommit3;
+
+ private RevCommit branchCommit1;
+
+ private RevCommit branchCommit2;
+
+ private IFileHistoryProvider historyProvider;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ testRepository = new TestRepository(gitDir);
+ testRepository.connect(project.getProject());
+
+ File file1 = testRepository.createFile(project.getProject(), "file1");
+ File file2 = testRepository.createFile(project.getProject(), "file2");
+
+ iFile1 = testRepository.getIFile(project.getProject(), file1);
+ iFile2 = testRepository.getIFile(project.getProject(), file2);
+
+ masterCommit1 = testRepository.addAndCommit(project.getProject(),
+ file1, "master-commit-1");
+ masterCommit2 = testRepository.addAndCommit(project.getProject(),
+ file2, "master-commit-2");
+ testRepository.createBranch(MASTER, BRANCH);
+
+ testRepository.appendFileContent(file1, "master-commit-3");
+ testRepository.appendFileContent(file2, "master-commit-3");
+ testRepository.track(file1);
+ testRepository.track(file2);
+ testRepository.addToIndex(project.getProject(), file1);
+ testRepository.addToIndex(project.getProject(), file2);
+ masterCommit3 = testRepository.commit("master-commit-3");
+
+ testRepository.checkoutBranch(BRANCH);
+ branchCommit1 = testRepository.appendContentAndCommit(
+ project.getProject(), file1, "branch-commit-1",
+ "branch-commit-1");
+ branchCommit2 = testRepository.appendContentAndCommit(
+ project.getProject(), file2, "branch-commit-2",
+ "branch-commit-2");
+
+ commits.add(masterCommit1);
+ commits.add(masterCommit2);
+ commits.add(masterCommit3);
+ commits.add(branchCommit1);
+
+ historyProvider = RepositoryProvider.getProvider(project.getProject())
+ .getFileHistoryProvider();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ testRepository.dispose();
+ super.tearDown();
+ }
+
+ @Test
+ public void queryFile1FullHistory() throws CoreException {
+ final List<RevCommit> expectedHistory = Arrays.asList(masterCommit1,
+ masterCommit3, branchCommit1);
+ assertFullHistoryMatches(iFile1, expectedHistory);
+ }
+
+ @Test
+ public void queryFile2FullHistory() throws CoreException {
+ final List<RevCommit> expectedHistory = Arrays.asList(masterCommit2,
+ masterCommit3, branchCommit2);
+ assertFullHistoryMatches(iFile2, expectedHistory);
+ }
+
+ private void assertFullHistoryMatches(IFile target,
+ List<RevCommit> expectedHistory) throws CoreException {
+ // Whatever the position of HEAD, the history should be the same
+ for (RevCommit commit : commits) {
+ testRepository.checkoutBranch(commit.getName());
+ final IFileHistory history = historyProvider.getFileHistoryFor(
+ target, IFileHistoryProvider.NONE,
+ new NullProgressMonitor());
+ assertNotNull(history);
+
+ final IFileRevision[] revisions = history.getFileRevisions();
+ assertEquals(expectedHistory.size(), revisions.length);
+ final List<RevCommit> commitList = new ArrayList<RevCommit>(
+ expectedHistory);
+ assertMatchingRevisions(Arrays.asList(revisions), commitList);
+ }
+ }
+
+ @Test
+ public void querySingleRevisions() throws CoreException {
+ for (RevCommit commit : commits) {
+ for (IFile target : Arrays.asList(iFile1, iFile2)) {
+ testRepository.checkoutBranch(commit.getName());
+ final IFileHistory history = historyProvider.getFileHistoryFor(
+ target, IFileHistoryProvider.SINGLE_REVISION,
+ new NullProgressMonitor());
+ assertNotNull(history);
+
+ final IFileRevision[] revisions = history.getFileRevisions();
+ assertEquals(1, revisions.length);
+ assertRevisionMatchCommit(revisions[0], commit);
+ }
+ }
+ }
+
+ @Test
+ public void queryFile1Contributors() {
+ final IFileHistory history = historyProvider.getFileHistoryFor(iFile1,
+ IFileHistoryProvider.NONE, new NullProgressMonitor());
+ assertNotNull(history);
+
+ final IFileRevision[] revisions = history.getFileRevisions();
+ IFileRevision branchFileRevision1 = null;
+ IFileRevision masterFileRevision3 = null;
+ IFileRevision masterFileRevision1 = null;
+ for (IFileRevision revision : revisions) {
+ final String revisionId = revision.getContentIdentifier();
+ if (branchCommit1.getName().equals(revisionId))
+ branchFileRevision1 = revision;
+ else if (masterCommit3.getName().equals(revisionId))
+ masterFileRevision3 = revision;
+ else if (masterCommit1.getName().equals(revisionId))
+ masterFileRevision1 = revision;
+ }
+ assertNotNull(branchFileRevision1);
+ assertNotNull(masterFileRevision3);
+ assertNotNull(masterFileRevision1);
+
+ /*
+ * The "direct" parent of branchCommit1 is masterCommit2. However, that
+ * commit did not contain file1. We thus expect the returned contributor
+ * to be masterCommit1.
+ */
+ final IFileRevision[] branchCommit1Parents = history
+ .getContributors(branchFileRevision1);
+ assertEquals(1, branchCommit1Parents.length);
+ assertRevisionMatchCommit(branchCommit1Parents[0], masterCommit1);
+
+ // Likewise for masterCommit3
+ final IFileRevision[] masterCommit3Parents = history
+ .getContributors(masterFileRevision3);
+ assertEquals(1, masterCommit3Parents.length);
+ assertRevisionMatchCommit(masterCommit3Parents[0], masterCommit1);
+
+ // masterCommit1 is our initial commit
+ final IFileRevision[] masterCommit1Parents = history
+ .getContributors(masterFileRevision1);
+ assertEquals(0, masterCommit1Parents.length);
+ }
+
+ @Test
+ public void queryFile2Contributors() {
+ final IFileHistory history = historyProvider.getFileHistoryFor(iFile2,
+ IFileHistoryProvider.NONE, new NullProgressMonitor());
+ assertNotNull(history);
+
+ final IFileRevision[] revisions = history.getFileRevisions();
+ IFileRevision masterFileRevision3 = null;
+ IFileRevision masterFileRevision2 = null;
+ IFileRevision branchFileRevision2 = null;
+ for (IFileRevision revision : revisions) {
+ final String revisionId = revision.getContentIdentifier();
+ if (masterCommit3.getName().equals(revisionId))
+ masterFileRevision3 = revision;
+ else if (masterCommit2.getName().equals(revisionId))
+ masterFileRevision2 = revision;
+ else if (branchCommit2.getName().equals(revisionId))
+ branchFileRevision2 = revision;
+ }
+ assertNotNull(masterFileRevision3);
+ assertNotNull(masterFileRevision2);
+ assertNotNull(branchFileRevision2);
+
+ final IFileRevision[] masterCommit3Parents = history
+ .getContributors(masterFileRevision3);
+ assertEquals(1, masterCommit3Parents.length);
+ assertRevisionMatchCommit(masterCommit3Parents[0], masterCommit2);
+
+ /*
+ * The direct parent of masterCommit2 is the initial commit,
+ * masterCommit1. However, file2 was not included in that commit. We
+ * thus expect no parent.
+ */
+ final IFileRevision[] masterCommit2Parents = history
+ .getContributors(masterFileRevision2);
+ assertEquals(0, masterCommit2Parents.length);
+
+ final IFileRevision[] branchCommit2Parents = history
+ .getContributors(branchFileRevision2);
+ assertEquals(1, branchCommit2Parents.length);
+ assertRevisionMatchCommit(branchCommit2Parents[0], masterCommit2);
+ }
+
+ @Test
+ public void queryFile1Targets() {
+ final IFileHistory history = historyProvider.getFileHistoryFor(iFile1,
+ IFileHistoryProvider.NONE, new NullProgressMonitor());
+ assertNotNull(history);
+
+ final IFileRevision[] revisions = history.getFileRevisions();
+ IFileRevision branchFileRevision1 = null;
+ IFileRevision masterFileRevision3 = null;
+ IFileRevision masterFileRevision1 = null;
+ for (IFileRevision revision : revisions) {
+ final String revisionId = revision.getContentIdentifier();
+ if (branchCommit1.getName().equals(revisionId))
+ branchFileRevision1 = revision;
+ else if (masterCommit3.getName().equals(revisionId))
+ masterFileRevision3 = revision;
+ else if (masterCommit1.getName().equals(revisionId))
+ masterFileRevision1 = revision;
+ }
+ assertNotNull(branchFileRevision1);
+ assertNotNull(masterFileRevision3);
+ assertNotNull(masterFileRevision1);
+
+ /*
+ * The "direct" child of masterCommit1 is masterCommit2. However, that
+ * commit did not contain file1. We thus expect the returned children to
+ * be masterCommit3 and branchCommit1, since the ignored masterCommit2
+ * is a branching point.
+ */
+ final IFileRevision[] masterCommit1Children = history
+ .getTargets(masterFileRevision1);
+ assertEquals(2, masterCommit1Children.length);
+ final List<RevCommit> expected = new ArrayList<RevCommit>(
+ Arrays.asList(masterCommit3, branchCommit1));
+ assertMatchingRevisions(Arrays.asList(masterCommit1Children), expected);
+
+ // masterCommit3 and branchCommit1 are leafs
+ final IFileRevision[] masterCommit3Children = history
+ .getTargets(masterFileRevision3);
+ assertEquals(0, masterCommit3Children.length);
+
+ final IFileRevision[] branchCommit1Children = history
+ .getTargets(branchFileRevision1);
+ assertEquals(0, branchCommit1Children.length);
+ }
+
+ @Test
+ public void queryFile2Targets() {
+ final IFileHistory history = historyProvider.getFileHistoryFor(iFile2,
+ IFileHistoryProvider.NONE, new NullProgressMonitor());
+ assertNotNull(history);
+
+ final IFileRevision[] revisions = history.getFileRevisions();
+ IFileRevision masterFileRevision3 = null;
+ IFileRevision masterFileRevision2 = null;
+ IFileRevision branchFileRevision2 = null;
+ for (IFileRevision revision : revisions) {
+ final String revisionId = revision.getContentIdentifier();
+ if (masterCommit3.getName().equals(revisionId))
+ masterFileRevision3 = revision;
+ else if (masterCommit2.getName().equals(revisionId))
+ masterFileRevision2 = revision;
+ else if (branchCommit2.getName().equals(revisionId))
+ branchFileRevision2 = revision;
+ }
+ assertNotNull(masterFileRevision3);
+ assertNotNull(masterFileRevision2);
+ assertNotNull(branchFileRevision2);
+
+ final IFileRevision[] masterCommit2Children = history
+ .getTargets(masterFileRevision2);
+ assertEquals(2, masterCommit2Children.length);
+ assertTrue(Arrays.asList(masterCommit2Children).contains(
+ masterFileRevision3));
+ assertTrue(Arrays.asList(masterCommit2Children).contains(
+ branchFileRevision2));
+
+ final IFileRevision[] masterCommit3Children = history
+ .getTargets(masterFileRevision3);
+ assertEquals(0, masterCommit3Children.length);
+
+ final IFileRevision[] branchCommit2Children = history
+ .getTargets(branchFileRevision2);
+ assertEquals(0, branchCommit2Children.length);
+ }
+
+ /*
+ * This aims at exerting the behavior of the EGit history provider when used
+ * through the Team APIs. This is the behavior extenders will see when
+ * interfacing with EGit through the synchronize view.
+ *
+ * The exact comparison with which we've reached the synchronize perspective
+ * should not be relevant. To keep this test as short as possible, we'll
+ * only test a single comparison.
+ */
+ @Test
+ public void queryHistoryThroughTeam() throws IOException, CoreException {
+ GitSynchronizeData gsd = new GitSynchronizeData(
+ testRepository.getRepository(), MASTER, BRANCH, false);
+ GitSynchronizeDataSet gsds = new GitSynchronizeDataSet(gsd);
+ GitResourceVariantTreeSubscriber subscriber = new GitResourceVariantTreeSubscriber(
+ gsds);
+ subscriber.init(new NullProgressMonitor());
+
+ IDiff diff = subscriber.getDiff(iFile2);
+ assertTrue(diff instanceof IThreeWayDiff);
+
+ IFileRevision sourceRevision = getSource(diff);
+ IFileRevision destinationRevision = getDestination(diff);
+ IFileRevision baseRevision = getBase(diff);
+
+ assertRevisionMatchCommit(baseRevision, masterCommit2);
+ assertRevisionMatchCommit(destinationRevision, branchCommit2);
+ assertRevisionMatchCommit(sourceRevision, masterCommit3);
+
+ final IFileHistory history = historyProvider.getFileHistoryFor(iFile2,
+ IFileHistoryProvider.NONE,
+ new NullProgressMonitor());
+ assertNotNull(history);
+
+ // no parent of masterCommit2 in file2's history
+ IFileRevision[] parents = history.getContributors(baseRevision);
+ assertEquals(0, parents.length);
+
+ /*
+ * branchCommit1 did not contain file2, so the "child" of masterCommit2
+ * (branching point) in file2's history is branchCommit2.
+ */
+ IFileRevision[] children = history.getTargets(baseRevision);
+ List<RevCommit> expectedChildren = new ArrayList<RevCommit>(
+ Arrays.asList(masterCommit3, branchCommit2));
+ assertEquals(expectedChildren.size(), children.length);
+ assertMatchingRevisions(Arrays.asList(children), expectedChildren);
+ }
+
+ private static IFileRevision getSource(IDiff diff) {
+ if (diff instanceof IResourceDiff)
+ return ((IResourceDiff) diff).getBeforeState();
+
+ if (diff instanceof IThreeWayDiff) {
+ final IThreeWayDiff twd = (IThreeWayDiff) diff;
+ final IDiff localChange = twd.getLocalChange();
+ if (localChange instanceof IResourceDiff)
+ return ((IResourceDiff) localChange).getAfterState();
+ }
+
+ return null;
+ }
+
+ private static IFileRevision getDestination(IDiff diff) {
+ if (diff instanceof IResourceDiff)
+ return ((IResourceDiff) diff).getAfterState();
+
+ if (diff instanceof IThreeWayDiff) {
+ final IThreeWayDiff twd = (IThreeWayDiff) diff;
+ final IDiff remoteChange = twd.getRemoteChange();
+ if (remoteChange instanceof IResourceDiff)
+ return ((IResourceDiff) remoteChange).getAfterState();
+
+ final IDiff localChange = twd.getLocalChange();
+ if (localChange instanceof IResourceDiff)
+ return ((IResourceDiff) localChange).getBeforeState();
+ }
+
+ return null;
+ }
+
+ private static IFileRevision getBase(IDiff diff) {
+ if (diff instanceof IThreeWayDiff) {
+ final IThreeWayDiff twd = (IThreeWayDiff) diff;
+ final IDiff remoteChange = twd.getRemoteChange();
+ if (remoteChange instanceof IResourceDiff)
+ return ((IResourceDiff) remoteChange).getBeforeState();
+
+ final IDiff localChange = twd.getLocalChange();
+ if (localChange instanceof IResourceDiff)
+ return ((IResourceDiff) localChange).getBeforeState();
+ }
+
+ return null;
+ }
+
+ private static void assertRevisionMatchCommit(IFileRevision revision,
+ RevCommit commit) {
+ assertEquals(commit.getAuthorIdent().getName(), revision.getAuthor());
+ assertEquals(commit.getFullMessage(), revision.getComment());
+ assertEquals(commit.getName(), revision.getContentIdentifier());
+ // Git is in seconds, Team in milliseconds
+ assertEquals(commit.getCommitTime(), revision.getTimestamp() / 1000);
+ }
+
+ private static void assertMatchingRevisions(List<IFileRevision> revisions,
+ List<RevCommit> commits) {
+ assertEquals(commits.size(), revisions.size());
+ // Copy list : we'll empty it as we go
+ for (IFileRevision revision : revisions) {
+ boolean foundMatch = false;
+ final Iterator<RevCommit> commitIterator = commits.iterator();
+ while (commitIterator.hasNext() && !foundMatch) {
+ final RevCommit commit = commitIterator.next();
+ if (revision.getContentIdentifier().equals(commit.getName())) {
+ assertRevisionMatchCommit(revision, commit);
+ foundMatch = true;
+ commitIterator.remove();
+ }
+ }
+ assertTrue(foundMatch);
+ }
+ assertTrue(commits.isEmpty());
+ }
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
index 0e481a0..a214493 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/CoreText.java
@@ -150,6 +150,9 @@ public class CoreText extends NLS {
public static String GitFileHistory_gitNotAttached;
/** */
+ public static String GitFileHistory_invalidCommit;
+
+ /** */
public static String GitFileHistory_invalidHeadRevision;
/** */
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties b/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
index d73a377..ee53a75 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/coretext.properties
@@ -57,6 +57,7 @@ UntrackOperation_writingIndex=Writing index for {0}
GitFileHistory_errorParsingHistory=Error parsing history for {0}.
GitFileHistory_gitNotAttached=Git not attached to project {0}.
+GitFileHistory_invalidCommit=Commit {0} is not part of the history for {1}.
GitFileHistory_invalidHeadRevision=Invalid HEAD revision for project {0}.
GitFileHistory_noHeadRevisionAvailable=No HEAD revision available from Git for project {0}.
GitProjectData_mappedResourceGone=Git mapped resource no longer exists in Eclipse: ''{0}''
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java
index 84af96f..5c0b268 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/storage/GitFileHistory.java
@@ -1,6 +1,7 @@
/*******************************************************************************
* Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
* Copyright (C) 2008, Shawn O. Pearce <spearce@spearce.org>
+ * Copyright (C) 2013, Laurent Goubet <laurent.goubet@obeo.fr>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
@@ -11,6 +12,7 @@ package org.eclipse.egit.core.internal.storage;
import java.io.IOException;
import java.util.Collections;
+import java.util.Map.Entry;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IAdaptable;
@@ -18,14 +20,20 @@ import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.egit.core.Activator;
import org.eclipse.egit.core.CoreText;
import org.eclipse.egit.core.project.RepositoryMapping;
+import org.eclipse.egit.core.synchronize.GitRemoteResource;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.history.IFileHistoryProvider;
import org.eclipse.team.core.history.IFileRevision;
import org.eclipse.team.core.history.provider.FileHistory;
+import org.eclipse.team.core.variants.IResourceVariant;
+import org.eclipse.jgit.errors.IncorrectObjectTypeException;
+import org.eclipse.jgit.errors.MissingObjectException;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Ref;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
+import org.eclipse.jgit.revwalk.RevWalk;
import org.eclipse.jgit.treewalk.filter.AndTreeFilter;
import org.eclipse.jgit.treewalk.filter.PathFilterGroup;
import org.eclipse.jgit.treewalk.filter.TreeFilter;
@@ -36,8 +44,6 @@ import org.eclipse.jgit.treewalk.filter.TreeFilter;
* listing all files with the same path.
*/
class GitFileHistory extends FileHistory implements IAdaptable {
- private static final int SINGLE_REVISION = IFileHistoryProvider.SINGLE_REVISION;
-
private static final IFileRevision[] NO_REVISIONS = {};
private static final int BATCH_SIZE = 256;
@@ -48,7 +54,7 @@ class GitFileHistory extends FileHistory implements IAdaptable {
private final Repository db;
- private final KidWalk walk;
+ private final RevWalk walk;
private final IFileRevision[] revisions;
@@ -94,7 +100,7 @@ class GitFileHistory extends FileHistory implements IAdaptable {
}
root = walk.parseCommit(headId);
- if ((flags & SINGLE_REVISION) == SINGLE_REVISION) {
+ if ((flags & IFileHistoryProvider.SINGLE_REVISION) != 0) {
// If all Eclipse wants is one revision it probably is
// for the editor "quick diff" feature. We can pass off
// just the repository HEAD, even though it may not be
@@ -105,6 +111,10 @@ class GitFileHistory extends FileHistory implements IAdaptable {
return new IFileRevision[] { single };
}
+ markStartAllRefs(walk, Constants.R_HEADS);
+ markStartAllRefs(walk, Constants.R_REMOTES);
+ markStartAllRefs(walk, Constants.R_TAGS);
+
walk.markStart(root);
} catch (IOException e) {
Activator.logError(NLS.bind(
@@ -137,34 +147,94 @@ class GitFileHistory extends FileHistory implements IAdaptable {
return r;
}
+ private void markStartAllRefs(RevWalk theWalk, String prefix)
+ throws IOException, MissingObjectException,
+ IncorrectObjectTypeException {
+ for (Entry<String, Ref> refEntry : db.getRefDatabase().getRefs(prefix)
+ .entrySet()) {
+ Ref ref = refEntry.getValue();
+ if (ref.isSymbolic())
+ continue;
+ markStartRef(theWalk, ref);
+ }
+ }
+
+ private void markStartRef(RevWalk theWalk, Ref ref) throws IOException,
+ IncorrectObjectTypeException {
+ try {
+ Object refTarget = theWalk.parseAny(ref.getLeaf().getObjectId());
+ if (refTarget instanceof RevCommit)
+ theWalk.markStart((RevCommit) refTarget);
+ } catch (MissingObjectException e) {
+ // If there is a ref which points to Nirvana then we should simply
+ // ignore this ref. We should not let a corrupt ref cause that the
+ // history view is not filled at all
+ }
+ }
+
public IFileRevision[] getContributors(final IFileRevision ifr) {
- if (!(ifr instanceof CommitFileRevision))
- return NO_REVISIONS;
+ String path = getGitPath(ifr);
+ RevCommit commit = getRevCommit(ifr);
- final CommitFileRevision rev = (CommitFileRevision) ifr;
- final String p = rev.getGitPath();
- final RevCommit c = rev.getRevCommit();
- final IFileRevision[] r = new IFileRevision[c.getParentCount()];
- for (int i = 0; i < r.length; i++)
- r[i] = new CommitFileRevision(db, c.getParent(i), p);
- return r;
+ if (path != null && commit != null) {
+ final IFileRevision[] r = new IFileRevision[commit.getParentCount()];
+ for (int i = 0; i < r.length; i++)
+ r[i] = new CommitFileRevision(db, commit.getParent(i), path);
+ return r;
+ }
+
+ return NO_REVISIONS;
}
public IFileRevision[] getTargets(final IFileRevision ifr) {
- if (!(ifr instanceof CommitFileRevision))
- return NO_REVISIONS;
+ String path = getGitPath(ifr);
+ RevCommit commit = getRevCommit(ifr);
- final CommitFileRevision rev = (CommitFileRevision) ifr;
- final String p = rev.getGitPath();
- final RevCommit rc = rev.getRevCommit();
- if (!(rc instanceof KidCommit))
- return NO_REVISIONS;
+ if (path != null && commit instanceof KidCommit) {
+ final KidCommit c = (KidCommit) commit;
+ final IFileRevision[] r = new IFileRevision[c.children.length];
+ for (int i = 0; i < r.length; i++)
+ r[i] = new CommitFileRevision(db, c.children[i], path);
+ return r;
+ }
- final KidCommit c = (KidCommit) rc;
- final IFileRevision[] r = new IFileRevision[c.children.length];
- for (int i = 0; i < r.length; i++)
- r[i] = new CommitFileRevision(db, c.children[i], p);
- return r;
+ return NO_REVISIONS;
+ }
+
+ private String getGitPath(IFileRevision revision) {
+ if (revision instanceof CommitFileRevision)
+ return ((CommitFileRevision) revision).getGitPath();
+ else if (revision instanceof IAdaptable) {
+ final IResourceVariant variant = (IResourceVariant) ((IAdaptable) revision)
+ .getAdapter(IResourceVariant.class);
+
+ if (variant instanceof GitRemoteResource)
+ return ((GitRemoteResource) variant).getPath();
+ }
+
+ return null;
+ }
+
+ private RevCommit getRevCommit(IFileRevision revision) {
+ if (revision instanceof CommitFileRevision)
+ return ((CommitFileRevision) revision).getRevCommit();
+ else if (revision instanceof IAdaptable) {
+ final IResourceVariant variant = (IResourceVariant) ((IAdaptable) revision)
+ .getAdapter(IResourceVariant.class);
+ if (variant instanceof GitRemoteResource) {
+ final RevCommit commit = ((GitRemoteResource) variant)
+ .getCommitId();
+ try {
+ return walk.parseCommit(commit);
+ } catch (IOException e) {
+ Activator.logError(NLS.bind(
+ CoreText.GitFileHistory_invalidCommit,
+ commit.getName(), resource.getName()), e);
+ }
+ }
+ }
+
+ return null;
}
public IFileRevision getFileRevision(final String id) {
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResource.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResource.java
index e8da1ee..d7e9263 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResource.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/synchronize/GitRemoteResource.java
@@ -15,7 +15,10 @@ import org.eclipse.jgit.lib.PersonIdent;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.team.core.variants.CachedResourceVariant;
-abstract class GitRemoteResource extends CachedResourceVariant {
+/**
+ * Base class for EGit's remote resource variants.
+ */
+public abstract class GitRemoteResource extends CachedResourceVariant {
private final String path;
@@ -70,7 +73,10 @@ abstract class GitRemoteResource extends CachedResourceVariant {
return commitId != null;
}
- RevCommit getCommitId() {
+ /**
+ * @return the commit Id for this resource variant.
+ */
+ public RevCommit getCommitId() {
return commitId;
}
@@ -82,7 +88,10 @@ abstract class GitRemoteResource extends CachedResourceVariant {
return objectId != null ? objectId : zeroId();
}
- String getPath() {
+ /**
+ * @return path to the resource.
+ */
+ public String getPath() {
return path;
}