/* * ==================================================================== * 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 java.util.Collection; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.wc2.compat.SvnCodec; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc2.SvnCopy; import org.tmatesoft.svn.core.wc2.SvnOperationFactory; import org.tmatesoft.svn.core.wc2.SvnRemoteCopy; import org.tmatesoft.svn.core.wc2.SvnTarget; /** * The <b>SVNCopyClient</b> provides methods to perform any kinds of copying and * moving that SVN supports - operating on both Working Copies (WC) and URLs. * * <p> * Copy operations allow a user to copy versioned files and directories with all * their previous history in several ways. * * <p> * Supported copy operations are: * <ul> * <li>Working Copy to Working Copy (WC-to-WC) copying - this operation copies * the source Working Copy item to the destination one and schedules the source * copy for addition with history. * <li>Working Copy to URL (WC-to-URL) copying - this operation commits to the * repository (exactly to that repository location that is specified by URL) a * copy of the Working Copy item. * <li>URL to Working Copy (URL-to-WC) copying - this operation will copy the * source item from the repository to the Working Copy item and schedule the * source copy for addition with history. * <li>URL to URL (URL-to-URL) copying - this is a fully repository-side * operation, it commits a copy of the source item to a specified repository * location (within the same repository, of course). * </ul> * * <p> * Besides just copying <b>SVNCopyClient</b> also is able to move a versioned * item - that is first making a copy of the source item and then scheduling the * source item for deletion when operating on a Working Copy, or right * committing the deletion of the source item when operating immediately on the * repository. * * <p> * Supported move operations are: * <ul> * <li>Working Copy to Working Copy (WC-to-WC) moving - this operation copies * the source Working Copy item to the destination one and schedules the source * item for deletion. * <li>URL to URL (URL-to-URL) moving - this is a fully repository-side * operation, it commits a copy of the source item to a specified repository * location and deletes the source item. * </ul> * * <p> * Overloaded <b>doCopy()</b> methods of <b>SVNCopyClient</b> are similar to <code>'svn copy'</code> * and <code>'svn move'</code> commands of the SVN command line client. * * @version 1.3 * @author TMate Software Ltd. * @since 1.2 * @see <a target="_top" href="http://svnkit.com/kb/examples/">Examples</a> */ public class SVNCopyClient extends SVNBasicClient { private ISVNCommitHandler commitHandler; private ISVNCommitParameters commitParamaters; private ISVNExternalsHandler externalsHandler; private boolean disableLocalModifications; /** * Constructs and initializes an <b>SVNCopyClient</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>SVNCopyClient</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>SVNCopyClient</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 SVNCopyClient(ISVNAuthenticationManager authManager, ISVNOptions options) { super(authManager, options); } /** * Constructs and initializes an <b>SVNCopyClient</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>SVNCopyClient</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 SVNCopyClient(ISVNRepositoryPool repositoryPool, ISVNOptions options) { super(repositoryPool, options); } public SVNCopyClient(SvnOperationFactory of) { super(of); } protected void initDefaults() { setCommitParameters(null); setCommitHandler(null); setExternalsHandler(null); } /** * Sets an implementation of <b>ISVNCommitHandler</b> to the commit handler * that will be used during commit operations to handle commit log messages. * The handler will receive a clien's log message and items (represented as * <b>SVNCommitItem</b> objects) that will be committed. Depending on * implementor's aims the initial log message can be modified (or something * else) and returned back. * * <p> * If using <b>SVNCopyClient</b> without specifying any commit handler then * a default one will be used - {@link DefaultSVNCommitHandler}. * * @param handler * an implementor's handler that will be used to handle commit * log messages * @see #getCommitHandler() * @see SVNCommitItem */ public void setCommitHandler(ISVNCommitHandler handler) { if (handler == null) { handler = new DefaultSVNCommitHandler(); } this.commitHandler = handler; } /** * Returns the specified commit handler (if set) being in use or a default * one (<b>DefaultSVNCommitHandler</b>) if no special implementations of * <b>ISVNCommitHandler</b> were previousely provided. * * @return the commit handler being in use or a default one * @see #setCommitHandler(ISVNCommitHandler) * @see DefaultSVNCommitHandler */ public ISVNCommitHandler getCommitHandler() { return commitHandler; } /** * Sets commit parameters to use. * * <p> * When no parameters are set {@link DefaultSVNCommitParameters default} * ones are used. * * @param parameters * commit parameters * @see #getCommitParameters() */ public void setCommitParameters(ISVNCommitParameters parameters) { if (parameters == null) { parameters = new DefaultSVNCommitParameters(); } this.commitParamaters = parameters; } /** * Returns commit parameters. * * <p> * If no user parameters were previously specified, once creates and returns * {@link DefaultSVNCommitParameters default} ones. * * @return commit parameters * @see #setCommitParameters(ISVNCommitParameters) */ public ISVNCommitParameters getCommitParameters() { return this.commitParamaters; } /** * Sets an externals handler to be used by this client object. * * @param externalsHandler * user's implementation of {@link ISVNExternalsHandler} * @see #getExternalsHandler() * @since 1.2 */ public void setExternalsHandler(ISVNExternalsHandler externalsHandler) { if (externalsHandler == null) { externalsHandler = ISVNExternalsHandler.DEFAULT; } this.externalsHandler = externalsHandler; } public void setDisableLocalModificationCopying(boolean disable) { this.disableLocalModifications = disable; } /** * Returns an externals handler used by this update client. * * <p/> * If no user's handler is provided then * {@link ISVNExternalsHandler#DEFAULT} is returned and used by this client * object by default. * * <p/> * For more information what externals handlers are for, please, refer to * {@link ISVNExternalsHandler} and * {@link #doCopy(SVNCopySource[], SVNURL, boolean, boolean, boolean, String, SVNProperties)}. * * @return externals handler being in use * @see #setExternalsHandler(ISVNExternalsHandler) * @since 1.2 */ public ISVNExternalsHandler getExternalsHandler() { return this.externalsHandler; } /** * Copies each source in <code>sources</code> to <code>dst</code>. * * <p/> * If multiple <code>sources</code> are given, <code>dst</code> must be a * directory, and <code>sources</code> will be copied as children of * <code>dst</code>. * * <p/> * Each <code>src</code> in <code>sources</code> must be files or * directories under version control, or URLs of a versioned item in the * repository. If <code>sources</code> has multiple items, they must be all * repository URLs or all working copy paths. * * <p/> * The parent of <code>dst</code> must already exist. * * <p/> * If <code>sources</code> has only one item, attempts to copy it to * <code>dst</code>. If <code>failWhenDstExists</code> is <span * class="javakeyword">false</span> and <code>dst</code> already exists, * attempts to copy the item as a child of <code>dst</code>. If * <code>failWhenDstExists</code> is <span class="javakeyword">true</span> * and <code>dst</code> already exists, throws an {@link SVNException} with * the {@link SVNErrorCode#ENTRY_EXISTS} error code. * * <p/> * If <code>sources</code> has multiple items, and * <code>failWhenDstExists</code> is <span class="javakeyword">false</span>, * all <code>sources</code> are copied as children of <code>dst</code>. If * any child of <code>dst</code> already exists with the same name any item * in <code>sources</code>, throws an {@link SVNException} with the * {@link SVNErrorCode#ENTRY_EXISTS} error code. * * <p/> * If <code>sources</code> has multiple items, and * <code>failWhenDstExists</code> is <span class="javakeyword">true</span>, * throws an {@link SVNException} with the * {@link SVNErrorCode#CLIENT_MULTIPLE_SOURCES_DISALLOWED}. * * <p/> * This method is just a variant of a local add operation, where * <code>sources</code> are scheduled for addition as copies. No changes * will happen to the repository until a commit occurs. This scheduling can * be removed with * {@link SVNWCClient#doRevert(File[], SVNDepth, Collection)}. * * <p/> * If * <code>makeParents is <span class="javakeyword">true</span>, creates any non-existent parent directories * also. * * <p/> * If the caller's {@link ISVNEventHandler} is non-<span class="javakeyword">null</span>, invokes it * for each item added at the new location. * * <p/> * Note: this routine requires repository access only when sources are urls. * * @param sources * array of copy sources * @param dst * destination working copy path * @param isMove * if <span class="javakeyword">true</span>, then it will be a * move operation (delete, then add with history) * @param makeParents * if <span class="javakeyword">true</span>, creates non-existent * parent directories as well * @param failWhenDstExists * controls whether to fail or not if <code>dst</code> already * exists * @throws SVNException * @since 1.2, SVN 1.5 */ public void doCopy(SVNCopySource[] sources, File dst, boolean isMove, boolean makeParents, boolean failWhenDstExists) throws SVNException { SvnCopy cp = getOperationsFactory().createCopy(); cp.setSingleTarget(SvnTarget.fromFile(dst)); cp.setMove(isMove); cp.setFailWhenDstExists(failWhenDstExists); cp.setMakeParents(makeParents); for (int i = 0; i < sources.length; i++) { cp.addCopySource(SvnCodec.copySource(sources[i])); } cp.run(); } /** * Copies each source in <code>sources</code> to <code>dst</code>. * * <p/> * If multiple <code>sources</code> are given, <code>dst</code> must be a * directory, and <code>sources</code> will be copied as children of * <code>dst</code>. * * <p/> * Each <code>src</code> in <code>sources</code> must be files or * directories under version control, or URLs of a versioned item in the * repository. If <code>sources</code> has multiple items, they must be all * repository URLs or all working copy paths. * * <p/> * The parent of <code>dst</code> must already exist. * * <p/> * If <code>sources</code> has only one item, attempts to copy it to * <code>dst</code>. If <code>failWhenDstExists</code> is <span * class="javakeyword">false</span> and <code>dst</code> already exists, * attempts to copy the item as a child of <code>dst</code>. If * <code>failWhenDstExists</code> is <span class="javakeyword">true</span> * and <code>dst</code> already exists, throws an {@link SVNException} with * the {@link SVNErrorCode#ENTRY_EXISTS} error code. * * <p/> * If <code>sources</code> has multiple items, and * <code>failWhenDstExists</code> is <span class="javakeyword">false</span>, * all <code>sources</code> are copied as children of <code>dst</code>. If * any child of <code>dst</code> already exists with the same name any item * in <code>sources</code>, throws an {@link SVNException} with the * {@link SVNErrorCode#ENTRY_EXISTS} error code. * * <p/> * If <code>sources</code> has multiple items, and * <code>failWhenDstExists</code> is <span class="javakeyword">true</span>, * throws an {@link SVNException} with the * {@link SVNErrorCode#CLIENT_MULTIPLE_SOURCES_DISALLOWED}. * * <p/> * This method is just a variant of a local add operation, where * <code>sources</code> are scheduled for addition as copies. No changes * will happen to the repository until a commit occurs. This scheduling can * be removed with * {@link SVNWCClient#doRevert(File[], SVNDepth, Collection)}. * * <p/> * If * <code>makeParents is <span class="javakeyword">true</span>, creates any non-existent parent directories * also. * * <p/> * If the caller's {@link ISVNEventHandler} is non-<span class="javakeyword">null</span>, invokes it * for each item added at the new location. * * <p/> * Note: this routine requires repository access only when sources are urls. * * @param sources * array of copy sources * @param dst * destination working copy path * @param isMove * if <span class="javakeyword">true</span>, then it will be a * move operation (delete, then add with history) * @param makeParents * if <span class="javakeyword">true</span>, creates non-existent * parent directories as well * @param failWhenDstExists * controls whether to fail or not if <code>dst</code> already * exists * @param allowMixedRevisions * allow to move directories with nodes with mixed revisions in it * @param metadataOnly * don't copy physical files * @throws SVNException * @since 1.8, SVN 1.8 */ public void doCopy(SVNCopySource[] sources, File dst, boolean isMove, boolean makeParents, boolean failWhenDstExists, boolean allowMixedRevisions, boolean metadataOnly) throws SVNException { SvnCopy cp = getOperationsFactory().createCopy(); cp.setSingleTarget(SvnTarget.fromFile(dst)); cp.setMove(isMove); cp.setFailWhenDstExists(failWhenDstExists); cp.setMakeParents(makeParents); cp.setIgnoreExternals(isIgnoreExternals()); cp.setAllowMixedRevisions(allowMixedRevisions); cp.setMetadataOnly(metadataOnly); for (int i = 0; i < sources.length; i++) { cp.addCopySource(SvnCodec.copySource(sources[i])); } cp.run(); } /** * Copies each source in <code>sources</code> to <code>dst</code>. * * <p/> * If multiple <code>sources</code> are given, <code>dst</code> must be a * directory, and <code>sources</code> will be copied as children of * <code>dst</code>. * * <p/> * Each <code>src</code> in <code>sources</code> must be files or * directories under version control, or URLs of a versioned item in the * repository. If <code>sources</code> has multiple items, they must be all * repository URLs or all working copy paths. * * <p/> * The parent of <code>dst</code> must already exist. * * <p/> * If <code>sources</code> has only one item, attempts to copy it to * <code>dst</code>. If <code>failWhenDstExists</code> is <span * class="javakeyword">false</span> and <code>dst</code> already exists, * attempts to copy the item as a child of <code>dst</code>. If * <code>failWhenDstExists</code> is <span class="javakeyword">true</span> * and <code>dst</code> already exists, throws an {@link SVNException} with * the {@link SVNErrorCode#FS_ALREADY_EXISTS} error code. * * <p/> * If <code>sources</code> has multiple items, and * <code>failWhenDstExists</code> is <span class="javakeyword">false</span>, * all <code>sources</code> are copied as children of <code>dst</code>. If * any child of <code>dst</code> already exists with the same name any item * in <code>sources</code>, throws an {@link SVNException} with the * {@link SVNErrorCode#FS_ALREADY_EXISTS} error code. * * <p/> * If <code>sources</code> has multiple items, and * <code>failWhenDstExists</code> is <span class="javakeyword">true</span>, * throws an {@link SVNException} with the * {@link SVNErrorCode#CLIENT_MULTIPLE_SOURCES_DISALLOWED}. * * <p/> * {@link ISVNAuthenticationManager Authentication manager} (whether * provided directly through the appropriate constructor or in an * {@link ISVNRepositoryPool} instance) and {@link #getCommitHandler() * commit handler} are used to immediately attempt to commit the copy action * in the repository. * * <p/> * If * <code>makeParents is <span class="javakeyword">true</span>, creates any non-existent parent directories * also. * * <p/> * If non-<span class="javakeyword">null</span>, <code>revisionProperties</code> * is an object holding additional, custom revision properties ( * <code>String</code> to {@link SVNPropertyValue} mappings) to be set on * the new revision. This table cannot contain any standard Subversion * properties. * * <p/> * If the caller's {@link ISVNEventHandler} is non-<span * class="javakeyword">null</span>, invokes it for each item added at the * new location. * * <p/> * When performing a wc-to-url copy (tagging|branching from a working copy) * it's possible to fix revisions of external working copies (if any) which * are located within the working copy being copied. For example, imagine * you have a working copy and on one of its subdirecotries you set an <span * class="javastring">"svn:externals"</span> property which does not contain * a revision number. Suppose you have made a tag from your working copy and * in some period of time a user checks out that tag. It could have happened * that the external project has evolved since the tag creation moment and * the tag version is nomore compatible with it. So, the user has a broken * project since it will not compile because of the API incompatibility * between the two versions of the external project: the HEAD one and the * one existed in the moment of the tag creation. That is why it appears * useful to fix externals revisions during a wc-to-url copy. To enable * externals revision fixing a user should implement * {@link ISVNExternalsHandler}. The user's implementation * {@link ISVNExternalsHandler#handleExternal(File, SVNURL, SVNRevision, SVNRevision, String, SVNRevision)} * method will be called on every external that will be met in the working * copy. If the user's implementation returns non-<span * class="javakeyword">null</span> external revision, it's compared with the * revisions fetched from the external definition. If they are different, * the user's revision will be written in the external definition of the * tag. Otherwise if the returned revision is equal to the revision from the * external definition or if the user's implementation returns <span * class="javakeyword">null</span> for that external, it will be skipped * (i.e. left as is, unprocessed). * * <p/> * Note: this routine requires repository access. * * @param sources * array of copy sources * @param dst * destination url * @param isMove * if <span class="javakeyword">true</span>, then it will be a * move operation (delete, then add with history) * @param makeParents * if <span class="javakeyword">true</span>, creates non-existent * parent directories as well * @param failWhenDstExists * controls whether to fail or not if <code>dst</code> already * exists * @param commitMessage * commit log message * @param revisionProperties * custom revision properties * @return information about the new committed revision * @throws SVNException * @since 1.2, SVN 1.5 */ public SVNCommitInfo doCopy(SVNCopySource[] sources, SVNURL dst, boolean isMove, boolean makeParents, boolean failWhenDstExists, String commitMessage, SVNProperties revisionProperties) throws SVNException { SvnRemoteCopy cp = getOperationsFactory().createRemoteCopy(); cp.setSingleTarget(SvnTarget.fromURL(dst)); cp.setMove(isMove); cp.setFailWhenDstExists(failWhenDstExists); cp.setMakeParents(makeParents); cp.setCommitMessage(commitMessage); cp.setRevisionProperties(revisionProperties); cp.setExternalsHandler(SvnCodec.externalsHandler(getExternalsHandler())); cp.setCommitHandler(SvnCodec.commitHandler(getCommitHandler())); cp.setDisableLocalModifications(disableLocalModifications); cp.setCommitParameters(SvnCodec.commitParameters(getCommitParameters())); for (int i = 0; i < sources.length; i++) { cp.addCopySource(SvnCodec.copySource(sources[i])); } return cp.run(); } /** * Converts a disjoint working copy to a copied one. * * <p/> * Note: this routine does not require repository access. However if it's * performed on an old format working copy where repository root urls were * not written, the routine will connect to the repository to fetch the * repository root url. * * @param nestedWC * the root of the working copy located in another working copy * (disjoint wc) * @throws SVNException * in the following cases: * <ul> * <li/>exception with {@link SVNErrorCode#UNSUPPORTED_FEATURE} * error code - if <code>nestedWC</code> is either not a * directory, or has no parent at all; if the current local * filesystem parent of <code>nestedWC</code> is actually a * child of it in the repository <li/>exception with * {@link SVNErrorCode#ENTRY_EXISTS} error code - if <code> * nestedWC</code> is not a disjoint working copy, i.e. there is * already a versioned item under the parent path of <code> * nestedWC</code>; if <code>nestedWC</code> is not in the * repository yet (has got a schedule for addition flag) <li/> * exception with {@link SVNErrorCode#WC_INVALID_SCHEDULE} error * code - if <code>nestedWC</code> is not from the same * repository as the parent directory; if the parent of <code> * nestedWC</code> is scheduled for deletion; if <code>nestedWC * </code> is scheduled for deletion <li/> * </ul> * @since 1.2.0 */ public void doCopy(File nestedWC) throws SVNException { SvnCopy cp = getOperationsFactory().createCopy(); cp.setDisjoint(true); cp.setSingleTarget(SvnTarget.fromFile(nestedWC)); cp.run(); } }