package org.tmatesoft.svn.core.internal.wc16;
import java.io.File;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;
import org.tmatesoft.svn.core.ISVNDirEntryHandler;
import org.tmatesoft.svn.core.ISVNLogEntryHandler;
import org.tmatesoft.svn.core.SVNAnnotationGenerator;
import org.tmatesoft.svn.core.SVNAuthenticationException;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNDirEntry;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNLock;
import org.tmatesoft.svn.core.SVNLogEntry;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNRevisionProperty;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.internal.io.dav.DAVRepository;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNURLUtil;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea;
import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry;
import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.ISVNAnnotateHandler;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.ISVNOptions;
import org.tmatesoft.svn.core.wc.ISVNRepositoryPool;
import org.tmatesoft.svn.core.wc.SVNDiffOptions;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNRevisionRange;
import org.tmatesoft.svn.core.wc.SVNWCUtil;
import org.tmatesoft.svn.util.SVNLogType;
/**
* The <b>SVNLogClient</b> class is intended for such purposes as getting
* revisions history, browsing repository entries and annotating file contents.
* <p>
* Here's a list of the <b>SVNLogClient</b>'s methods matched against
* corresponing commands of the <b>SVN</b> 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>doLog()</td>
* <td>'svn log'</td>
* </tr>
* <tr bgcolor="#EAEAEA" align="left">
* <td>doList()</td>
* <td>'svn list'</td>
* </tr>
* <tr bgcolor="#EAEAEA" align="left">
* <td>doAnnotate()</td>
* <td>'svn blame'</td>
* </tr>
* </table>
*
* @version 1.3
* @author TMate Software Ltd.
* @since 1.2
*/
public class SVNLogClient16 extends SVNBasicDelegate {
private SVNDiffOptions myDiffOptions;
/**
* Constructs and initializes an <b>SVNLogClient</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>SVNLogClient</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>SVNLogClient</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 SVNLogClient16(ISVNAuthenticationManager authManager, ISVNOptions options) {
super(authManager, options);
}
/**
* Constructs and initializes an <b>SVNLogClient</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>SVNLogClient</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 SVNLogClient16(ISVNRepositoryPool repositoryPool, ISVNOptions options) {
super(repositoryPool, options);
}
/**
* Sets diff options for this client to use in annotate operations.
*
* @param diffOptions
* diff options object
*/
public void setDiffOptions(SVNDiffOptions diffOptions) {
this.myDiffOptions = diffOptions;
}
/**
* Gets the diff options that are used in annotate operations by this
* client. Creates a new one if none was used before.
*
* @return diff options
*/
public SVNDiffOptions getDiffOptions() {
return this.myDiffOptions;
}
/**
* Obtains annotation information for each file text line from a repository
* (using a Working Copy path to get a corresponding URL) and passes it to a
* provided annotation handler.
* <p/>
* This method is equivalent to a call to
* <code>doAnnotate(path, pegRevision, startRevision, endRevision, false, false, handler, null)</code>.
*
* @param path
* a WC file item to be annotated
* @param pegRevision
* a revision in which <code>path</code> is first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from
* @param endRevision
* a revision for an operation to stop at
* @param handler
* a caller's handler to process annotation information
* @throws SVNException
* if <code>startRevision > endRevision</code>
* @see #doAnnotate(File,SVNRevision,SVNRevision,SVNRevision,boolean,boolean,ISVNAnnotateHandler,String)
*/
public void doAnnotate(File path, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, ISVNAnnotateHandler handler) throws SVNException {
doAnnotate(path, pegRevision, startRevision, endRevision, false, false, handler, null);
}
/**
* Obtains annotation information for each file text line from a repository
* (using a Working Copy path to get a corresponding URL) and passes it to a
* provided annotation handler.
* <p/>
* This method is equivalent to a call to
* <code>doAnnotate(path, pegRevision, startRevision, endRevision, ignoreMimeType, false, handler, null)</code>.
*
* @param path
* a WC file item to be annotated
* @param pegRevision
* a revision in which <code>path</code> is first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from
* @param endRevision
* a revision for an operation to stop at
* @param ignoreMimeType
* forces operation to run (all files to be treated as text, no
* matter what SVNKit has inferred from the mime-type property)
* @param handler
* a caller's handler to process annotation information
* @throws SVNException
* @see #doAnnotate(File,SVNRevision,SVNRevision,SVNRevision,boolean,boolean,ISVNAnnotateHandler,String)
* @since 1.1
*/
public void doAnnotate(File path, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, boolean ignoreMimeType, ISVNAnnotateHandler handler) throws SVNException {
doAnnotate(path, pegRevision, startRevision, endRevision, ignoreMimeType, false, handler, null);
}
/**
* Invokes <code>handler</code> on each line-blame item associated with
* revision <code>endRevision</code> of <code>path</code>, using
* <code>startRevision</code> as the default source of all blame.
* <p/>
* <code>pegRevision</code> indicates in which revision <code>path</code> is
* valid. If <code>pegRevision</code> is {@link SVNRevision#UNDEFINED}, then
* it defaults to {@link SVNRevision#WORKING}.
* <p/>
* If <code>startRevision</code> is <span class="javakeyword">null</span> or
* {@link SVNRevision#isValid() invalid}, then it defaults to revision 1. If
* <code>endRevision</code> is <span class="javakeyword">null</span> or
* {@link SVNRevision#isValid() invalid}, then in defaults to
* {@link SVNRevision#HEAD}.
* <p/>
* Note: this routine requires repository access.
*
* @param path
* a WC file item to be annotated
* @param pegRevision
* a revision in which <code>path</code> is first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from
* @param endRevision
* a revision for an operation to stop at
* @param ignoreMimeType
* forces operation to run (all files to be treated as text, no
* matter what SVNKit has inferred from the mime-type property)
* @param includeMergedRevisions
* if <span class="javakeyword">true</span>, then also returns
* data based upon revisions which have been merged to
* <code>path</code>
* @param handler
* a caller's handler to process annotation information
* @param inputEncoding
* character set to decode input bytes with
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#CLIENT_BAD_REVISION}
* error code - if both <code>startRevision</code> and <code>
* endRevision</code> are either <span
* class="javakeyword">null</span> or
* {@link SVNRevision#isValid() invalid} <li/>exception with
* {@link SVNErrorCode#UNSUPPORTED_FEATURE} error code - if
* either of <code>startRevision</code> or <code>endRevision
* </code> is {@link SVNRevision#WORKING} <li/>exception with
* {@link SVNErrorCode#CLIENT_IS_BINARY_FILE} error code - if
* any of the revisions of <code>path</code> have a binary
* mime-type, unless <code>ignoreMimeType</code> is <span
* class="javakeyword">true</span>, in which case blame
* information will be generated regardless of the MIME types of
* the revisions
* </ul>
* @since 1.2, SVN 1.5
*/
public void doAnnotate(File path, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, boolean ignoreMimeType, boolean includeMergedRevisions, ISVNAnnotateHandler handler,
String inputEncoding) throws SVNException {
if (startRevision == null || !startRevision.isValid()) {
startRevision = SVNRevision.create(1);
}
if (endRevision == null || !endRevision.isValid()) {
endRevision = pegRevision;
}
if (startRevision == SVNRevision.WORKING || endRevision == SVNRevision.WORKING) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Blame of the WORKING revision is not supported");
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNRepository repos = createRepository(null, path, null, pegRevision, endRevision, null);
long endRev = getRevisionNumber(endRevision, repos, path);
long startRev = getRevisionNumber(startRevision, repos, path);
if (endRev < startRev) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Start revision must precede end revision"), SVNLogType.DEFAULT);
}
File tmpFile = new File(path.getParentFile(), SVNFileUtil.getAdminDirectoryName());
tmpFile = new File(tmpFile, "tmp/text-base");
if (!tmpFile.isDirectory()) {
tmpFile = SVNFileUtil.createTempDirectory("annotate");
}
doAnnotate(path.getAbsolutePath(), startRev, tmpFile, repos, endRev, ignoreMimeType, handler, inputEncoding, includeMergedRevisions);
}
/**
* Obtains annotation information for each file text line from a repository
* and passes it to a provided annotation handler.
* <p>
* This method is equivalent to a call to
* <code>doAnnotate(url, pegRevision, startRevision, endRevision, false, false, handler, null)</code>.
*
* @param url
* a URL of a text file that is to be annotated
* @param pegRevision
* a revision in which <code>path</code> is first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from
* @param endRevision
* a revision for an operation to stop at
* @param handler
* a caller's handler to process annotation information
* @throws SVNException
* if <code>startRevision > endRevision</code>
* @see #doAnnotate(SVNURL,SVNRevision,SVNRevision,SVNRevision,boolean,boolean,ISVNAnnotateHandler,String)
*/
public void doAnnotate(SVNURL url, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, ISVNAnnotateHandler handler) throws SVNException {
doAnnotate(url, pegRevision, startRevision, endRevision, false, false, handler, null);
}
/**
* Obtains annotation information for each file text line from a repository
* and passes it to a provided annotation handler.
* <p>
* This method is equivalent to a call to
* <code>doAnnotate(url, pegRevision, startRevision, endRevision, false, false, handler, inputEncoding)</code>.
*
* @param url
* a URL of a text file that is to be annotated
* @param pegRevision
* a revision in which <code>path</code> is first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from
* @param endRevision
* a revision for an operation to stop at
* @param handler
* a caller's handler to process annotation information
* @param inputEncoding
* a desired character set (encoding) of text lines
* @throws SVNException
* @see #doAnnotate(SVNURL,SVNRevision,SVNRevision,SVNRevision,boolean,boolean,ISVNAnnotateHandler,String)
*/
public void doAnnotate(SVNURL url, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, ISVNAnnotateHandler handler, String inputEncoding) throws SVNException {
doAnnotate(url, pegRevision, startRevision, endRevision, false, false, handler, inputEncoding);
}
/**
* Obtains annotation information for each file text line from a repository
* and passes it to a provided annotation handler.
* <p>
* This method is equivalent to a call to
*
* <code>doAnnotate(url, pegRevision, startRevision, endRevision, ignoreMimeType, false, handler, inputEncoding)</code>.
*
* @param url
* a URL of a text file that is to be annotated
* @param pegRevision
* a revision in which <code>path</code> is first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from
* @param endRevision
* a revision for an operation to stop at
* @param ignoreMimeType
* forces operation to run (all files to be treated as text, no
* matter what SVNKit has inferred from the mime-type property)
* @param handler
* a caller's handler to process annotation information
* @param inputEncoding
* a desired character set (encoding) of text lines
* @throws SVNException
* @see #doAnnotate(SVNURL,SVNRevision,SVNRevision,SVNRevision,boolean,boolean,ISVNAnnotateHandler,String)
* @since 1.1
*/
public void doAnnotate(SVNURL url, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, boolean ignoreMimeType, ISVNAnnotateHandler handler, String inputEncoding)
throws SVNException {
doAnnotate(url, pegRevision, startRevision, endRevision, ignoreMimeType, false, handler, inputEncoding);
}
/**
* Invokes <code>handler</code> on each line-blame item associated with
* revision <code>endRevision</code> of <code>url</code>, using
* <code>startRevision</code> as the default source of all blame.
* <p/>
* <code>pegRevision</code> indicates in which revision <code>url</code> is
* valid. If <code>pegRevision</code> is {@link SVNRevision#UNDEFINED}, then
* it defaults to {@link SVNRevision#HEAD}.
* <p/>
* If <code>startRevision</code> is <span class="javakeyword">null</span> or
* {@link SVNRevision#isValid() invalid}, then it defaults to revision 1. If
* <code>endRevision</code> is <span class="javakeyword">null</span> or
* {@link SVNRevision#isValid() invalid}, then in defaults to
* {@link SVNRevision#HEAD}.
* <p/>
* Note: this routine requires repository access
*
* @param url
* a URL of a text file that is to be annotated
* @param pegRevision
* a revision in which <code>url</code> is first looked up in the
* repository
* @param startRevision
* a revision for an operation to start from
* @param endRevision
* a revision for an operation to stop at
* @param ignoreMimeType
* forces operation to run (all files to be treated as text, no
* matter what SVNKit has inferred from the mime-type property)
* @param includeMergedRevisions
* if <span class="javakeyword">true</span>, then also returns
* data based upon revisions which have been merged to
* <code>url</code>
* @param handler
* a caller's handler to process annotation information
* @param inputEncoding
* character set to decode input bytes with
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#CLIENT_BAD_REVISION}
* error code - if both <code>startRevision</code> and <code>
* endRevision</code> are either <span
* class="javakeyword">null</span> or
* {@link SVNRevision#isValid() invalid} <li/>exception with
* {@link SVNErrorCode#UNSUPPORTED_FEATURE} error code - if
* either of <code>startRevision</code> or <code>endRevision
* </code> is {@link SVNRevision#WORKING} <li/>exception with
* {@link SVNErrorCode#CLIENT_IS_BINARY_FILE} error code - if
* any of the revisions of <code>url</code> have a binary
* mime-type, unless <code>ignoreMimeType</code> is <span
* class="javakeyword">true</span>, in which case blame
* information will be generated regardless of the MIME types of
* the revisions
* </ul>
* @since 1.2, SVN 1.5
*/
public void doAnnotate(SVNURL url, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, boolean ignoreMimeType, boolean includeMergedRevisions,
ISVNAnnotateHandler handler, String inputEncoding) throws SVNException {
if (startRevision == null || !startRevision.isValid()) {
startRevision = SVNRevision.create(1);
}
if (endRevision == null || !endRevision.isValid()) {
endRevision = pegRevision;
}
if (startRevision == SVNRevision.WORKING || endRevision == SVNRevision.WORKING) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Blame of the WORKING revision is not supported");
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNRepository repos = createRepository(url, null, null, pegRevision, endRevision, null);
long endRev = getRevisionNumber(endRevision, repos, null);
long startRev = getRevisionNumber(startRevision, repos, null);
if (endRev < startRev) {
SVNErrorManager.error(SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Start revision must precede end revision"), SVNLogType.DEFAULT);
}
File tmpFile = SVNFileUtil.createTempDirectory("annotate");
doAnnotate(repos.getLocation().toString(), startRev, tmpFile, repos, endRev, ignoreMimeType, handler, inputEncoding, includeMergedRevisions);
}
/**
* Gets commit log messages with other revision specific information from a
* repository (using Working Copy paths to get corresponding URLs) and
* passes them to a log entry handler for processing. Useful for observing
* the history of affected paths, author, date and log comments information
* per revision.
* <p>
* Calling this method is equivalent to
*
* <code>doLog(paths, startRevision, endRevision, SVNRevision.UNDEFINED, stopOnCopy, discoverChangedPaths, false, limit, null, handler)</code>.
*
* @param paths
* an array of Working Copy paths, should not be <span
* class="javakeyword">null</span>
* @param startRevision
* a revision for an operation to start from (including this
* revision)
* @param endRevision
* a revision for an operation to stop at (including this
* revision)
* @param stopOnCopy
* <span class="javakeyword">true</span> not to cross copies
* while traversing history, otherwise copies history will be
* also included into processing
* @param discoverChangedPaths
* <span class="javakeyword">true</span> to report of all changed
* paths for every revision being processed (those paths will be
* available by calling
* {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
* @param limit
* a maximum number of log entries to be processed
* @param handler
* a caller's log entry handler
* @throws SVNException
* if one of the following is true:
* <ul>
* <li>a path is not under version control <li>can not obtain a
* URL of a WC path - there's no such entry in the Working Copy
* <li><code>paths</code> contain entries that belong to
* different repositories
* </ul>
* @see #doLog(File[],SVNRevision,SVNRevision,SVNRevision,boolean,boolean,boolean,long,String[],ISVNLogEntryHandler)
*/
public void doLog(File[] paths, SVNRevision startRevision, SVNRevision endRevision, boolean stopOnCopy, boolean discoverChangedPaths, long limit, final ISVNLogEntryHandler handler)
throws SVNException {
doLog(paths, startRevision, endRevision, SVNRevision.UNDEFINED, stopOnCopy, discoverChangedPaths, false, limit, null, handler);
}
/**
* Invokes <code>handler</code> on each log message from
* <code>startRevision</code> to <code>endRevision</code> in turn, inclusive
* (but never invokes <code>handler</code> on a given log message more than
* once).
* <p/>
* <code>handler</code> is invoked only on messages whose revisions involved
* a change to some path in <code>paths</code>. <code>pegRevision</code>
* indicates in which revision <code>paths</code> are valid. If
* <code>pegRevision</code> is{@link SVNRevision#isValid() invalid}, it
* defaults to {@link SVNRevision#WORKING}.
* <p/>
* If <code>limit</code> is non-zero, only invokes <code>handler</code> on
* the first <code>limit</code> logs.
* <p/>
* If <code>discoverChangedPaths</code> is set, then the changed paths
* <code>Map</code> argument will be passed to a constructor of
* {@link SVNLogEntry} on each invocation of <code>handler</code>.
* <p/>
* If <code>stopOnCopy</code> is set, copy history (if any exists) will not
* be traversed while harvesting revision logs for each target.
* <p/>
* If <code>includeMergedRevisions</code> is set, log information for
* revisions which have been merged to <code>paths</code> will also be
* returned.
* <p/>
* Refer to {@link org.tmatesoft.svn.core.SVNLogEntry#hasChildren()} for
* additional information on how to handle mergeinfo information during a
* log operation.
* <p/>
* If
* <code>revisionProperties is <span class="javakeyword">null</span>, retrieves all revision properties;
* else, retrieves only the revprops named in the array (i.e. retrieves none if the array is empty).
* <p/>
* If <code>startRevision</code> is {@link SVNRevision#isValid() valid} but
* <code>endRevision</code> is not, then <code>endRevision</code> defaults
* to <code>startRevision</code>. If both <code>startRevision</code> and
* <code>endRevision</code> are invalid, then <code>endRevision</code>
* defaults to revision <code>0</code>, and <code>startRevision</code>
* defaults either to <code>pegRevision</code> in case the latter one is
* valid, or to {@link SVNRevision#BASE}, if it is not.
* <p/>
* Important: to avoid an exception with the
* {@link SVNErrorCode#FS_NO_SUCH_REVISION} error code when invoked against
* an empty repository (i.e. one not containing a revision 1), callers
* should specify the range {@link SVNRevision#HEAD}:<code>0</code>.
* <p/>
* If the caller has provided a non-<span class="javakeyword">null</span>
* {@link ISVNEventHandler}, it will be called with the
* {@link SVNEventAction#SKIP} event action on any unversioned paths.
* <p/>
* Note: this routine requires repository access.
*
* @param paths
* an array of Working Copy paths, for which log messages are
* desired
* @param startRevision
* a revision for an operation to start from (including this
* revision)
* @param endRevision
* a revision for an operation to stop at (including this
* revision)
* @param pegRevision
* a revision in which <code>paths</code> are first looked up in
* the repository
* @param stopOnCopy
* <span class="javakeyword">true</span> not to cross copies
* while traversing history, otherwise copies history will be
* also included into processing
* @param discoverChangedPaths
* <span class="javakeyword">true</span> to report of all changed
* paths for every revision being processed (those paths will be
* available by calling
* {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
* @param includeMergedRevisions
* if <span class="javakeyword">true</span>, merged revisions
* will be also reported
* @param limit
* a maximum number of log entries to be processed
* @param revisionProperties
* names of revision properties to retrieve
* @param handler
* a caller's log entry handler
* @throws SVNException
* if one of the following is true:
* <ul>
* <li>can not obtain a URL of a WC path - there's no such entry
* in the Working Copy <li><code>paths</code> contain entries
* that belong to different repositories
* </ul>
* @since 1.2, SVN 1.5
*/
public void doLog(File[] paths, SVNRevision startRevision, SVNRevision endRevision, SVNRevision pegRevision, boolean stopOnCopy, boolean discoverChangedPaths, boolean includeMergedRevisions,
long limit, String[] revisionProperties, final ISVNLogEntryHandler handler) throws SVNException {
Collection revisionRanges = new ArrayList(1);
revisionRanges.add(new SVNRevisionRange(startRevision, endRevision));
doLog(paths, revisionRanges, pegRevision, stopOnCopy, discoverChangedPaths, includeMergedRevisions, limit, revisionProperties, handler);
}
/**
* Invokes <code>handler</code> on each log message from the given
* <code>revisionRanges</code> in turn, inclusive (but never invokes
* <code>handler</code> on a given log message more than once).
* <p/>
* <code>handler</code> is invoked only on messages whose revisions involved
* a change to some path in <code>paths</code>. <code>pegRevision</code>
* indicates in which revision <code>paths</code> are valid. If
* <code>pegRevision</code> is{@link SVNRevision#isValid() invalid}, it
* defaults to {@link SVNRevision#WORKING}.
* <p/>
* If <code>limit</code> is non-zero, only invokes <code>handler</code> on
* the first <code>limit</code> logs.
* <p/>
* If <code>discoverChangedPaths</code> is set, then the changed paths
* <code>Map</code> argument will be passed to a constructor of
* {@link SVNLogEntry} on each invocation of <code>handler</code>.
* <p/>
* If <code>stopOnCopy</code> is set, copy history (if any exists) will not
* be traversed while harvesting revision logs for each target.
* <p/>
* If <code>includeMergedRevisions</code> is set, log information for
* revisions which have been merged to <code>paths</code> will also be
* returned.
* <p/>
* Refer to {@link org.tmatesoft.svn.core.SVNLogEntry#hasChildren()} for
* additional information on how to handle mergeinfo information during a
* log operation.
* <p/>
* If
* <code>revisionProperties is <span class="javakeyword">null</span>, retrieves all revision properties;
* else, retrieves only the revprops named in the array (i.e. retrieves none if the array is empty).
* <p/>
* For every {@link SVNRevisionRange} in <code>revisionRanges</code>: <b/>
* If <code>startRevision</code> is {@link SVNRevision#isValid() valid} but
* <code>endRevision</code> is not, then <code>endRevision</code> defaults
* to <code>startRevision</code>. If both <code>startRevision</code> and
* <code>endRevision</code> are invalid, then <code>endRevision</code>
* defaults to revision <code>0</code>, and <code>startRevision</code>
* defaults either to <code>pegRevision</code> in case the latter one is
* valid, or to {@link SVNRevision#BASE}, if it is not.
* <p/>
* Important: to avoid an exception with the
* {@link SVNErrorCode#FS_NO_SUCH_REVISION} error code when invoked against
* an empty repository (i.e. one not containing a revision 1), callers
* should specify the range {@link SVNRevision#HEAD}:<code>0</code>.
* <p/>
* If the caller has provided a non-<span class="javakeyword">null</span>
* {@link ISVNEventHandler}, it will be called with the
* {@link SVNEventAction#SKIP} event action on any unversioned paths.
* <p/>
* Note: this routine requires repository access.
*
* @param paths
* an array of Working Copy paths, for which log messages are
* desired
* @param revisionRanges
* collection of {@link SVNRevisionRange} objects
* @param pegRevision
* a revision in which <code>paths</code> are first looked up in
* the repository
* @param stopOnCopy
* <span class="javakeyword">true</span> not to cross copies
* while traversing history, otherwise copies history will be
* also included into processing
* @param discoverChangedPaths
* <span class="javakeyword">true</span> to report of all changed
* paths for every revision being processed (those paths will be
* available by calling
* {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
* @param includeMergedRevisions
* if <span class="javakeyword">true</span>, merged revisions
* will be also reported
* @param limit
* a maximum number of log entries to be processed
* @param revisionProperties
* names of revision properties to retrieve
* @param handler
* a caller's log entry handler
* @throws SVNException
* if one of the following is true:
* <ul>
* <li>can not obtain a URL of a WC path - there's no such entry
* in the Working Copy <li><code>paths</code> contain entries
* that belong to different repositories
* </ul>
* @since 1.3, SVN 1.6
*/
public void doLog(File[] paths, Collection revisionRanges, SVNRevision pegRevision, boolean stopOnCopy, boolean discoverChangedPaths, boolean includeMergedRevisions, long limit,
String[] revisionProperties, final ISVNLogEntryHandler handler) throws SVNException {
if (paths == null || paths.length == 0 || handler == null) {
return;
}
SVNRevision sessionRevision = SVNRevision.UNDEFINED;
List editedRevisionRanges = new LinkedList();
for (Iterator revRangesIter = revisionRanges.iterator(); revRangesIter.hasNext();) {
SVNRevisionRange revRange = (SVNRevisionRange) revRangesIter.next();
if (revRange.getStartRevision().isValid() && !revRange.getEndRevision().isValid()) {
revRange = new SVNRevisionRange(revRange.getStartRevision(), revRange.getStartRevision());
} else if (!revRange.getStartRevision().isValid()) {
SVNRevision start = SVNRevision.UNDEFINED;
SVNRevision end = SVNRevision.UNDEFINED;
if (!pegRevision.isValid()) {
start = SVNRevision.BASE;
} else {
start = pegRevision;
}
if (!revRange.getEndRevision().isValid()) {
end = SVNRevision.create(0);
}
revRange = new SVNRevisionRange(start, end);
}
if (!revRange.getStartRevision().isValid() || !revRange.getEndRevision().isValid()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Missing required revision specification");
SVNErrorManager.error(err, SVNLogType.WC);
}
editedRevisionRanges.add(revRange);
if (!sessionRevision.isValid()) {
SVNRevision start = revRange.getStartRevision();
SVNRevision end = revRange.getEndRevision();
if (SVNRevision.isValidRevisionNumber(start.getNumber()) && SVNRevision.isValidRevisionNumber(end.getNumber())) {
sessionRevision = start.getNumber() > end.getNumber() ? start : end;
} else if (start.getDate() != null && end.getDate() != null) {
sessionRevision = start.getDate().compareTo(end.getDate()) > 0 ? start : end;
}
}
}
if (limit > Integer.MAX_VALUE) {
limit = Integer.MAX_VALUE;
}
ISVNLogEntryHandler wrappingHandler = new ISVNLogEntryHandler() {
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
checkCancelled();
handler.handleLogEntry(logEntry);
}
};
SVNURL[] urls = new SVNURL[paths.length];
SVNWCAccess wcAccess = createWCAccess();
Collection wcPaths = new ArrayList();
for (int i = 0; i < paths.length; i++) {
checkCancelled();
File path = paths[i];
wcPaths.add(path.getAbsolutePath().replace(File.separatorChar, '/'));
SVNAdminArea area = wcAccess.probeOpen(path, false, 0);
SVNEntry entry = wcAccess.getVersionedEntry(path, false);
if (entry.getURL() == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "Entry ''{0}'' has no URL", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
urls[i] = entry.getSVNURL();
if (area != null) {
wcAccess.closeAdminArea(area.getRoot());
}
}
if (urls.length == 0) {
return;
}
String[] wcPathsArray = (String[]) wcPaths.toArray(new String[wcPaths.size()]);
String rootWCPath = SVNPathUtil.condencePaths(wcPathsArray, null, true);
Collection targets = new TreeSet();
SVNURL baseURL = SVNURLUtil.condenceURLs(urls, targets, true);
if (baseURL == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "target log paths belong to different repositories");
SVNErrorManager.error(err, SVNLogType.WC);
}
if (targets.isEmpty()) {
targets.add("");
}
if (!pegRevision.isValid()) {
pegRevision = SVNRevision.WORKING;
}
SVNRepository repos = null;
if (rootWCPath != null && needsWC(pegRevision)) {
File root = new File(rootWCPath);
SVNAdminArea area = wcAccess.probeOpen(root, false, 0);
repos = createRepository(null, root, area, pegRevision, sessionRevision, null);
if (area != null) {
wcAccess.closeAdminArea(area.getRoot());
}
} else {
repos = createRepository(baseURL, null, null, pegRevision, sessionRevision, null);
}
String[] targetPaths = (String[]) targets.toArray(new String[targets.size()]);
for (int i = 0; i < targetPaths.length; i++) {
targetPaths[i] = SVNEncodingUtil.uriDecode(targetPaths[i]);
}
for (Iterator revRangesIter = editedRevisionRanges.iterator(); revRangesIter.hasNext();) {
checkCancelled();
SVNRevisionRange revRange = (SVNRevisionRange) revRangesIter.next();
SVNRevision startRevision = revRange.getStartRevision();
SVNRevision endRevision = revRange.getEndRevision();
if (startRevision.isLocal() || endRevision.isLocal()) {
for (int i = 0; i < paths.length; i++) {
checkCancelled();
long startRev = getRevisionNumber(startRevision, repos, paths[i]);
long endRev = getRevisionNumber(endRevision, repos, paths[i]);
repos.log(targetPaths, startRev, endRev, discoverChangedPaths, stopOnCopy, limit, includeMergedRevisions, revisionProperties, wrappingHandler);
}
} else {
long startRev = getRevisionNumber(startRevision, repos, null);
long endRev = getRevisionNumber(endRevision, repos, null);
repos.log(targetPaths, startRev, endRev, discoverChangedPaths, stopOnCopy, limit, includeMergedRevisions, revisionProperties, wrappingHandler);
}
}
}
/**
* Gets commit log messages with other revision specific information from a
* repository (using Working Copy paths to get corresponding URLs) and
* passes them to a log entry handler for processing. Useful for observing
* the history of affected paths, author, date and log comments information
* per revision.
* <p>
* Calling this method is equivalent to
*
* <code>doLog(paths, startRevision, endRevision, pegRevision, stopOnCopy, discoverChangedPaths, false, limit, null, handler)</code>.
*
* @param paths
* an array of Working Copy paths, should not be <span
* class="javakeyword">null</span>
* @param pegRevision
* a revision in which <code>path</code> is first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from (including this
* revision)
* @param endRevision
* a revision for an operation to stop at (including this
* revision)
* @param stopOnCopy
* <span class="javakeyword">true</span> not to cross copies
* while traversing history, otherwise copies history will be
* also included into processing
* @param discoverChangedPaths
* <span class="javakeyword">true</span> to report of all changed
* paths for every revision being processed (those paths will be
* available by calling
* {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
* @param limit
* a maximum number of log entries to be processed
* @param handler
* a caller's log entry handler
* @throws SVNException
* if one of the following is true:
* <ul>
* <li>a path is not under version control <li>can not obtain a
* URL of a WC path - there's no such entry in the Working Copy
* <li><code>paths</code> contain entries that belong to
* different repositories
* </ul>
* @see #doLog(File[],SVNRevision,SVNRevision,SVNRevision,boolean,boolean,boolean,long,String[],ISVNLogEntryHandler)
*/
public void doLog(File[] paths, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, boolean stopOnCopy, boolean discoverChangedPaths, long limit,
final ISVNLogEntryHandler handler) throws SVNException {
doLog(paths, startRevision, endRevision, pegRevision, stopOnCopy, discoverChangedPaths, false, limit, null, handler);
}
/**
* Gets commit log messages with other revision specific information from a
* repository and passes them to a log entry handler for processing. Useful
* for observing the history of affected paths, author, date and log
* comments information per revision.
* <p>
* Calling this method is equivalent to
*
* <code> doLog(url, paths, pegRevision, startRevision, endRevision, stopOnCopy, discoverChangedPaths, false, limit, null, handler)</code>.
*
* @param url
* a target URL
* @param paths
* an array of paths relative to the target <code>url</code>
* @param pegRevision
* a revision in which <code>url</code> is first looked up
* @param startRevision
* a revision for an operation to start from (including this
* revision)
* @param endRevision
* a revision for an operation to stop at (including this
* revision)
* @param stopOnCopy
* <span class="javakeyword">true</span> not to cross copies
* while traversing history, otherwise copies history will be
* also included into processing
* @param discoverChangedPaths
* <span class="javakeyword">true</span> to report of all changed
* paths for every revision being processed (those paths will be
* available by calling
* {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
* @param limit
* a maximum number of log entries to be processed
* @param handler
* a caller's log entry handler
* @throws SVNException
* @see #doLog(SVNURL,String[],SVNRevision,SVNRevision,SVNRevision,boolean,boolean,boolean,long,String[],ISVNLogEntryHandler)
* @since 1.1, new in Subversion 1.4
*/
public void doLog(SVNURL url, String[] paths, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, boolean stopOnCopy, boolean discoverChangedPaths, long limit,
final ISVNLogEntryHandler handler) throws SVNException {
doLog(url, paths, pegRevision, startRevision, endRevision, stopOnCopy, discoverChangedPaths, false, limit, null, handler);
}
/**
* Invokes <code>handler</code> on each log message from
* <code>startRevision</code> to <code>endRevision</code> in turn, inclusive
* (but never invokes <code>handler</code> on a given log message more than
* once).
* <p/>
* <code>handler</code> is invoked only on messages whose revisions involved
* a change to some path in <code>paths</code>. <code>pegRevision</code>
* indicates in which revision <code>paths</code> are valid. If
* <code>pegRevision</code> is{@link SVNRevision#isValid() invalid}, it
* defaults to {@link SVNRevision#HEAD}.
* <p/>
* If <code>limit</code> is non-zero, only invokes <code>handler</code> on
* the first <code>limit</code> logs.
* <p/>
* If <code>discoverChangedPaths</code> is set, then the changed paths
* <code>Map</code> argument will be passed to a constructor of
* {@link SVNLogEntry} on each invocation of <code>handler</code>.
* <p/>
* If <code>stopOnCopy</code> is set, copy history (if any exists) will not
* be traversed while harvesting revision logs for each target.
* <p/>
* If <code>includeMergedRevisions</code> is set, log information for
* revisions which have been merged to <code>paths</code> will also be
* returned.
* <p/>
* If
* <code>revisionProperties is <span class="javakeyword">null</span>, retrieves all revision properties;
* else, retrieves only the revision properties named in the array (i.e. retrieves none if the array is empty).
* <p/>
* Important: to avoid an exception with the {@link SVNErrorCode#FS_NO_SUCH_REVISION} error code
* when invoked against an empty repository (i.e. one not containing a revision 1), callers should specify
* the range {@link SVNRevision#HEAD}:<code>0</code>.
* <p/>
* Note: this routine requires repository access.
*
* @param url
* repository URL
* @param paths
* an array of paths relative to <code>url</code>
* @param pegRevision
* a revision in which <code>paths</code> are first looked up in
* the repository
* @param startRevision
* a revision for an operation to start from (including this
* revision)
* @param endRevision
* a revision for an operation to stop at (including this
* revision)
* @param stopOnCopy
* <span class="javakeyword">true</span> not to cross copies
* while traversing history, otherwise copies history will be
* also included into processing
* @param discoverChangedPaths
* <span class="javakeyword">true</span> to report of all changed
* paths for every revision being processed (those paths will be
* available by calling
* {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
* @param includeMergedRevisions
* if <span class="javakeyword">true</span>, merged revisions
* will be also reported
* @param limit
* a maximum number of log entries to be processed
* @param revisionProperties
* names of revision properties to retrieve
* @param handler
* a caller's log entry handler
* @throws SVNException
* @since 1.2, SVN 1.5
*/
public void doLog(SVNURL url, String[] paths, SVNRevision pegRevision, SVNRevision startRevision, SVNRevision endRevision, boolean stopOnCopy, boolean discoverChangedPaths,
boolean includeMergedRevisions, long limit, String[] revisionProperties, final ISVNLogEntryHandler handler) throws SVNException {
Collection revisionRanges = new ArrayList(1);
revisionRanges.add(new SVNRevisionRange(startRevision, endRevision));
doLog(url, paths, pegRevision, revisionRanges, stopOnCopy, discoverChangedPaths, includeMergedRevisions, limit, revisionProperties, handler);
}
/**
* Invokes <code>handler</code> on each log message from the given
* <code></code> in turn, inclusive (but never invokes <code>handler</code>
* on a given log message more than once).
* <p/>
* <code>handler</code> is invoked only on messages whose revisions involved
* a change to some path in <code>paths</code>. <code>pegRevision</code>
* indicates in which revision <code>paths</code> are valid. If
* <code>pegRevision</code> is{@link SVNRevision#isValid() invalid}, it
* defaults to {@link SVNRevision#HEAD}.
* <p/>
* If <code>limit</code> is non-zero, only invokes <code>handler</code> on
* the first <code>limit</code> logs.
* <p/>
* If <code>discoverChangedPaths</code> is set, then the changed paths
* <code>Map</code> argument will be passed to a constructor of
* {@link SVNLogEntry} on each invocation of <code>handler</code>.
* <p/>
* If <code>stopOnCopy</code> is set, copy history (if any exists) will not
* be traversed while harvesting revision logs for each target.
* <p/>
* If <code>includeMergedRevisions</code> is set, log information for
* revisions which have been merged to <code>paths</code> will also be
* returned.
* <p/>
* If
* <code>revisionProperties is <span class="javakeyword">null</span>, retrieves all revision properties;
* else, retrieves only the revision properties named in the array (i.e. retrieves none if the array is empty).
* <p/>
* Important: to avoid an exception with the {@link SVNErrorCode#FS_NO_SUCH_REVISION} error code
* when invoked against an empty repository (i.e. one not containing a revision 1), callers should specify
* the range {@link SVNRevision#HEAD}:<code>0</code>.
* <p/>
* Note: this routine requires repository access.
*
* @param url
* repository URL
* @param paths
* an array of paths relative to <code>url</code>
* @param pegRevision
* a revision in which <code>paths</code> are first looked up in
* the repository
* @param revisionRanges
* collection of {@link SVNRevisionRange} objects
* @param stopOnCopy
* <span class="javakeyword">true</span> not to cross copies
* while traversing history, otherwise copies history will be
* also included into processing
* @param discoverChangedPaths
* <span class="javakeyword">true</span> to report of all changed
* paths for every revision being processed (those paths will be
* available by calling
* {@link org.tmatesoft.svn.core.SVNLogEntry#getChangedPaths()})
* @param includeMergedRevisions
* if <span class="javakeyword">true</span>, merged revisions
* will be also reported
* @param limit
* a maximum number of log entries to be processed
* @param revisionProperties
* names of revision properties to retrieve
* @param handler
* a caller's log entry handler
* @throws SVNException
* @since 1.3, SVN 1.6
*/
public void doLog(SVNURL url, String[] paths, SVNRevision pegRevision, Collection revisionRanges, boolean stopOnCopy, boolean discoverChangedPaths, boolean includeMergedRevisions, long limit,
String[] revisionProperties, final ISVNLogEntryHandler handler) throws SVNException {
SVNRevision sessionRevision = SVNRevision.UNDEFINED;
List editedRevisionRanges = new LinkedList();
for (Iterator revRangesIter = revisionRanges.iterator(); revRangesIter.hasNext();) {
SVNRevisionRange revRange = (SVNRevisionRange) revRangesIter.next();
if (revRange.getStartRevision().isValid() && !revRange.getEndRevision().isValid()) {
revRange = new SVNRevisionRange(revRange.getStartRevision(), revRange.getStartRevision());
} else if (!revRange.getStartRevision().isValid()) {
SVNRevision start = SVNRevision.UNDEFINED;
SVNRevision end = SVNRevision.UNDEFINED;
if (!pegRevision.isValid()) {
start = SVNRevision.HEAD;
} else {
start = pegRevision;
}
if (!revRange.getEndRevision().isValid()) {
end = SVNRevision.create(0);
}
revRange = new SVNRevisionRange(start, end);
}
if (!revRange.getStartRevision().isValid() || !revRange.getEndRevision().isValid()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Missing required revision specification");
SVNErrorManager.error(err, SVNLogType.WC);
}
if (needsWC(revRange.getStartRevision()) || needsWC(revRange.getEndRevision())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Revision type requires a working copy path, not a URL");
SVNErrorManager.error(err, SVNLogType.WC);
}
editedRevisionRanges.add(revRange);
if (!sessionRevision.isValid()) {
SVNRevision start = revRange.getStartRevision();
SVNRevision end = revRange.getEndRevision();
if (SVNRevision.isValidRevisionNumber(start.getNumber()) && SVNRevision.isValidRevisionNumber(end.getNumber())) {
sessionRevision = start.getNumber() > end.getNumber() ? start : end;
} else if (start.getDate() != null && end.getDate() != null) {
sessionRevision = start.getDate().compareTo(end.getDate()) > 0 ? start : end;
}
}
}
if (needsWC(pegRevision)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Revision type requires a working copy path, not a URL");
SVNErrorManager.error(err, SVNLogType.WC);
}
if (limit > Integer.MAX_VALUE) {
limit = Integer.MAX_VALUE;
}
paths = paths == null || paths.length == 0 ? new String[] {
""
} : paths;
ISVNLogEntryHandler wrappingHandler = new ISVNLogEntryHandler() {
public void handleLogEntry(SVNLogEntry logEntry) throws SVNException {
checkCancelled();
handler.handleLogEntry(logEntry);
}
};
SVNRepository repos = sessionRevision.isValid() ? createRepository(url, null, null, pegRevision, sessionRevision, null) : createRepository(url, null, null, true);
for (Iterator revRangesIter = editedRevisionRanges.iterator(); revRangesIter.hasNext();) {
checkCancelled();
SVNRevisionRange revRange = (SVNRevisionRange) revRangesIter.next();
long startRev = getRevisionNumber(revRange.getStartRevision(), repos, null);
long endRev = getRevisionNumber(revRange.getEndRevision(), repos, null);
repos.log(paths, startRev, endRev, discoverChangedPaths, stopOnCopy, limit, includeMergedRevisions, revisionProperties, wrappingHandler);
}
}
/**
* Browses directory entries from a repository (using Working Copy paths to
* get corresponding URLs) and uses the provided dir entry handler to
* process them.
* <p>
* On every entry that this method stops it gets some useful entry
* information which is packed into an
* {@link org.tmatesoft.svn.core.SVNDirEntry}object and passed to the
* <code>handler</code>'s
* {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry)
* handleDirEntry()} method.
*
* @param path
* a WC item to get its repository location
* @param pegRevision
* a revision in which the item's URL is first looked up
* @param revision
* a target revision
* @param fetchLocks
* <span class="javakeyword">true</span> to fetch locks
* information from a repository
* @param recursive
* <span class="javakeyword">true</span> to descend recursively
* (relevant for directories)
* @param handler
* a caller's directory entry handler (to process info on an
* entry)
* @throws SVNException
* @deprecated use
* {@link #doList(File,SVNRevision,SVNRevision,boolean,SVNDepth,int,ISVNDirEntryHandler)}
* instead
*/
public void doList(File path, SVNRevision pegRevision, SVNRevision revision, boolean fetchLocks, boolean recursive, ISVNDirEntryHandler handler) throws SVNException {
doList(path, pegRevision, revision, fetchLocks, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES, SVNDirEntry.DIRENT_ALL, handler);
}
/**
* Reports the directory entry, and possibly children, for <code>path</code>
* at <code>revision</code>. The actual node revision selected is determined
* by the path as it exists in <code>pegRevision</code>. If
* <code>pegRevision</code> is {@link SVNRevision#isValid() invalid}, then
* it defaults to {@link SVNRevision#WORKING}.
* <p/>
* Reports directory entries by invoking <code>handler</code>,
* {@link SVNDirEntry#setRelativePath(String) setting} a relative to
* <code>path</code> path to the {@link SVNDirEntry} object. The directory
* entry for <code>path</code> is reported using an empty path. If
* <code>path</code> is a directory, also reports its children.
* <p/>
* If <code>depth</code> is {@link SVNDepth#EMPTY}, lists just
* <code>path</code> itself. If <code>depth</code> is {@link SVNDepth#FILES}
* , lists <code>path</code> and its file entries. If
* {@link SVNDepth#IMMEDIATES}, lists its immediate file and directory
* entries. If {@link SVNDepth#INFINITY}, lists file entries and recurses
* (with {@link SVNDepth#INFINITY}) on directory entries.
* <p/>
* <code>entryFields</code> controls which fields in the {@link SVNDirEntry}
* are filled in. To have them totally filled in use
* {@link SVNDirEntry#DIRENT_ALL}, otherwise simply bitwise OR together the
* combination of fields you care about.
* <p/>
* Note: this routine requires repository access.
*
* @param path
* a WC item to get its repository location
* @param pegRevision
* a revision in which the item's URL is first looked up
* @param revision
* a target revision
* @param fetchLocks
* <span class="javakeyword">true</span>, includes locks when
* reporting directory entries
* @param depth
* tree depth to process
* @param entryFields
* entry fields to fill
* @param handler
* a caller's directory entry handler (to process info on an
* entry)
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#FS_NOT_FOUND} error
* code - if <code>path</code> is non-existent in the repository
* <ul/>
* @since 1.2, SVN 1.5
*/
public void doList(File path, SVNRevision pegRevision, SVNRevision revision, boolean fetchLocks, SVNDepth depth, int entryFields, ISVNDirEntryHandler handler) throws SVNException {
if (revision == null || !revision.isValid()) {
revision = SVNRevision.BASE;
}
SVNRepository repos = createRepository(null, path, null, pegRevision, revision, null);
long rev = getRevisionNumber(revision, repos, path);
doList(repos, rev, handler, fetchLocks, depth, entryFields);
}
/**
* Browses directory entries from a repository (using Working Copy paths to
* get corresponding URLs) and uses the provided dir entry handler to
* process them.
* <p>
* On every entry that this method stops it gets some useful entry
* information which is packed into an
* {@link org.tmatesoft.svn.core.SVNDirEntry}object and passed to the
* <code>handler</code>'s
* {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry)
* handleDirEntry()} method.
*
* @param path
* a WC item to get its repository location
* @param pegRevision
* a revision in which the item's URL is first looked up
* @param revision
* a target revision
* @param recursive
* <span class="javakeyword">true</span> to descend recursively
* (relevant for directories)
* @param handler
* a caller's directory entry handler (to process info on an
* entry)
* @throws SVNException
* @deprecated use
* {@link #doList(File,SVNRevision,SVNRevision,boolean,SVNDepth,int,ISVNDirEntryHandler)}
* instead
*/
public void doList(File path, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNDirEntryHandler handler) throws SVNException {
doList(path, pegRevision, revision, false, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES, SVNDirEntry.DIRENT_ALL, handler);
}
/**
* Browses directory entries from a repository and uses the provided dir
* entry handler to process them. This method is especially useful when
* having no Working Copy.
* <p>
* On every entry that this method stops it gets some useful entry
* information which is packed into an
* {@link org.tmatesoft.svn.core.SVNDirEntry}object and passed to the
* <code>handler</code>'s
* {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry)
* handleDirEntry()} method.
*
* @param url
* a repository location to be "listed"
* @param pegRevision
* a revision in which the item's URL is first looked up
* @param revision
* a target revision
* @param fetchLocks
* <span class="javakeyword">true</span> to fetch locks
* information from repository
* @param recursive
* <span class="javakeyword">true</span> to descend recursively
* (relevant for directories)
* @param handler
* a caller's directory entry handler (to process info on an
* entry)
* @throws SVNException
* @see #doList(File,SVNRevision,SVNRevision,boolean,ISVNDirEntryHandler)
*/
public void doList(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean fetchLocks, boolean recursive, ISVNDirEntryHandler handler) throws SVNException {
doList(url, pegRevision, revision, fetchLocks, recursive ? SVNDepth.INFINITY : SVNDepth.IMMEDIATES, SVNDirEntry.DIRENT_ALL, handler);
}
/**
* Reports the directory entry, and possibly children, for <code>url</code>
* at <code>revision</code>. The actual node revision selected is determined
* by the path as it exists in <code>pegRevision</code>. If
* <code>pegRevision</code> is {@link SVNRevision#isValid() invalid}, then
* it defaults to {@link SVNRevision#HEAD}.
* <p/>
* Reports directory entries by invoking <code>handler</code>,
* {@link SVNDirEntry#setRelativePath(String) setting} a relative to
* <code>url</code> path to the {@link SVNDirEntry} object. The directory
* entry for <code>url</code> is reported using an empty path. If
* <code>url</code> is a directory, also reports its children.
* <p/>
* If <code>depth</code> is {@link SVNDepth#EMPTY}, lists just
* <code>url</code> itself. If <code>depth</code> is {@link SVNDepth#FILES},
* lists <code>url</code> and its file entries. If
* {@link SVNDepth#IMMEDIATES}, lists its immediate file and directory
* entries. If {@link SVNDepth#INFINITY}, lists file entries and recurses
* (with {@link SVNDepth#INFINITY}) on directory entries.
* <p/>
* <code>entryFields</code> controls which fields in the {@link SVNDirEntry}
* are filled in. To have them totally filled in use
* {@link SVNDirEntry#DIRENT_ALL}, otherwise simply bitwise OR together the
* combination of fields you care about.
* <p/>
* Note: this routine requires repository access.
*
* @param url
* a repository url to be "listed"
* @param pegRevision
* a revision in which the item's URL is first looked up
* @param revision
* a target revision
* @param fetchLocks
* <span class="javakeyword">true</span>, includes locks when
* reporting directory entries
* @param depth
* tree depth to process
* @param entryFields
* entry fields to fill
* @param handler
* a caller's directory entry handler (to process info on an
* entry)
* @throws SVNException
* in the following cases:
* <ul>
* <li/>exception with {@link SVNErrorCode#FS_NOT_FOUND} error
* code - if <code>url</code> is non-existent in the repository
* <ul/>
* @since 1.2, SVN 1.5
*/
public void doList(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean fetchLocks, SVNDepth depth, int entryFields, ISVNDirEntryHandler handler) throws SVNException {
long[] pegRev = new long[] {
-1
};
SVNRepository repos = createRepository(url, null, null, pegRevision, revision, pegRev);
if (pegRev[0] < 0) {
pegRev[0] = getRevisionNumber(revision, repos, null);
}
doList(repos, pegRev[0], handler, fetchLocks, depth, entryFields);
}
/**
* Browses directory entries from a repository and uses the provided dir
* entry handler to process them. This method is especially useful when
* having no Working Copy.
* <p>
* On every entry that this method stops it gets some useful entry
* information which is packed into an
* {@link org.tmatesoft.svn.core.SVNDirEntry}object and passed to the
* <code>handler</code>'s
* {@link org.tmatesoft.svn.core.ISVNDirEntryHandler#handleDirEntry(SVNDirEntry)
* handleDirEntry()} method.
*
* @param url
* a repository location to be "listed"
* @param pegRevision
* a revision in which the item's URL is first looked up
* @param revision
* a target revision
* @param recursive
* <span class="javakeyword">true</span> to descend recursively
* (relevant for directories)
* @param handler
* a caller's directory entry handler (to process info on an
* entry)
* @throws SVNException
* @deprecated use
* {@link #doList(SVNURL,SVNRevision,SVNRevision,boolean,SVNDepth,int,ISVNDirEntryHandler)}
* instead
*/
public void doList(SVNURL url, SVNRevision pegRevision, SVNRevision revision, boolean recursive, ISVNDirEntryHandler handler) throws SVNException {
doList(url, pegRevision, revision, false, recursive, handler);
}
private boolean needsWC(SVNRevision revision) {
return revision == SVNRevision.BASE || revision == SVNRevision.COMMITTED || revision == SVNRevision.WORKING || revision == SVNRevision.PREVIOUS;
}
private void doAnnotate(String path, long startRev, File tmpFile, SVNRepository repos, long endRev, boolean ignoreMimeType, ISVNAnnotateHandler handler, String inputEncoding,
boolean includeMergedRevisions) throws SVNException {
SVNAnnotationGenerator generator = new SVNAnnotationGenerator(path, tmpFile, startRev, ignoreMimeType, includeMergedRevisions, getDiffOptions(), inputEncoding, handler, this);
boolean useSpool = handler != null && !handler.getClass().getName().startsWith("org.tmatesoft.svn.");
boolean oldSpool = false;
if (useSpool && repos instanceof DAVRepository) {
oldSpool = ((DAVRepository) repos).isSpoolResponse();
((DAVRepository) repos).setSpoolResponse(true);
}
try {
repos.getFileRevisions("", startRev > 0 ? startRev - 1 : startRev, endRev, includeMergedRevisions, generator);
if (!generator.isLastRevisionReported()) {
generator.reportAnnotations(handler, inputEncoding);
}
} finally {
if (useSpool && repos instanceof DAVRepository) {
((DAVRepository) repos).setSpoolResponse(oldSpool);
}
generator.dispose();
SVNFileUtil.deleteAll(tmpFile, !"text-base".equals(tmpFile.getName()), null);
}
}
private void doList(SVNRepository repos, long rev, final ISVNDirEntryHandler handler, boolean fetchLocks, SVNDepth depth, int entryFields) throws SVNException {
SVNURL url = repos.getLocation();
SVNURL reposRoot = repos.getRepositoryRoot(false);
SVNDirEntry entry = null;
SVNException error = null;
try {
entry = repos.info("", rev);
} catch (SVNException svne) {
if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED) {
error = svne;
} else {
throw svne;
}
}
if (error != null) {
SVNNodeKind kind = repos.checkPath("", rev);
if (kind != SVNNodeKind.NONE) {
if (!url.equals(reposRoot)) {
String name = SVNPathUtil.tail(repos.getLocation().getPath());
repos.setLocation(repos.getLocation().removePathTail(), false);
Collection dirEntries = repos.getDir("", rev, null, entryFields, (Collection) null);
repos.setLocation(url, false);
for (Iterator ents = dirEntries.iterator(); ents.hasNext();) {
SVNDirEntry dirEntry = (SVNDirEntry) ents.next();
if (name.equals(dirEntry.getName())) {
entry = dirEntry;
break;
}
}
if (entry != null) {
entry.setRelativePath(kind == SVNNodeKind.FILE ? name : "");
}
} else {
SVNProperties props = new SVNProperties();
repos.getDir("", rev, props, entryFields, (Collection) null);
SVNProperties revProps = repos.getRevisionProperties(rev, null);
String author = revProps.getStringValue(SVNRevisionProperty.AUTHOR);
String dateStr = revProps.getStringValue(SVNRevisionProperty.DATE);
Date datestamp = null;
if (dateStr != null) {
datestamp = SVNDate.parseDateString(dateStr);
}
entry = new SVNDirEntry(url, reposRoot, "", kind, 0, !props.isEmpty(), rev, datestamp, author);
entry.setRelativePath("");
}
}
} else if (entry != null) {
entry.setRelativePath(entry.getKind() == SVNNodeKind.DIR ? "" : entry.getName());
}
if (entry == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "URL ''{0}'' non-existent in that revision", url);
SVNErrorManager.error(err, SVNLogType.WC);
}
final Map locksMap = new SVNHashMap();
if (fetchLocks) {
SVNLock[] locks = new SVNLock[0];
try {
locks = repos.getLocks("");
} catch (SVNException e) {
if (!(e.getErrorMessage() != null && e.getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_IMPLEMENTED)) {
throw e;
}
}
if (locks != null && locks.length > 0) {
SVNURL root = repos.getRepositoryRoot(true);
for (int i = 0; i < locks.length; i++) {
String repositoryPath = locks[i].getPath();
locksMap.put(root.appendPath(repositoryPath, false), locks[i]);
}
}
}
ISVNDirEntryHandler nestedHandler = new ISVNDirEntryHandler() {
public void handleDirEntry(SVNDirEntry dirEntry) throws SVNException {
dirEntry.setLock((SVNLock) locksMap.get(dirEntry.getURL()));
handler.handleDirEntry(dirEntry);
}
};
nestedHandler.handleDirEntry(entry);
if (entry.getKind() == SVNNodeKind.DIR && (depth == SVNDepth.FILES || depth == SVNDepth.IMMEDIATES || depth == SVNDepth.INFINITY)) {
list(repos, "", rev, depth, entryFields, nestedHandler);
}
}
private static void list(SVNRepository repository, String path, long rev, SVNDepth depth, int entryFields, ISVNDirEntryHandler handler) throws SVNException {
if (depth == SVNDepth.EMPTY) {
return;
}
Collection entries = new TreeSet();
try {
entries = repository.getDir(path, rev, null, entryFields, entries);
} catch (SVNAuthenticationException e) {
return;
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.RA_NOT_AUTHORIZED) {
return;
}
throw e;
}
for (Iterator iterator = entries.iterator(); iterator.hasNext();) {
SVNDirEntry entry = (SVNDirEntry) iterator.next();
String childPath = SVNPathUtil.append(path, entry.getName());
entry.setRelativePath(childPath);
if (entry.getKind() == SVNNodeKind.FILE || depth == SVNDepth.IMMEDIATES || depth == SVNDepth.INFINITY) {
handler.handleDirEntry(entry);
}
if (entry.getKind() == SVNNodeKind.DIR && entry.getDate() != null && depth == SVNDepth.INFINITY) {
list(repository, childPath, rev, depth, entryFields, handler);
}
}
}
}