/*
* ====================================================================
* Copyright (c) 2004-2012 TMate Software Ltd. All rights reserved.
*
* This software is licensed as described in the file COPYING, which
* you should have received as part of this distribution. The terms
* are also available at http://svnkit.com/license.html
* If newer versions of this license are posted there, you may use a
* newer version instead, at your option.
* ====================================================================
*/
package org.tmatesoft.svn.core.internal.wc;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.Arrays;
import java.util.Iterator;
import java.util.Map;
import java.util.TreeMap;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNMergeRange;
import org.tmatesoft.svn.core.SVNMergeRangeList;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNProperty;
import org.tmatesoft.svn.core.SVNPropertyValue;
import org.tmatesoft.svn.core.SVNRevisionProperty;
import org.tmatesoft.svn.core.internal.delta.SVNDeltaReader;
import org.tmatesoft.svn.core.internal.io.fs.FSCommitter;
import org.tmatesoft.svn.core.internal.io.fs.FSDeltaConsumer;
import org.tmatesoft.svn.core.internal.io.fs.FSFS;
import org.tmatesoft.svn.core.internal.io.fs.FSRevisionNode;
import org.tmatesoft.svn.core.internal.io.fs.FSRevisionRoot;
import org.tmatesoft.svn.core.internal.io.fs.FSTransactionInfo;
import org.tmatesoft.svn.core.internal.io.fs.FSTransactionRoot;
import org.tmatesoft.svn.core.internal.util.FixedSizeInputStream;
import org.tmatesoft.svn.core.internal.util.SVNHashMap;
import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.io.diff.SVNDeltaGenerator;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.admin.ISVNAdminEventHandler;
import org.tmatesoft.svn.core.wc.admin.SVNAdminEvent;
import org.tmatesoft.svn.core.wc.admin.SVNAdminEventAction;
import org.tmatesoft.svn.core.wc.admin.SVNUUIDAction;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class DefaultLoadHandler implements ISVNLoadHandler {
private FSFS myFSFS;
private RevisionBaton myCurrentRevisionBaton;
private NodeBaton myCurrentNodeBaton;
private boolean myIsUsePreCommitHook;
private boolean myIsUsePostCommitHook;
private Map myRevisionsMap;
private String myParentDir;
private SVNUUIDAction myUUIDAction;
private SVNDeltaReader myDeltaReader;
private SVNDeltaGenerator myDeltaGenerator;
private ISVNAdminEventHandler myProgressHandler;
public DefaultLoadHandler(boolean usePreCommitHook, boolean usePostCommitHook, SVNUUIDAction uuidAction,
String parentDir, ISVNAdminEventHandler progressHandler) {
myProgressHandler = progressHandler;
myIsUsePreCommitHook = usePreCommitHook;
myIsUsePostCommitHook = usePostCommitHook;
myUUIDAction = uuidAction;
myParentDir = SVNPathUtil.canonicalizePath(parentDir);
myRevisionsMap = new SVNHashMap();
}
public void setFSFS(FSFS fsfs) {
myFSFS = fsfs;
}
public void closeRevision() throws SVNException {
if (myCurrentRevisionBaton != null) {
myCurrentRevisionBaton.getConsumer().close();
RevisionBaton baton = myCurrentRevisionBaton;
myCurrentRevisionBaton = null;
if (baton.myRevision <= 0) {
return;
}
long oldRevision = baton.myRevision;
long newRevision = -1;
try {
newRevision = baton.getCommitter().commitTxn(myIsUsePreCommitHook, myIsUsePostCommitHook, null, null);
} catch (SVNException svne) {
try {
FSCommitter.abortTransaction(myFSFS, baton.myTxn.getTxnId());
} catch (SVNException svne2) {
//
}
throw svne;
}
if (baton.myDatestamp == null) {
myFSFS.setRevisionProperty(baton.myRevision, SVNRevisionProperty.DATE, null);
}
File revProps = myFSFS.getRevisionPropertiesFile(baton.myRevision, true);
if (!revProps.exists()) {
OutputStream os = SVNFileUtil.openFileForWriting(revProps);
try {
SVNWCProperties.setProperties(new SVNProperties(), os, SVNWCProperties.SVN_HASH_TERMINATOR);
} finally {
SVNFileUtil.closeFile(os);
}
}
myRevisionsMap.put(new Long(oldRevision), new Long(newRevision));
if (baton.myDatestamp != null) {
myFSFS.setRevisionProperty(newRevision, SVNRevisionProperty.DATE, baton.myDatestamp);
}
String message;
if (newRevision == baton.myRevision) {
message = "\n------- Committed revision " + newRevision + " >>>";
} else {
message = "\n------- Committed new rev " + newRevision + " (loaded from original rev " + baton.myRevision + ") >>>";
}
if (myProgressHandler != null) {
SVNAdminEvent event = new SVNAdminEvent(newRevision, baton.myRevision, SVNAdminEventAction.REVISION_LOADED, message);
myProgressHandler.handleAdminEvent(event, ISVNEventHandler.UNKNOWN);
}
}
}
public void openRevision(Map headers) throws SVNException {
myCurrentRevisionBaton = new RevisionBaton();
long revision = -1;
if (headers.containsKey(SVNAdminHelper.DUMPFILE_REVISION_NUMBER)) {
try {
revision = Long.parseLong((String) headers.get(SVNAdminHelper.DUMPFILE_REVISION_NUMBER));
} catch (NumberFormatException nfe) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.STREAM_MALFORMED_DATA, "Cannot parse revision ({0}) in dump file", headers.get(SVNAdminHelper.DUMPFILE_REVISION_NUMBER));
SVNErrorManager.error(err, SVNLogType.FSFS);
}
}
myCurrentRevisionBaton.myRevision = revision;
long headRevision = myFSFS.getYoungestRevision();
myCurrentRevisionBaton.myRevisionOffset = revision - (headRevision + 1);
if (revision > 0) {
myCurrentRevisionBaton.myTxn = FSTransactionRoot.beginTransaction(headRevision, 0, myFSFS);
myCurrentRevisionBaton.myTxnRoot = myFSFS.createTransactionRoot(myCurrentRevisionBaton.myTxn);
String message = "<<< Started new transaction, based on original revision " + revision;
if (myProgressHandler != null) {
SVNAdminEvent event = new SVNAdminEvent(revision, SVNAdminEventAction.REVISION_LOAD, message);
myProgressHandler.handleAdminEvent(event, ISVNEventHandler.UNKNOWN);
}
}
}
public void openNode(Map headers) throws SVNException {
if (myCurrentRevisionBaton.myRevision == 0) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.STREAM_MALFORMED_DATA, "Malformed dumpstream: Revision 0 must not contain node records");
SVNErrorManager.error(err, SVNLogType.FSFS);
}
myCurrentNodeBaton = createNodeBaton(headers);
String message;
switch (myCurrentNodeBaton.myAction) {
case SVNAdminHelper.NODE_ACTION_CHANGE:
message = " * editing path : " + myCurrentNodeBaton.myPath + " ...";
if (myProgressHandler != null) {
SVNAdminEvent event = new SVNAdminEvent(SVNAdminEventAction.REVISION_LOAD_EDIT_PATH, myCurrentNodeBaton.myPath, message);
myProgressHandler.handleAdminEvent(event, ISVNEventHandler.UNKNOWN);
}
break;
case SVNAdminHelper.NODE_ACTION_DELETE:
message = " * deleting path : " + myCurrentNodeBaton.myPath + " ...";
if (myProgressHandler != null) {
SVNAdminEvent event = new SVNAdminEvent(SVNAdminEventAction.REVISION_LOAD_DELETE_PATH, myCurrentNodeBaton.myPath, message);
myProgressHandler.handleAdminEvent(event, ISVNEventHandler.UNKNOWN);
}
myCurrentRevisionBaton.getCommitter().deleteNode(myCurrentNodeBaton.myPath);
break;
case SVNAdminHelper.NODE_ACTION_ADD:
message = " * adding path : " + myCurrentNodeBaton.myPath + " ...";
if (maybeAddWithHistory(myCurrentNodeBaton)) {
message += "COPIED...";
}
if (myProgressHandler != null) {
SVNAdminEvent event = new SVNAdminEvent(SVNAdminEventAction.REVISION_LOAD_ADD_PATH, myCurrentNodeBaton.myPath, message);
myProgressHandler.handleAdminEvent(event, ISVNEventHandler.UNKNOWN);
}
break;
case SVNAdminHelper.NODE_ACTION_REPLACE:
message = " * replacing path : " + myCurrentNodeBaton.myPath + " ...";
myCurrentRevisionBaton.getCommitter().deleteNode(myCurrentNodeBaton.myPath);
if (maybeAddWithHistory(myCurrentNodeBaton)) {
message += "COPIED...";
}
if (myProgressHandler != null) {
SVNAdminEvent event = new SVNAdminEvent(SVNAdminEventAction.REVISION_LOAD_REPLACE_PATH, myCurrentNodeBaton.myPath, message);
myProgressHandler.handleAdminEvent(event, ISVNEventHandler.UNKNOWN);
}
break;
default:
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.STREAM_UNRECOGNIZED_DATA, "Unrecognized node-action on node ''{0}''", myCurrentNodeBaton.myPath);
SVNErrorManager.error(err, SVNLogType.FSFS);
}
}
public void parseUUID(String uuid) throws SVNException {
if (myUUIDAction == SVNUUIDAction.IGNORE_UUID) {
return;
}
if (myUUIDAction != SVNUUIDAction.FORCE_UUID) {
long latestRevision = myFSFS.getYoungestRevision();
if (latestRevision != 0) {
return;
}
}
myFSFS.setUUID(uuid);
}
public void closeNode() throws SVNException {
myCurrentNodeBaton = null;
}
public void applyTextDelta() throws SVNException {
FSDeltaConsumer fsConsumer = myCurrentRevisionBaton.getConsumer();
fsConsumer.applyTextDelta(myCurrentNodeBaton.myPath, myCurrentNodeBaton.myBaseChecksum);
}
public void setFullText() throws SVNException {
FSDeltaConsumer fsConsumer = myCurrentRevisionBaton.getConsumer();
fsConsumer.applyText(myCurrentNodeBaton.myPath);
}
public void parseTextBlock(InputStream dumpStream, long contentLength, boolean isDelta) throws SVNException {
FSDeltaConsumer fsConsumer = myCurrentRevisionBaton.getConsumer();
try {
if (isDelta) {
applyTextDelta();
} else {
setFullText();
}
String checksum = null;
byte[] buffer = null;
if (contentLength == 0) {
getDeltaGenerator().sendDelta(myCurrentNodeBaton.myPath, SVNFileUtil.DUMMY_IN, fsConsumer, false);
} else {
if (!isDelta) {
//
InputStream tgt = new FixedSizeInputStream(dumpStream, contentLength);
checksum = getDeltaGenerator().sendDelta(myCurrentNodeBaton.myPath, tgt, fsConsumer, true);
} else {
buffer = new byte[SVNFileUtil.STREAM_CHUNK_SIZE];
SVNDeltaReader deltaReader = null;
try {
while (contentLength > 0) {
int numToRead = contentLength > SVNFileUtil.STREAM_CHUNK_SIZE ? SVNFileUtil.STREAM_CHUNK_SIZE : (int) contentLength;
int read = 0;
while(numToRead > 0) {
int numRead = dumpStream.read(buffer, read, numToRead);
if (numRead < 0) {
SVNAdminHelper.generateIncompleteDataError();
}
read += numRead;
numToRead -= numRead;
}
deltaReader = getDeltaReader();
deltaReader.nextWindow(buffer, 0, read, myCurrentNodeBaton.myPath, fsConsumer);
contentLength -= read;
}
} catch (IOException ioe) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, ioe.getMessage());
SVNErrorManager.error(err, ioe, SVNLogType.FSFS);
}
if (deltaReader != null) {
deltaReader.reset(myCurrentNodeBaton.myPath, fsConsumer);
}
fsConsumer.textDeltaEnd(myCurrentNodeBaton.myPath);
checksum = fsConsumer.getChecksum();
}
}
if (checksum != null && myCurrentNodeBaton.myResultChecksum != null) {
if (!checksum.equals(myCurrentNodeBaton.myResultChecksum)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH, "Checksum mismatch for ''{0}'':\n expected: {1}\n actual: {2}\n",
new Object[] { myCurrentNodeBaton.myPath, myCurrentNodeBaton.myResultChecksum, checksum });
SVNErrorManager.error(err, SVNLogType.FSFS);
}
}
} catch (SVNException svne) {
fsConsumer.abort();
throw svne;
}
}
public void removeNodeProperties() throws SVNException {
FSTransactionRoot txnRoot = myCurrentRevisionBaton.myTxnRoot;
FSRevisionNode node = txnRoot.getRevisionNode(myCurrentNodeBaton.myPath);
SVNProperties props = node.getProperties(myFSFS);
for (Iterator propNames = props.nameSet().iterator(); propNames.hasNext();) {
String propName = (String) propNames.next();
myCurrentRevisionBaton.getCommitter().changeNodeProperty(myCurrentNodeBaton.myPath, propName, null);
}
}
public void setRevisionProperty(String propertyName, SVNPropertyValue propertyValue) throws SVNException {
if (myCurrentRevisionBaton.myRevision > 0) {
myFSFS.setTransactionProperty(myCurrentRevisionBaton.myTxn.getTxnId(), propertyName, propertyValue);
if (SVNRevisionProperty.DATE.equals(propertyName)) {
myCurrentRevisionBaton.myDatestamp = propertyValue;
}
} else if (myCurrentRevisionBaton.myRevision == 0) {
long youngestRevision = myFSFS.getYoungestRevision();
if (youngestRevision == 0) {
myFSFS.setRevisionProperty(0, propertyName, propertyValue);
}
}
}
public void setUsePreCommitHook(boolean use) {
myIsUsePreCommitHook = use;
}
public void setUsePostCommitHook(boolean use) {
myIsUsePostCommitHook = use;
}
public void setParentDir(String parentDir) {
myParentDir = parentDir;
}
public void setUUIDAction(SVNUUIDAction action) {
myUUIDAction = action;
}
public void deleteNodeProperty(String propertyName) throws SVNException {
myCurrentRevisionBaton.getCommitter().changeNodeProperty(myCurrentNodeBaton.myPath, propertyName, null);
}
public void setNodeProperty(String propertyName, SVNPropertyValue propertyValue) throws SVNException {
if (SVNProperty.MERGE_INFO.equals(propertyName)) {
Map mergeInfo = renumberMergeInfoRevisions(propertyValue);
if (myParentDir != null) {
mergeInfo = prefixMergeInfoPaths(mergeInfo);
}
String mergeInfoString = SVNMergeInfoUtil.formatMergeInfoToString(mergeInfo, null);
propertyValue = SVNPropertyValue.create(mergeInfoString);
}
myCurrentRevisionBaton.getCommitter().changeNodeProperty(myCurrentNodeBaton.myPath, propertyName,
propertyValue);
}
private SVNDeltaReader getDeltaReader() {
if (myDeltaReader == null) {
myDeltaReader = new SVNDeltaReader();
}
return myDeltaReader;
}
private SVNDeltaGenerator getDeltaGenerator() {
if (myDeltaGenerator == null) {
myDeltaGenerator = new SVNDeltaGenerator();
}
return myDeltaGenerator;
}
private boolean maybeAddWithHistory(NodeBaton nodeBaton) throws SVNException {
if (nodeBaton.myCopyFromPath == null) {
if (nodeBaton.myKind == SVNNodeKind.FILE) {
myCurrentRevisionBaton.getCommitter().makeFile(nodeBaton.myPath);
} else if (nodeBaton.myKind == SVNNodeKind.DIR) {
myCurrentRevisionBaton.getCommitter().makeDir(nodeBaton.myPath);
}
return false;
}
long srcRevision = nodeBaton.myCopyFromRevision - myCurrentRevisionBaton.myRevisionOffset;
Long copyFromRevision = new Long(nodeBaton.myCopyFromRevision);
if (myRevisionsMap.containsKey(copyFromRevision)) {
Long revision = (Long) myRevisionsMap.get(copyFromRevision);
srcRevision = revision.longValue();
}
if (!SVNRevision.isValidRevisionNumber(srcRevision)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NO_SUCH_REVISION, "Relative source revision {0} is not available in current repository", new Long(srcRevision));
SVNErrorManager.error(err, SVNLogType.FSFS);
}
FSRevisionRoot copyRoot = myFSFS.createRevisionRoot(srcRevision);
if (nodeBaton.myCopySourceChecksum != null) {
FSRevisionNode revNode = copyRoot.getRevisionNode(nodeBaton.myCopyFromPath);
String hexDigest = revNode.getFileMD5Checksum();
if (hexDigest != null && !hexDigest.equals(nodeBaton.myCopySourceChecksum)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH,
"Copy source checksum mismatch on copy from ''{0}''@{1}\n" +
" to ''{2}'' in rev based on r{3}:\n" +
" expected: {4}\n" +
" actual: {5}\n", new Object[] { nodeBaton.myCopyFromPath,
String.valueOf(srcRevision), nodeBaton.myPath,
String.valueOf(myCurrentRevisionBaton.myRevision),
nodeBaton.myCopySourceChecksum, hexDigest });
SVNErrorManager.error(err, SVNLogType.FSFS);
}
}
myCurrentRevisionBaton.getCommitter().makeCopy(copyRoot, nodeBaton.myCopyFromPath, nodeBaton.myPath, true);
return true;
}
private NodeBaton createNodeBaton(Map headers) throws SVNException {
NodeBaton baton = new NodeBaton();
baton.myKind = SVNNodeKind.UNKNOWN;
if (headers.containsKey(SVNAdminHelper.DUMPFILE_NODE_PATH)) {
String nodePath = (String) headers.get(SVNAdminHelper.DUMPFILE_NODE_PATH);
if (myParentDir != null) {
baton.myPath = SVNPathUtil.getAbsolutePath(SVNPathUtil.append(myParentDir, nodePath));
} else {
baton.myPath = SVNPathUtil.getAbsolutePath(SVNPathUtil.canonicalizePath(nodePath));
}
}
if (headers.containsKey(SVNAdminHelper.DUMPFILE_NODE_KIND)) {
baton.myKind = SVNNodeKind.parseKind((String) headers.get(SVNAdminHelper.DUMPFILE_NODE_KIND));
}
baton.myAction = SVNAdminHelper.NODE_ACTION_UNKNOWN;
if (headers.containsKey(SVNAdminHelper.DUMPFILE_NODE_ACTION)) {
String action = (String) headers.get(SVNAdminHelper.DUMPFILE_NODE_ACTION);
if ("change".equals(action)) {
baton.myAction = SVNAdminHelper.NODE_ACTION_CHANGE;
} else if ("add".equals(action)) {
baton.myAction = SVNAdminHelper.NODE_ACTION_ADD;
} else if ("delete".equals(action)) {
baton.myAction = SVNAdminHelper.NODE_ACTION_DELETE;
} else if ("replace".equals(action)) {
baton.myAction = SVNAdminHelper.NODE_ACTION_REPLACE;
}
}
baton.myCopyFromRevision = -1;
if (headers.containsKey(SVNAdminHelper.DUMPFILE_NODE_COPYFROM_REVISION)) {
try {
baton.myCopyFromRevision = Long.parseLong((String) headers.get(SVNAdminHelper.DUMPFILE_NODE_COPYFROM_REVISION));
} catch (NumberFormatException nfe) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.STREAM_MALFORMED_DATA, "Cannot parse revision ({0}) in dump file", headers.get(SVNAdminHelper.DUMPFILE_NODE_COPYFROM_REVISION));
SVNErrorManager.error(err, SVNLogType.FSFS);
}
}
if (headers.containsKey(SVNAdminHelper.DUMPFILE_NODE_COPYFROM_PATH)) {
String copyFromPath = (String) headers.get(SVNAdminHelper.DUMPFILE_NODE_COPYFROM_PATH);
if (myParentDir != null) {
baton.myCopyFromPath = SVNPathUtil.append(myParentDir, copyFromPath);
} else {
baton.myCopyFromPath = SVNPathUtil.canonicalizePath(copyFromPath);
}
baton.myCopyFromPath = SVNPathUtil.getAbsolutePath(baton.myCopyFromPath);
}
if (headers.containsKey(SVNAdminHelper.DUMPFILE_TEXT_CONTENT_MD5)) {
baton.myResultChecksum = (String) headers.get(SVNAdminHelper.DUMPFILE_TEXT_CONTENT_MD5);
}
if (headers.containsKey(SVNAdminHelper.DUMPFILE_TEXT_DELTA_BASE_MD5)) {
baton.myBaseChecksum = (String) headers.get(SVNAdminHelper.DUMPFILE_TEXT_DELTA_BASE_MD5);
}
if (headers.containsKey(SVNAdminHelper.DUMPFILE_TEXT_COPY_SOURCE_MD5)) {
baton.myCopySourceChecksum = (String) headers.get(SVNAdminHelper.DUMPFILE_TEXT_COPY_SOURCE_MD5);
}
return baton;
}
private Map renumberMergeInfoRevisions(SVNPropertyValue mergeInfoProp) throws SVNException {
String mergeInfoString = SVNPropertyValue.getPropertyAsString(mergeInfoProp);
Map mergeInfo = SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(mergeInfoString), null);
for (Iterator mergeInfoIter = mergeInfo.keySet().iterator(); mergeInfoIter.hasNext();) {
String mergeSource = (String) mergeInfoIter.next();
SVNMergeRangeList rangeList = (SVNMergeRangeList) mergeInfo.get(mergeSource);
SVNMergeRange[] ranges = rangeList.getRanges();
for (int i = 0; i < ranges.length; i++) {
SVNMergeRange range = ranges[i];
Long revFromMap = (Long) myRevisionsMap.get(new Long(range.getStartRevision()));
if (revFromMap != null && SVNRevision.isValidRevisionNumber(revFromMap.longValue())) {
range.setStartRevision(revFromMap.longValue());
}
revFromMap = (Long) myRevisionsMap.get(new Long(range.getEndRevision()));
if (revFromMap != null && SVNRevision.isValidRevisionNumber(revFromMap.longValue())) {
range.setEndRevision(revFromMap.longValue());
}
}
Arrays.sort(ranges);
}
return mergeInfo;
}
private Map prefixMergeInfoPaths(Map mergeInfo) {
Map prefixedMergeInfo = new TreeMap();
for (Iterator mergeInfoIter = mergeInfo.keySet().iterator(); mergeInfoIter.hasNext();) {
String mergeSource = (String) mergeInfoIter.next();
SVNMergeRangeList rangeList = (SVNMergeRangeList) mergeInfo.get(mergeSource);
mergeSource = mergeSource.startsWith("/") ? mergeSource.substring(1) : mergeSource;
mergeSource = SVNPathUtil.append(myParentDir, mergeSource);
if (mergeSource.charAt(0) != '/') {
mergeSource = '/' + mergeSource;
}
prefixedMergeInfo.put(mergeSource, rangeList);
}
return prefixedMergeInfo;
}
private class RevisionBaton {
FSTransactionInfo myTxn;
FSTransactionRoot myTxnRoot;
long myRevision;
long myRevisionOffset;
SVNPropertyValue myDatestamp;
private FSCommitter myCommitter;
private FSDeltaConsumer myDeltaConsumer;
public FSDeltaConsumer getConsumer() {
if (myDeltaConsumer == null) {
myDeltaConsumer = new FSDeltaConsumer("", myTxnRoot, myFSFS, getCommitter(), null, null);
}
return myDeltaConsumer;
}
public FSCommitter getCommitter() {
if (myCommitter == null) {
myCommitter = new FSCommitter(myFSFS, myTxnRoot, myTxn, null, null);
}
return myCommitter;
}
}
private class NodeBaton {
String myPath;
SVNNodeKind myKind;
int myAction;
String myBaseChecksum;
String myResultChecksum;
String myCopySourceChecksum;
long myCopyFromRevision;
String myCopyFromPath;
}
}