/*
* ====================================================================
* 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.Arrays;
import java.util.HashSet;
import java.util.Set;
import org.tmatesoft.svn.core.SVNCommitInfo;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
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.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc2.compat.SvnCodec;
import org.tmatesoft.svn.core.internal.wc2.compat.SvnCodec.SVNCommitPacketWrapper;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc2.*;
import org.tmatesoft.svn.util.SVNLogType;
/**
* The <b>SVNCommitClient</b> class provides methods to perform operations that
* relate to committing changes to an SVN repository. These operations are
* similar to respective commands of the native SVN command line client and
* include ones which operate on working copy items as well as ones that operate
* only on a repository.
*
* <p>
* Here's a list of the <b>SVNCommitClient</b>'s commit-related methods matched
* against corresponing commands of the SVN command line client:
*
* <table cellpadding="3" cellspacing="1" border="0" width="40%" bgcolor="#999933">
* <tr bgcolor="#ADB8D9" align="left">
* <td><b>SVNKit</b></td>
* <td><b>Subversion</b></td>
* </tr>
* <tr bgcolor="#EAEAEA" align="left">
* <td>doCommit()</td>
* <td>'svn commit'</td>
* </tr>
* <tr bgcolor="#EAEAEA" align="left">
* <td>doImport()</td>
* <td>'svn import'</td>
* </tr>
* <tr bgcolor="#EAEAEA" align="left">
* <td>doDelete()</td>
* <td>'svn delete URL'</td>
* </tr>
* <tr bgcolor="#EAEAEA" align="left">
* <td>doMkDir()</td>
* <td>'svn mkdir URL'</td>
* </tr>
* </table>
*
* @version 1.3
* @author TMate Software Ltd.
* @since 1.2
* @see <a target="_top" href="http://svnkit.com/kb/examples/">Examples</a>
*/
public class SVNCommitClient extends SVNBasicClient {
private ISVNCommitHandler commitHandler;
private ISVNCommitParameters commitParameters;
private boolean failOnMultipleRepositories;
/**
* Constructs and initializes an <b>SVNCommitClient</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>SVNCommitClient</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>SVNCommitClient</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 SVNCommitClient(ISVNAuthenticationManager authManager, ISVNOptions options) {
super(authManager, options);
setCommitHander(null);
setCommitParameters(null);
setCommitHandler(null);
}
/**
* Constructs and initializes an <b>SVNCommitClient</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>SVNCommitClient</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 SVNCommitClient(ISVNRepositoryPool repositoryPool, ISVNOptions options) {
super(repositoryPool, options);
}
public SVNCommitClient(SvnOperationFactory of) {
super(of);
}
protected void initDefaults() {
setCommitHander(null);
setCommitParameters(null);
setCommitHandler(null);
}
/**
* @param handler
* @deprecated use {@link #setCommitHandler(ISVNCommitHandler)} instead
*/
public void setCommitHander(ISVNCommitHandler handler) {
if (handler == null) {
handler = new DefaultSVNCommitHandler();
}
this.commitHandler = handler;
}
/**
* 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>SVNCommitClient</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 ISVNCommitHandler
*/
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 previously provided.
*
* @return the commit handler being in use or a default one
* @see #setCommitHander(ISVNCommitHandler)
* @see ISVNCommitHandler
* @see DefaultSVNCommitHandler
*/
public ISVNCommitHandler getCommitHandler() {
return this.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) {
this.commitParameters = 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.commitParameters;
}
/**
* Committs removing specified URL-paths from the repository. This call is
* equivalent to <code>doDelete(urls, commitMessage, null)</code>.
*
* @param urls
* an array containing URL-strings that represent repository
* locations to be removed
* @param commitMessage
* a string to be a commit log message
* @return information on a new revision as the result of the commit
* @throws SVNException
* if one of the following is true:
* <ul>
* <li>a URL does not exist
* <li>probably some of URLs refer to different repositories
* </ul>
* @see #doDelete(SVNURL[], String, SVNProperties)
*/
public SVNCommitInfo doDelete(SVNURL[] urls, String commitMessage) throws SVNException {
SvnRemoteDelete delete = getOperationsFactory().createRemoteDelete();
if (getCommitHandler() != null)
delete.setCommitHandler(SvnCodec.commitHandler(getCommitHandler()));
for (int i = 0; i < urls.length; i++) {
delete.addTarget(SvnTarget.fromURL(urls[i]));
}
delete.setCommitMessage(commitMessage);
return delete.run();
}
/**
* Deletes items from a repository.
*
* <p/>
* If non-<span class="javakeyword">null</span>,
* <code>revisionProperties</code> holds additional, custom revision
* properties (<code>String</code> names mapped to {@link SVNPropertyValue}
* values) to be set on the new revision. This table cannot contain any
* standard Subversion properties.
*
* <p/>
* {@link #getCommitHandler() Commit handler} will be asked for a commit log
* message.
*
* <p/>
* If the caller's {@link ISVNEventHandler event handler} is not <span
* class="javakeyword">null</span> and if the commit succeeds, the handler
* will be called with {@link SVNEventAction#COMMIT_COMPLETED} event action.
*
* @param urls
* repository urls to delete
* @param commitMessage
* commit log message
* @param revisionProperties
* custom revision properties
* @return information about the new committed revision
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#RA_ILLEGAL_URL} error
* code - if cannot compute common root url for <code>urls
* </code> <li/>exception with {@link SVNErrorCode#FS_NOT_FOUND}
* error code - if some of <code>urls</code> does not exist
* </ul>
* @since 1.2.0, SVN 1.5.0
*/
public SVNCommitInfo doDelete(SVNURL[] urls, String commitMessage, SVNProperties revisionProperties) throws SVNException {
SvnRemoteDelete delete = getOperationsFactory().createRemoteDelete();
if (getCommitHandler() != null)
delete.setCommitHandler(SvnCodec.commitHandler(getCommitHandler()));
for (int i = 0; i < urls.length; i++) {
delete.addTarget(SvnTarget.fromURL(urls[i]));
}
delete.setCommitMessage(commitMessage);
delete.setRevisionProperties(revisionProperties);
return delete.run();
}
/**
* Committs a creation of a new directory/directories in the repository.
*
* @param urls
* an array containing URL-strings that represent new repository
* locations to be created
* @param commitMessage
* a string to be a commit log message
* @return information on a new revision as the result of the commit
* @throws SVNException
* if some of URLs refer to different repositories
*/
public SVNCommitInfo doMkDir(SVNURL[] urls, String commitMessage) throws SVNException {
SvnRemoteMkDir mkdir = getOperationsFactory().createRemoteMkDir();
if (getCommitHandler() != null)
mkdir.setCommitHandler(SvnCodec.commitHandler(getCommitHandler()));
for (int i = 0; i < urls.length; i++) {
mkdir.addTarget(SvnTarget.fromURL(urls[i]));
}
mkdir.setCommitMessage(commitMessage);
return mkdir.run();
}
/**
* Creates directory(ies) in a repository.
*
* <p/>
* If <code>makeParents</code> is <span class="javakeyword">true</span>,
* creates any non-existent parent directories also.
*
* <p/>
* If non-<span class="javakeyword">null</span>,
* <code>revisionProperties</code> holds additional, custom revision
* properties (<code>String</code> names mapped to {@link SVNPropertyValue}
* values) to be set on the new revision. This table cannot contain any
* standard Subversion properties.
*
* <p/>
* {@link #getCommitHandler() Commit handler} will be asked for a commit log
* message.
*
* <p/>
* If the caller's {@link ISVNEventHandler event handler} is not <span
* class="javakeyword">null</span> and if the commit succeeds, the handler
* will be called with {@link SVNEventAction#COMMIT_COMPLETED} event action.
*
* @param urls
* repository locations to create
* @param commitMessage
* commit log message
* @param revisionProperties
* custom revision properties
* @param makeParents
* if <span class="javakeyword">true</span>, creates all
* non-existent parent directories
* @return information about the new committed revision
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#RA_ILLEGAL_URL} error
* code - if cannot compute common root url for <code>urls
* </code> <li/>exception with {@link SVNErrorCode#FS_NOT_FOUND}
* error code - if some of <code>urls</code> does not exist
* </ul>
* @since 1.2.0, SVN 1.5.0
*/
public SVNCommitInfo doMkDir(SVNURL[] urls, String commitMessage, SVNProperties revisionProperties, boolean makeParents) throws SVNException {
SvnRemoteMkDir mkdir = getOperationsFactory().createRemoteMkDir();
if (getCommitHandler() != null)
mkdir.setCommitHandler(SvnCodec.commitHandler(getCommitHandler()));
for (int i = 0; i < urls.length; i++) {
mkdir.addTarget(SvnTarget.fromURL(urls[i]));
}
mkdir.setCommitMessage(commitMessage);
mkdir.setRevisionProperties(revisionProperties);
mkdir.setMakeParents(makeParents);
return mkdir.run();
}
/**
* Committs an addition of a local unversioned file or directory into the
* repository.
*
* <p/>
* This method is identical to
* <code>doImport(path, dstURL, commitMessage, null, true, false, SVNDepth.fromRecurse(recursive))</code>.
*
* @param path
* a local unversioned file or directory to be imported into the
* repository
* @param dstURL
* a URL-string that represents a repository location where the
* <code>path</code> will be imported
* @param commitMessage
* a string to be a commit log message
* @param recursive
* this flag is relevant only when the <code>path</code> is a
* directory: if <span class="javakeyword">true</span> then the
* entire directory tree will be imported including all child
* directories, otherwise only items located in the directory
* itself
* @return information on a new revision as the result of the commit
* @throws SVNException
* if one of the following is true:
* <ul>
* <li><code>dstURL</code> is invalid <li>the path denoted by
* <code>dstURL</code> already exists <li><code>path</code>
* contains a reserved name - <i>'.svn'</i>
* </ul>
* @deprecated use
* {@link #doImport(File, SVNURL, String, SVNProperties, boolean, boolean, SVNDepth)}
* instead
*/
public SVNCommitInfo doImport(File path, SVNURL dstURL, String commitMessage, boolean recursive) throws SVNException {
return doImport(path, dstURL, commitMessage, null, true, true, SVNDepth.fromRecurse(recursive));
}
/**
* Committs an addition of a local unversioned file or directory into the
* repository.
*
* <p/>
* This method is identical to
* <code>doImport(path, dstURL, commitMessage, null, useGlobalIgnores, false, SVNDepth.fromRecurse(recursive))</code>.
*
* @param path
* a local unversioned file or directory to be imported into the
* repository
* @param dstURL
* a URL-string that represents a repository location where the
* <code>path</code> will be imported
* @param commitMessage
* a string to be a commit log message
* @param useGlobalIgnores
* if <span class="javakeyword">true</span> then those paths that
* match global ignore patterns controlled by a config options
* driver (see
* {@link org.tmatesoft.svn.core.wc.ISVNOptions#getIgnorePatterns()}
* ) will not be imported, otherwise global ignore patterns are
* not used
* @param recursive
* this flag is relevant only when the <code>path</code> is a
* directory: if <span class="javakeyword">true</span> then the
* entire directory tree will be imported including all child
* directories, otherwise only items located in the directory
* itself
* @return information on a new revision as the result of the commit
* @throws SVNException
* if one of the following is true:
* <ul>
* <li><code>dstURL</code> is invalid <li>the path denoted by
* <code>dstURL</code> already exists <li><code>path</code>
* contains a reserved name - <i>'.svn'</i>
* </ul>
* @deprecated use
* {@link #doImport(File, SVNURL, String, SVNProperties, boolean, boolean, SVNDepth)}
* instead
*/
public SVNCommitInfo doImport(File path, SVNURL dstURL, String commitMessage, boolean useGlobalIgnores, boolean recursive) throws SVNException {
return doImport(path, dstURL, commitMessage, null, useGlobalIgnores, true, SVNDepth.fromRecurse(recursive));
}
/**
* Imports file or directory <code>path</code> into repository directory
* <code>dstURL</code> at HEAD revision. If some components of
* <code>dstURL</code> do not exist, then creates parent directories as
* necessary.
*
* <p/>
* If <code>path</code> is a directory, the contents of that directory are
* imported directly into the directory identified by <code>dstURL</code>.
* Note that the directory <code>path</code> itself is not imported -- that
* is, the base name of <code>path<code> is not part of the import.
*
* <p/>
* If <code>path</code> is a file, then the parent of <code>dstURL</code> is
* the directory receiving the import. The base name of <code>dstURL</code>
* is the filename in the repository. In this case if <code>dstURL</code>
* already exists, throws {@link SVNException}.
*
* <p/>
* If the caller's {@link ISVNEventHandler event handler} is not <span
* class="javakeyword">null</span> it will be called as the import
* progresses with {@link SVNEventAction#COMMIT_ADDED} action. If the commit
* succeeds, the handler will be called with
* {@link SVNEventAction#COMMIT_COMPLETED} event action.
*
* <p/>
* If non-<span class="javakeyword">null</span>,
* <code>revisionProperties</code> holds additional, custom revision
* properties (<code>String</code> names mapped to {@link SVNPropertyValue}
* values) to be set on the new revision. This table cannot contain any
* standard Subversion properties.
*
* <p/>
* {@link #getCommitHandler() Commit handler} will be asked for a commit log
* message.
*
* <p/>
* If <code>depth</code> is {@link SVNDepth#EMPTY}, imports just
* <code>path</code> and nothing below it. If {@link SVNDepth#FILES},
* imports <code>path</code> and any file children of <code>path</code>. If
* {@link SVNDepth#IMMEDIATES}, imports <code>path</code>, any file
* children, and any immediate subdirectories (but nothing underneath those
* subdirectories). If {@link SVNDepth#INFINITY}, imports <code>path</code>
* and everything under it fully recursively.
*
* <p/>
* If <code>useGlobalIgnores</code> is <span
* class="javakeyword">false</span>, doesn't add files or directories that
* match ignore patterns.
*
* <p/>
* If <code>ignoreUnknownNodeTypes</code> is <span
* class="javakeyword">false</span>, ignores files of which the node type is
* unknown, such as device files and pipes.
*
* @param path
* path to import
* @param dstURL
* import destination url
* @param commitMessage
* commit log message
* @param revisionProperties
* custom revision properties
* @param useGlobalIgnores
* whether matching against global ignore patterns should take
* place
* @param ignoreUnknownNodeTypes
* whether to ignore files of unknown node types or not
* @param depth
* tree depth to process
* @return information about the new committed revision
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#ENTRY_NOT_FOUND}
* error code - if <code>path</code> does not exist <li/>
* exception with {@link SVNErrorCode#ENTRY_EXISTS} error code -
* if <code>dstURL</code> already exists and <code>path</code>
* is a file <li/>exception with
* {@link SVNErrorCode#CL_ADM_DIR_RESERVED} error code - if
* trying to import an item with a reserved SVN name (like
* <code>'.svn'</code> or <code>'_svn'</code>)
* </ul>
* @since 1.2.0, New in SVN 1.5.0
*/
public SVNCommitInfo doImport(File path, SVNURL dstURL, String commitMessage, SVNProperties revisionProperties, boolean useGlobalIgnores, boolean ignoreUnknownNodeTypes, SVNDepth depth) throws SVNException {
return doImport(path, dstURL, commitMessage, revisionProperties, useGlobalIgnores, ignoreUnknownNodeTypes, depth, true);
}
/**
* Imports file or directory <code>path</code> into repository directory
* <code>dstURL</code> at HEAD revision. If some components of
* <code>dstURL</code> do not exist, then creates parent directories as
* necessary.
*
* <p/>
* If <code>path</code> is a directory, the contents of that directory are
* imported directly into the directory identified by <code>dstURL</code>.
* Note that the directory <code>path</code> itself is not imported -- that
* is, the base name of <code>path<code> is not part of the import.
*
* <p/>
* If <code>path</code> is a file, then the parent of <code>dstURL</code> is
* the directory receiving the import. The base name of <code>dstURL</code>
* is the filename in the repository. In this case if <code>dstURL</code>
* already exists, throws {@link SVNException}.
*
* <p/>
* If the caller's {@link ISVNEventHandler event handler} is not <span
* class="javakeyword">null</span> it will be called as the import
* progresses with {@link SVNEventAction#COMMIT_ADDED} action. If the commit
* succeeds, the handler will be called with
* {@link SVNEventAction#COMMIT_COMPLETED} event action.
*
* <p/>
* If non-<span class="javakeyword">null</span>,
* <code>revisionProperties</code> holds additional, custom revision
* properties (<code>String</code> names mapped to {@link SVNPropertyValue}
* values) to be set on the new revision. This table cannot contain any
* standard Subversion properties.
*
* <p/>
* {@link #getCommitHandler() Commit handler} will be asked for a commit log
* message.
*
* <p/>
* If <code>depth</code> is {@link SVNDepth#EMPTY}, imports just
* <code>path</code> and nothing below it. If {@link SVNDepth#FILES},
* imports <code>path</code> and any file children of <code>path</code>. If
* {@link SVNDepth#IMMEDIATES}, imports <code>path</code>, any file
* children, and any immediate subdirectories (but nothing underneath those
* subdirectories). If {@link SVNDepth#INFINITY}, imports <code>path</code>
* and everything under it fully recursively.
*
* <p/>
* If <code>useGlobalIgnores</code> is <span
* class="javakeyword">false</span>, doesn't add files or directories that
* match ignore patterns.
*
* <p/>
* If <code>ignoreUnknownNodeTypes</code> is <span
* class="javakeyword">false</span>, ignores files of which the node type is
* unknown, such as device files and pipes.
*
* @param path
* path to import
* @param dstURL
* import destination url
* @param commitMessage
* commit log message
* @param revisionProperties
* custom revision properties
* @param useGlobalIgnores
* whether matching against global ignore patterns should take
* place
* @param ignoreUnknownNodeTypes
* whether to ignore files of unknown node types or not
* @param depth
* tree depth to process
* @param applyAutoProperties
* whether to apply auto-properties
* @return information about the new committed revision
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#ENTRY_NOT_FOUND}
* error code - if <code>path</code> does not exist <li/>
* exception with {@link SVNErrorCode#ENTRY_EXISTS} error code -
* if <code>dstURL</code> already exists and <code>path</code>
* is a file <li/>exception with
* {@link SVNErrorCode#CL_ADM_DIR_RESERVED} error code - if
* trying to import an item with a reserved SVN name (like
* <code>'.svn'</code> or <code>'_svn'</code>)
* </ul>
* @since 1.8
*/
public SVNCommitInfo doImport(File path, SVNURL dstURL, String commitMessage, SVNProperties revisionProperties, boolean useGlobalIgnores, boolean ignoreUnknownNodeTypes, SVNDepth depth, boolean applyAutoProperties) throws SVNException {
SvnImport svnImport = getOperationsFactory().createImport();
svnImport.setCommitHandler(SvnCodec.commitHandler(getCommitHandler()));
svnImport.setCommitMessage(commitMessage);
svnImport.setRevisionProperties(revisionProperties);
svnImport.addTarget(SvnTarget.fromURL(dstURL));
svnImport.setSource(path);
svnImport.setDepth(depth);
svnImport.setUseGlobalIgnores(useGlobalIgnores);
svnImport.setApplyAutoProperties(applyAutoProperties);
return svnImport.run();
}
/**
* Committs local changes to the repository.
*
* <p/>
* This method is identical to
* <code>doCommit(paths, keepLocks, commitMessage, null, null, false, force, SVNDepth.fromRecurse(recursive))</code>.
*
* @param paths
* an array of local items which should be traversed to commit
* changes they have to the repository
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then the commit will left them locked,
* otherwise the items will be unlocked after the commit succeeds
* @param commitMessage
* a string to be a commit log message
* @param force
* <span class="javakeyword">true</span> to force a non-recursive
* commit; if <code>recursive</code> is set to <span
* class="javakeyword">true</span> the <code>force</code> flag is
* ignored
* @param recursive
* relevant only for directory items: if <span
* class="javakeyword">true</span> then the entire directory tree
* will be committed including all child directories, otherwise
* only items located in the directory itself
* @return information on a new revision as the result of the commit
* @throws SVNException
* @deprecated use
* {@link #doCommit(File[], boolean, String, SVNProperties, String[], boolean, boolean, SVNDepth)}
* instead
*/
public SVNCommitInfo doCommit(File[] paths, boolean keepLocks, String commitMessage, boolean force, boolean recursive) throws SVNException {
return doCommit(paths, keepLocks, commitMessage, null, null, false, force, SVNDepth.fromRecurse(recursive));
}
/**
* Commits files or directories into repository.
*
* <p/>
* <code>paths</code> need not be canonicalized nor condensed; this method
* will take care of that. If
* <code>targets has zero elements, then do nothing and return
* immediately without error.
*
* <p/>
* If non-<span class="javakeyword">null</span>, <code>revisionProperties</code>
* holds additional, custom revision properties (<code>String</code> names
* mapped to {@link SVNPropertyValue} values) to be set on the new revision.
* This table cannot contain any standard Subversion properties.
*
* <p/>
* If the caller's {@link ISVNEventHandler event handler} is not <span
* class="javakeyword">null</span> it will be called as the commit
* progresses with any of the following actions:
* {@link SVNEventAction#COMMIT_MODIFIED},
* {@link SVNEventAction#COMMIT_ADDED},
* {@link SVNEventAction#COMMIT_DELETED},
* {@link SVNEventAction#COMMIT_REPLACED}. If the commit succeeds, the
* handler will be called with {@link SVNEventAction#COMMIT_COMPLETED} event
* action.
*
* <p/>
* If <code>depth</code> is {@link SVNDepth#INFINITY}, commits all changes
* to and below named targets. If <code>depth</code> is
* {@link SVNDepth#EMPTY}, commits only named targets (that is, only
* property changes on named directory targets, and property and content
* changes for named file targets). If <code>depth</code> is
* {@link SVNDepth#FILES}, behaves as above for named file targets, and for
* named directory targets, commits property changes on a named directory
* and all changes to files directly inside that directory. If
* {@link SVNDepth#IMMEDIATES}, behaves as for {@link SVNDepth#FILES}, and
* for subdirectories of any named directory target commits as though for
* {@link SVNDepth#EMPTY}.
*
* <p/>
* Unlocks paths in the repository, unless <code>keepLocks</code> is <span
* class="javakeyword">true</span>.
*
* <p/>
* <code>changelists</code> is an array of <code>String</code> changelist
* names, used as a restrictive filter on items that are committed; that is,
* doesn't commit anything unless it's a member of one of those changelists.
* After the commit completes successfully, removes changelist associations
* from the targets, unless <code>keepChangelist</code> is set. If
* <code>changelists</code> is empty (or altogether <span
* class="javakeyword">null</span>), no changelist filtering occurs.
*
* <p/>
* If no exception is thrown and {@link SVNCommitInfo#getNewRevision()} is
* invalid (<code><0</code>), then the commit was a no-op; nothing needed
* to be committed.
*
* @param paths
* paths to commit
* @param keepLocks
* whether to unlock or not files in the repository
* @param commitMessage
* commit log message
* @param revisionProperties
* custom revision properties
* @param changelists
* changelist names array
* @param keepChangelist
* whether to remove <code>changelists</code> or not
* @param force
* <span class="javakeyword">true</span> to force a non-recursive
* commit; if <code>depth</code> is {@link SVNDepth#INFINITY} the
* <code>force</code> flag is ignored
* @param depth
* tree depth to process
* @return information about the new committed revision
* @throws SVNException
* @since 1.2.0, New in Subversion 1.5.0
*/
public SVNCommitInfo doCommit(File[] paths, boolean keepLocks, String commitMessage, SVNProperties revisionProperties, String[] changelists, boolean keepChangelist, boolean force, SVNDepth depth)
throws SVNException {
SVNCommitPacket[] packets = doCollectCommitItems(paths, keepLocks, force, depth, true, changelists);
if (packets != null) {
SVNCommitInfo[] infos = doCommit(packets, keepLocks, keepChangelist, commitMessage, revisionProperties);
if (infos != null && infos.length > 0) {
return infos[0];
}
}
return SVNCommitInfo.NULL;
}
/**
* Committs local changes made to the Working Copy items to the repository.
*
* <p>
* This method is identical to
* <code>doCommit(commitPacket, keepLocks, false, commitMessage, null)</code>.
*
* <p>
* <code>commitPacket</code> contains commit items ({@link SVNCommitItem})
* which represent local Working Copy items that were changed and are to be
* committed. Commit items are gathered into a single
* {@link SVNCommitPacket} by invoking
* {@link #doCollectCommitItems(File[], boolean, boolean, boolean)
* doCollectCommitItems()}.
*
* @param commitPacket
* a single object that contains items to be committed
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then the commit will left them locked,
* otherwise the items will be unlocked after the commit succeeds
* @param commitMessage
* a string to be a commit log message
* @return information on a new revision as the result of the commit
* @throws SVNException
* @see SVNCommitItem
*/
public SVNCommitInfo doCommit(SVNCommitPacket commitPacket, boolean keepLocks, String commitMessage) throws SVNException {
return doCommit(commitPacket, keepLocks, false, commitMessage, null);
}
/**
* Commits files or directories into repository.
*
* <p/>
* This method is identical to
* {@link #doCommit(File[], boolean, String, SVNProperties, String[], boolean, boolean, SVNDepth)}
* except for it receives a commit packet instead of paths array. The
* aforementioned method collects commit items into a commit packet given
* working copy paths. This one accepts already collected commit items
* provided in <code>commitPacket</code>.
*
* <p/>
* <code>commitPacket</code> contains commit items ({@link SVNCommitItem})
* which represent local Working Copy items that are to be committed. Commit
* items are gathered in a single {@link SVNCommitPacket} by invoking either
* {@link #doCollectCommitItems(File[], boolean, boolean, SVNDepth, String[])}
* or
* {@link #doCollectCommitItems(File[], boolean, boolean, SVNDepth, boolean, String[])}.
*
* <p/>
* For more details on parameters, please, refer to
* {@link #doCommit(File[], boolean, String, SVNProperties, String[], boolean, boolean, SVNDepth)}.
*
* @param commitPacket
* a single object that contains items to be committed
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then the commit will left them locked,
* otherwise the items will be unlocked after the commit succeeds
* @param keepChangelist
* whether to remove changelists or not
* @param commitMessage
* commit log message
* @param revisionProperties
* custom revision properties
* @return information about the new committed revision
* @throws SVNException
* @since 1.2.0, SVN 1.5.0
*/
public SVNCommitInfo doCommit(SVNCommitPacket commitPacket, boolean keepLocks, boolean keepChangelist, String commitMessage, SVNProperties revisionProperties) throws SVNException {
SVNCommitInfo[] infos = doCommit(new SVNCommitPacket[] {commitPacket}, keepLocks, keepChangelist, commitMessage, revisionProperties);
if (infos != null && infos.length > 0) {
return infos[0];
}
return SVNCommitInfo.NULL;
}
/**
* Committs local changes, made to the Working Copy items, to the
* repository.
*
* <p>
* <code>commitPackets</code> is an array of packets that contain commit
* items (<b>SVNCommitItem</b>) which represent local Working Copy items
* that were changed and are to be committed. Commit items are gathered in a
* single <b>SVNCommitPacket</b> by invoking
* {@link #doCollectCommitItems(File[], boolean, boolean, boolean)
* doCollectCommitItems()}.
*
* <p>
* This allows to commit separate trees of Working Copies "belonging" to
* different repositories. One packet per one repository. If repositories
* are different (it means more than one commit will be done),
* <code>commitMessage</code> may be replaced by a commit handler to be a
* specific one for each commit.
*
* <p>
* This method is identical to
* <code>doCommit(commitPackets, keepLocks, false, commitMessage, null)</code>.
*
* @param commitPackets
* logically grouped items to be committed
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then the commit will left them locked,
* otherwise the items will be unlocked after the commit succeeds
* @param commitMessage
* a string to be a commit log message
* @return committed information
* @throws SVNException
*/
public SVNCommitInfo[] doCommit(SVNCommitPacket[] commitPackets, boolean keepLocks, String commitMessage) throws SVNException {
return doCommit(commitPackets, keepLocks, false, commitMessage, null);
}
/**
* Commits files or directories into repository.
*
* <p>
* <code>commitPackets</code> is an array of packets that contain commit
* items ({@link SVNCommitItem}) which represent local Working Copy items
* that were changed and are to be committed. Commit items are gathered in a
* single {@link SVNCommitPacket} by invoking
* {@link #doCollectCommitItems(File[], boolean, boolean, SVNDepth, String[])}
* or
* {@link #doCollectCommitItems(File[], boolean, boolean, SVNDepth, boolean, String[])}.
*
* <p>
* This allows to commit items from separate Working Copies checked out from
* the same or different repositories. For each commit packet
* {@link #getCommitHandler() commit handler} is invoked to produce a commit
* message given the one <code>commitMessage</code> passed to this method.
* Each commit packet is committed in a separate transaction.
*
* <p/>
* For more details on parameters, please, refer to
* {@link #doCommit(File[], boolean, String, SVNProperties, String[], boolean, boolean, SVNDepth)}.
*
* @param commitPackets
* commit packets containing commit commit items per one commit
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then the commit will left them locked,
* otherwise the items will be unlocked by the commit
* @param keepChangelist
* whether to remove changelists or not
* @param commitMessage
* a string to be a commit log message
* @param revisionProperties
* custom revision properties
* @return information about the new committed revisions
* @throws SVNException
* @since 1.2.0, SVN 1.5.0
*/
public SVNCommitInfo[] doCommit(SVNCommitPacket[] commitPackets, boolean keepLocks, boolean keepChangelist, String commitMessage, SVNProperties revisionProperties) throws SVNException {
final SVNCommitInfo[] infos = new SVNCommitInfo[commitPackets.length];
if (commitPackets.length == 0) {
return infos;
}
// assert that all commit packets belongs to the same operation;
SvnCommit sharedOperation = null;
for (int i = 0; i < commitPackets.length; i++) {
final SvnCommit commitOperation = ((SVNCommitPacketWrapper) commitPackets[i]).getOperation();
if (sharedOperation == null) {
sharedOperation = commitOperation;
}
if (commitOperation != sharedOperation) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Commit packets created by different commit operations may not be mixed.");
SVNErrorManager.error(err, SVNLogType.WC);
}
}
// dispose packets that were collected, but are not part of this commit request.
final SvnCommitPacket[] operationPackets = sharedOperation.splitCommitPackets(sharedOperation.isCombinePackets());
final Set<SvnCommitPacket> userPacketsSet = new HashSet<SvnCommitPacket>();
for (int i = 0; i < commitPackets.length; i++) {
final SvnCommitPacket userPacket = ((SVNCommitPacketWrapper) commitPackets[i]).getPacket();
userPacketsSet.add(userPacket);
}
for (int i = 0; i < operationPackets.length; i++) {
if (!userPacketsSet.contains(operationPackets[i])) {
operationPackets[i].dispose();
}
}
SvnCommit commit = sharedOperation;
commit.setIncludeDirectoryExternals(!isIgnoreExternals());
commit.setIncludeFileExternals(!isIgnoreExternals());
commit.setCommitMessage(commitMessage);
commit.setCommitHandler(SvnCodec.commitHandler(getCommitHandler()));
commit.setCommitParameters(SvnCodec.commitParameters(getCommitParameters()));
if (revisionProperties != null) {
for (String propertyName : revisionProperties.nameSet()) {
SVNPropertyValue value = revisionProperties.getSVNPropertyValue(propertyName);
if (value != null) {
commit.setRevisionProperty(propertyName, value);
}
}
}
commit.setKeepLocks(keepLocks);
commit.setKeepChangelists(keepChangelist);
commit.setReceiver(new ISvnObjectReceiver<SVNCommitInfo>() {
int index = 0;
public void receive(SvnTarget target, SVNCommitInfo object) throws SVNException {
infos[index++] = object;
}
});
commit.run();
return infos;
}
/**
* Collects commit items (containing detailed information on each Working
* Copy item that was changed and need to be committed to the repository)
* into a single {@link SVNCommitPacket}.
*
* <p/>
* This method is equivalent to
* <code>doCollectCommitItems(paths, keepLocks, force, SVNDepth.fromRecurse(recursive), null)</code>.
*
* @param paths
* an array of local items which should be traversed to collect
* information on every changed item (one <b>SVNCommitItem</b>
* per each modified local item)
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then these items will be left locked
* after traversing all of them, otherwise the items will be
* unlocked
* @param force
* forces collecting commit items for a non-recursive commit
* @param recursive
* relevant only for directory items: if <span
* class="javakeyword">true</span> then the entire directory tree
* will be traversed including all child directories, otherwise
* only items located in the directory itself will be processed
* @return an <b>SVNCommitPacket</b> containing all Working Copy items
* having local modifications and represented as
* <b>SVNCommitItem</b> objects; if no modified items were found
* then {@link SVNCommitPacket#EMPTY} is returned
* @throws SVNException
* @deprecated use
* {@link #doCollectCommitItems(File[], boolean, boolean, SVNDepth, String[])}
* instead
*/
public SVNCommitPacket doCollectCommitItems(File[] paths, boolean keepLocks, boolean force, boolean recursive) throws SVNException {
SVNCommitPacket[] packets = doCollectCommitItems(paths, keepLocks, force, SVNDepth.fromRecurse(recursive), true, null);
if (packets != null && packets.length > 0) {
return packets[0];
}
return SVNCommitPacket.EMPTY;
}
/**
* Collects commit items (containing detailed information on each Working
* Copy item that contains changes and need to be committed to the
* repository) into a single {@link SVNCommitPacket}. Further this commit
* packet can be passed to
* {@link #doCommit(SVNCommitPacket, boolean, boolean, String, SVNProperties)}
* .
*
* <p/>
* For more details on parameters, please, refer to
* {@link #doCommit(File[], boolean, String, SVNProperties, String[], boolean, boolean, SVNDepth)}.
*
* @param paths
* an array of local items which should be traversed to collect
* information on every changed item (one <b>SVNCommitItem</b>
* per each modified local item)
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then these items will be left locked
* after traversing all of them, otherwise the items will be
* unlocked
* @param force
* forces collecting commit items for a non-recursive commit
* @param depth
* tree depth to process
* @param changelists
* changelist names array
* @return commit packet containing commit items
* @throws SVNException
* @since 1.2.0
*/
public SVNCommitPacket doCollectCommitItems(File[] paths, boolean keepLocks, boolean force, SVNDepth depth, String[] changelists) throws SVNException {
SVNCommitPacket[] packets = doCollectCommitItems(paths, keepLocks, force, depth, true, changelists);
if (packets != null && packets.length > 0) {
return packets[0];
}
return SVNCommitPacket.EMPTY;
}
/**
* Collects commit items (containing detailed information on each Working
* Copy item that was changed and need to be committed to the repository)
* into different <b>SVNCommitPacket</b>s.
*
* <p/>
* This method is identical to
* <code>doCollectCommitItems(paths, keepLocks, force, SVNDepth.fromRecurse(recursive), combinePackets, null)</code>.
*
* @param paths
* an array of local items which should be traversed to collect
* information on every changed item (one <b>SVNCommitItem</b>
* per each modified local item)
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then these items will be left locked
* after traversing all of them, otherwise the items will be
* unlocked
* @param force
* forces collecting commit items for a non-recursive commit
* @param recursive
* relevant only for directory items: if <span
* class="javakeyword">true</span> then the entire directory tree
* will be traversed including all child directories, otherwise
* only items located in the directory itself will be processed
* @param combinePackets
* if <span class="javakeyword">true</span> then collected commit
* packets will be joined into a single one, so that to be
* committed in a single transaction
* @return an array of commit packets
* @throws SVNException
* @deprecated use
* {@link #doCollectCommitItems(File[], boolean, boolean, SVNDepth, boolean, String[])}
* instead
*/
public SVNCommitPacket[] doCollectCommitItems(File[] paths, boolean keepLocks, boolean force, boolean recursive, boolean combinePackets) throws SVNException {
return doCollectCommitItems(paths, keepLocks, force, SVNDepth.fromRecurse(recursive), combinePackets, null);
}
/**
* Collects commit items (containing detailed information on each Working
* Copy item that was changed and need to be committed to the repository)
* into different <code>SVNCommitPacket</code>s. This method may be
* considered as an advanced version of the
* {@link #doCollectCommitItems(File[], boolean, boolean, SVNDepth, String[])}
* method. Its main difference from the aforementioned method is that it
* provides an ability to collect commit items from different working copies
* checked out from the same repository and combine them into a single
* commit packet. This is attained via setting <code>combinePackets</code>
* into <span class="javakeyword">true</span>. However even if
* <code>combinePackets</code> is set, combining may only occur if (besides
* that the paths must be from the same repository) URLs of
* <code>paths</code> are formed of identical components, that is protocol
* name, host name, port number (if any) must match for all paths. Otherwise
* combining will not occur.
*
* <p/>
* Combined items will be committed in a single transaction.
*
* <p/>
* For details on other parameters, please, refer to
* {@link #doCommit(File[], boolean, String, SVNProperties, String[], boolean, boolean, SVNDepth)}.
*
* @param paths
* an array of local items which should be traversed to collect
* information on every changed item (one <b>SVNCommitItem</b>
* per each modified local item)
* @param keepLocks
* if <span class="javakeyword">true</span> and there are local
* items that were locked then these items will be left locked
* after traversing all of them, otherwise the items will be
* unlocked
* @param force
* forces collecting commit items for a non-recursive commit
* @param depth
* tree depth to process
* @param combinePackets
* whether combining commit packets into a single commit packet
* is allowed or not
* @param changelists
* changelist names array
* @return array of commit packets
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#ENTRY_MISSING_URL}
* error code - if working copy root of either path has no url
* </ul>
* @since 1.2.0
*/
public SVNCommitPacket[] doCollectCommitItems(File[] paths, boolean keepLocks, boolean force, SVNDepth depth, boolean combinePackets, String[] changelists) throws SVNException {
SvnCommit commit = getOperationsFactory().createCommit();
for (int i = 0; i < paths.length; i++) {
commit.addTarget(SvnTarget.fromFile(paths[i]));
}
commit.setIncludeFileExternals(!isIgnoreExternals());
commit.setIncludeDirectoryExternals(!isIgnoreExternals());
commit.setKeepLocks(keepLocks);
commit.setDepth(depth);
commit.setForce(force);
commit.setFailOnMultipleRepositories(this.failOnMultipleRepositories);
commit.setCommitParameters(SvnCodec.commitParameters(getCommitParameters()));
commit.setCombinePackets(combinePackets);
if (changelists != null && changelists.length > 0) {
commit.setApplicalbeChangelists(Arrays.asList(changelists));
}
SvnCommitPacket packet = commit.collectCommitItems();
if (packet != null) {
final SvnCommitPacket[] packets = commit.splitCommitPackets(combinePackets);
final SVNCommitPacket[] result = new SVNCommitPacket[packets.length];
for (int i = 0; i < packets.length; i++) {
result[i] = SvnCodec.commitPacket(commit, packets[i]);
}
return result;
}
return new SVNCommitPacket[0];
}
public void setFailOnMultipleRepositories(boolean fail) {
failOnMultipleRepositories = fail;
}
}