/* * ==================================================================== * Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved. * * This software is licensed as described in the file COPYING, which * you should have received as part of this distribution. The terms * are also available at http://svnkit.com/license.html * If newer versions of this license are posted there, you may use a * newer version instead, at your option. * ==================================================================== */ package org.tmatesoft.svn.core.wc; import java.io.File; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.wc16.SVNMoveClient16; import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc2.SvnCopy; import org.tmatesoft.svn.core.wc2.SvnCopySource; import org.tmatesoft.svn.core.wc2.SvnOperationFactory; import org.tmatesoft.svn.core.wc2.SvnTarget; /** * The <b>SVNMoveClient</b> provides an extra client-side functionality over * standard (i.e. compatible with the SVN command line client) move operations. * This class helps to overcome the SVN limitations regarding move operations. * Using <b>SVNMoveClient</b> you can easily: * <ul> * <li>move versioned items to other versioned ones within the same Working * Copy, what even allows to replace items scheduled for deletion, or those that * are missing but are still under version control and have a node kind * different from the node kind of the source (!); * <li>move versioned items belonging to one Working Copy to versioned items * that belong to absolutely different Working Copy; * <li>move versioned items to unversioned ones; * <li>move unversioned items to versioned ones; * <li>move unversioned items to unversioned ones; * <li>revert any of the kinds of moving listed above; * <li>complete a copy/move operation for a file, that is if you have manually * copied/moved a versioned file to an unversioned file in a Working copy, you * can run a 'virtual' copy/move on these files to copy/move all the necessary * administrative version control information. * </ul> * * @version 1.3 * @author TMate Software Ltd. * @since 1.2 */ public class SVNMoveClient extends SVNBasicClient { /** * Constructs and initializes an <b>SVNMoveClient</b> object with the * specified run-time configuration and authentication drivers. * * <p> * If <code>options</code> is <span class="javakeyword">null</span>, then * this <b>SVNMoveClient</b> will be using a default run-time configuration * driver which takes client-side settings from the default SVN's run-time * configuration area but is not able to change those settings (read more on * {@link ISVNOptions} and {@link SVNWCUtil}). * * <p> * If <code>authManager</code> is <span class="javakeyword">null</span>, * then this <b>SVNMoveClient</b> will be using a default authentication and * network layers driver (see * {@link SVNWCUtil#createDefaultAuthenticationManager()}) which uses * server-side settings and auth storage from the default SVN's run-time * configuration area (or system properties if that area is not found). * * @param authManager * an authentication and network layers driver * @param options * a run-time configuration options driver */ public SVNMoveClient(ISVNAuthenticationManager authManager, ISVNOptions options) { super(authManager, options); } /** * Constructs and initializes an <b>SVNMoveClient</b> object with the * specified run-time configuration and repository pool object. * * <p/> * If <code>options</code> is <span class="javakeyword">null</span>, then * this <b>SVNMoveClient</b> will be using a default run-time configuration * driver which takes client-side settings from the default SVN's run-time * configuration area but is not able to change those settings (read more on * {@link ISVNOptions} and {@link SVNWCUtil}). * * <p/> * If <code>repositoryPool</code> is <span class="javakeyword">null</span>, * then {@link org.tmatesoft.svn.core.io.SVNRepositoryFactory} will be used * to create {@link SVNRepository repository access objects}. * * @param repositoryPool * a repository pool object * @param options * a run-time configuration options driver */ public SVNMoveClient(ISVNRepositoryPool repositoryPool, ISVNOptions options) { super(repositoryPool, options); } public SVNMoveClient(SvnOperationFactory of) { super(of); } /** * Moves a source item to a destination one. * * <p> * <code>dst</code> should not exist. Furher it's considered to be versioned * if its parent directory is under version control, otherwise * <code>dst</code> is considered to be unversioned. * * <p> * If both <code>src</code> and <code>dst</code> are unversioned, then * simply moves <code>src</code> to <code>dst</code> in the filesystem. * * <p> * If <code>src</code> is versioned but <code>dst</code> is not, then * exports <code>src</code> to <code>dst</code> in the filesystem and * removes <code>src</code> from version control. * * <p> * If <code>dst</code> is versioned but <code>src</code> is not, then moves * <code>src</code> to <code>dst</code> (even if <code>dst</code> is * scheduled for deletion). * * <p> * If both <code>src</code> and <code>dst</code> are versioned and located * within the same Working Copy, then moves <code>src</code> to * <code>dst</code> (even if <code>dst</code> is scheduled for deletion), or * tries to replace <code>dst</code> with <code>src</code> if the former is * missing and has a node kind different from the node kind of the source. * If <code>src</code> is scheduled for addition with history, * <code>dst</code> will be set the same ancestor URL and revision from * which the source was copied. If <code>src</code> and <code>dst</code> are * located in different Working Copies, then this method copies * <code>src</code> to <code>dst</code>, tries to put the latter under * version control and finally removes <code>src</code>. * * @param src * a source path * @param dst * a destination path * @throws SVNException * if one of the following is true: * <ul> * <li><code>dst</code> already exists <li><code>src</code> does * not exist * </ul> */ public void doMove(File src, File dst) throws SVNException { final SvnWcGeneration srcGeneration = SvnOperationFactory.detectWcGeneration(src, true, false); final SvnWcGeneration dstGeneration = SvnOperationFactory.detectWcGeneration(dst, true, true); if (srcGeneration == SvnWcGeneration.V17 && dstGeneration == SvnWcGeneration.V17) { final SvnCopy copy = getOperationsFactory().createCopy(); copy.setMove(true); copy.setSingleTarget(SvnTarget.fromFile(dst)); copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(src), SVNRevision.WORKING)); copy.setFailWhenDstExists(true); copy.run(); } else { try { final SVNMoveClient16 oldClient = new SVNMoveClient16(getOperationsFactory().getAuthenticationManager(), getOptions()); oldClient.doMove(src, dst); } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_UNSUPPORTED_FORMAT) { final SvnCopy cp = getOperationsFactory().createCopy(); cp.setMove(true); cp.setSingleTarget(SvnTarget.fromFile(dst)); cp.addCopySource(SvnCopySource.create(SvnTarget.fromFile(src), SVNRevision.WORKING)); cp.setFailWhenDstExists(true); cp.run(); } } } } /** * Reverts a previous move operation back. Provided in pair with * {@link #doMove(File, File) doMove()} and used to roll back move * operations. In this case <code>src</code> is considered to be the target * of the previsous move operation, and <code>dst</code> is regarded to be * the source of that same operation which have been moved to * <code>src</code> and now is to be restored. * * <p> * <code>dst</code> could exist in that case if it has been a WC directory * that was scheduled for deletion during the previous move operation. * Furher <code>dst</code> is considered to be versioned if its parent * directory is under version control, otherwise <code>dst</code> is * considered to be unversioned. * * <p> * If both <code>src</code> and <code>dst</code> are unversioned, then * simply moves <code>src</code> back to <code>dst</code> in the filesystem. * * <p> * If <code>src</code> is versioned but <code>dst</code> is not, then * unmoves <code>src</code> to <code>dst</code> in the filesystem and * removes <code>src</code> from version control. * * <p> * If <code>dst</code> is versioned but <code>src</code> is not, then first * tries to make a revert on <code>dst</code> - if it has not been committed * yet, it will be simply reverted. However in the case <code>dst</code> has * been already removed from the repository, <code>src</code> will be copied * back to <code>dst</code> and scheduled for addition. Then * <code>src</code> is removed from the filesystem. * * <p> * If both <code>src</code> and <code>dst</code> are versioned then the * following situations are possible: * <ul> * <li>If <code>dst</code> is still scheduled for deletion, then it is * reverted back and <code>src</code> is scheduled for deletion. * <li>in the case if <code>dst</code> exists but is not scheduled for * deletion, <code>src</code> is cleanly exported to <code>dst</code> and * removed from version control. * <li>if <code>dst</code> and <code>src</code> are from different * repositories (appear to be in different Working Copies), then * <code>src</code> is copied to <code>dst</code> (with scheduling * <code>dst</code> for addition, but not with history since copying is made * in the filesystem only) and removed from version control. * <li>if both <code>dst</code> and <code>src</code> are in the same * repository (appear to be located in the same Working Copy) and: * <ul style="list-style-type: lower-alpha"> * <li>if <code>src</code> is scheduled for addition with history, then * copies <code>src</code> to <code>dst</code> specifying the source * ancestor's URL and revision (i.e. the ancestor of the source is the * ancestor of the destination); * <li>if <code>src</code> is already under version control, then copies * <code>src</code> to <code>dst</code> specifying the source URL and * revision as the ancestor (i.e. <code>src</code> itself is the ancestor of * <code>dst</code>); * <li>if <code>src</code> is just scheduled for addition (without history), * then simply copies <code>src</code> to <code>dst</code> (only in the * filesystem, without history) and schedules <code>dst</code> for addition; * </ul> * then <code>src</code> is removed from version control. * </ul> * * @param src * a source path * @param dst * a destination path * @throws SVNException * if <code>src</code> does not exist * */ // move that considered as move undo. public void undoMove(File src, File dst) throws SVNException { SVNMoveClient16 oldClient = new SVNMoveClient16(getOperationsFactory().getAuthenticationManager(), getOptions()); oldClient.undoMove(src, dst); } /** * Copies/moves administrative version control information of a source file * to administrative information of a destination file. For example, if you * have manually copied/moved a source file to a target one (manually means * just in the filesystem, not using version control operations) and then * would like to turn this copying/moving into a complete version control * copy or move operation, use this method that will finish all the work for * you - it will copy/move all the necessary administrative information * (kept in the source <i>.svn</i> directory) to the target <i>.svn</i> * directory. * * <p> * In that case when you have your files copied/moved in the filesystem, you * can not perform standard (version control) copying/moving - since the * target already exists and the source may be already deleted. Use this * method to overcome that restriction. * * @param src * a source file path (was copied/moved to <code>dst</code>) * @param dst * a destination file path * @param move * if <span class="javakeyword">true</span> then completes moving * <code>src</code> to <code>dst</code>, otherwise completes * copying <code>src</code> to <code>dst</code> * @throws SVNException * if one of the following is true: * <ul> * <li><code>move = </code><span class="javakeyword">true</span> * and <code>src</code> still exists <li><code>dst</code> does * not exist <li><code>dst</code> is a directory <li><code>src * </code> is a directory <li><code>src</code> is not under * version control <li><code>dst</code> is already under version * control <li>if <code>src</code> is copied but not scheduled * for addition, and SVNKit is not able to locate the copied * directory root for <code>src</code> * </ul> */ public void doVirtualCopy(File src, File dst, boolean move) throws SVNException { final SvnCopy copy = getOperationsFactory().createCopy(); copy.addCopySource(SvnCopySource.create(SvnTarget.fromFile(src), SVNRevision.WORKING)); copy.setSingleTarget(SvnTarget.fromFile(dst)); copy.setMove(move); copy.setVirtual(true); copy.run(); } }