/* * ==================================================================== * 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; } }