summaryrefslogtreecommitdiffstatsabout
diff options
context:
space:
mode:
authorJens Baumgart2010-06-16 05:05:36 (EDT)
committer Matthias Sohn2010-06-16 05:05:36 (EDT)
commita2ade7f6fdaffc8bf698480d0fe4f341d1248787 (patch)
tree5cc7b80c4de0603b8aec19bf23a5df1b4d75a0ed
parent983bac3236669ae9e15323026c90775c59552a9d (diff)
downloadegit-a2ade7f6fdaffc8bf698480d0fe4f341d1248787.zip
egit-a2ade7f6fdaffc8bf698480d0fe4f341d1248787.tar.gz
egit-a2ade7f6fdaffc8bf698480d0fe4f341d1248787.tar.bz2
Delete invalid projects after work dir checkoutrefs/changes/21/821/5
Operations performing a work dir checkout (e.g. branch, reset) may delete .project files. This leads to inconsistent projects causing follow-up problems (checkout conflicts). Details of the problem can be found in the related bug. This fix deletes projects that got inconsistent by the work dir checkout. In future there could also be a handling for projects added by a work dir checkout (e.g. asking the user to open these projects). A test case for BranchOperation was added. Also TagOperation doesn't need to refresh projects since it doesn't modify any resources hence this call to refreshProjects was removed. Bug: 316104 Change-Id: I0a0de37c2e7866a4090366752b8cfbe69fb285c0 Signed-off-by: Jens Baumgart <jens.baumgart@sap.com> Signed-off-by: Matthias Sohn <matthias.sohn@sap.com>
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/GitTestCase.java22
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestProject.java11
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestUtils.java41
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/op/T0004_BranchOperationTest.java115
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java66
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java4
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/MergeOperation.java4
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java6
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/op/TagOperation.java3
9 files changed, 232 insertions, 40 deletions
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/GitTestCase.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/GitTestCase.java
index e788a52..6824e60 100644
--- a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/GitTestCase.java
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/GitTestCase.java
@@ -43,31 +43,13 @@ public abstract class GitTestCase {
project = new TestProject(true);
gitDir = new File(project.getProject().getWorkspace().getRoot()
.getRawLocation().toFile(), Constants.DOT_GIT);
- rmrf(gitDir);
+ TestUtils.rmrf(gitDir);
}
@After
public void tearDown() throws Exception {
project.dispose();
- rmrf(gitDir);
- }
-
- private void rmrf(File d) throws IOException {
- if (!d.exists())
- return;
-
- File[] files = d.listFiles();
- if (files != null) {
- for (int i = 0; i < files.length; ++i) {
- if (files[i].isDirectory())
- rmrf(files[i]);
- else if (!files[i].delete())
- throw new IOException(files[i] + " in use or undeletable");
- }
- }
- if (!d.delete())
- throw new IOException(d + " in use or undeletable");
- assert !d.exists();
+ TestUtils.rmrf(gitDir);
}
protected ObjectId createFile(Repository repository, IProject project, String name, String content) throws IOException {
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestProject.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestProject.java
index bff3c9e..24ab17b 100644
--- a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestProject.java
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestProject.java
@@ -9,6 +9,8 @@
*******************************************************************************/
package org.eclipse.egit.core.test;
+import java.io.File;
+import java.io.IOException;
import java.net.URL;
import org.eclipse.core.resources.IFolder;
@@ -37,6 +39,7 @@ public class TestProject {
public IJavaProject javaProject;
private IPackageFragmentRoot sourceFolder;
+ private String location;
/**
* @throws CoreException
@@ -58,6 +61,7 @@ public class TestProject {
project.delete(true, null);
project.create(null);
project.open(null);
+ location = project.getLocation().toOSString();
javaProject = JavaCore.create(project);
IFolder binFolder = createBinFolder();
setJavaNature();
@@ -101,9 +105,12 @@ public class TestProject {
return cu.getTypes()[0];
}
- public void dispose() throws CoreException {
+ public void dispose() throws CoreException, IOException {
waitForIndexer();
- project.delete(true, true, null);
+ if (project.exists())
+ project.delete(true, true, null);
+ else
+ TestUtils.rmrf(new File(location));
}
private IFolder createBinFolder() throws CoreException {
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestUtils.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestUtils.java
new file mode 100644
index 0000000..487d86e
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/TestUtils.java
@@ -0,0 +1,41 @@
+/*******************************************************************************
+ * Copyright (C) 2007, Robin Rosenberg <robin.rosenberg@dewire.com>
+ * Copyright (C) 2010, Jens Baumgart <jens.baumgart@sap.com>
+ *
+ * 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 java.io.File;
+import java.io.IOException;
+
+public class TestUtils {
+ /**
+ * This method deletes a file / subtree
+ *
+ * @param d
+ * file / folder to delete
+ * @throws IOException
+ */
+ public static void rmrf(File d) throws IOException {
+ if (!d.exists())
+ return;
+
+ File[] files = d.listFiles();
+ if (files != null) {
+ for (int i = 0; i < files.length; ++i) {
+ if (files[i].isDirectory())
+ rmrf(files[i]);
+ else if (!files[i].delete())
+ throw new IOException(files[i] + " in use or undeletable");
+ }
+ }
+ if (!d.delete())
+ throw new IOException(d + " in use or undeletable");
+ assert !d.exists();
+ }
+
+}
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/op/T0004_BranchOperationTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/op/T0004_BranchOperationTest.java
new file mode 100644
index 0000000..a40e737
--- /dev/null
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/test/op/T0004_BranchOperationTest.java
@@ -0,0 +1,115 @@
+package org.eclipse.egit.core.test.op;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+import java.io.File;
+import java.io.IOException;
+
+import org.eclipse.egit.core.op.BranchOperation;
+import org.eclipse.egit.core.test.GitTestCase;
+import org.eclipse.jgit.api.CommitCommand;
+import org.eclipse.jgit.api.ConcurrentRefUpdateException;
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.JGitInternalException;
+import org.eclipse.jgit.api.NoHeadException;
+import org.eclipse.jgit.api.NoMessageException;
+import org.eclipse.jgit.api.WrongRepositoryStateException;
+import org.eclipse.jgit.errors.UnmergedPathException;
+import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.GitIndex;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.Ref;
+import org.eclipse.jgit.lib.RefUpdate;
+import org.eclipse.jgit.lib.Repository;
+import org.eclipse.jgit.lib.GitIndex.Entry;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+public class T0004_BranchOperationTest extends GitTestCase{
+
+ private static final String TEST = Constants.R_HEADS + "test";
+ private static final String MASTER = Constants.R_HEADS + "master";
+ Repository repository;
+
+ @Before
+ public void setUp() throws Exception {
+ super.setUp();
+ repository = new Repository(gitDir);
+ repository.create();
+ }
+
+ @After
+ public void tearDown() throws Exception {
+ repository.close();
+ repository = null;
+ super.tearDown();
+ }
+
+
+ @Test
+ public void testBranchOperation() throws Exception {
+ // create first commit containing a dummy file
+ createInitialCommit();
+ // create branch test and switch to branch test
+ createBranch(MASTER, TEST);
+ new BranchOperation(repository, TEST).execute(null);
+ assertTrue(repository.getFullBranch().equals(TEST));
+ // add .project to version control and commit
+ String path = project.getProject().getLocation().append(".project").toOSString();
+ File file = new File(path);
+ track(file);
+ commit("Add .project file");
+ // switch back to master branch
+ // .project must disappear, related Eclipse project must be deleted
+ new BranchOperation(repository, MASTER).execute(null);
+ assertFalse(file.exists());
+ assertFalse(project.getProject().exists());
+ // switch back to master test
+ // .project must reappear
+ new BranchOperation(repository, TEST).execute(null);
+ assertTrue(file.exists());
+ }
+
+ private void createInitialCommit() throws IOException, NoHeadException, NoMessageException, ConcurrentRefUpdateException, JGitInternalException, WrongRepositoryStateException {
+ String repoPath = project.getProject().getWorkspace().getRoot().getLocation().toOSString();
+ File file = new File(repoPath, "dummy");
+ file.createNewFile();
+ track(file);
+ commit("testBranchOperation\n\nfirst commit\n");
+ }
+
+ private void createBranch(String refName, String newRefName) throws IOException {
+ RefUpdate updateRef;
+ updateRef = repository.updateRef(newRefName);
+ Ref startRef = repository.getRef(refName);
+ ObjectId startAt = repository.resolve(refName);
+ String startBranch;
+ if (startRef != null)
+ startBranch = refName;
+ else
+ startBranch = startAt.name();
+ startBranch = repository.shortenRefName(startBranch);
+ updateRef.setNewObjectId(startAt);
+ updateRef.setRefLogMessage("branch: Created from " + startBranch, false); //$NON-NLS-1$
+ updateRef.update();
+ }
+
+ private void track(File file) throws IOException {
+ GitIndex index = repository.getIndex();
+ Entry entry = index.add(repository.getWorkDir(), file);
+ entry.setAssumeValid(false);
+ index.write();
+ }
+
+ private void commit(String message) throws NoHeadException, NoMessageException, UnmergedPathException, ConcurrentRefUpdateException, JGitInternalException, WrongRepositoryStateException {
+ Git git = new Git(repository);
+ CommitCommand commitCommand = git.commit();
+ commitCommand.setAuthor("J. Git", "j.git@egit.org");
+ commitCommand.setCommitter(commitCommand.getAuthor());
+ commitCommand.setMessage(message);
+ commitCommand.call();
+ }
+
+}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java
index 8585f19..40a0194 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java
@@ -9,6 +9,8 @@
package org.eclipse.egit.core.internal.util;
import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
@@ -24,36 +26,76 @@ import org.eclipse.jgit.lib.Repository;
* TODO: rename to RefreshUtil or ResourceUtil?
*/
public class ProjectUtil {
+
/**
- * The method refreshes all projects contained in the given Git repository
+ * The method returns all valid projects contained in the given Git
+ * repository. A project is considered as valid if the .project file exists.
+ * @see ProjectUtil#refreshValidProjects(IProject[], IProgressMonitor)
* @param repository
- * @param monitor
+ * @return valid projects
* @throws CoreException
*/
- public static void refreshProjects(Repository repository,
- IProgressMonitor monitor) throws CoreException {
+ public static IProject[] getValidProjects(Repository repository)
+ throws CoreException {
final IProject[] projects = ResourcesPlugin.getWorkspace().getRoot()
.getProjects();
+ List<IProject> result = new ArrayList<IProject>();
+ final File parentFile = repository.getWorkDir();
+ for (IProject p : projects) {
+ String projectFilePath = p.getLocation()
+ .append(".project").toOSString(); //$NON-NLS-1$
+ File projectFile = new File(projectFilePath);
+ if (projectFile.exists()) {
+ final File file = p.getLocation().toFile();
+ if (file.getAbsolutePath().startsWith(
+ parentFile.getAbsolutePath())) {
+ result.add(p);
+ }
+ }
+ }
+ return result.toArray(new IProject[result.size()]);
+ }
+
+ /**
+ * The method refreshes the given projects. Projects with missing .project
+ * file are deleted. The method should be called in the following flow:<br>
+ * <ol>
+ * <li>Call {@link ProjectUtil#getValidProjects(Repository)}
+ * <li>Perform a workdir checkout (e.g. branch, reset)
+ * <li>Call
+ * {@link ProjectUtil#refreshValidProjects(IProject[], IProgressMonitor)}
+ * </ol>
+ *
+ * @param projects
+ * list of valid projects before workdir checkout.
+ * @param monitor
+ * @throws CoreException
+ */
+ public static void refreshValidProjects(IProject[] projects,
+ IProgressMonitor monitor) throws CoreException {
try {
monitor.beginTask(CoreText.ProjectUtil_refreshingProjects,
projects.length);
- final File parentFile = repository.getWorkDir();
for (IProject p : projects) {
if (monitor.isCanceled())
break;
- final File file = p.getLocation().toFile();
- if (file.getAbsolutePath().startsWith(
- parentFile.getAbsolutePath())) {
- p.refreshLocal(IResource.DEPTH_INFINITE,
- new SubProgressMonitor(monitor, 1));
- monitor.worked(1);
- }
+ String projectFilePath = p.getLocation().append(".project").toOSString(); //$NON-NLS-1$
+ File projectFile = new File(projectFilePath);
+ if (projectFile.exists())
+ p.refreshLocal(IResource.DEPTH_INFINITE,
+ new SubProgressMonitor(monitor, 1));
+
+ else
+ p.delete(false, true, new SubProgressMonitor(monitor, 1));
+
+ monitor.worked(1);
}
} finally {
monitor.done();
}
}
+
/**
* The method refreshes resources
*
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
index b7e30a2..bf11904 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/BranchOperation.java
@@ -12,6 +12,7 @@ package org.eclipse.egit.core.op;
import java.io.IOException;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -83,6 +84,7 @@ public class BranchOperation implements IEGitOperation {
IWorkspaceRunnable action = new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
+ IProject[] validProjects = ProjectUtil.getValidProjects(repository);
monitor.beginTask(NLS.bind(
CoreText.BranchOperation_performingBranch, refName), 6);
lookupRefs();
@@ -100,7 +102,7 @@ public class BranchOperation implements IEGitOperation {
updateHeadRef();
monitor.worked(1);
- ProjectUtil.refreshProjects(repository, new SubProgressMonitor(
+ ProjectUtil.refreshValidProjects(validProjects, new SubProgressMonitor(
monitor, 1));
monitor.worked(1);
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/MergeOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/MergeOperation.java
index 0b9dfda..048ea01 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/MergeOperation.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/MergeOperation.java
@@ -12,6 +12,7 @@ package org.eclipse.egit.core.op;
import java.io.IOException;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -82,6 +83,7 @@ public class MergeOperation implements IEGitOperation {
IWorkspaceRunnable action = new IWorkspaceRunnable() {
public void run(IProgressMonitor mymonitor) throws CoreException {
+ IProject[] validProjects = ProjectUtil.getValidProjects(repository);
mymonitor.beginTask(NLS.bind(CoreText.MergeOperation_ProgressMerge, refName), 3);
Git git = new Git(repository);
mymonitor.worked(1);
@@ -110,7 +112,7 @@ public class MergeOperation implements IEGitOperation {
} catch (InvalidMergeHeadsException e) {
throw new TeamException(e.getLocalizedMessage(), e.getCause());
}
- ProjectUtil.refreshProjects(repository, new SubProgressMonitor(
+ ProjectUtil.refreshValidProjects(validProjects, new SubProgressMonitor(
mymonitor, 1));
mymonitor.done();
}
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java
index c446134..f5545de 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/ResetOperation.java
@@ -13,6 +13,7 @@ package org.eclipse.egit.core.op;
import java.io.File;
import java.io.IOException;
+import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
@@ -116,6 +117,9 @@ public class ResetOperation implements IEGitOperation {
monitor.beginTask(NLS.bind(CoreText.ResetOperation_performingReset,
type.toString().toLowerCase(), refName), 7);
+ IProject[] validProjects = null;
+ if (type == ResetType.HARD)
+ validProjects = ProjectUtil.getValidProjects(repository);
mapObjects();
monitor.worked(1);
@@ -145,7 +149,7 @@ public class ResetOperation implements IEGitOperation {
if (type == ResetType.HARD)
// only refresh if working tree changes
- ProjectUtil.refreshProjects(repository, new SubProgressMonitor(
+ ProjectUtil.refreshValidProjects(validProjects, new SubProgressMonitor(
monitor, 1));
monitor.done();
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/TagOperation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/TagOperation.java
index 77cf93d..9da07d7 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/op/TagOperation.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/op/TagOperation.java
@@ -13,10 +13,8 @@ import java.io.IOException;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.ISchedulingRule;
import org.eclipse.egit.core.CoreText;
-import org.eclipse.egit.core.internal.util.ProjectUtil;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.ObjectLoader;
@@ -67,7 +65,6 @@ public class TagOperation implements IEGitOperation {
updateRepo();
monitor.worked(1);
- ProjectUtil.refreshProjects(repo, SubMonitor.convert(monitor, 1));
} finally {
monitor.done();
}