| /******************************************************************************* |
| * Copyright (C) 2011, Robin Rosenberg |
| * |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License 2.0 |
| * which accompanies this distribution, and is available at |
| * https://www.eclipse.org/legal/epl-2.0/ |
| * |
| * SPDX-License-Identifier: EPL-2.0 |
| */ |
| package org.eclipse.egit.core; |
| |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import org.eclipse.core.filesystem.URIUtil; |
| import org.eclipse.core.resources.IFile; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IProjectDescription; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.preferences.IEclipsePreferences; |
| import org.eclipse.core.runtime.preferences.InstanceScope; |
| import org.eclipse.egit.core.op.AddToIndexOperation; |
| import org.eclipse.egit.core.project.RepositoryMapping; |
| import org.eclipse.egit.core.test.TestProject; |
| import org.eclipse.egit.core.test.TestRepository; |
| import org.eclipse.egit.core.test.TestUtils; |
| import org.eclipse.jgit.dircache.DirCache; |
| import org.eclipse.jgit.dircache.DirCacheBuilder; |
| import org.eclipse.jgit.dircache.DirCacheEntry; |
| import org.eclipse.jgit.errors.CorruptObjectException; |
| import org.eclipse.jgit.junit.MockSystemReader; |
| import org.eclipse.jgit.lib.Constants; |
| import org.eclipse.jgit.lib.FileMode; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.storage.file.FileRepositoryBuilder; |
| import org.eclipse.jgit.util.FS; |
| import org.eclipse.jgit.util.FileUtils; |
| import org.eclipse.jgit.util.SystemReader; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| import org.junit.experimental.theories.DataPoints; |
| import org.junit.experimental.theories.Theories; |
| import org.junit.experimental.theories.Theory; |
| import org.junit.runner.RunWith; |
| |
| /** |
| * All sorts of interesting cases |
| */ |
| @RunWith(Theories.class) |
| public class GitMoveDeleteHookTest { |
| @DataPoints |
| public static boolean[] autoStage = { true, false }; |
| |
| TestUtils testUtils = new TestUtils(); |
| |
| TestRepository testRepository; |
| |
| Repository repository; |
| |
| Set<File> testDirs = new HashSet<>(); |
| |
| File workspaceSupplement; |
| |
| File workspace; |
| |
| @Before |
| public void setUp() throws Exception { |
| Activator.getDefault().getRepositoryCache().clear(); |
| MockSystemReader mockSystemReader = new MockSystemReader(); |
| SystemReader.setInstance(mockSystemReader); |
| mockSystemReader.setProperty(Constants.GIT_CEILING_DIRECTORIES_KEY, |
| ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile() |
| .getParentFile().getAbsoluteFile().toString()); |
| workspaceSupplement = testUtils.createTempDir("wssupplement"); |
| testDirs.add(testUtils.getBaseTempDir()); |
| workspace = ResourcesPlugin.getWorkspace().getRoot().getLocation().toFile().getAbsoluteFile(); |
| } |
| |
| @After |
| public void tearDown() throws IOException, CoreException { |
| ResourcesPlugin.getWorkspace().getRoot().delete(IResource.FORCE, null); |
| if (testRepository != null) |
| testRepository.dispose(); |
| repository = null; |
| for (File d : testDirs) |
| if (d.exists()) |
| FileUtils.delete(d, FileUtils.RECURSIVE | FileUtils.RETRY); |
| SystemReader.setInstance(null); |
| } |
| |
| private TestProject initRepoInsideProjectInsideWorkspace() |
| throws Exception { |
| TestProject project = new TestProject(true, "Project-1", true, workspaceSupplement); |
| File gitDir = new File(project.getProject().getLocationURI().getPath(), |
| Constants.DOT_GIT); |
| testDirs.add(gitDir); |
| testRepository = new TestRepository(gitDir); |
| repository = testRepository.getRepository(); |
| testRepository.connect(project.getProject()); |
| registerWorkspaceRelativeTestDir("Project-1"); |
| return project; |
| } |
| |
| private TestProject initRepoInsideProjectOutsideWorkspace() |
| throws Exception { |
| TestProject project = new TestProject(true, "Project-1", false, |
| workspaceSupplement); |
| File gitDir = new File(project.getProject().getLocationURI().getPath(), |
| Constants.DOT_GIT); |
| testDirs.add(gitDir); |
| testRepository = new TestRepository(gitDir); |
| repository = testRepository.getRepository(); |
| testRepository.connect(project.getProject()); |
| return project; |
| } |
| |
| private TestProject initRepoAboveProjectInsideWs(String srcParent, String d) |
| throws Exception { |
| return initRepoAboveProject(srcParent, d, true); |
| } |
| |
| private TestProject initRepoAboveProject(String srcParent, String d, boolean insidews) |
| throws Exception { |
| registerWorkspaceRelativeTestDir(srcParent); |
| TestProject project = new TestProject(true, srcParent + "Project-1", insidews, workspaceSupplement); |
| File gd = new File(insidews?workspace:workspaceSupplement, d); |
| |
| File gitDir = new File(gd, Constants.DOT_GIT); |
| testDirs.add(gitDir); |
| testRepository = new TestRepository(gitDir); |
| repository = testRepository.getRepository(); |
| testRepository.connect(project.getProject()); |
| return project; |
| } |
| |
| @Theory |
| public void testDeleteFile(boolean autoStageDelete) throws Exception { |
| IEclipsePreferences p = InstanceScope.INSTANCE |
| .getNode(Activator.getPluginId()); |
| p.putBoolean(GitCorePreferences.core_autoStageDeletion, |
| autoStageDelete); |
| |
| TestProject project = initRepoInsideProjectInsideWorkspace(); |
| testUtils.addFileToProject(project.getProject(), "file.txt", |
| "some text"); |
| testUtils.addFileToProject(project.getProject(), "file2.txt", |
| "some more text"); |
| IFile file = project.getProject().getFile("file.txt"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { file, |
| project.getProject().getFile("file2.txt") }); |
| addToIndexOperation.execute(null); |
| |
| // Validate pre-conditions |
| DirCache dirCache = DirCache.read(repository.getIndexFile(), |
| FS.DETECTED); |
| assertEquals(2, dirCache.getEntryCount()); |
| assertNotNull(dirCache.getEntry("file.txt")); |
| assertNotNull(dirCache.getEntry("file2.txt")); |
| // Modify the content before the move |
| testUtils.changeContentOfFile(project.getProject(), file, "other text"); |
| TestUtils.waitForJobs(500, 10000, JobFamilies.INDEX_DIFF_CACHE_UPDATE); |
| |
| file.delete(true, null); |
| |
| TestUtils.waitForJobs(500, 10000, JobFamilies.INDEX_DIFF_CACHE_UPDATE); |
| |
| // Check index for the deleted file |
| dirCache.read(); |
| if (autoStageDelete) { |
| assertEquals(1, dirCache.getEntryCount()); |
| assertNull(dirCache.getEntry("file.txt")); |
| } else { |
| assertEquals(2, dirCache.getEntryCount()); |
| assertNotNull(dirCache.getEntry("file.txt")); |
| } |
| assertNotNull(dirCache.getEntry("file2.txt")); |
| // Actual file is deleted |
| assertFalse(file.exists()); |
| // But a non-affected file remains |
| assertTrue(project.getProject().getFile("file2.txt").exists()); |
| } |
| |
| @Test |
| public void testDeleteFolder() throws Exception { |
| TestProject project = initRepoInsideProjectInsideWorkspace(); |
| testUtils.addFileToProject(project.getProject(), "folder/file.txt", |
| "some text"); |
| testUtils.addFileToProject(project.getProject(), "folder2/file.txt", |
| "some other text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { |
| project.getProject().getFile("folder/file.txt"), |
| project.getProject().getFile("folder2/file.txt") }); |
| addToIndexOperation.execute(null); |
| |
| DirCache dirCache = DirCache.read(repository.getIndexFile(), |
| FS.DETECTED); |
| assertNotNull(dirCache.getEntry("folder/file.txt")); |
| assertNotNull(dirCache.getEntry("folder2/file.txt")); |
| // Modify the content before the move |
| testUtils.changeContentOfFile(project.getProject(), project |
| .getProject().getFile("folder/file.txt"), "other text"); |
| project.getProject().getFolder("folder").delete(true, null); |
| |
| dirCache.read(); |
| // Unlike delete file, dircache is untouched... pretty illogical |
| // TODO: Change the behavior of the hook. |
| assertNotNull(dirCache.getEntry("folder/file.txt")); |
| // Not moved file still there |
| assertNotNull(dirCache.getEntry("folder2/file.txt")); |
| } |
| |
| @Test |
| public void testDeleteProject() throws Exception { |
| TestProject project = initRepoAboveProjectInsideWs("P/", ""); |
| testUtils.addFileToProject(project.getProject(), "file.txt", |
| "some text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { project.getProject().getFile("file.txt") }); |
| addToIndexOperation.execute(null); |
| |
| RepositoryMapping mapping = RepositoryMapping.getMapping(project |
| .getProject()); |
| IPath gitDirAbsolutePath = mapping.getGitDirAbsolutePath(); |
| Repository db = FileRepositoryBuilder.create(gitDirAbsolutePath |
| .toFile()); |
| DirCache index = DirCache.read(db.getIndexFile(), db.getFS()); |
| assertNotNull(index.getEntry("P/Project-1/file.txt")); |
| db.close(); |
| db = null; |
| project.getProject().delete(true, null); |
| assertNull(RepositoryMapping.getMapping(project.getProject())); |
| // Check that the repo is still there. Being a bit paranoid we look for |
| // a file |
| assertTrue(gitDirAbsolutePath.toString(), |
| gitDirAbsolutePath.append("HEAD").toFile().exists()); |
| |
| db = FileRepositoryBuilder.create(gitDirAbsolutePath.toFile()); |
| index = DirCache.read(db.getIndexFile(), db.getFS()); |
| // FIXME: Shouldn't we unstage deleted projects? |
| assertNotNull(index.getEntry("P/Project-1/file.txt")); |
| db.close(); |
| } |
| |
| private void configureAutoStageMoves(boolean autoStageMoves) { |
| IEclipsePreferences p = InstanceScope.INSTANCE |
| .getNode(Activator.getPluginId()); |
| p.putBoolean(GitCorePreferences.core_autoStageMoves, autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveFile(boolean autoStageMoves) throws Exception { |
| configureAutoStageMoves(autoStageMoves); |
| |
| TestProject project = initRepoInsideProjectInsideWorkspace(); |
| testUtils.addFileToProject(project.getProject(), "file.txt", |
| "some text"); |
| testUtils.addFileToProject(project.getProject(), "file2.txt", |
| "some more text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { project.getProject().getFile("file.txt"), |
| project.getProject().getFile("file2.txt") }); |
| addToIndexOperation.execute(null); |
| |
| // Validate pre-conditions |
| DirCache dirCache = DirCache.read(repository.getIndexFile(), |
| FS.DETECTED); |
| assertNotNull(dirCache.getEntry("file.txt")); |
| assertNotNull(dirCache.getEntry("file2.txt")); |
| assertNull(dirCache.getEntry("data.txt")); |
| assertFalse(project.getProject().getFile("data.txt").exists()); |
| ObjectId oldContentId = dirCache.getEntry("file.txt").getObjectId(); |
| // Modify the content before the move |
| testUtils.changeContentOfFile(project.getProject(), project |
| .getProject().getFile("file.txt"), "other text"); |
| project.getProject() |
| .getFile("file.txt") |
| .move(project.getProject().getFile("data.txt").getFullPath(), |
| false, null); |
| |
| dirCache.read(); |
| assertTrue(project.getProject().getFile("data.txt").exists()); |
| if (autoStageMoves) { |
| assertNotNull(dirCache.getEntry("data.txt")); |
| // Same content in index as before the move |
| assertEquals(oldContentId, |
| dirCache.getEntry("data.txt").getObjectId()); |
| } else { |
| assertNull(dirCache.getEntry("data.txt")); |
| } |
| |
| // Not moved file still in its old place |
| assertNotNull(dirCache.getEntry("file2.txt")); |
| } |
| |
| /** |
| * Rename "folder" to "dir". |
| * @param autoStageMoves |
| * @throws Exception |
| */ |
| @Theory |
| public void testMoveFolder(boolean autoStageMoves) throws Exception { |
| configureAutoStageMoves(autoStageMoves); |
| |
| TestProject project = initRepoInsideProjectInsideWorkspace(); |
| testUtils.addFileToProject(project.getProject(), "folder/file.txt", |
| "some text"); |
| testUtils.addFileToProject(project.getProject(), "folder2/file.txt", |
| "some other text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { |
| project.getProject().getFile("folder/file.txt"), |
| project.getProject().getFile("folder2/file.txt") }); |
| addToIndexOperation.execute(null); |
| |
| DirCache dirCache = DirCache.read(repository.getIndexFile(), |
| FS.DETECTED); |
| assertNotNull(dirCache.getEntry("folder/file.txt")); |
| assertNotNull(dirCache.getEntry("folder2/file.txt")); |
| assertNull(dirCache.getEntry("dir/file.txt")); |
| assertFalse(project.getProject().getFile("dir/file.txt").exists()); |
| ObjectId oldContentId = dirCache.getEntry("folder/file.txt") |
| .getObjectId(); |
| // Modify the content before the move |
| testUtils.changeContentOfFile(project.getProject(), project |
| .getProject().getFile("folder/file.txt"), "other text"); |
| project.getProject() |
| .getFolder("folder") |
| .move(project.getProject().getFolder("dir").getFullPath(), |
| false, null); |
| |
| dirCache.read(); |
| assertTrue(project.getProject().getFile("dir/file.txt").exists()); |
| if (autoStageMoves) { |
| assertNull(dirCache.getEntry("folder/file.txt")); |
| assertNotNull(dirCache.getEntry("dir/file.txt")); |
| // Same content in index as before the move |
| assertEquals(oldContentId, |
| dirCache.getEntry("dir/file.txt").getObjectId()); |
| } else { |
| assertNotNull(dirCache.getEntry("folder/file.txt")); |
| assertNull(dirCache.getEntry("dir/file.txt")); |
| } |
| // Not moved file still there |
| assertNotNull(dirCache.getEntry("folder2/file.txt")); |
| } |
| |
| /** |
| * Rename and move a project in the workspace containing a Git repository. |
| * <p> |
| * The repository will be moved with the project. |
| * Note that there is no way to rename a project in the workspace without |
| * moving it. See https://bugs.eclipse.org/358828 for a discussion. |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testMoveAndRenameProjectContainingGitRepo() throws Exception { |
| ResourcesPlugin.getWorkspace().getRoot().getProject("Project-1").delete(true, null); |
| ResourcesPlugin.getWorkspace().getRoot().getProject("P2").delete(true, null); |
| |
| TestProject project = initRepoInsideProjectInsideWorkspace(); |
| testUtils.addFileToProject(project.getProject(), "file.txt", |
| "some text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { project.getProject().getFile("file.txt") }); |
| addToIndexOperation.execute(null); |
| IProjectDescription description = project.getProject().getDescription(); |
| description.setName("P2"); |
| registerWorkspaceRelativeTestDir("P2"); |
| project.getProject().move(description, |
| IResource.FORCE | IResource.SHALLOW, null); |
| IProject project2 = ResourcesPlugin.getWorkspace().getRoot() |
| .getProject("P2"); |
| assertNotNull(RepositoryMapping.getMapping(project2.getProject())); |
| Repository movedRepo = RepositoryMapping.getMapping(project2) |
| .getRepository(); |
| assertEquals("P2", |
| movedRepo.getDirectory().getParentFile() |
| .getName()); |
| DirCache dc = movedRepo.readDirCache(); |
| assertEquals(1, dc.getEntryCount()); |
| assertEquals("file.txt", dc.getEntry(0).getPathString()); |
| |
| assertFalse(ResourcesPlugin.getWorkspace().getRoot().getProject("Project-1").exists()); |
| } |
| |
| /** |
| * Rename a project outside the workspace containing a Git repository. |
| * <p> |
| * Note the similarity of the code with {@link #testMoveAndRenameProjectContainingGitRepo()} |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testRenameProjectOutsideWorkspaceContainingGitRepo() throws Exception { |
| ResourcesPlugin.getWorkspace().getRoot().getProject("Project-1").delete(true, null); |
| ResourcesPlugin.getWorkspace().getRoot().getProject("P2").delete(true, null); |
| TestProject project = initRepoInsideProjectOutsideWorkspace(); |
| testUtils.addFileToProject(project.getProject(), "file.txt", |
| "some text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { project.getProject().getFile("file.txt") }); |
| addToIndexOperation.execute(null); |
| IProjectDescription description = project.getProject().getDescription(); |
| description.setName("P2"); |
| project.getProject().move(description, |
| IResource.FORCE | IResource.SHALLOW, null); |
| IProject project2 = ResourcesPlugin.getWorkspace().getRoot() |
| .getProject("P2"); |
| assertNotNull(RepositoryMapping.getMapping(project2.getProject())); |
| Repository movedRepo = RepositoryMapping.getMapping(project2) |
| .getRepository(); |
| assertEquals("Project-1", |
| movedRepo.getDirectory().getParentFile() |
| .getName()); |
| DirCache dc = movedRepo.readDirCache(); |
| assertEquals(1, dc.getEntryCount()); |
| assertEquals("file.txt", dc.getEntry(0).getPathString()); |
| |
| assertFalse(ResourcesPlugin.getWorkspace().getRoot().getProject("Project-1").exists()); |
| } |
| |
| /** |
| * Move a project outside the workspace containing a Git repository, but do not rename it. |
| * <p> |
| * Note the similarity of the code with {@link #testMoveAndRenameProjectContainingGitRepo()} |
| * |
| * @throws Exception |
| */ |
| @Test |
| public void testMoveButDoNotRenameProjectOutsideWorkspaceContainingGitRepo() throws Exception { |
| ResourcesPlugin.getWorkspace().getRoot().getProject("Project-1").delete(true, null); |
| ResourcesPlugin.getWorkspace().getRoot().getProject("P2").delete(true, null); |
| TestProject project = initRepoInsideProjectOutsideWorkspace(); |
| testUtils.addFileToProject(project.getProject(), "file.txt", |
| "some text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { project.getProject().getFile("file.txt") }); |
| addToIndexOperation.execute(null); |
| IProjectDescription description = project.getProject().getDescription(); |
| description.setLocationURI(URIUtil.toURI(new Path(new File(project.getWorkspaceSupplement(), "P2").getAbsolutePath()))); |
| project.getProject().move(description, |
| IResource.FORCE | IResource.SHALLOW, null); |
| IProject project2 = ResourcesPlugin.getWorkspace().getRoot() |
| .getProject("Project-1"); // same name |
| assertNotNull(RepositoryMapping.getMapping(project2.getProject())); |
| Repository movedRepo = RepositoryMapping.getMapping(project2) |
| .getRepository(); |
| assertEquals("P2", |
| movedRepo.getDirectory().getParentFile() |
| .getName()); |
| DirCache dc = movedRepo.readDirCache(); |
| assertEquals(1, dc.getEntryCount()); |
| assertEquals("file.txt", dc.getEntry(0).getPathString()); |
| |
| assertFalse(ResourcesPlugin.getWorkspace().getRoot().getProject("P2").exists()); |
| } |
| |
| |
| @Theory |
| public void testMoveProjectWithinGitRepoMoveAtSameTopLevel( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoWithinWorkspace("", "Project-1", "", "P2", |
| "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitRepoMoveFromTopOneLevelDown( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoWithinWorkspace("", "Project-1", "X/", "P2", |
| "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitRepoMoveFromOneLevelDownToTop( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoWithinWorkspace("P/", "Project-1", "", "P2", |
| "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitRepoMoveFromOneLevelDownToSameDepth( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoWithinWorkspace("P/", "Project-1", "X/", |
| "P2", "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitRepoMoveFromOneLevelDownOutsideTheRepo( |
| boolean autoStageMoves) throws Exception { |
| dotestMoveProjectWithinRepoWithinWorkspace("P/", "Project-1", "P/", |
| "P2", "P/", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitOutsideWorkspaceRepoMoveAtSameTopLevel( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoOutsideWorkspace("", "Project-1", "", "P2", |
| "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitOutsideWorkspaceRepoMoveFromTopOneLevelDown( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoOutsideWorkspace("", "Project-1", "X/", "P2", |
| "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitOutsideWorkspaceRepoMoveFromOneLevelDownToTop( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoOutsideWorkspace("P/", "Project-1", "", "P2", |
| "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitOutsideWorkspaceRepoMoveFromOneLevelDownToSameDepth( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoOutsideWorkspace("P/", "Project-1", "X/", |
| "P2", "", autoStageMoves); |
| } |
| |
| @Theory |
| public void testMoveProjectWithinGitOutsideWorkspaceRepoMoveFromOneLevelDownOutsideTheRepo( |
| boolean autoStageMoves) |
| throws Exception { |
| dotestMoveProjectWithinRepoOutsideWorkspace("P/", "Project-1", "P/", |
| "P2", "P/", autoStageMoves); |
| } |
| |
| |
| @Theory |
| public void testMoveProjectWithinGitRepoMoveFromLevelZeroDownOne( |
| boolean autoStageMoves) |
| throws Exception { |
| // In this case we'd expect the project to move, but not the repository |
| // TODO: Eclipse cannot do this even without the Git plugin either, |
| // TODO: See Bug 307140) |
| try { |
| dotestMoveProjectWithinRepoWithinWorkspace("P/", "Project-1", |
| "P/Project-1/", "P2", "P/Project-1/", autoStageMoves); |
| if (!"true".equals(System.getProperty("egit.assume_307140_fixed"))) |
| fail("ResourceException expected, core functionality dangerously broken and therefore forbidden"); |
| } catch (CoreException e) { |
| if ("true".equals(System.getProperty("egit.assume_307140_fixed"))) |
| throw e; |
| } |
| } |
| |
| @Theory |
| public void testMoveFileWithConflictsShouldBeCanceled( |
| boolean autoStageMoves) throws Exception { |
| configureAutoStageMoves(autoStageMoves); |
| |
| TestProject project = initRepoInsideProjectInsideWorkspace(); |
| String filePath = "file.txt"; |
| IFile file = testUtils.addFileToProject(project.getProject(), filePath, "some text"); |
| |
| Repository repo = testRepository.getRepository(); |
| DirCache index = repo.lockDirCache(); |
| DirCacheBuilder builder = index.builder(); |
| addUnmergedEntry(filePath, builder); |
| builder.commit(); |
| |
| try { |
| file.move(new Path("destination.txt"), false, null); |
| fail("Expected move of file with conflicts to fail."); |
| } catch (CoreException e) { |
| IStatus status = e.getStatus(); |
| assertNotNull(status); |
| assertEquals(IStatus.WARNING, status.getSeverity()); |
| } |
| |
| assertTrue("File should still exist at old location", file.exists()); |
| DirCache indexAfter = repo.readDirCache(); |
| DirCacheEntry entry = indexAfter.getEntry(filePath); |
| assertEquals("Expected entry to still be in non-zero (conflict) stage", |
| DirCacheEntry.STAGE_1, entry.getStage()); |
| } |
| |
| @Theory |
| public void testMoveFolderWithFileWithConflictsShouldBeCanceled( |
| boolean autoStageMoves) throws Exception { |
| configureAutoStageMoves(autoStageMoves); |
| |
| TestProject project = initRepoInsideProjectInsideWorkspace(); |
| String filePath = "folder/file.txt"; |
| IFile file = testUtils.addFileToProject(project.getProject(), filePath, "some text"); |
| |
| Repository repo = testRepository.getRepository(); |
| DirCache index = repo.lockDirCache(); |
| DirCacheBuilder builder = index.builder(); |
| addUnmergedEntry(filePath, builder); |
| builder.commit(); |
| |
| try { |
| project.getProject() |
| .getFolder("folder") |
| .move(project.getProject().getFolder("newfolder") |
| .getFullPath(), false, null); |
| fail("Expected move of folder with file with conflicts to fail."); |
| } catch (CoreException e) { |
| IStatus status = e.getStatus(); |
| assertNotNull(status); |
| assertEquals(IStatus.WARNING, status.getSeverity()); |
| } |
| |
| assertTrue("File should still exist at old location", file.exists()); |
| DirCache indexAfter = repo.readDirCache(); |
| DirCacheEntry entry = indexAfter.getEntry(filePath); |
| assertEquals("Expected entry to still be in non-zero (conflict) stage", |
| DirCacheEntry.STAGE_1, entry.getStage()); |
| } |
| |
| private static void addUnmergedEntry(String filePath, DirCacheBuilder builder) { |
| DirCacheEntry stage1 = new DirCacheEntry(filePath, DirCacheEntry.STAGE_1); |
| DirCacheEntry stage2 = new DirCacheEntry(filePath, DirCacheEntry.STAGE_2); |
| DirCacheEntry stage3 = new DirCacheEntry(filePath, DirCacheEntry.STAGE_3); |
| stage1.setFileMode(FileMode.REGULAR_FILE); |
| stage2.setFileMode(FileMode.REGULAR_FILE); |
| stage3.setFileMode(FileMode.REGULAR_FILE); |
| builder.add(stage1); |
| builder.add(stage2); |
| builder.add(stage3); |
| } |
| |
| private void dotestMoveProjectWithinRepoWithinWorkspace(String srcParent, |
| String srcProjectName, String dstParent, String dstProjecName, |
| String gitDir, boolean autoStageMoves) throws CoreException, |
| IOException, Exception, |
| CorruptObjectException { |
| dotestMoveProjectWithinRepo(srcParent, srcProjectName, dstParent, |
| dstProjecName, gitDir, true, autoStageMoves); |
| } |
| |
| private void dotestMoveProjectWithinRepoOutsideWorkspace(String srcParent, |
| String srcProjectName, String dstParent, String dstProjecName, |
| String gitDir, boolean autoStageMoves) throws CoreException, |
| IOException, Exception, |
| CorruptObjectException { |
| dotestMoveProjectWithinRepo(srcParent, srcProjectName, dstParent, |
| dstProjecName, gitDir, false, autoStageMoves); |
| } |
| |
| private void dotestMoveProjectWithinRepo(String srcParent, |
| String srcProjectName, String dstParent, String dstProjecName, |
| String gitDir, boolean sourceInsideWs, boolean autoStageMoves) |
| throws Exception { |
| configureAutoStageMoves(autoStageMoves); |
| String gdRelativeSrcParent = srcParent + srcProjectName + "/"; |
| if (gdRelativeSrcParent.startsWith(gitDir)) |
| gdRelativeSrcParent = gdRelativeSrcParent |
| .substring(gitDir.length()); |
| testDirs.add(new File(dstParent)); |
| String gdRelativeDstParent = dstParent + dstProjecName + "/"; |
| if (gdRelativeDstParent.startsWith(gitDir)) |
| gdRelativeDstParent = gdRelativeDstParent |
| .substring(gitDir.length()); |
| |
| registerWorkspaceRelativeTestDirProject(srcParent, srcProjectName); |
| registerWorkspaceRelativeTestDirProject(dstParent, dstProjecName); |
| |
| // Old cruft may be laying around |
| TestProject project = initRepoAboveProject(srcParent, gitDir, sourceInsideWs); |
| IProject project0 = project.getProject().getWorkspace().getRoot() |
| .getProject(dstProjecName); |
| project0.delete(true, null); |
| |
| testUtils.addFileToProject(project.getProject(), "file.txt", |
| "some text"); |
| AddToIndexOperation addToIndexOperation = new AddToIndexOperation( |
| new IResource[] { project.getProject().getFile("file.txt") }); |
| addToIndexOperation.execute(null); |
| |
| // Check condition before move |
| DirCache dirCache = DirCache.read(repository.getIndexFile(), |
| FS.DETECTED); |
| assertNotNull(dirCache.getEntry(gdRelativeSrcParent + "file.txt")); |
| ObjectId oldContentId = dirCache.getEntry( |
| gdRelativeSrcParent + "file.txt").getObjectId(); |
| |
| // Modify the content before the move, we want to see the staged content |
| // as it was before the move in the index |
| testUtils.changeContentOfFile(project.getProject(), project |
| .getProject().getFile("file.txt"), "other text"); |
| IProjectDescription description = project.getProject().getDescription(); |
| description.setName(dstProjecName); |
| if (sourceInsideWs) |
| if (dstParent.length() > 0) |
| description.setLocationURI(URIUtil.toURI(project.getProject() |
| .getWorkspace().getRoot().getLocation() |
| .append(dstParent + dstProjecName))); |
| else |
| description.setLocationURI(null); |
| else |
| description.setLocationURI(URIUtil.toURI(new Path(workspaceSupplement + "/" + dstParent + "/" + dstProjecName))); |
| project.getProject().move(description, |
| IResource.FORCE | IResource.SHALLOW, null); |
| IProject project2 = project.getProject().getWorkspace().getRoot() |
| .getProject(dstProjecName); |
| assertTrue(project2.exists()); |
| assertNotNull(RepositoryMapping.getMapping(project2)); |
| |
| // Check that our file exists on disk has a new location in the index |
| dirCache.read(); |
| assertTrue(project2.getFile("file.txt").exists()); |
| if (autoStageMoves) { |
| assertNotNull(dirCache.getEntry(gdRelativeDstParent + "file.txt")); |
| // Same content in index as before the move, i.e. not same as on |
| // disk |
| assertEquals(oldContentId, dirCache |
| .getEntry(gdRelativeDstParent + "file.txt").getObjectId()); |
| } else { |
| assertNull(dirCache.getEntry(gdRelativeDstParent + "file.txt")); |
| } |
| } |
| |
| |
| private void registerWorkspaceRelativeTestDirProject(String parent, String projName) { |
| if ((parent != null) && !parent.isEmpty()) |
| registerWorkspaceRelativeTestDir(parent); |
| else |
| registerWorkspaceRelativeTestDir(projName); |
| } |
| |
| private void registerWorkspaceRelativeTestDir(String relativeDir) { |
| if ((relativeDir != null) && !relativeDir.isEmpty()) { |
| File d = new File(workspace, relativeDir); |
| testDirs.add(d); |
| } |
| } |
| } |