diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactory.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactory.java index 5a59fd7dba..6156ad6cf5 100644 --- a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactory.java +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactory.java @@ -24,20 +24,9 @@ package sonia.scm.repository.spi; -import org.tmatesoft.svn.core.SVNDepth; -import org.tmatesoft.svn.core.SVNException; -import org.tmatesoft.svn.core.SVNURL; -import org.tmatesoft.svn.core.wc.SVNClientManager; -import org.tmatesoft.svn.core.wc.SVNRevision; -import org.tmatesoft.svn.core.wc2.SvnCheckout; -import org.tmatesoft.svn.core.wc2.SvnOperationFactory; -import org.tmatesoft.svn.core.wc2.SvnTarget; -import sonia.scm.repository.InternalRepositoryException; -import sonia.scm.repository.Repository; import sonia.scm.repository.SvnWorkingCopyFactory; import sonia.scm.repository.work.WorkingCopyPool; import sonia.scm.repository.work.SimpleWorkingCopyFactory; -import sonia.scm.repository.work.WorkingCopyPool.ParentAndClone; import javax.inject.Inject; import java.io.File; @@ -50,44 +39,13 @@ public class SimpleSvnWorkingCopyFactory extends SimpleWorkingCopyFactory { - final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); - - SVNURL source; - try { - source = SVNURL.fromFile(context.getDirectory()); - } catch (SVNException ex) { - throw new InternalRepositoryException(context.getRepository(), "error creating svn url from central directory", ex); - } - - try { - final SvnCheckout checkout = svnOperationFactory.createCheckout(); - checkout.setSingleTarget(SvnTarget.fromFile(workingCopy)); - checkout.setSource(SvnTarget.fromURL(source)); - checkout.run(); - } catch (SVNException ex) { - throw new InternalRepositoryException(context.getRepository(), "error running svn checkout", ex); - } finally { - svnOperationFactory.dispose(); - } - - return new ParentAndClone<>(context.getDirectory(), workingCopy, workingCopy); - }; + protected WorkingCopyInitializer getInitializer(SvnContext context) { + return new SvnWorkingCopyInitializer(context); } @Override protected WorkingCopyReclaimer getReclaimer(SvnContext context) { - return (target, initialBranch) -> { - SVNClientManager clientManager = SVNClientManager.newInstance(); - try { - clientManager.getWCClient().doCleanup(target); - clientManager.getUpdateClient().doUpdate(target, SVNRevision.HEAD, SVNDepth.fromRecurse(true), false, false); - } catch (SVNException e) { - throw new ReclaimFailedException(e); - } - return new ParentAndClone<>(context.getDirectory(), target, target); - }; + return new SvnWorkingCopyReclaimer(context); } @Override diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnWorkingCopyInitializer.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnWorkingCopyInitializer.java new file mode 100644 index 0000000000..06a2edba02 --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnWorkingCopyInitializer.java @@ -0,0 +1,70 @@ +/* + * 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 org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.SVNURL; +import org.tmatesoft.svn.core.wc2.SvnCheckout; +import org.tmatesoft.svn.core.wc2.SvnOperationFactory; +import org.tmatesoft.svn.core.wc2.SvnTarget; +import sonia.scm.repository.InternalRepositoryException; +import sonia.scm.repository.work.SimpleWorkingCopyFactory; +import sonia.scm.repository.work.WorkingCopyFailedException; +import sonia.scm.repository.work.WorkingCopyPool; + +import java.io.File; + +class SvnWorkingCopyInitializer implements SimpleWorkingCopyFactory.WorkingCopyInitializer { + private final SvnContext context; + + public SvnWorkingCopyInitializer(SvnContext context) { + this.context = context; + } + + @Override + public WorkingCopyPool.ParentAndClone initialize(File workingCopy, String initialBranch) throws WorkingCopyFailedException { + final SvnOperationFactory svnOperationFactory = new SvnOperationFactory(); + + SVNURL source; + try { + source = SVNURL.fromFile(context.getDirectory()); + } catch (SVNException ex) { + throw new InternalRepositoryException(context.getRepository(), "error creating svn url from central directory", ex); + } + + try { + final SvnCheckout checkout = svnOperationFactory.createCheckout(); + checkout.setSingleTarget(SvnTarget.fromFile(workingCopy)); + checkout.setSource(SvnTarget.fromURL(source)); + checkout.run(); + } catch (SVNException ex) { + throw new InternalRepositoryException(context.getRepository(), "error running svn checkout", ex); + } finally { + svnOperationFactory.dispose(); + } + + return new WorkingCopyPool.ParentAndClone<>(context.getDirectory(), workingCopy, workingCopy); + } +} diff --git a/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnWorkingCopyReclaimer.java b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnWorkingCopyReclaimer.java new file mode 100644 index 0000000000..9ed5c6006f --- /dev/null +++ b/scm-plugins/scm-svn-plugin/src/main/java/sonia/scm/repository/spi/SvnWorkingCopyReclaimer.java @@ -0,0 +1,56 @@ +/* + * 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 org.tmatesoft.svn.core.SVNException; +import org.tmatesoft.svn.core.wc.SVNClientManager; +import org.tmatesoft.svn.core.wc.SVNRevision; +import sonia.scm.repository.work.SimpleWorkingCopyFactory; +import sonia.scm.repository.work.WorkingCopyPool; + +import java.io.File; + +import static org.tmatesoft.svn.core.SVNDepth.INFINITY; + +class SvnWorkingCopyReclaimer implements SimpleWorkingCopyFactory.WorkingCopyReclaimer { + private final SvnContext context; + + public SvnWorkingCopyReclaimer(SvnContext context) { + this.context = context; + } + + @Override + public WorkingCopyPool.ParentAndClone reclaim(File target, String initialBranch) throws SimpleWorkingCopyFactory.ReclaimFailedException { + SVNClientManager clientManager = SVNClientManager.newInstance(); + try { + clientManager.getWCClient().doRevert(new File[] {target}, INFINITY, null); + clientManager.getWCClient().doCleanup(target, true, true, true, true, true, false); + clientManager.getUpdateClient().doUpdate(target, SVNRevision.HEAD, INFINITY, false, false); + } catch (SVNException e) { + throw new SimpleWorkingCopyFactory.ReclaimFailedException(e); + } + return new WorkingCopyPool.ParentAndClone<>(context.getDirectory(), target, target); + } +} diff --git a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactoryTest.java b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactoryTest.java index 9cac5f205d..d5fc3c70fe 100644 --- a/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactoryTest.java +++ b/scm-plugins/scm-svn-plugin/src/test/java/sonia/scm/repository/spi/SimpleSvnWorkingCopyFactoryTest.java @@ -29,7 +29,7 @@ import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; import org.tmatesoft.svn.core.SVNException; -import sonia.scm.repository.Repository; +import sonia.scm.repository.work.CachingAllWorkingCopyPool; import sonia.scm.repository.work.NoneCachingWorkingCopyPool; import sonia.scm.repository.work.WorkdirProvider; import sonia.scm.repository.work.WorkingCopy; @@ -87,16 +87,44 @@ public class SimpleSvnWorkingCopyFactoryTest extends AbstractSvnCommandTestBase try (WorkingCopy workingCopy = factory.createWorkingCopy(createContext(), null)) { directory = workingCopy.getDirectory(); workingRepository = workingCopy.getWorkingRepository(); - } + assertThat(directory).doesNotExist(); assertThat(workingRepository).doesNotExist(); } @Test - public void shouldReturnRepository() { - SimpleSvnWorkingCopyFactory factory = new SimpleSvnWorkingCopyFactory(new NoneCachingWorkingCopyPool(workdirProvider)); - Repository scmRepository = createContext().getRepository(); - assertThat(scmRepository).isSameAs(repository); + public void shouldDeleteUntrackedFileOnReclaim() throws IOException { + SimpleSvnWorkingCopyFactory factory = new SimpleSvnWorkingCopyFactory(new CachingAllWorkingCopyPool(workdirProvider)); + + WorkingCopy workingCopy = factory.createWorkingCopy(createContext(), null); + File directory = workingCopy.getWorkingRepository(); + File untracked = new File(directory, "untracked"); + untracked.createNewFile(); + + workingCopy.close(); + assertThat(untracked).exists(); + + workingCopy = factory.createWorkingCopy(createContext(), null); + + assertThat(workingCopy.getWorkingRepository()).isEqualTo(directory); + assertThat(untracked).doesNotExist(); + } + + @Test + public void shouldRestoreDeletedFileOnReclaim() throws IOException { + SimpleSvnWorkingCopyFactory factory = new SimpleSvnWorkingCopyFactory(new CachingAllWorkingCopyPool(workdirProvider)); + + WorkingCopy workingCopy = factory.createWorkingCopy(createContext(), null); + File directory = workingCopy.getWorkingRepository(); + File a_txt = new File(directory, "a.txt"); + a_txt.delete(); + workingCopy.close(); + assertThat(a_txt).doesNotExist(); + + workingCopy = factory.createWorkingCopy(createContext(), null); + + assertThat(workingCopy.getWorkingRepository()).isEqualTo(directory); + assertThat(a_txt).exists(); } }