diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java index d84e6e163c..63c3769b50 100644 --- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java +++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactory.java @@ -27,11 +27,14 @@ package sonia.scm.repository.spi; import com.aragost.javahg.BaseRepository; import com.aragost.javahg.Repository; import com.aragost.javahg.commands.CloneCommand; +import com.aragost.javahg.commands.ExecutionException; import com.aragost.javahg.commands.PullCommand; +import com.aragost.javahg.commands.StatusCommand; import com.aragost.javahg.commands.UpdateCommand; import com.aragost.javahg.commands.flags.CloneCommandFlags; import sonia.scm.repository.util.WorkingCopyPool; import sonia.scm.repository.util.SimpleWorkingCopyFactory; +import sonia.scm.util.IOUtil; import sonia.scm.web.HgRepositoryEnvironmentBuilder; import javax.inject.Inject; @@ -71,11 +74,22 @@ public class SimpleHgWorkingCopyFactory extends SimpleWorkingCopyFactory reclaimRepository(HgCommandContext context, File target, String initialBranch) throws IOException { + protected ParentAndClone reclaimRepository(HgCommandContext context, File target, String initialBranch) throws ReclaimFailedException { Repository centralRepository = openCentral(context); - BaseRepository clone = Repository.open(target); - UpdateCommand.on(clone).rev(initialBranch).execute(); - return new ParentAndClone<>(centralRepository, clone, target); + try { + BaseRepository clone = Repository.open(target); + for (String unknown : StatusCommand.on(clone).execute().getUnknown()) { + delete(clone.getDirectory(), unknown); + } + UpdateCommand.on(clone).rev(initialBranch).clean().execute(); + return new ParentAndClone<>(centralRepository, clone, target); + } catch (ExecutionException | IOException e) { + throw new ReclaimFailedException(e); + } + } + + private void delete(File directory, String unknownFile) throws IOException { + IOUtil.delete(new File(directory, unknownFile)); } @Override diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactoryTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactoryTest.java new file mode 100644 index 0000000000..1a3de5cf58 --- /dev/null +++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/SimpleHgWorkingCopyFactoryTest.java @@ -0,0 +1,150 @@ +/* + * MIT License + * + * Copyright (c) 2020-present Cloudogu GmbH and Contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package sonia.scm.repository.spi; + +import com.aragost.javahg.Repository; +import com.google.inject.util.Providers; +import org.assertj.core.api.Assertions; +import org.junit.Assert; +import org.junit.Before; +import org.junit.Rule; +import org.junit.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.junit.rules.TemporaryFolder; +import org.junitpioneer.jupiter.TempDirectory; +import sonia.scm.repository.HgHookManager; +import sonia.scm.repository.HgTestUtil; +import sonia.scm.repository.util.CachingAllWorkingCopyPool; +import sonia.scm.repository.util.NoneCachingWorkingCopyPool; +import sonia.scm.repository.util.WorkdirProvider; +import sonia.scm.repository.util.WorkingCopy; +import sonia.scm.web.HgRepositoryEnvironmentBuilder; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.util.Collections; + +import static org.assertj.core.api.Assertions.assertThat; + +@ExtendWith(TempDirectory.class) +public class SimpleHgWorkingCopyFactoryTest extends AbstractHgCommandTestBase { + + @Rule + public TemporaryFolder temporaryFolder = new TemporaryFolder(); + + private WorkdirProvider workdirProvider; + + private SimpleHgWorkingCopyFactory workingCopyFactory; + + @Before + public void bindScmProtocol() throws IOException { + workdirProvider = new WorkdirProvider(temporaryFolder.newFolder()); + HgHookManager hookManager = HgTestUtil.createHookManager(); + HgRepositoryEnvironmentBuilder environmentBuilder = new HgRepositoryEnvironmentBuilder(handler, hookManager); + workingCopyFactory = new SimpleHgWorkingCopyFactory(Providers.of(environmentBuilder), new CachingAllWorkingCopyPool(workdirProvider)) { + @Override + public void configure(com.aragost.javahg.commands.PullCommand pullCommand) { + // we do not want to configure http hooks in this unit test + } + }; + } + + @Test + public void shouldSwitchBranch() { + WorkingCopy workingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "default"); + + File initialDirectory = workingCopy.getDirectory(); + workingCopy.close(); + + assertThat(initialDirectory).exists(); + assertThat(initialDirectory.toPath().resolve("f.txt")).exists(); + + WorkingCopy cachedWorkingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "test-branch"); + assertThat(cachedWorkingCopy.getDirectory()).isEqualTo(initialDirectory); + assertThat(cachedWorkingCopy.getDirectory().toPath().resolve("f.txt")).doesNotExist(); + } + + @Test + public void shouldReplaceFileWithContentFromNewBranch() throws IOException { + WorkingCopy workingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "test-branch"); + + File initialDirectory = workingCopy.getDirectory(); + Path fileToBeReplaced = initialDirectory.toPath().resolve("f.txt"); + Files.createFile(fileToBeReplaced); + Files.write(fileToBeReplaced, Collections.singleton("some content")); + + workingCopy.close(); + + assertThat(initialDirectory).exists(); + assertThat(fileToBeReplaced).hasContent("some content"); + + WorkingCopy cachedWorkingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "default"); + assertThat(cachedWorkingCopy.getDirectory()).isEqualTo(initialDirectory); + assertThat(cachedWorkingCopy.getDirectory().toPath().resolve("f.txt")).exists(); + assertThat(fileToBeReplaced).hasContent("f"); + } + + @Test + public void shouldDeleteUntrackedFile() throws IOException { + WorkingCopy workingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "test-branch"); + + File initialDirectory = workingCopy.getDirectory(); + Path fileToBeDeleted = initialDirectory.toPath().resolve("x.txt"); + Files.createFile(fileToBeDeleted); + Files.write(fileToBeDeleted, Collections.singleton("some content")); + + workingCopy.close(); + + assertThat(initialDirectory).exists(); + assertThat(fileToBeDeleted).hasContent("some content"); + + WorkingCopy cachedWorkingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "default"); + assertThat(cachedWorkingCopy.getDirectory()).isEqualTo(initialDirectory); + assertThat(cachedWorkingCopy.getDirectory().toPath().resolve("x.txt")).doesNotExist(); + } + + @Test + public void shouldDeleteUntrackedDirectory() throws IOException { + WorkingCopy workingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "test-branch"); + + File initialDirectory = workingCopy.getDirectory(); + Path directoryToBeDeleted = initialDirectory.toPath().resolve("newDir"); + Files.createDirectories(directoryToBeDeleted); + Path fileToBeDeleted = directoryToBeDeleted.resolve("y.txt"); + Files.createFile(fileToBeDeleted); + Files.write(fileToBeDeleted, Collections.singleton("some content")); + + workingCopy.close(); + + assertThat(initialDirectory).exists(); + assertThat(fileToBeDeleted).hasContent("some content"); + + WorkingCopy cachedWorkingCopy = workingCopyFactory.createWorkingCopy(cmdContext, "default"); + assertThat(cachedWorkingCopy.getDirectory()).isEqualTo(initialDirectory); + assertThat(cachedWorkingCopy.getDirectory().toPath().resolve("newDir")).doesNotExist(); + } +}