You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@subversion.apache.org by pr...@apache.org on 2013/07/09 10:48:47 UTC
svn commit: r1501136 [3/10] - in /subversion/branches/verify-keep-going: ./
build/ build/ac-macros/ build/generator/ subversion/bindings/javahl/native/
subversion/bindings/javahl/src/org/apache/subversion/javahl/
subversion/bindings/javahl/src/org/apac...
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/ISVNRemote.java Tue Jul 9 08:48:43 2013
@@ -28,6 +28,8 @@ import org.apache.subversion.javahl.call
import java.util.Date;
import java.util.Map;
+import java.util.Set;
+import java.io.OutputStream;
/**
* Encapsulates an RA session object and related operations.
@@ -43,7 +45,7 @@ public interface ISVNRemote
void dispose();
/**
- * Cancel the active operation.
+ * Cancel the active operation, including any ongoing edits.
* @throws ClientException
*/
void cancelOperation() throws ClientException;
@@ -155,10 +157,280 @@ public interface ISVNRemote
throws ClientException;
/**
- * Create a commit editor instance, rooted at the current session URL.
+ * Return an editor for committing changes to the session's
+ * repository, setting the revision properties from
+ * <code>revisionProperties</code>. The revisions being committed
+ * against are passed to the editor functions. The root of the commit
+ * is the session's URL.
+ * <p>
+ * <code>revisionProperties</code> is a hash mapping property names to
+ * property values. The commit log message is expected to be in the
+ * {@link Property#REV_LOG} element. <code>revisionProperties</code>
+ * can not contain either of {@link Property#REV_DATE} or
+ * {@link Property#REV_AUTHOR}.
+ * <p>
+ * Before {@link ISVNEditor#complete()} returns, but after the commit
+ * has succeeded, it will invoke <code>commitCallback</code> (if not
+ * <code>null</code>) with filled-in {@link CommitInfo}. If
+ * <code>commitCallback</code> returns an error, that error will be
+ * returned from {@link ISVNEditor#complete()}, otherwise
+ * {@link ISVNEditor#complete()} will return successfully (unless it
+ * encountered an error before invoking <code>commitCallback</code>).
+ * The callback will not be called if the commit was a no-op
+ * (i.e., nothing was committed).
+ * <p>
+ * <code>lockTokens</code>, if not <code>null</code>, is a hash
+ * mapping paths (relative to the session's URL) to lock tokens. The
+ * server checks that the correct token is provided for each
+ * committed, locked path. <code>lockTokens</code> must live during
+ * the whole commit operation.
+ * <p>
+ * If <cpde>keepLocks</code> is <cpde>true</code>, then do not release
+ * locks on committed objects. Else, automatically release such
+ * locks.
+ * <p>
+ * The caller may not perform any remote operations using this session
+ * before finishing the edit.
+ * @throws ClientException
+ */
+ ISVNEditor getCommitEditor(Map<String, byte[]> revisionProperties,
+ CommitCallback commitCallback,
+ Set<Lock> lockTokens,
+ boolean keepLocks)
+ throws ClientException;
+
+ /**
+ * Fetch the contents and properties of file <code>path</code> at
+ * <code>revision</code>. <code>revision</code> may be
+ * {@link org.apache.subversion.javahl.types.Revision#SVN_INVALID_REVNUM}
+ * indicating that the HEAD revision should be
+ * used. <code>path</code> is interpreted relative to the
+ * session's URL.
+ * <p>
+
+ * If <code>revision</code> is
+ * {@link org.apache.subversion.javahl.types.Revision#SVN_INVALID_REVNUM}.
+ * returns the actual revision that was retrieved; otherwise
+ * returns <code>revision</code>.
+ * <p>
+ * If <code>contents</code> is not <code>null</code>, push the
+ * contents of the file into the stream.
+ * <p>
+ * If <code>properties</code> is not <code>null</code>, set
+ * <code>properties</code> to contain the properties of the file. This
+ * means <em>all</em> properties: not just ones controlled by the
+ * user and stored in the repository, but immutable ones generated
+ * by the SCM system itself (e.g. 'wcprops', 'entryprops',
+ * etc.). Any existing contents of the <code>properties</code> map
+ * will be discarded by calling {@link java.util.Map#clear()}, if the
+ * map implementation supports that operation.
+ * <p>
+ * The implementations of <code>contents</code> and
+ * <code>properties</code> may not perform any ISVNRemote
+ * operations using this session.
+ * @return The revision of the file that was retreived.
+ * @throws ClientException
+ */
+ long getFile(long revision, String path,
+ OutputStream contents,
+ Map<String, byte[]> properties)
+ throws ClientException;
+
+ /**
+ * Fetch the contents and properties of directory <code>path</code>
+ * at <code>revision</code>. <code>revision</code> may be
+ * {@link org.apache.subversion.javahl.types.Revision#SVN_INVALID_REVNUM},
+ * indicating that the HEAD revision should be
+ * used. <code>path</code> is interpreted relative to the
+ * session's URL.
+ * <p>
+ * If <code>dirents</code> is not <code>null</code>, it will
+ * contain all the entries of the directory; the keys will be the
+ * entry basenames. Any existing contente of the
+ * <code>dirents</code> collection will be discarded by calling
+ * {@link java.util.Map#clear()}, if the collection implementation
+ * supports that operation.
+ * <p>
+ * <code>direntFields</code> controls which portions of the DirEntry
+ * objects are filled in. To have them completely filled in, just pass
+ * DirEntry.Fields.all, othewise pass a bitwise OR of any of the
+ * DirEntry.Fields flags you would like to have.
+ * <p>
+ * If <code>properties</code> is not <code>null</code>, set
+ * <code>properties</code> to contain the properties of the directory.
+ * This means <em>all</em> properties: not just ones controlled by the
+ * user and stored in the repository, but immutable ones generated
+ * by the SCM system itself (e.g. 'wcprops', 'entryprops',
+ * etc.). Any existing contents of the <code>properties</code> map
+ * will be discarded by calling {@link java.util.Map#clear()}, if the
+ * map implementation supports that operation.
+ * <p>
+ * The implementations of <code>dirents</code> and
+ * <code>properties</code> may not perform any ISVNRemote
+ * operations using this session.
+ * @return The revision of the directory that was retreived.
+ * @throws ClientException
+ */
+ long getDirectory(long revision, String path,
+ int direntFields,
+ Map<String, DirEntry> dirents,
+ Map<String, byte[]> properties)
+ throws ClientException;
+
+ /**
+ * Retreive the merginfo for <code>paths</code>, whose elements
+ * are relative to the session's URL. The request will fail if any
+ * one of <code>paths</code> does not exist in the given
+ * <code>revision</code>.
+ * <p>
+ * <b>Note:</b> If the server doesn't support retrieval of
+ * mergeinfo (which can happen even for file:// URLs, if the
+ * repository itself hasn't been upgraded), an unsupported feature
+ * exception is thrown in preference to any other error that might
+ * otherwise be returned.
+ *
+ * @param revision The revision to look for <code>paths</code>
+ * in. Defaults to the youngest revision when
+ * {@link Revision.SVN_INVALID_REVNUM}.
+ * @param inherit Indicates whether explicit, explicit or
+ * inherited, or only inherited mergeinfo for
+ * <code>paths</code> is retrieved.
+ * @param includeDescendants When <code>true</code>, additionally
+ * return the mergeinfo for any descendant of any element
+ * of <code>paths</code> which has the mergeinfo explicitly
+ * set on it. (Note that inheritance is only taken into
+ * account for the elements in <code>paths</code>;
+ * descendants of the elements in <code>paths</code> which
+ * get their mergeinfo via inheritance are not included.)
+ *
+ * @return A dictionary of {@link Mergeinfo} objects for the given
+ * <code>paths</code>, or <code>null</code> if no
+ * mergeinfo is available..
* @throws ClientException
*/
- ISVNEditor getCommitEditor() throws ClientException;
+ Map<String, Mergeinfo> getMergeinfo(Iterable<String> paths, long revision,
+ Mergeinfo.Inheritance inherit,
+ boolean includeDescendants)
+ throws ClientException;
+
+ // TODO: update
+ // TODO: switch
+
+ /**
+ * Ask for a description of the status of a working copy with
+ * respect to <code>revision</code> of the session's repository,
+ * or the HEAD revision if <code>revision</code> is
+ * {@link org.apache.subversion.javahl.types.Revision#SVN_INVALID_REVNUM}.
+ * <p>
+ * The client begins by providing a <code>receiver</code> to
+ * the remote session; this object must contain knowledge of where
+ * the change will begin in the working copy.
+ * <p>
+ * In return, the client receives an {@link ISVNReporter}
+ * instance, which it uses to describe its working copy by making
+ * calls to its methods.
+ * <p>
+ * When finished, the client calls {@link ISVNReporter#finishReport}.
+ * This results in <code>receiver</code> being called once for
+ * every path in the working copy that is different from the
+ * repository. <code>statusTarget</code> is an optional single
+ * path component that restricts the scope of the status report to
+ * an entry in the directory represented by the session's URL, or
+ * empty if the entire directory is meant to be examined.
+ * <p>
+ * Get status as deeply as <code>depth</code> indicates. If
+ * <code>depth</code> is
+ * {@link org.apache.subversion.javahl.types.Depth#unknown},
+ * get the status down to the ambient depth of the working
+ * copy. If <code>depth</code> is deeper than the working copy,
+ * include changes that would be needed to populate the working
+ * copy to that depth.
+ * <p>
+ * The caller may not perform any operations using this session
+ * before finishing the report, and may not perform any operations
+ * using this session from within the implementation of
+ * <code>receiver</code>.
+ * <p>
+ * <b>Note:</b> The reporter provided by this function does
+ * <em>not</em> supply copy-from information to the editor
+ * methods.
+ * <p>
+ * <b>Note:</b> In order to prevent pre-1.5 servers from doing
+ * more work than needed, and sending too much data back, a
+ * pre-1.5 'recurse' directive may be sent to the server, based on
+ * <code>depth</code>.
+ * @throws ClientException
+ */
+ ISVNReporter status(String statusTarget,
+ long revision, Depth depth,
+ RemoteStatus receiver)
+ throws ClientException;
+
+ // TODO: diff
+
+ /**
+ * Invoke <code>callback</code> for each log message from
+ * <code>start</code> to <code>end</code>. <code>start</code> may be greater or less than <code>end</code>;
+ * this just controls whether the log messages are processed in descending
+ * or ascending revision number order.
+ * <p>
+ * If <code>start</code> or <code>end</code> is
+ * {@link org.apache.subversion.javahl.types.Revision#SVN_INVALID_REVNUM},
+ * the HEAD revision is uses for that argument. If eiter is an
+ * invaild non-existent revision, an error will be returned.
+ * <p>
+ * If <code>paths</code> is not <code>null</code> and has one or
+ * more elements, then only show revisions in which at least one
+ * of <code>paths</code> was changed (i.e., if file, text or props
+ * changed; if dir, props changed or an entry was added or
+ * deleted).
+ * <p>
+ * If <code>limit</code> is non-zero only invoke @a receiver on
+ * the first code>limit</code> logs.
+ * <p>
+ * If <code>discoverPath</code> is set, then each call to
+ * <code>callback</code> the list of changed paths in that
+ * revision.
+ * <p>
+ * If <code>strictNodeHistory</code> is set, copy history will not be
+ * traversed (if any exists) when harvesting the revision logs for
+ * each path.
+ * <p>
+ * If <code>includeMergedRevisions</code> is set, log information
+ * for revisions which have been merged to @a targets will also be
+ * returned.
+ * <p>
+ * If <code>revisionProperties</code> is <code>null</code>,
+ * retrieve all revision properties; otherwise, retrieve only the
+ * revision properties contained in the set (i.e. retrieve none if
+ * the set is empty).
+ * <p>
+ * The implementation of <code>callback</code> may not perform any
+ * operations using this session. If the invocation of
+ * <code>callback</code> throws an exception, the operation will
+ * stop.
+ * <p>
+ * <b>Note:</b> If <code>paths</code> is <code>null</code> or
+ * empty, the result depends on the server. Pre-1.5 servers will
+ * send nothing; 1.5 servers will effectively perform the log
+ * operation on the root of the repository. This behavior may be
+ * changed in the future to ensure consistency across all
+ * pedigrees of server.
+ * <p>
+ * <b>Note:</b> Pre-1.5 servers do not support custom revprop
+ * retrieval; <code>revisionProperties</code> is <code>null</code>
+ * or contains a revprop other than svn:author, svn:date, or
+ * svn:log, an not-implemented error is returned.
+ *
+ * @throws ClientException
+ */
+ void getLog(Iterable<String> paths,
+ long startRevision, long endRevision, int limit,
+ boolean strictNodeHistory, boolean discoverPath,
+ boolean includeMergedRevisions,
+ Iterable<String> revisionProperties,
+ LogMessageCallback callback)
+ throws ClientException;
/**
* Return the kind of the node in path at revision.
@@ -168,6 +440,14 @@ public interface ISVNRemote
NodeKind checkPath(String path, long revision)
throws ClientException;
+ // TODO: stat
+ // TODO: getLocations
+ // TODO: getLocationSegments
+ // TODO: getFileRevisions
+ // TODO: lock
+ // TODO: unlock
+ // TODO: getLock
+
/**
* Return a dictionary containing all locks on or below the given path.
* @param path A path relative to the sessionn URL
@@ -180,6 +460,11 @@ public interface ISVNRemote
Map<String, Lock> getLocks(String path, Depth depth)
throws ClientException;
+ // TODO: replayRange
+ // TODO: replay
+ // TODO: getDeletedRevision
+ // TODO: getInheritedProperties
+
/**
* Check if the server associated with this session has
* the given <code>capability</code>.
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/SVNClient.java Tue Jul 9 08:48:43 2013
@@ -573,6 +573,8 @@ public class SVNClient implements ISVNCl
public native void setConfigDirectory(String configDir)
throws ClientException;
+ public native void setConfigEventHandler(ConfigEvent configHandler);
+
public native String getConfigDirectory()
throws ClientException;
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/CommitEditor.java Tue Jul 9 08:48:43 2013
@@ -31,8 +31,8 @@ import org.apache.subversion.javahl.JNIO
import org.apache.subversion.javahl.ClientException;
import java.io.InputStream;
-import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Implementation of ISVNEditor that drives commits.
@@ -40,132 +40,124 @@ import java.util.Map;
*/
public class CommitEditor extends JNIObject implements ISVNEditor
{
- public void dispose() {/* TODO */}
-
- public void addDirectory(String relativePath,
- Iterable<String> children,
- Map<String, byte[]> properties,
- long replacesRevision)
- throws ClientException
- {
- notimplemented("addDirectory");
- }
-
- public void addFile(String relativePath,
- Checksum checksum,
- InputStream contents,
- Map<String, byte[]> properties,
- long replacesRevision)
- throws ClientException
- {
- notimplemented("addFile");
- }
-
- public void addSymlink(String relativePath,
- String target,
- Map<String, byte[]> properties,
- long replacesRevision)
- throws ClientException
- {
- notimplemented("addSymlink");
- }
-
- public void addAbsent(String relativePath,
- NodeKind kind,
- long replacesRevision)
- throws ClientException
- {
- notimplemented("addAbsent");
- }
-
- public void alterDirectory(String relativePath,
- long revision,
- Iterable<String> children,
- Map<String, byte[]> properties)
- throws ClientException
- {
- notimplemented("alterDirectory");
- }
-
- public void alterFile(String relativePath,
- long revision,
- Checksum checksum,
- InputStream contents,
- Map<String, byte[]> properties)
- throws ClientException
- {
- notimplemented("alterFile");
- }
-
- public void alterSymlink(String relativePath,
- long revision,
- String target,
- Map<String, byte[]> properties)
- throws ClientException
+ public void dispose()
{
- notimplemented("alterSymlink");
+ session.disposeEditor(this);
+ nativeDispose();
}
- public void delete(String relativePath,
- long revision)
- throws ClientException
- {
- notimplemented("delete");
- }
+ public native void addDirectory(String relativePath,
+ Iterable<String> children,
+ Map<String, byte[]> properties,
+ long replacesRevision)
+ throws ClientException;
+
+ public native void addFile(String relativePath,
+ Checksum checksum,
+ InputStream contents,
+ Map<String, byte[]> properties,
+ long replacesRevision)
+ throws ClientException;
- public void copy(String sourceRelativePath,
- long sourceRevision,
- String destinationRelativePath,
- long replacesRevision)
- throws ClientException
- {
- notimplemented("copy");
- }
+ /**
+ * <b>Note:</b> Not implemented.
+ */
+ public native void addSymlink(String relativePath,
+ String target,
+ Map<String, byte[]> properties,
+ long replacesRevision)
+ throws ClientException;
+
+ public native void addAbsent(String relativePath,
+ NodeKind kind,
+ long replacesRevision)
+ throws ClientException;
+
+ public native void alterDirectory(String relativePath,
+ long revision,
+ Iterable<String> children,
+ Map<String, byte[]> properties)
+ throws ClientException;
+
+ public native void alterFile(String relativePath,
+ long revision,
+ Checksum checksum,
+ InputStream contents,
+ Map<String, byte[]> properties)
+ throws ClientException;
- public void move(String sourceRelativePath,
- long sourceRevision,
- String destinationRelativePath,
- long replacesRevision)
- throws ClientException
- {
- notimplemented("move");
- }
+ /**
+ * <b>Note:</b> Not implemented.
+ */
+ public native void alterSymlink(String relativePath,
+ long revision,
+ String target,
+ Map<String, byte[]> properties)
+ throws ClientException;
+
+ public native void delete(String relativePath,
+ long revision)
+ throws ClientException;
+
+ public native void copy(String sourceRelativePath,
+ long sourceRevision,
+ String destinationRelativePath,
+ long replacesRevision)
+ throws ClientException;
+
+ public native void move(String sourceRelativePath,
+ long sourceRevision,
+ String destinationRelativePath,
+ long replacesRevision)
+ throws ClientException;
- public void rotate(List<RotatePair> elements) throws ClientException
- {
- notimplemented("rotate");
- }
+// public native void rotate(Iterable<RotatePair> elements)
+// throws ClientException;
- public void complete() throws ClientException
- {
- notimplemented("complete");
- }
+ public native void complete() throws ClientException;
- public void abort() throws ClientException
- {
- notimplemented("abort");
- }
+ public native void abort() throws ClientException;
/**
* This factory method called from RemoteSession.getCommitEditor.
*/
- static final CommitEditor createInstance(RemoteSession owner)
+ static final
+ CommitEditor createInstance(RemoteSession session,
+ Map<String, byte[]> revisionProperties,
+ CommitCallback commitCallback,
+ Set<Lock> lockTokens,
+ boolean keepLocks)
throws ClientException
{
- // FIXME: temporary implementation
- return new CommitEditor(0L);
+ long cppAddr = nativeCreateInstance(session, revisionProperties,
+ commitCallback, lockTokens, keepLocks);
+ return new CommitEditor(cppAddr, session);
}
/**
- * This constructor is called from JNI to get an instance.
+ * This constructor is called from the factory to get an instance.
*/
- protected CommitEditor(long cppAddr)
+ protected CommitEditor(long cppAddr, RemoteSession session)
{
super(cppAddr);
+ this.session = session;
}
- private void notimplemented(String name)
- {
- throw new RuntimeException("Not implemented: " + name);
- }
+ /** Stores a reference to the session that created this editor. */
+ protected RemoteSession session;
+
+ @Override
+ public native void finalize() throws Throwable;
+
+ /*
+ * Wrapped private native implementation declarations.
+ */
+ private native void nativeDispose();
+ private static final native
+ long nativeCreateInstance(RemoteSession session,
+ Map<String, byte[]> revisionProperties,
+ CommitCallback commitCallback,
+ Set<Lock> lockTokens,
+ boolean keepLocks)
+ throws ClientException;
}
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteFactory.java Tue Jul 9 08:48:43 2013
@@ -49,11 +49,13 @@ public class RemoteFactory
* Initializing constructor. Any or all of its arguments may be null.
*/
public RemoteFactory(String configDirectory,
+ ConfigEvent configHandler,
String username, String password,
UserPasswordCallback prompt,
ProgressCallback progress)
{
setConfigDirectory(configDirectory);
+ setConfigEventHandler(configHandler);
setUsername(username);
setPassword(password);
setPrompt(prompt);
@@ -116,26 +118,45 @@ public class RemoteFactory
this.configDirectory = configDirectory;
}
+ /**
+ * Set an event handler that will be called every time the
+ * configuration is loaded.
+ */
+ public void setConfigEventHandler(ConfigEvent configHandler)
+ {
+ this.configHandler = configHandler;
+ }
+
/**
* Open a persistent session to a repository.
+ * <p>
+ * <b>Note:</b> The URL can point to a subtree of the repository.
+ * <p>
+ * <b>Note:</b> The session object inherits the progress callback,
+ * configuration directory and authentication info.
+ *
* @param url The initial session root URL.
* @throws RetryOpenSession If the session URL was redirected
* @throws SubversionException If an URL redirect cycle was detected
* @throws ClientException
- * @note The URL can point to a subtree of the repository.
- * @note The session object inherits the progress callback,
- * configuration directory and authentication info.
*/
public ISVNRemote openRemoteSession(String url)
throws ClientException, SubversionException
{
return open(1, url, null,
- configDirectory, username, password, prompt, progress);
+ configDirectory, configHandler,
+ username, password, prompt, progress);
}
/**
* Open a persistent session to a repository.
+ * <p>
+ * <b>Note:</b> The URL can point to a subtree of the repository.
+ * <p>
+ * <b>Note:</b> The session object inherits the progress callback,
+ * configuration directory and authentication info.
+ *
* @param url The initial session root URL.
* @param retryAttempts The number of times to retry the operation
* if the given URL is redirected.
@@ -144,9 +165,6 @@ public class RemoteFactory
* @throws RetryOpenSession If the session URL was redirected
* @throws SubversionException If an URL redirect cycle was detected
* @throws ClientException
- * @note The URL can point to a subtree of the repository.
- * @note The session object inherits the progress callback,
- * configuration directory and authentication info.
*/
public ISVNRemote openRemoteSession(String url, int retryAttempts)
throws ClientException, SubversionException
@@ -155,21 +173,27 @@ public class RemoteFactory
throw new IllegalArgumentException(
"retryAttempts must be positive");
return open(retryAttempts, url, null,
- configDirectory, username, password, prompt, progress);
+ configDirectory, configHandler,
+ username, password, prompt, progress);
}
/**
* Open a persistent session to a repository.
+ * <p>
+ * <b>Note:</b> The URL can point to a subtree of the repository.
+ * <p>
+ * <b>Note:</b> If the UUID does not match the repository,
+ * this function fails.
+ * <p>
+ * <b>Note:</b> The session object inherits the progress callback,
+ * configuration directory and authentication info.
+ *
* @param url The initial session root URL.
* @param reposUUID The expected repository UUID; may not be null..
* @throws IllegalArgumentException If <code>reposUUID</code> is null.
* @throws RetryOpenSession If the session URL was redirected
* @throws SubversionException If an URL redirect cycle was detected
* @throws ClientException
- * @note The URL can point to a subtree of the repository.
- * @note If the UUID does not match the repository, this function fails.
- * @note The session object inherits the progress callback,
- * configuration directory and authentication info.
*/
public ISVNRemote openRemoteSession(String url, String reposUUID)
throws ClientException, SubversionException
@@ -177,11 +201,21 @@ public class RemoteFactory
if (reposUUID == null)
throw new IllegalArgumentException("reposUUID may not be null");
return open(1, url, reposUUID,
- configDirectory, username, password, prompt, progress);
+ configDirectory, configHandler,
+ username, password, prompt, progress);
}
/**
* Open a persistent session to a repository.
+ * <p>
+ * <b>Note:</b> The URL can point to a subtree of the repository.
+ * <p>
+ * <b>Note:</b> If the UUID does not match the repository,
+ * this function fails.
+ * <p>
+ * <b>Note:</b> The session object inherits the progress callback,
+ * configuration directory and authentication info.
+ *
* @param url The initial session root URL.
* @param reposUUID The expected repository UUID; may not be null..
* @param retryAttempts The number of times to retry the operation
@@ -191,10 +225,6 @@ public class RemoteFactory
* @throws RetryOpenSession If the session URL was redirected
* @throws SubversionException If an URL redirect cycle was detected
* @throws ClientException
- * @note The URL can point to a subtree of the repository.
- * @note If the UUID does not match the repository, this function fails.
- * @note The session object inherits the progress callback,
- * configuration directory and authentication info.
*/
public ISVNRemote openRemoteSession(String url, String reposUUID,
int retryAttempts)
@@ -206,10 +236,12 @@ public class RemoteFactory
throw new IllegalArgumentException(
"retryAttempts must be positive");
return open(retryAttempts, url, reposUUID,
- configDirectory, username, password, prompt, progress);
+ configDirectory, configHandler,
+ username, password, prompt, progress);
}
private String configDirectory;
+ private ConfigEvent configHandler;
private String username;
private String password;
private UserPasswordCallback prompt;
@@ -219,6 +251,7 @@ public class RemoteFactory
private static native ISVNRemote open(int retryAttempts,
String url, String reposUUID,
String configDirectory,
+ ConfigEvent configHandler,
String username, String password,
UserPasswordCallback prompt,
ProgressCallback progress)
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/remote/RemoteSession.java Tue Jul 9 08:48:43 2013
@@ -28,14 +28,16 @@ import org.apache.subversion.javahl.call
import org.apache.subversion.javahl.ISVNRemote;
import org.apache.subversion.javahl.ISVNEditor;
+import org.apache.subversion.javahl.ISVNReporter;
import org.apache.subversion.javahl.JNIObject;
import org.apache.subversion.javahl.OperationContext;
import org.apache.subversion.javahl.ClientException;
import java.lang.ref.WeakReference;
-import java.util.HashSet;
import java.util.Date;
import java.util.Map;
+import java.util.Set;
+import java.io.OutputStream;
import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
@@ -44,17 +46,27 @@ public class RemoteSession extends JNIOb
{
public void dispose()
{
- if (editors != null)
+ if (editorReference != null)
{
- // Deactivate all open editors
- for (WeakReference<ISVNEditor> ref : editors)
+ // Deactivate the open editor
+ ISVNEditor ed = editorReference.get();
+ if (ed != null)
{
- ISVNEditor ed = ref.get();
- if (ed == null)
- continue;
ed.dispose();
- ref.clear();
+ editorReference.clear();
}
+ editorReference = null;
+ }
+ if (reporterReference != null)
+ {
+ // Deactivate the open reporter
+ ISVNReporter rp = reporterReference.get();
+ if (rp != null)
+ {
+ rp.dispose();
+ reporterReference.clear();
+ }
+ reporterReference = null;
}
nativeDispose();
}
@@ -107,21 +119,100 @@ public class RemoteSession extends JNIOb
public native byte[] getRevisionProperty(long revision, String propertyName)
throws ClientException;
- public ISVNEditor getCommitEditor() throws ClientException
+ public ISVNEditor getCommitEditor(Map<String, byte[]> revisionProperties,
+ CommitCallback commitCallback,
+ Set<Lock> lockTokens,
+ boolean keepLocks)
+ throws ClientException
{
- ISVNEditor ed = CommitEditor.createInstance(this);
- if (editors == null)
- editors = new HashSet<WeakReference<ISVNEditor>>();
- editors.add(new WeakReference<ISVNEditor>(ed));
+ check_inactive(editorReference, reporterReference);
+ ISVNEditor ed =
+ CommitEditor.createInstance(this, revisionProperties,
+ commitCallback, lockTokens, keepLocks);
+ if (editorReference != null)
+ editorReference.clear();
+ editorReference = new WeakReference<ISVNEditor>(ed);
return ed;
}
+ public long getFile(long revision, String path,
+ OutputStream contents,
+ Map<String, byte[]> properties)
+ throws ClientException
+ {
+ maybe_clear(properties);
+ return nativeGetFile(revision, path, contents, properties);
+ }
+
+ public long getDirectory(long revision, String path,
+ int direntFields,
+ Map<String, DirEntry> dirents,
+ Map<String, byte[]> properties)
+ throws ClientException
+ {
+ maybe_clear(dirents);
+ maybe_clear(properties);
+ return nativeGetDirectory(revision, path,
+ direntFields, dirents, properties);
+ }
+
+ public native Map<String, Mergeinfo>
+ getMergeinfo(Iterable<String> paths, long revision,
+ Mergeinfo.Inheritance inherit,
+ boolean includeDescendants)
+ throws ClientException;
+
+ // TODO: update
+ // TODO: switch
+
+ public ISVNReporter status(String statusTarget,
+ long revision, Depth depth,
+ RemoteStatus receiver)
+ throws ClientException
+ {
+ check_inactive(editorReference, reporterReference);
+ StateReporter rp = StateReporter.createInstance(this);
+
+ // At this point, the reporter is not active/valid.
+ StatusEditor editor = new StatusEditor(receiver);
+ nativeStatus(statusTarget, revision, depth, editor, rp);
+ // Now it should be valid.
+
+ if (reporterReference != null)
+ reporterReference.clear();
+ reporterReference = new WeakReference<ISVNReporter>(rp);
+ return rp;
+ }
+
+ // TODO: diff
+
+ public native void getLog(Iterable<String> paths,
+ long startRevision, long endRevision, int limit,
+ boolean strictNodeHistory, boolean discoverPath,
+ boolean includeMergedRevisions,
+ Iterable<String> revisionProperties,
+ LogMessageCallback callback)
+ throws ClientException;
+
public native NodeKind checkPath(String path, long revision)
throws ClientException;
+ // TODO: stat
+ // TODO: getLocations
+ // TODO: getLocationSegments
+ // TODO: getFileRevisions
+ // TODO: lock
+ // TODO: unlock
+ // TODO: getLock
+
public native Map<String, Lock> getLocks(String path, Depth depth)
throws ClientException;
+ // TODO: replayRange
+ // TODO: replay
+ // TODO: getDeletedRevision
+ // TODO: getInheritedProperties
+
public boolean hasCapability(Capability capability)
throws ClientException
{
@@ -148,6 +239,20 @@ public class RemoteSession extends JNIOb
byte[] oldValue,
byte[] newValue)
throws ClientException;
+ private native long nativeGetFile(long revision, String path,
+ OutputStream contents,
+ Map<String, byte[]> properties)
+ throws ClientException;
+ private native long nativeGetDirectory(long revision, String path,
+ int direntFields,
+ Map<String, DirEntry> dirents,
+ Map<String, byte[]> properties)
+ throws ClientException;
+ private native void nativeStatus(String statusTarget,
+ long revision, Depth depth,
+ ISVNEditor statusEditor,
+ ISVNReporter reporter)
+ throws ClientException;
private native boolean nativeHasCapability(String capability)
throws ClientException;
@@ -158,8 +263,75 @@ public class RemoteSession extends JNIOb
private class RemoteSessionContext extends OperationContext {}
/*
- * The set of open editors. We need this in order to dispose/abort
- * the editors when the session is disposed.
+ * A reference to the current active editor. We need this in order
+ * to dispose/abort the editor when the session is disposed. And
+ * furthermore, there can be only one editor or reporter active at
+ * any time.
*/
- private HashSet<WeakReference<ISVNEditor>> editors;
+ private WeakReference<ISVNEditor> editorReference;
+
+ /*
+ * The commit editor calls this when disposed to clear the
+ * reference. Note that this function will be called during our
+ * dispose, so make sure they don't step on each others' toes.
+ */
+ void disposeEditor(ISVNEditor editor)
+ {
+ if (editorReference == null)
+ return;
+ ISVNEditor ed = editorReference.get();
+ if (ed == null)
+ return;
+ if (ed != editor)
+ throw new IllegalStateException("Disposing unknown editor");
+ editorReference.clear();
+ }
+
+ /*
+ * A reference to the current active reporter. We need this in
+ * order to dispose/abort the report when the session is
+ * disposed. And furthermore, there can be only one reporter or
+ * editor active at any time.
+ */
+ private WeakReference<ISVNReporter> reporterReference;
+
+ /*
+ * The update reporter calls this when disposed to clear the
+ * reference. Note that this function will be called during our
+ * dispose, so make sure they don't step on each others' toes.
+ */
+ void disposeReporter(ISVNReporter reporter)
+ {
+ if (reporterReference == null)
+ return;
+ ISVNReporter rp = reporterReference.get();
+ if (rp == null)
+ return;
+ if (rp != reporter)
+ throw new IllegalStateException("Disposing unknown reporter");
+ reporterReference.clear();
+ }
+
+ /*
+ * Private helper methods.
+ */
+ private final static void maybe_clear(Map clearable)
+ {
+ if (clearable != null && !clearable.isEmpty())
+ try {
+ clearable.clear();
+ } catch (UnsupportedOperationException ex) {
+ // ignored
+ }
+ }
+
+ private final static
+ void check_inactive(WeakReference<ISVNEditor> editorReference,
+ WeakReference<ISVNReporter> reporterReference)
+ {
+ if (editorReference != null && editorReference.get() != null)
+ throw new IllegalStateException("An editor is already active");
+ if (reporterReference != null && reporterReference.get() != null)
+ throw new IllegalStateException("A reporter is already active");
+ }
}
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Mergeinfo.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Mergeinfo.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Mergeinfo.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/Mergeinfo.java Tue Jul 9 08:48:43 2013
@@ -69,6 +69,31 @@ public class Mergeinfo implements java.i
}
/**
+ * The three ways to request mergeinfo affecting a given path
+ * in {@link org.apache.subversion.javahl.ISVNRemote#getMergeinfo}.
+ * @since 1.9
+ */
+ public static enum Inheritance
+ {
+ /** Explicit mergeinfo only. */
+ explicit,
+
+ /**
+ * Explicit mergeinfo, or if that doesn't exist, the inherited
+ * mergeinfo from a target's nearest (path-wise, not history-wise)
+ * ancestor.
+ */
+ inherited,
+
+ /**
+ * Mergeinfo inherited from a target's nearest (path-wise,
+ * not history-wise) ancestor, regardless of whether target
+ * has explicit mergeinfo.
+ */
+ nearest_ancestor;
+ }
+
+ /**
* Add one or more RevisionRange objects to merge info. If the
* merge source is already stored, the list of revisions is
* replaced.
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RevisionRange.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RevisionRange.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RevisionRange.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/RevisionRange.java Tue Jul 9 08:48:43 2013
@@ -36,25 +36,35 @@ public class RevisionRange implements Co
// http://java.sun.com/j2se/1.4/pdf/serial-spec.pdf
// http://java.sun.com/j2se/1.5.0/docs/guide/serialization/spec/version.html#6678
// http://java.sun.com/javase/6/docs/platform/serialization/spec/version.html#6678
- private static final long serialVersionUID = 1L;
+ private static final long serialVersionUID = 2L;
private Revision from;
private Revision to;
+ private boolean inheritable;
/**
* Creates a new instance. Called by native library.
*/
- @SuppressWarnings("unused")
- private RevisionRange(long from, long to)
+ protected RevisionRange(long from, long to, boolean inheritable)
{
this.from = Revision.getInstance(from);
this.to = Revision.getInstance(to);
+ this.inheritable = inheritable;
+ }
+
+ /** @since 1.9 */
+ public RevisionRange(Revision from, Revision to, boolean inheritable)
+ {
+ this.from = from;
+ this.to = to;
+ this.inheritable = inheritable;
}
public RevisionRange(Revision from, Revision to)
{
this.from = from;
this.to = to;
+ this.inheritable = true;
}
/**
@@ -70,6 +80,11 @@ public class RevisionRange implements Co
return;
}
+ this.inheritable = !revisionElement.endsWith("*");
+ if (!this.inheritable)
+ revisionElement =
+ revisionElement.substring(0, revisionElement.length() - 1);
+
int hyphen = revisionElement.indexOf('-');
if (hyphen > 0)
{
@@ -113,14 +128,20 @@ public class RevisionRange implements Co
return to;
}
+ public boolean isInheritable()
+ {
+ return inheritable;
+ }
+
public String toString()
{
if (from != null && to != null)
{
- if (from.equals(to))
- return from.toString();
- else
- return from.toString() + '-' + to.toString();
+ String rep = (from.equals(to) ? from.toString()
+ : from.toString() + '-' + to.toString());
+ if (!inheritable)
+ return rep + '*';
+ return rep;
}
return super.toString();
}
@@ -138,7 +159,7 @@ public class RevisionRange implements Co
public int hashCode()
{
final int prime = 31;
- int result = 1;
+ int result = (inheritable ? 1 : 2);
result = prime * result + ((from == null) ? 0 : from.hashCode());
result = prime * result + ((to == null) ? 0 : to.hashCode());
return result;
@@ -178,10 +199,12 @@ public class RevisionRange implements Co
return false;
}
- return true;
+ return (inheritable == other.inheritable);
}
/**
+ * <b>Note:</b> Explicitly ignores inheritable state.
+ *
* @param range The RevisionRange to compare this object to.
*/
public int compareTo(RevisionRange range)
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/VersionExtended.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/VersionExtended.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/VersionExtended.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/src/org/apache/subversion/javahl/types/VersionExtended.java Tue Jul 9 08:48:43 2013
@@ -45,9 +45,9 @@ public class VersionExtended
/**
* @return The canonical host triplet (arch-vendor-osname) of the
* system where libsvn_subr was compiled.
- *
- * @note On Unix-like systems (includng Mac OS X), this string is
- * the same as the output of the config.guess script for the
+ * <p>
+ * <b>Note:</b> On Unix-like systems (includng Mac OS X), this string
+ * is the same as the output of the config.guess script for the
* underlying Subversion libraries.
*/
public native String getBuildHost();
@@ -60,8 +60,8 @@ public class VersionExtended
/**
* @return The canonical host triplet (arch-vendor-osname) of the
* system where the current process is running.
- *
- * @note This string may not be the same as the output of
+ * <p>
+ * <b>Note:</b> This string may not be the same as the output of
* config.guess on the same system.
*/
public native String getRuntimeHost();
@@ -139,8 +139,8 @@ public class VersionExtended
/**
* @return Iterator for an immutable internal list of #LoadedLib
* describing loaded shared libraries. The the list may be empty.
- *
- * @note On Mac OS X, the loaded frameworks, private frameworks
+ * <p>
+ * <b>Note:</b> On Mac OS X, the loaded frameworks, private frameworks
* and system libraries will not be listed.
*/
public java.util.Iterator<LoadedLib> getLoadedLibs()
@@ -178,7 +178,8 @@ public class VersionExtended
/**
* Implementation of java.util.Iterator#remove().
- * @note Not implemented, all sequences are immutable.
+ * <p>
+ * <b>Note:</b> Not implemented, all sequences are immutable.
*/
public void remove()
{
@@ -219,7 +220,8 @@ public class VersionExtended
/**
* Implementation of java.util.Iterator#remove().
- * @note Not implemented, all sequences are immutable.
+ * <p>
+ * <b>Note:</b> Not implemented, all sequences are immutable.
*/
public void remove()
{
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/BasicTests.java Tue Jul 9 08:48:43 2013
@@ -251,16 +251,17 @@ public class BasicTests extends SVNTests
public void testMergeinfoParser() throws Throwable
{
String mergeInfoPropertyValue =
- "/trunk:1-300,305,307,400-405\n/branches/branch:308-400";
+ "/trunk:1-300,305*,307,400-405*\n" +
+ "/branches/branch:308-400";
Mergeinfo info = new Mergeinfo(mergeInfoPropertyValue);
Set<String> paths = info.getPaths();
assertEquals(2, paths.size());
List<RevisionRange> trunkRange = info.getRevisionRange("/trunk");
assertEquals(4, trunkRange.size());
assertEquals("1-300", trunkRange.get(0).toString());
- assertEquals("305", trunkRange.get(1).toString());
+ assertEquals("305*", trunkRange.get(1).toString());
assertEquals("307", trunkRange.get(2).toString());
- assertEquals("400-405", trunkRange.get(3).toString());
+ assertEquals("400-405*", trunkRange.get(3).toString());
List<RevisionRange> branchRange =
info.getRevisionRange("/branches/branch");
assertEquals(1, branchRange.size());
@@ -3299,6 +3300,16 @@ public class BasicTests extends SVNTests
}
}
+ private static class CountingProgressListener implements ProgressCallback
+ {
+ public void onProgress(ProgressEvent event)
+ {
+ // TODO: Examine the byte counts from "event".
+ gotProgress = true;
+ }
+ public boolean gotProgress = false;
+ }
+
public void testDataTransferProgressReport() throws Throwable
{
// ### FIXME: This isn't working over ra_local, because
@@ -3308,25 +3319,13 @@ public class BasicTests extends SVNTests
// build the test setup
OneTest thisTest = new OneTest();
- ProgressCallback listener = new ProgressCallback()
- {
- public void onProgress(ProgressEvent event)
- {
- // TODO: Examine the byte counts from "event".
- throw new RuntimeException("Progress reported as expected");
- }
- };
+ CountingProgressListener listener = new CountingProgressListener();
client.setProgressCallback(listener);
// Perform an update to exercise the progress notification.
- try
- {
- update(thisTest);
+ update(thisTest);
+ if (!listener.gotProgress)
fail("No progress reported");
- }
- catch (RuntimeException progressReported)
- {
- }
}
/**
Modified: subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java
URL: http://svn.apache.org/viewvc/subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java?rev=1501136&r1=1501135&r2=1501136&view=diff
==============================================================================
--- subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java (original)
+++ subversion/branches/verify-keep-going/subversion/bindings/javahl/tests/org/apache/subversion/javahl/SVNRemoteTests.java Tue Jul 9 08:48:43 2013
@@ -24,15 +24,24 @@ package org.apache.subversion.javahl;
import org.apache.subversion.javahl.*;
import org.apache.subversion.javahl.remote.*;
+import org.apache.subversion.javahl.callback.*;
import org.apache.subversion.javahl.types.*;
import java.util.Arrays;
+import java.util.ArrayList;
+import java.util.Collections;
import java.util.Date;
+import java.util.List;
import java.util.Set;
import java.util.Map;
+import java.util.HashMap;
import java.util.HashSet;
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.nio.charset.Charset;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
/**
* This class is used for testing the SVNReposAccess class
@@ -59,12 +68,14 @@ public class SVNRemoteTests extends SVNT
thisTest = new OneTest();
}
- public static ISVNRemote getSession(String url, String configDirectory)
+ public static ISVNRemote getSession(String url, String configDirectory,
+ ConfigEvent configHandler)
{
try
{
RemoteFactory factory = new RemoteFactory();
factory.setConfigDirectory(configDirectory);
+ factory.setConfigEventHandler(configHandler);
factory.setUsername(USERNAME);
factory.setPassword(PASSWORD);
factory.setPrompt(new DefaultPromptUserPassword());
@@ -81,7 +92,7 @@ public class SVNRemoteTests extends SVNT
private ISVNRemote getSession()
{
- return getSession(getTestRepoUrl(), super.conf.getAbsolutePath());
+ return getSession(getTestRepoUrl(), super.conf.getAbsolutePath(), null);
}
/**
@@ -100,7 +111,7 @@ public class SVNRemoteTests extends SVNT
try
{
session = new RemoteFactory(
- super.conf.getAbsolutePath(),
+ super.conf.getAbsolutePath(), null,
USERNAME, PASSWORD,
new DefaultPromptUserPassword(), null)
.openRemoteSession(getTestRepoUrl());
@@ -235,13 +246,13 @@ public class SVNRemoteTests extends SVNT
public void testGetCommitEditor() throws Exception
{
ISVNRemote session = getSession();
- session.getCommitEditor();
+ session.getCommitEditor(null, null, null, false);
}
public void testDisposeCommitEditor() throws Exception
{
ISVNRemote session = getSession();
- session.getCommitEditor();
+ session.getCommitEditor(null, null, null, false);
session.dispose();
}
@@ -288,7 +299,8 @@ public class SVNRemoteTests extends SVNT
if (!atomic)
return;
- byte[] oldValue = USERNAME.getBytes(UTF8);
+ byte[] oldValue = client.revProperty(getTestRepoUrl(), "svn:author",
+ Revision.getInstance(1));
byte[] newValue = "rayjandom".getBytes(UTF8);
try
{
@@ -297,16 +309,26 @@ public class SVNRemoteTests extends SVNT
}
catch (ClientException ex)
{
- String msg = ex.getMessage();
- int index = msg.indexOf('\n');
- assertEquals(msg.substring(0, index),
- "Disabled repository feature");
+ ClientException.ErrorMessage error = null;
+ for (ClientException.ErrorMessage m : ex.getAllMessages())
+ if (!m.isGeneric()) {
+ error = m;
+ break;
+ }
+
+ if (error == null)
+ fail("Failed with no error message");
+
+ if (error.getCode() != 175002 && // SVN_ERR_RA_DAV_REQUEST_FAILED
+ error.getCode() != 165006) // SVN_ERR_REPOS_DISABLED_FEATURE
+ fail(error.getMessage());
+
return;
}
byte[] check = client.revProperty(getTestRepoUrl(), "svn:author",
Revision.getInstance(1));
- assertTrue(Arrays.equals(newValue, check));
+ assertTrue(Arrays.equals(check, newValue));
}
public void testGetRevpropList() throws Exception
@@ -327,4 +349,738 @@ public class SVNRemoteTests extends SVNT
byte[] propval = session.getRevisionProperty(1, "svn:author");
assertTrue(Arrays.equals(propval, USERNAME.getBytes(UTF8)));
}
+
+ public void testGetFile() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ ByteArrayOutputStream contents = new ByteArrayOutputStream();
+ HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
+ properties.put("fakename", "fakecontents".getBytes(UTF8));
+ long fetched_rev =
+ session.getFile(Revision.SVN_INVALID_REVNUM, "A/B/lambda",
+ contents, properties);
+ assertEquals(fetched_rev, 1);
+ assertEquals(contents.toString("UTF-8"),
+ "This is the file 'lambda'.");
+ for (Map.Entry<String, byte[]> e : properties.entrySet()) {
+ final String key = e.getKey();
+ assertTrue(key.startsWith("svn:entry:")
+ || key.startsWith("svn:wc:"));
+ }
+ }
+
+ public void testGetDirectory() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ HashMap<String, DirEntry> dirents = new HashMap<String, DirEntry>();
+ dirents.put("E", null);
+ dirents.put("F", null);
+ dirents.put("lambda", null);
+ HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
+ properties.put("fakename", "fakecontents".getBytes(UTF8));
+ long fetched_rev =
+ session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/B",
+ DirEntry.Fields.all, dirents, properties);
+ assertEquals(fetched_rev, 1);
+ assertEquals(dirents.get("E").getPath(), "E");
+ assertEquals(dirents.get("F").getPath(), "F");
+ assertEquals(dirents.get("lambda").getPath(), "lambda");
+ for (Map.Entry<String, byte[]> e : properties.entrySet()) {
+ final String key = e.getKey();
+ assertTrue(key.startsWith("svn:entry:")
+ || key.startsWith("svn:wc:"));
+ }
+ }
+
+ private final class CommitContext implements CommitCallback
+ {
+ public final ISVNEditor editor;
+ public CommitContext(ISVNRemote session, String logstr)
+ throws ClientException
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ byte[] log = (logstr == null
+ ? new byte[0]
+ : logstr.getBytes(UTF8));
+ HashMap<String, byte[]> revprops = new HashMap<String, byte[]>();
+ revprops.put("svn:log", log);
+ editor = session.getCommitEditor(revprops, this, null, false);
+ }
+
+ public void commitInfo(CommitInfo info) { this.info = info; }
+ public long getRevision() { return info.getRevision(); }
+
+ private CommitInfo info;
+ }
+
+ public void testEditorCopy() throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc =
+ new CommitContext(session, "Copy A/B/lambda -> A/B/omega");
+
+ try {
+ // FIXME: alter dir A/B first
+ cc.editor.copy("A/B/lambda", 1, "A/B/omega",
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.file,
+ session.checkPath("A/B/lambda",
+ Revision.SVN_INVALID_REVNUM));
+ assertEquals(NodeKind.file,
+ session.checkPath("A/B/omega",
+ Revision.SVN_INVALID_REVNUM));
+ }
+
+ public void testEditorMove() throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc =
+ new CommitContext(session, "Move A/B/lambda -> A/B/omega");
+
+ try {
+ // FIXME: alter dir A/B first
+ cc.editor.move("A/B/lambda", 1, "A/B/omega",
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.none,
+ session.checkPath("A/B/lambda",
+ Revision.SVN_INVALID_REVNUM));
+ assertEquals(NodeKind.file,
+ session.checkPath("A/B/omega",
+ Revision.SVN_INVALID_REVNUM));
+ }
+
+ public void testEditorDelete() throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc =
+ new CommitContext(session, "Delete all greek files");
+
+ String[] filePaths = { "iota",
+ "A/mu",
+ "A/B/lambda",
+ "A/B/E/alpha",
+ "A/B/E/beta",
+ "A/D/gamma",
+ "A/D/G/pi",
+ "A/D/G/rho",
+ "A/D/G/tau",
+ "A/D/H/chi",
+ "A/D/H/omega",
+ "A/D/H/psi" };
+
+ try {
+ // FIXME: alter a bunch of dirs first
+ for (String path : filePaths)
+ cc.editor.delete(path, 1);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ for (String path : filePaths)
+ assertEquals(NodeKind.none,
+ session.checkPath(path, Revision.SVN_INVALID_REVNUM));
+ }
+
+ public void testEditorMkdir() throws Exception
+ {
+ ISVNRemote session = getSession();
+ CommitContext cc = new CommitContext(session, "Make hebrew dir");
+
+ try {
+ // FIXME: alter dir . first
+ cc.editor.addDirectory("ALEPH",
+ new ArrayList<String>(),
+ new HashMap<String, byte[]>(),
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.dir,
+ session.checkPath("ALEPH",
+ Revision.SVN_INVALID_REVNUM));
+ }
+
+ public void testEditorSetDirProps() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ byte[] ignoreval = "*.pyc\n.gitignore\n".getBytes(UTF8);
+ HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+ props.put("svn:ignore", ignoreval);
+
+ CommitContext cc = new CommitContext(session, "Add svn:ignore");
+ try {
+ cc.editor.alterDirectory("", 1, null, props);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertTrue(Arrays.equals(ignoreval,
+ client.propertyGet(session.getSessionUrl(),
+ "svn:ignore",
+ Revision.HEAD,
+ Revision.HEAD)));
+ }
+
+ private static byte[] SHA1(byte[] text) throws NoSuchAlgorithmException
+ {
+ MessageDigest md = MessageDigest.getInstance("SHA-1");
+ return md.digest(text);
+ }
+
+ public void testEditorAddFile() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ byte[] eolstyle = "native".getBytes(UTF8);
+ HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+ props.put("svn:eol-style", eolstyle);
+
+ byte[] contents = "This is file 'xi'.".getBytes(UTF8);
+ Checksum hash = new Checksum(SHA1(contents), Checksum.Kind.SHA1);
+ ByteArrayInputStream stream = new ByteArrayInputStream(contents);
+
+ CommitContext cc = new CommitContext(session, "Add A/xi");
+ try {
+ // FIXME: alter dir A first
+ cc.editor.addFile("A/xi", hash, stream, props,
+ Revision.SVN_INVALID_REVNUM);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ assertEquals(NodeKind.file,
+ session.checkPath("A/xi",
+ Revision.SVN_INVALID_REVNUM));
+
+ byte[] propval = client.propertyGet(session.getSessionUrl() + "/A/xi",
+ "svn:eol-style",
+ Revision.HEAD,
+ Revision.HEAD);
+ assertTrue(Arrays.equals(eolstyle, propval));
+ }
+
+ public void testEditorSetFileContents() throws Exception
+ {
+ Charset UTF8 = Charset.forName("UTF-8");
+ ISVNRemote session = getSession();
+
+ byte[] contents = "This is modified file 'alpha'.".getBytes(UTF8);
+ Checksum hash = new Checksum(SHA1(contents), Checksum.Kind.SHA1);
+ ByteArrayInputStream stream = new ByteArrayInputStream(contents);
+
+ CommitContext cc =
+ new CommitContext(session, "Change contents of A/B/E/alpha");
+ try {
+ cc.editor.alterFile("A/B/E/alpha", 1, hash, stream, null);
+ cc.editor.complete();
+ } finally {
+ cc.editor.dispose();
+ }
+
+ assertEquals(2, cc.getRevision());
+ assertEquals(2, session.getLatestRevision());
+ ByteArrayOutputStream checkcontents = new ByteArrayOutputStream();
+ client.streamFileContent(session.getSessionUrl() + "/A/B/E/alpha",
+ Revision.HEAD, Revision.HEAD, checkcontents);
+ assertTrue(Arrays.equals(contents, checkcontents.toByteArray()));
+ }
+
+ // public void testEditorRotate() throws Exception
+ // {
+ // ISVNRemote session = getSession();
+ //
+ // ArrayList<ISVNEditor.RotatePair> rotation =
+ // new ArrayList<ISVNEditor.RotatePair>(3);
+ // rotation.add(new ISVNEditor.RotatePair("A/B", 1));
+ // rotation.add(new ISVNEditor.RotatePair("A/C", 1));
+ // rotation.add(new ISVNEditor.RotatePair("A/D", 1));
+ //
+ // CommitContext cc =
+ // new CommitContext(session, "Rotate A/B -> A/C -> A/D");
+ // try {
+ // // No alter-dir of A is needed, children remain the same.
+ // cc.editor.rotate(rotation);
+ // cc.editor.complete();
+ // } finally {
+ // cc.editor.dispose();
+ // }
+ //
+ // assertEquals(2, cc.getRevision());
+ // assertEquals(2, session.getLatestRevision());
+ //
+ // HashMap<String, DirEntry> dirents = new HashMap<String, DirEntry>();
+ // HashMap<String, byte[]> properties = new HashMap<String, byte[]>();
+ //
+ // // A/B is now what used to be A/D, so A/B/H must exist
+ // session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/B",
+ // DirEntry.Fields.all, dirents, properties);
+ // assertEquals(dirents.get("H").getPath(), "H");
+ //
+ // // A/C is now what used to be A/B, so A/C/F must exist
+ // session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/C",
+ // DirEntry.Fields.all, dirents, properties);
+ // assertEquals(dirents.get("F").getPath(), "F");
+ //
+ // // A/D is now what used to be A/C and must be empty
+ // session.getDirectory(Revision.SVN_INVALID_REVNUM, "A/D",
+ // DirEntry.Fields.all, dirents, properties);
+ // assertTrue(dirents.isEmpty());
+ // }
+
+ // Sanity check so that we don't forget about unimplemented methods.
+ public void testEditorNotImplemented() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ HashMap<String, byte[]> props = new HashMap<String, byte[]>();
+ // ArrayList<ISVNEditor.RotatePair> rotation =
+ // new ArrayList<ISVNEditor.RotatePair>();
+
+ CommitContext cc = new CommitContext(session, "not implemented");
+ try {
+ String exmsg;
+
+ try {
+ exmsg = "";
+ cc.editor.addSymlink("", "", props, 1);
+ } catch (RuntimeException ex) {
+ exmsg = ex.getMessage();
+ }
+ assertEquals("Not implemented: CommitEditor.addSymlink", exmsg);
+
+ try {
+ exmsg = "";
+ cc.editor.alterSymlink("", 1, "", null);
+ } catch (RuntimeException ex) {
+ exmsg = ex.getMessage();
+ }
+ assertEquals("Not implemented: CommitEditor.alterSymlink", exmsg);
+
+ // try {
+ // exmsg = "";
+ // cc.editor.rotate(rotation);
+ // } catch (RuntimeException ex) {
+ // exmsg = ex.getMessage();
+ // }
+ // assertEquals("Not implemented: CommitEditor.rotate", exmsg);
+ } finally {
+ cc.editor.dispose();
+ }
+ }
+
+ private static final class LogMsg
+ {
+ public Set<ChangePath> changedPaths;
+ public long revision;
+ public Map<String, byte[]> revprops;
+ public boolean hasChildren;
+ }
+
+ private static final class LogReceiver implements LogMessageCallback
+ {
+ public final ArrayList<LogMsg> logs = new ArrayList<LogMsg>();
+
+ public void singleMessage(Set<ChangePath> changedPaths,
+ long revision,
+ Map<String, byte[]> revprops,
+ boolean hasChildren)
+ {
+ LogMsg msg = new LogMsg();
+ msg.changedPaths = changedPaths;
+ msg.revision = revision;
+ msg.revprops = revprops;
+ msg.hasChildren = hasChildren;
+ logs.add(msg);
+ }
+ }
+
+ public void testGetLog() throws Exception
+ {
+ ISVNRemote session = getSession();
+ LogReceiver receiver = new LogReceiver();
+
+ session.getLog(null,
+ Revision.SVN_INVALID_REVNUM,
+ Revision.SVN_INVALID_REVNUM,
+ 0, false, false, false, null,
+ receiver);
+
+ assertEquals(1, receiver.logs.size());
+ }
+
+ public void testGetLogMissing() throws Exception
+ {
+ ISVNRemote session = getSession();
+ LogReceiver receiver = new LogReceiver();
+
+ ArrayList<String> paths = new ArrayList<String>(1);
+ paths.add("X");
+
+ boolean exception = false;
+ try {
+ session.getLog(paths,
+ Revision.SVN_INVALID_REVNUM,
+ Revision.SVN_INVALID_REVNUM,
+ 0, false, false, false, null,
+ receiver);
+ } catch (ClientException ex) {
+ assertEquals("Filesystem has no item",
+ ex.getAllMessages().get(0).getMessage());
+ exception = true;
+ }
+
+ assertEquals(0, receiver.logs.size());
+ assertTrue(exception);
+ }
+
+ public void testConfigHandler() throws Exception
+ {
+ ConfigEvent handler = new ConfigEvent()
+ {
+ public void onLoad(ISVNConfig cfg)
+ {
+ //System.out.println("config:");
+ onecat(cfg.config());
+ //System.out.println("servers:");
+ onecat(cfg.servers());
+ }
+
+ private void onecat(ISVNConfig.Category cat)
+ {
+ for (String sec : cat.sections()) {
+ //System.out.println(" [" + sec + "]");
+ ISVNConfig.Enumerator en = new ISVNConfig.Enumerator()
+ {
+ public void option(String name, String value)
+ {
+ //System.out.println(" " + name
+ // + " = " + value);
+ }
+ };
+ cat.enumerate(sec, en);
+ }
+ }
+ };
+
+ ISVNRemote session = getSession(getTestRepoUrl(),
+ super.conf.getAbsolutePath(),
+ handler);
+ session.getLatestRevision(); // Make sure the configuration gets loaded
+ }
+
+ private static class RemoteStatusReceiver implements RemoteStatus
+ {
+ static class StatInfo implements Comparable<StatInfo>
+ {
+ public String relpath = null;
+ public char kind = ' '; // F, D, L
+ public boolean textChanged = false;
+ public boolean propsChanged = false;
+ public boolean deleted = false;
+ public Entry info = null;
+
+ StatInfo(String relpath, char kind, boolean added)
+ {
+ this.relpath = relpath;
+ this.kind = kind;
+ this.deleted = !added;
+ }
+
+ StatInfo(String relpath, char kind,
+ boolean textChanged, boolean propsChanged,
+ Entry info)
+ {
+ this.relpath = relpath;
+ this.kind = kind;
+ this.textChanged = textChanged;
+ this.propsChanged = propsChanged;
+ this.info = info;
+ }
+
+ @Override
+ public boolean equals(Object statinfo)
+ {
+ final StatInfo that = (StatInfo)statinfo;
+ return this.relpath.equals(that.relpath);
+ }
+
+ public int compareTo(StatInfo that)
+ {
+ return this.relpath.compareTo(that.relpath);
+ }
+ }
+
+ private boolean debug;
+
+ public RemoteStatusReceiver()
+ {
+ this.debug = false;
+ }
+
+ public RemoteStatusReceiver(boolean debug)
+ {
+ this.debug = debug;
+ }
+
+ public ArrayList<StatInfo> status = new ArrayList<StatInfo>();
+
+ public void addedDirectory(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: A (dir) " +
+ relativePath);
+ status.add(new StatInfo(relativePath, 'D', true));
+ }
+
+ public void addedFile(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: A (file) "
+ + relativePath);
+ status.add(new StatInfo(relativePath, 'F', true));
+ }
+
+ public void addedSymlink(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: A (link) "
+ + relativePath);
+ status.add(new StatInfo(relativePath, 'L', true));
+ }
+
+ public void modifiedDirectory(String relativePath,
+ boolean childrenModified,
+ boolean propsModified,
+ Entry nodeInfo)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (childrenModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (dir) " + relativePath);
+ status.add(new StatInfo(relativePath, 'D',
+ childrenModified, propsModified,
+ nodeInfo));
+ }
+
+ public void modifiedFile(String relativePath,
+ boolean textModified,
+ boolean propsModified,
+ Entry nodeInfo)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (textModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (file) " + relativePath);
+ status.add(new StatInfo(relativePath, 'F',
+ textModified, propsModified,
+ nodeInfo));
+ }
+
+ public void modifiedSymlink(String relativePath,
+ boolean targetModified,
+ boolean propsModified,
+ Entry nodeInfo)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: " +
+ (targetModified ? 'M' : '_') +
+ (propsModified ? 'M' : '_') +
+ " (link) " + relativePath);
+ status.add(new StatInfo(relativePath, 'L',
+ targetModified, propsModified,
+ nodeInfo));
+
+ }
+
+ public void deleted(String relativePath)
+ {
+ if (debug)
+ System.err.println("RemoteStatus: D "
+ + relativePath);
+ status.add(new StatInfo(relativePath, ' ', false));
+ }
+ }
+
+ public void testSimpleStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 0, Depth.infinity, true, null);
+ assertEquals(1, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+ assertEquals(21, receiver.status.size());
+ }
+
+ public void testPropchangeStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "Property change on A/D/gamma";
+ }
+ };
+ client.propertySetRemote(getTestRepoUrl() + "/A/D/gamma",
+ 1L, "foo", "bar".getBytes(), cmcb,
+ false, null, null);
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 1, Depth.infinity, false, null);
+ assertEquals(2, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+
+ assertEquals(4, receiver.status.size());
+
+ // ra_serf returns the entries in inverted order compared to ra_local.
+ Collections.sort(receiver.status);
+ RemoteStatusReceiver.StatInfo mod = receiver.status.get(3);
+ assertEquals("A/D/gamma", mod.relpath);
+ assertEquals('F', mod.kind);
+ assertEquals(false, mod.textChanged);
+ assertEquals(true, mod.propsChanged);
+ assertEquals(false, mod.deleted);
+ assertEquals(2, mod.info.getCommittedRevision());
+ }
+
+ public void testDeletedStatus() throws Exception
+ {
+ ISVNRemote session = getSession();
+
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "Delete A/mu";
+ }
+ };
+ HashSet<String> paths = new HashSet<String>(1);
+ paths.add(getTestRepoUrl() + "/A/mu");
+ client.remove(paths, false, false, null, cmcb, null);
+
+ RemoteStatusReceiver receiver = new RemoteStatusReceiver();
+ ISVNReporter rp = session.status(null, Revision.SVN_INVALID_REVNUM,
+ Depth.infinity, receiver);
+ try {
+ rp.setPath("", 1, Depth.infinity, false, null);
+ assertEquals(2, rp.finishReport());
+ } finally {
+ rp.dispose();
+ }
+ assertEquals(3, receiver.status.size());
+
+ // ra_serf returns the entries in inverted order compared to ra_local.
+ Collections.sort(receiver.status);
+ RemoteStatusReceiver.StatInfo mod = receiver.status.get(2);
+ assertEquals("A/mu", mod.relpath);
+ assertEquals(' ', mod.kind);
+ assertEquals(false, mod.textChanged);
+ assertEquals(false, mod.propsChanged);
+ assertEquals(true, mod.deleted);
+ }
+
+ public void testTrivialMergeinfo() throws Exception
+ {
+ ISVNRemote session = getSession();
+ ArrayList<String> paths = new ArrayList<String>(1);
+ paths.add("");
+
+ Map<String, Mergeinfo> catalog =
+ session.getMergeinfo(paths, 1L, Mergeinfo.Inheritance.explicit,
+ false);
+ assertEquals(null, catalog);
+ }
+
+ public void testBranchMergeinfo() throws Exception
+ {
+ CommitMessageCallback cmcb = new CommitMessageCallback() {
+ public String getLogMessage(Set<CommitItem> x) {
+ return "testBranchMergeinfo";
+ }
+ };
+
+ ISVNRemote session = getSession();
+
+ // Create a branch
+ ArrayList<CopySource> dirA = new ArrayList<CopySource>(1);
+ dirA.add(new CopySource(getTestRepoUrl() + "/A",
+ Revision.HEAD, Revision.HEAD));
+ client.copy(dirA, getTestRepoUrl() + "/Abranch",
+ false, false, true, null, cmcb, null);
+
+ // Check mergeinfo on new branch
+ ArrayList<String> paths = new ArrayList<String>(1);
+ paths.add("Abranch");
+ Map<String, Mergeinfo> catalog =
+ session.getMergeinfo(paths, 2L, Mergeinfo.Inheritance.explicit,
+ false);
+ assertEquals(null, catalog);
+
+ // Modify source and merge to branch
+ client.propertySetRemote(getTestRepoUrl() + "/A/D/gamma",
+ 2L, "foo", "bar".getBytes(), cmcb,
+ false, null, null);
+ client.update(thisTest.getWCPathSet(), Revision.HEAD, Depth.infinity,
+ false, false, true, false);
+ client.merge(getTestRepoUrl() + "/A", Revision.HEAD, null,
+ thisTest.getWCPath() + "/Abranch", false, Depth.infinity,
+ false, false, false, false);
+ client.commit(thisTest.getWCPathSet(), Depth.infinity, false, false,
+ null, null, cmcb, null);
+
+ // Check inherited mergeinfo on updated branch
+ paths.set(0, "Abranch/mu");
+ catalog = session.getMergeinfo(paths, 4L,
+ Mergeinfo.Inheritance.nearest_ancestor,
+ false);
+ assertEquals(1, catalog.size());
+ List<RevisionRange> ranges =
+ catalog.get("Abranch/mu").getRevisions("/A/mu");
+ assertEquals(1, ranges.size());
+ assertEquals("1-3", ranges.get(0).toString());
+ }
}