package org.tmatesoft.svn.test;

import org.junit.Assert;
import org.junit.Assume;
import org.junit.Ignore;
import org.junit.Test;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.auth.BasicAuthenticationManager;
import org.tmatesoft.svn.core.internal.wc.DefaultSVNOptions;
import org.tmatesoft.svn.core.internal.wc.SVNExternal;
import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb;
import org.tmatesoft.svn.core.internal.wc17.db.statement.SVNWCDbSchema;
import org.tmatesoft.svn.core.internal.wc2.compat.SvnCodec;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc2.*;

import java.io.File;
import java.util.*;

public class MergeTest {

    @Ignore("Temporarily ignored")
    @Test
    public void testConflictResolution() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testConflictResolution", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("file", "base".getBytes());
            SVNCommitInfo commitInfo1 = commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.changeFile("file", "theirs".getBytes());
            commitBuilder2.commit();

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url, commitInfo1.getNewRevision());
            final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory();

            final File file = new File(workingCopyDirectory, "file");
            TestUtil.writeFileContentsString(file, "mine");

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            runResolve(svnOperationFactory, file, SVNConflictChoice.MINE_CONFLICT);

            final String fileContentsString = TestUtil.readFileContentsString(file);
            Assert.assertEquals("mine", fileContentsString);

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testRemoteAddOverUnversionedFileConflictResolution() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testRemoteAddOverUnversionedFileConflictResolution", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.addFile("file", "their".getBytes());
            commitBuilder2.commit();

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url, 1);
            final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory();
            final File file = workingCopy.getFile("file");

            SVNFileUtil.ensureDirectoryExists(file.getParentFile());
            TestUtil.writeFileContentsString(file, "mine");

            boolean isExceptionExpected = !TestUtil.isNewWorkingCopyTest();

            if (isExceptionExpected) {
                final SvnUpdate update = svnOperationFactory.createUpdate();
                update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
                try {
                    update.run();
                    Assert.fail("An exception should be thrown");
                } catch (SVNException e) {
                    //expected for WC 1.6
                    Assert.assertEquals(SVNErrorCode.WC_OBSTRUCTED_UPDATE, e.getErrorMessage().getErrorCode());
                }
            } else {
                final SvnUpdate update = svnOperationFactory.createUpdate();
                update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
                update.run();

                runResolve(svnOperationFactory, file, SVNConflictChoice.MERGED);

                Assert.assertEquals("mine", TestUtil.readFileContentsString(file));

                final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
                Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
                Assert.assertFalse(statuses.get(file).isConflicted());
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testBothDeletedTreeConflict() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testBothDeletedTreeConflict", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("file");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.delete("file");
            commitBuilder2.commit();

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url, 1);
            final File file = workingCopy.getFile("file");

            workingCopy.delete(file);

            update(svnOperationFactory, workingCopy);

            final SvnGetInfo getInfo = svnOperationFactory.createGetInfo();
            getInfo.setSingleTarget(SvnTarget.fromFile(file));
            final SvnInfo svnInfo = getInfo.run();

            final Collection<SVNConflictDescription> conflicts = svnInfo.getWcInfo().getConflicts();
            Assert.assertEquals(1, conflicts.size());

            final SVNTreeConflictDescription conflict = (SVNTreeConflictDescription) conflicts.iterator().next();
            Assert.assertEquals(SVNConflictAction.DELETE, conflict.getConflictAction());
            Assert.assertEquals(SVNConflictReason.DELETED, conflict.getConflictReason());
            Assert.assertEquals(url, conflict.getSourceLeftVersion().getRepositoryRoot());
            Assert.assertNull(conflict.getSourceRightVersion());

            final SvnSchedule schedule = svnInfo.getWcInfo().getSchedule();
            Assert.assertEquals(SvnSchedule.NORMAL, schedule);

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopy.getWorkingCopyDirectory());
            final SvnStatus fileStatus = statuses.get(file);

            Assert.assertTrue(fileStatus.isConflicted());
            Assert.assertEquals(SVNStatusType.STATUS_CONFLICTED, fileStatus.getNodeStatus());

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateDeletedLockedFileDoesntCauseTreeConflictDavAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllApacheOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateDeletedLockedFileDoesntCauseTreeConflictDavAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithDavAccess(loginToPassword);

            final CommitBuilder commitBuilder = new CommitBuilder(url);
            commitBuilder.setAuthenticationManager(authenticationManager);
            commitBuilder.addFile("directory/file");
            commitBuilder.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "directory/file");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateDeletedLockedFileDoesntCauseTreeConflictSvnAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllSvnserveOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateDeletedLockedFileDoesntCauseTreeConflictSvnAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithSvnAccess(loginToPassword);

            final CommitBuilder commitBuilder = new CommitBuilder(url);
            commitBuilder.setAuthenticationManager(authenticationManager);
            commitBuilder.addFile("directory/file");
            commitBuilder.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "directory/file");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateOnRenamedParentOfLockedFileDoesntCauseTreeConflictDavAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllApacheOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateOnRenamedParentOfLockedFileDoesntCauseTreeConflictDavAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithDavAccess(loginToPassword);

            final CommitBuilder commitBuilder = new CommitBuilder(url);
            commitBuilder.setAuthenticationManager(authenticationManager);
            commitBuilder.addFile("directory/file");
            commitBuilder.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "directory/file");
            final File directory = new File(workingCopyDirectory, "directory");
            final File renamedDirectory = new File(workingCopyDirectory, "renamedDirectory");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnCopy copy = svnOperationFactory.createCopy();
            copy.setMove(true);
            copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(directory), SVNRevision.WORKING));
            copy.setSingleTarget(SvnTarget.fromFile(renamedDirectory));
            copy.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(directory).getNodeStatus());
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldDirectoryStatus = SvnCodec.status(context, statuses.get(directory));
                Assert.assertNull(oldDirectoryStatus.getTreeConflict());
                final SVNStatus oldFileStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldFileStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateOnRenamedParentOfLockedFileDoesntCauseTreeConflictSvnAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllSvnserveOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateOnRenamedParentOfLockedFileDoesntCauseTreeConflictSvnAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithSvnAccess(loginToPassword);

            final CommitBuilder commitBuilder = new CommitBuilder(url);
            commitBuilder.setAuthenticationManager(authenticationManager);
            commitBuilder.addFile("directory/file");
            commitBuilder.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "directory/file");
            final File directory = new File(workingCopyDirectory, "directory");
            final File renamedDirectory = new File(workingCopyDirectory, "renamedDirectory");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnCopy copy = svnOperationFactory.createCopy();
            copy.setMove(true);
            copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(directory), SVNRevision.WORKING));
            copy.setSingleTarget(SvnTarget.fromFile(renamedDirectory));
            copy.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(directory).getNodeStatus());
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldDirectoryStatus = SvnCodec.status(context, statuses.get(directory));
                Assert.assertNull(oldDirectoryStatus.getTreeConflict());
                final SVNStatus oldFileStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldFileStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateOnRenamedParentOfLockedFileAnotherFileDeletedDavAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllApacheOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateOnRenamedParentOfLockedFileAnotherFileDeletedDavAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithDavAccess(loginToPassword);

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.setAuthenticationManager(authenticationManager);
            commitBuilder1.addFile("directory/file");
            commitBuilder1.addFile("directory/fileToDelete");
            commitBuilder1.addFile("fileToDelete");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.setAuthenticationManager(authenticationManager);
            commitBuilder2.delete("directory/fileToDelete");
            commitBuilder2.delete("fileToDelete");
            commitBuilder2.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "directory/file");
            final File directory = new File(workingCopyDirectory, "directory");
            final File renamedDirectory = new File(workingCopyDirectory, "renamedDirectory");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnCopy copy = svnOperationFactory.createCopy();
            copy.setMove(true);
            copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(directory), SVNRevision.WORKING));
            copy.setSingleTarget(SvnTarget.fromFile(renamedDirectory));
            copy.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(directory).getNodeStatus());
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldDirectoryStatus = SvnCodec.status(context, statuses.get(directory));
                Assert.assertNull(oldDirectoryStatus.getTreeConflict());
                final SVNStatus oldFileStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldFileStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateOnRenamedParentOfLockedFileAnotherFileDeletedSvnAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllSvnserveOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateOnRenamedParentOfLockedFileAnotherFileDeletedSvnAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithSvnAccess(loginToPassword);

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.setAuthenticationManager(authenticationManager);
            commitBuilder1.addFile("directory/file");
            commitBuilder1.addFile("directory/fileToDelete");
            commitBuilder1.addFile("fileToDelete");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.setAuthenticationManager(authenticationManager);
            commitBuilder2.delete("directory/fileToDelete");
            commitBuilder2.delete("fileToDelete");
            commitBuilder2.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "directory/file");
            final File directory = new File(workingCopyDirectory, "directory");
            final File renamedDirectory = new File(workingCopyDirectory, "renamedDirectory");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnCopy copy = svnOperationFactory.createCopy();
            copy.setMove(true);
            copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(directory), SVNRevision.WORKING));
            copy.setSingleTarget(SvnTarget.fromFile(renamedDirectory));
            copy.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(directory).getNodeStatus());
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldDirectoryStatus = SvnCodec.status(context, statuses.get(directory));
                Assert.assertNull(oldDirectoryStatus.getTreeConflict());
                final SVNStatus oldFileStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldFileStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateOnMovedParentOfLockedFileDoesntCauseTreeConflictDavAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllApacheOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateOnRenamedParentOfLockedFileDoesntCauseTreeConflictDavAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithDavAccess(loginToPassword);

            final CommitBuilder commitBuilder = new CommitBuilder(url);
            commitBuilder.setAuthenticationManager(authenticationManager);
            commitBuilder.addFile("directory/subdirectory/file");
            commitBuilder.addDirectory("anotherDirectory");
            commitBuilder.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File directory = new File(workingCopyDirectory, "directory");
            final File subdirectory = new File(directory, "subdirectory");
            final File file = new File(subdirectory, "file");
            final File anotherDirectory = new File(workingCopyDirectory, "anotherDirectory");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnCopy copy = svnOperationFactory.createCopy();
            copy.setFailWhenDstExists(false);
            copy.setMove(true);
            copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(subdirectory), SVNRevision.WORKING));
            copy.setSingleTarget(SvnTarget.fromFile(anotherDirectory));
            copy.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(subdirectory).getNodeStatus());
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldDirectoryStatus = SvnCodec.status(context, statuses.get(subdirectory));
                Assert.assertNull(oldDirectoryStatus.getTreeConflict());
                final SVNStatus oldFileStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldFileStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testUpdateOnMovedParentOfLockedFileDoesntCauseTreeConflictSvnAccess() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        Assume.assumeTrue(TestUtil.areAllSvnserveOptionsSpecified(options));

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testUpdateOnRenamedParentOfLockedFileDoesntCauseTreeConflictSvnAccess", options);
        try {
            final BasicAuthenticationManager authenticationManager = new BasicAuthenticationManager("user", "password");
            svnOperationFactory.setAuthenticationManager(authenticationManager);

            final Map<String, String> loginToPassword = new HashMap<String, String>();
            loginToPassword.put("user", "password");
            final SVNURL url = sandbox.createSvnRepositoryWithSvnAccess(loginToPassword);

            final CommitBuilder commitBuilder = new CommitBuilder(url);
            commitBuilder.setAuthenticationManager(authenticationManager);
            commitBuilder.addFile("directory/subdirectory/file");
            commitBuilder.addDirectory("anotherDirectory");
            commitBuilder.commit();

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File directory = new File(workingCopyDirectory, "directory");
            final File subdirectory = new File(directory, "subdirectory");
            final File file = new File(subdirectory, "file");
            final File anotherDirectory = new File(workingCopyDirectory, "anotherDirectory");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(url));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.run();

            final SvnSetLock setLock = svnOperationFactory.createSetLock();
            setLock.setSingleTarget(SvnTarget.fromFile(file));
            setLock.run();

            final SvnScheduleForRemoval scheduleForRemoval = svnOperationFactory.createScheduleForRemoval();
            scheduleForRemoval.setSingleTarget(SvnTarget.fromFile(file));
            scheduleForRemoval.run();

            final SvnCopy copy = svnOperationFactory.createCopy();
            copy.setFailWhenDstExists(false);
            copy.setMove(true);
            copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(subdirectory), SVNRevision.WORKING));
            copy.setSingleTarget(SvnTarget.fromFile(anotherDirectory));
            copy.run();

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(subdirectory).getNodeStatus());
            Assert.assertEquals(SVNStatusType.STATUS_DELETED, statuses.get(file).getNodeStatus());
            Assert.assertNotNull(statuses.get(file).getLock());

            final SVNWCContext context = new SVNWCContext(svnOperationFactory.getOptions(), svnOperationFactory.getEventHandler());
            try {
                final SVNStatus oldDirectoryStatus = SvnCodec.status(context, statuses.get(subdirectory));
                Assert.assertNull(oldDirectoryStatus.getTreeConflict());
                final SVNStatus oldFileStatus = SvnCodec.status(context, statuses.get(file));
                Assert.assertNull(oldFileStatus.getTreeConflict());
            } finally {
                context.close();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }


    @Test
    public void testMergeFileAdditionAndDeletion() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testMergeFileAdditionAndDeletion", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addDirectory("directory1/directory");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.addDirectoryByCopying("directory2", "directory1");
            commitBuilder2.commit();

            final CommitBuilder commitBuilder3 = new CommitBuilder(url);
            commitBuilder3.addFile("directory2/directory/file", "contents".getBytes());
            commitBuilder3.commit();

            final CommitBuilder commitBuilder4 = new CommitBuilder(url);
            commitBuilder4.delete("directory2/directory/file");
            commitBuilder4.commit();

            final SVNURL directory1Url = url.appendPath("directory1", false);
            final SVNURL directory2Url = url.appendPath("directory2", false);

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(directory1Url);
            final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory();

            final SvnMerge merge = svnOperationFactory.createMerge();
            merge.addRevisionRange(SvnRevisionRange.create(SVNRevision.create(2), SVNRevision.create(3)));
            merge.addRevisionRange(SvnRevisionRange.create(SVNRevision.create(3), SVNRevision.create(4)));
            merge.setSource(SvnTarget.fromURL(directory2Url), false);
            merge.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            merge.run();

            Assert.assertFalse(workingCopy.getFile("directory/file").exists());

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testConflictOnFileExternalUpdate() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testConflictOnFileExternalUpdate", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final SVNExternal external = new SVNExternal("file", url.appendPath("file", false).toString(), SVNRevision.HEAD, SVNRevision.HEAD, false, false, true);

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addDirectory("directory");
            commitBuilder1.addFile("file");

            commitBuilder1.setDirectoryProperty("directory", SVNProperty.EXTERNALS, SVNPropertyValue.create(external.toString()));
            commitBuilder1.commit();

            final SVNURL directoryUrl = url.appendPath("directory", false);

            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "file");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.setSource(SvnTarget.fromURL(directoryUrl));
            checkout.setIgnoreExternals(false);
            checkout.run();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.changeFile("file", "theirs".getBytes());
            commitBuilder2.commit();

            TestUtil.writeFileContentsString(file, "mine");

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final Set<String> expectedNames = new HashSet<String>();
            expectedNames.add("file");
            expectedNames.add("file.r1");
            expectedNames.add("file.r2");
            expectedNames.add("file.mine");

            final Set<String> actualNames = new HashSet<String>();
            final File[] files = SVNFileListUtil.listFiles(workingCopyDirectory);
            for (File child : files) {
                String name = SVNFileUtil.getFileName(child);
                if (SVNFileUtil.getAdminDirectoryName().equals(name)) {
                    continue;
                }
                actualNames.add(name);
            }

            Assert.assertEquals(expectedNames, actualNames);

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testMergeRecordsActualPropertiesForFileWithMergeInfo() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        Assume.assumeTrue(TestUtil.isNewWorkingCopyTest());

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testMergeRecordsActualPropertiesForFileWithMergeInfo", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("directory/file");
            commitBuilder1.setFileProperty("directory/file", SVNProperty.MERGE_INFO, SVNPropertyValue.create("/somefile:1"));
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.addFileByCopying("directory/copiedFile", "directory/file");
            commitBuilder2.delete("directory/file");
            commitBuilder2.commit();

            final CommitBuilder commitBuilder3 = new CommitBuilder(url);
            commitBuilder3.addDirectoryByCopying("copiedDirectory", "directory", 1);
            commitBuilder3.commit();

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url.appendPath("copiedDirectory", false));
            final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory();

            final SvnMerge merge = svnOperationFactory.createMerge();
            merge.addRevisionRange(SvnRevisionRange.create(SVNRevision.create(1), SVNRevision.create(2)));
            merge.setSource(SvnTarget.fromURL(url.appendPath("directory", false)), false);
            merge.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            merge.run();

            Assert.assertEquals(2, TestUtil.getTableSize(workingCopy, SVNWCDbSchema.ACTUAL_NODE.name()));

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testImplicitGapIsExcludedFromMerging() throws Exception {
        //SVNKIT-305
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testImplicitGapIsExcludedFromMerging", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("trunk/file");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.delete("trunk/file");
            commitBuilder2.commit();

            final CommitBuilder commitBuilder3 = new CommitBuilder(url);
            commitBuilder3.addDirectoryByCopying("branches/branch", "trunk", 1);
            commitBuilder3.commit();

            final CommitBuilder commitBuilder4 = new CommitBuilder(url); //implicit src gap
            commitBuilder4.commit();

            final CommitBuilder commitBuilder5 = new CommitBuilder(url);
            commitBuilder5.delete("branches/branch");
            commitBuilder5.commit();

            final CommitBuilder commitBuilder6 = new CommitBuilder(url);
            commitBuilder6.addDirectoryByCopying("branches/branch", "trunk", 3);
            commitBuilder6.commit();

            final CommitBuilder commitBuilder7 = new CommitBuilder(url);
            commitBuilder7.addFileByCopying("trunk/file", "trunk/file", 1);
            commitBuilder7.commit();

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url.appendPath("trunk", false));

            final SVNClientManager clientManager = SVNClientManager.newInstance();
            try {
                final SVNDiffClient diffClient = clientManager.getDiffClient();

                workingCopy.setProperty(workingCopy.getFile("file"), SVNProperty.MERGE_INFO, SVNPropertyValue.create("/branches/branch/file:3")); //the value doesn't matter
                workingCopy.setProperty(workingCopy.getWorkingCopyDirectory(), SVNProperty.MERGE_INFO, SVNPropertyValue.create("/branches/branch:6-7"));
                Assert.assertEquals(8, workingCopy.commit(""));
                workingCopy.updateToRevision(-1);

                diffClient.doMerge(url.appendPath("branches/branch", false), SVNRevision.HEAD, Arrays.asList(
                        new SVNRevisionRange(SVNRevision.create(1), SVNRevision.HEAD)
                ), workingCopy.getWorkingCopyDirectory(), SVNDepth.INFINITY, true, false, false, false);

                Assert.assertEquals(9, workingCopy.commit(""));

            } finally {
                clientManager.dispose();
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testMergeAfterShallowMergeProducesCorrectMergeinfo() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testMergeAfterShallowMergeProducesCorrectMergeinfo", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("directory/subdirectory/file");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.addDirectoryByCopying("copiedDirectory", "directory");
            commitBuilder2.commit();

            final CommitBuilder commitBuilder3 = new CommitBuilder(url);
            commitBuilder3.changeFile("directory/subdirectory/file", "contents".getBytes());
            commitBuilder3.commit();

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url);

            final SvnMerge shallowMerge = svnOperationFactory.createMerge();
            shallowMerge.addRevisionRange(SvnRevisionRange.create(SVNRevision.create(1), SVNRevision.HEAD));
            shallowMerge.setSource(SvnTarget.fromURL(url.appendPath("directory", false)), false);
            shallowMerge.setSingleTarget(SvnTarget.fromFile(workingCopy.getFile("copiedDirectory")));
            shallowMerge.setDepth(SVNDepth.FILES);
            shallowMerge.run();

            final SvnMerge merge = svnOperationFactory.createMerge();
            merge.addRevisionRange(SvnRevisionRange.create(SVNRevision.create(1), SVNRevision.HEAD));
            merge.setSource(SvnTarget.fromURL(url.appendPath("directory", false)), false);
            merge.setSingleTarget(SvnTarget.fromFile(workingCopy.getFile("copiedDirectory")));
            merge.setDepth(SVNDepth.INFINITY);
            merge.run();

            final SvnGetProperties getProperties = svnOperationFactory.createGetProperties();
            getProperties.setSingleTarget(SvnTarget.fromFile(workingCopy.getFile("copiedDirectory")));
            final SVNProperties properties = getProperties.run();

            Assert.assertEquals("/directory:2-3", SVNPropertyValue.getPropertyAsString(properties.getSVNPropertyValue(SVNProperty.MERGE_INFO)));

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testPostponingConflictDoesntCreateEditedFile() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testPostponingConflictDoesntCreateEditedFile", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("file", "base".getBytes());
            SVNCommitInfo commitInfo1 = commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.changeFile("file", "theirs".getBytes());
            commitBuilder2.commit();

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(url, commitInfo1.getNewRevision());
            final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory();

            final File file = workingCopy.getFile("file");
            TestUtil.writeFileContentsString(file, "mine");

            final File anyFile = workingCopy.getFile("anyFile");
            TestUtil.writeFileContentsString(anyFile, "anyFile");

            final DefaultSVNOptions svnOptions = new DefaultSVNOptions();
            svnOptions.setConflictHandler(new ISVNConflictHandler() {
                public SVNConflictResult handleConflict(SVNConflictDescription conflictDescription) throws SVNException {
                    return new SVNConflictResult(SVNConflictChoice.POSTPONE, anyFile);
                }
            });
            svnOperationFactory.setOptions(svnOptions);

            final SvnUpdate update = svnOperationFactory.createUpdate();
            update.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            update.run();

            final File editedFile = workingCopy.getFile("file.edited");
            Assert.assertFalse(editedFile.exists());

            final SvnGetInfo getInfo = svnOperationFactory.createGetInfo();
            getInfo.setSingleTarget(SvnTarget.fromFile(file));
            final SvnInfo svnInfo = getInfo.run();

            final Collection<SVNConflictDescription> conflicts = svnInfo.getWcInfo().getConflicts();
            Assert.assertEquals(1, conflicts.size());

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testMergeReintegrate() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testMergeReintegrate", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addDirectory("trunk");
            commitBuilder1.addDirectory("branches");
            commitBuilder1.addDirectory("tags");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.addFile("trunk/file", "string1".getBytes());
            commitBuilder2.commit();

            final CommitBuilder commitBuilder3 = new CommitBuilder(url);
            commitBuilder3.addDirectoryByCopying("branches/branch", "trunk");
            commitBuilder3.commit();

            final CommitBuilder commitBuilder4 = new CommitBuilder(url);
            commitBuilder4.changeFile("trunk/file", "string2".getBytes());
            commitBuilder4.commit();

            final CommitBuilder commitBuilder5 = new CommitBuilder(url);
            commitBuilder5.setDirectoryProperty("branches/branch", SVNProperty.MERGE_INFO, SVNPropertyValue.create("/trunk:4"));
            commitBuilder5.changeFile("branches/branch/file", "string2".getBytes());
            commitBuilder5.commit();

            final CommitBuilder commitBuilder6 = new CommitBuilder(url);
            commitBuilder6.changeFile("branches/branch/file", "string3".getBytes());
            commitBuilder6.commit();

            final SVNURL trunkUrl = url.appendPath("trunk", false);
            final SVNURL branchUrl = url.appendPath("branches/branch", false);
            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(trunkUrl);
            final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory();
            final File file = workingCopy.getFile("file");

            final SVNURL trunkFileUrl = trunkUrl.appendPath("file", false);
            final SVNURL branchFileUrl = branchUrl.appendPath("file", false);

            final SvnMerge merge = svnOperationFactory.createMerge();
            merge.addRevisionRange(SvnRevisionRange.create(SVNRevision.create(0), SVNRevision.HEAD));
            merge.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            merge.setSource(SvnTarget.fromURL(branchUrl), false);
            merge.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            for (Map.Entry<File, SvnStatus> entry : statuses.entrySet()) {
                final SvnStatus status = entry.getValue();
                boolean conflicted = status.isConflicted();

                Assert.assertFalse(conflicted);
            }

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testMergeWC17() throws Exception {
        //SVNKIT-430
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testMergeWC17", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("trunk/file");
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.addDirectoryByCopying("branches/branch", "trunk");
            commitBuilder2.commit();

            final CommitBuilder commitBuilder3 = new CommitBuilder(url);
            commitBuilder3.changeFile("trunk/file", "ours".getBytes());
            commitBuilder3.changeFile("branches/branch/file", "theirs".getBytes());
            commitBuilder3.commit();

            final SVNURL trunkUrl = url.appendPath("trunk", false);
            final SVNURL branchUrl = url.appendPath("branches/branch", false);
            final File workingCopyDirectory = sandbox.createDirectory("wc");
            final File file = new File(workingCopyDirectory, "file");

            final SvnCheckout checkout = svnOperationFactory.createCheckout();
            checkout.setSource(SvnTarget.fromURL(trunkUrl));
            checkout.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            checkout.setTargetWorkingCopyFormat(ISVNWCDb.WC_FORMAT_17);
            checkout.run();

            final SvnMerge merge = svnOperationFactory.createMerge();
            merge.addRevisionRange(SvnRevisionRange.create(SVNRevision.create(0), SVNRevision.HEAD));
            merge.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            merge.setSource(SvnTarget.fromURL(branchUrl), false);
            merge.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            final SvnStatus fileStatus = statuses.get(file);

            Assert.assertTrue(fileStatus.isConflicted());
            Assert.assertEquals(ISVNWCDb.WC_FORMAT_17, fileStatus.getWorkingCopyFormat());

        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    @Test
    public void testReverseMerge() throws Exception {
        final TestOptions options = TestOptions.getInstance();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testReverseMerge", options);
        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        try {
            // Create local repository
            final SVNURL url = sandbox.createSvnRepository();

            // Check out a working copy
            final WorkingCopy wc = sandbox.checkoutNewWorkingCopy(url);
            final File config = wc.getFile("config");
            config.createNewFile();
            wc.add(config);
            wc.commit("file created");

            wc.delete(config);
            config.mkdir();
            wc.add(config);
            Assert.assertTrue(config.isDirectory());

            long rev = wc.commit("file replaced with dir");
            // Update to HEAD
            wc.updateToRevision(-1);

            final File workingCopyDirectory = wc.getWorkingCopyDirectory();
            final SVNURL configUrl = url.appendPath("config", false);

            // Roll back last revision changes
            SvnMerge merge = svnOperationFactory.createMerge();
            merge.addRevisionRange(SvnCodec.revisionRange(new SVNRevisionRange(SVNRevision.create(rev), SVNRevision.create(rev - 1))));
            merge.setDepth(SVNDepth.UNKNOWN);
            merge.setDryRun(false);
            merge.setForce(true);
            merge.setIgnoreAncestry(false);
            merge.setIgnoreMergeInfo(false);
            merge.setMergeOptions(null);
            merge.setRecordOnly(false);
            merge.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            merge.setSource(SvnTarget.fromFile(workingCopyDirectory), false);
            merge.run();

            final Map<File, SvnStatus> statuses = TestUtil.getStatuses(svnOperationFactory, workingCopyDirectory);
            final SvnStatus status = statuses.get(config);
            Assert.assertEquals(SVNStatusType.STATUS_REPLACED, status.getNodeStatus());
            Assert.assertTrue(status.isCopied());
            Assert.assertFalse(status.isConflicted());
            Assert.assertEquals(configUrl, status.getCopyFromUrl());

        } finally {
            sandbox.dispose();
        }
    }

    @Test
    public void testMergeAutomaticWorkaround() throws Exception {
        final TestOptions options = TestOptions.getInstance();

        final SvnOperationFactory svnOperationFactory = new SvnOperationFactory();
        final Sandbox sandbox = Sandbox.createWithCleanup(getTestName() + ".testMergeFileAdditionAndDeletion", options);
        try {
            final SVNURL url = sandbox.createSvnRepository();

            final CommitBuilder commitBuilder1 = new CommitBuilder(url);
            commitBuilder1.addFile("trunk/file", "base".getBytes());
            commitBuilder1.commit();

            final CommitBuilder commitBuilder2 = new CommitBuilder(url);
            commitBuilder2.addDirectoryByCopying("branches/branch", "trunk", 1);
            commitBuilder2.commit();

            final CommitBuilder commitBuilder3 = new CommitBuilder(url);
            commitBuilder3.changeFile("branches/branch/file", "theirs".getBytes());
            commitBuilder3.commit();

            final SVNURL trunkUrl = url.appendPath("trunk", false);
            final SVNURL branchUrl = url.appendPath("branches/branch", false);

            final WorkingCopy workingCopy = sandbox.checkoutNewWorkingCopy(trunkUrl);
            final File workingCopyDirectory = workingCopy.getWorkingCopyDirectory();

            final SvnMerge merge = svnOperationFactory.createMerge();
            merge.setSource(SvnTarget.fromURL(branchUrl), false);
            merge.setSingleTarget(SvnTarget.fromFile(workingCopyDirectory));
            merge.run();

            Assert.assertEquals("theirs", SVNFileUtil.readFile(workingCopy.getFile("file")));
        } finally {
            svnOperationFactory.dispose();
            sandbox.dispose();
        }
    }

    private void update(SvnOperationFactory svnOperationFactory, WorkingCopy workingCopy) throws SVNException {
        final SvnUpdate update = svnOperationFactory.createUpdate();
        update.setSingleTarget(SvnTarget.fromFile(workingCopy.getWorkingCopyDirectory()));
        update.run();
    }

    private void runResolve(SvnOperationFactory svnOperationFactory, File file, SVNConflictChoice resolution) throws SVNException {
        final SVNClientManager clientManager = SVNClientManager.newInstance(svnOperationFactory.getOptions(), svnOperationFactory.getRepositoryPool());
        try {
            final SVNWCClient wcClient = clientManager.getWCClient();
            wcClient.doResolve(file, SVNDepth.INFINITY, resolution);
        } finally {
            clientManager.dispose();
        }
    }

    private String getTestName() {
        return "MergeTest";
    }
}
