package org.tmatesoft.svn.core.internal.wc2.ng;
import java.io.File;
import java.util.*;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNErrorCode;
import org.tmatesoft.svn.core.SVNErrorMessage;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNNodeKind;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.internal.util.SVNDate;
import org.tmatesoft.svn.core.internal.util.SVNPathUtil;
import org.tmatesoft.svn.core.internal.util.SVNSkel;
import org.tmatesoft.svn.core.internal.wc.SVNErrorManager;
import org.tmatesoft.svn.core.internal.wc.SVNEventFactory;
import org.tmatesoft.svn.core.internal.wc.SVNFileListUtil;
import org.tmatesoft.svn.core.internal.wc.SVNFileType;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc17.SVNStatusEditor17;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
import org.tmatesoft.svn.core.internal.wc17.SVNWCUtils;
import org.tmatesoft.svn.core.internal.wc17.db.*;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbKind;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbStatus;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbRepositoryInfo;
import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbRepositoryInfo.RepositoryInfoField;
import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.AdditionInfo;
import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.ExternalNodeInfo;
import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.NodeInfo;
import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.wc.*;
import org.tmatesoft.svn.core.wc2.ISvnObjectReceiver;
import org.tmatesoft.svn.core.wc2.SvnChecksum;
import org.tmatesoft.svn.core.wc2.SvnCopy;
import org.tmatesoft.svn.core.wc2.SvnCopySource;
import org.tmatesoft.svn.core.wc2.SvnGetStatus;
import org.tmatesoft.svn.core.wc2.SvnOperationFactory;
import org.tmatesoft.svn.core.wc2.SvnScheduleForAddition;
import org.tmatesoft.svn.core.wc2.SvnStatus;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.util.SVNLogType;
public class SvnNgWcToWcCopy extends SvnNgOperationRunner<Void, SvnCopy> {
@Override
public boolean isApplicable(SvnCopy operation, SvnWcGeneration wcGeneration) throws SVNException {
return areAllSourcesLocal(operation) && operation.getFirstTarget().isLocal();
}
private boolean areAllSourcesLocal(SvnCopy operation) {
for(SvnCopySource source : operation.getSources()) {
if (source.getSource().isFile()) {
if (operation.isMove()) {
continue;
}
if (isLocalRevision(source.getRevision()) && isLocalRevision(source.getSource().getResolvedPegRevision())) {
continue;
}
}
return false;
}
return true;
}
private boolean isLocalRevision(SVNRevision revision) {
return revision == SVNRevision.WORKING || revision == SVNRevision.UNDEFINED;
}
@Override
protected Void run(SVNWCContext context) throws SVNException {
boolean timestampSleep = false;
Collection<SvnCopySource> sources = getOperation().getSources();
try {
timestampSleep = tryRun(context, sources, getFirstTarget());
return null;
} catch (SVNException e) {
SVNErrorCode code = e.getErrorMessage().getErrorCode();
if (!getOperation().isFailWhenDstExists()
&& getOperation().getSources().size() == 1
&& (code == SVNErrorCode.ENTRY_EXISTS || code == SVNErrorCode.FS_ALREADY_EXISTS)) {
SvnCopySource source = sources.iterator().next();
timestampSleep = tryRun(context, sources, new File(getFirstTarget(), source.getSource().getFile().getName()));
return null;
}
throw e;
} finally {
if (timestampSleep) {
sleepForTimestamp();
}
}
}
protected boolean tryRun(SVNWCContext context, Collection<SvnCopySource> sources, File target) throws SVNException {
if (getOperation().isDisjoint()) {
return disjointCopy(context, target);
} else {
return copy(context, sources, target);
}
}
/**
* The method performs "disjoint" copy (see SVNCopyClient#doCopy(File))
* The algorithm is:
* 1. Create a fake working copy
* 2. Move wc.db from the nested working copy to the fake
* 3. Move all pristine files to the parent working copy
* 4. Perform metadata copying
* @param context
* @param nestedWC
* @return
* @throws SVNException
*/
private boolean disjointCopy(SVNWCContext context, File nestedWC) throws SVNException {
nestedWC = new File(nestedWC.getAbsolutePath().replace(File.separatorChar, '/'));
final File nestedWCParent = nestedWC.getParentFile();
checkForDisjointCopyPossibility(context, nestedWC, nestedWCParent);
context.getDb().close();
final File wcRoot = context.getDb().getWCRoot(nestedWCParent);
final File tempDirectory = new File(getAdminDirectory(wcRoot), "tmp");
SVNFileUtil.ensureDirectoryExists(tempDirectory);
final File fakeWorkingCopyDirectory = SVNFileUtil.createUniqueDir(tempDirectory, "disjoint-copy", "tmp", true);
moveWcDb(nestedWC, fakeWorkingCopyDirectory);
copyPristineFiles(nestedWC, wcRoot, true);
SVNFileUtil.deleteAll(getAdminDirectory(nestedWC), true);
context.getDb().forgetDirectoryTemp(nestedWC);
File lockRoot = null;
try {
lockRoot = context.acquireWriteLock(wcRoot, true, true);
copy(context, fakeWorkingCopyDirectory, nestedWC, true);
} finally {
if (lockRoot != null) {
context.releaseWriteLock(lockRoot);
}
}
return true;
}
private void checkForDisjointCopyPossibility(SVNWCContext context, File nestedWC, File nestedWCParent) throws SVNException {
SVNFileType nestedWCType = SVNFileType.getType(nestedWC);
if (nestedWCType != SVNFileType.DIRECTORY) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE,
"This kind of copy can be run on a root of a disjoint wc directory only");
SVNErrorManager.error(err, SVNLogType.WC);
}
if (nestedWCParent == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE,
"{0} seems to be not a disjoint wc since it has no parent", nestedWC);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (!(context.getDb() instanceof SVNWCDb)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE,
"Unsupported working copy format", nestedWC);
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNWCDb wcdb = (SVNWCDb) context.getDb();
if (hasMetadataInParentWc(wcdb, nestedWC, nestedWCParent)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS,
"Entry ''{0}'' already exists in parent directory", nestedWC.getName());
SVNErrorManager.error(err, SVNLogType.WC);
}
final ISVNWCDb.WCDbBaseInfo baseInfo = context.getDb().getBaseInfo(nestedWC,
ISVNWCDb.WCDbBaseInfo.BaseInfoField.reposRootUrl, ISVNWCDb.WCDbBaseInfo.BaseInfoField.reposRelPath);
final ISVNWCDb.WCDbBaseInfo parentBaseInfo = context.getDb().getBaseInfo(nestedWCParent,
ISVNWCDb.WCDbBaseInfo.BaseInfoField.reposRootUrl, ISVNWCDb.WCDbBaseInfo.BaseInfoField.reposRelPath);
if (baseInfo.reposRootUrl != null && parentBaseInfo.reposRootUrl != null &&
!baseInfo.reposRootUrl.equals(parentBaseInfo.reposRootUrl)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE,
"Cannot copy to ''{0}'', as it is not from repository ''{1}''; it is from ''{2}''",
new Object[] { nestedWCParent, baseInfo.reposRootUrl,
baseInfo.reposRootUrl });
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNWCContext.ScheduleInternalInfo parentSchedule = context.getNodeScheduleInternal(nestedWCParent, true, true);
if (parentSchedule.schedule == SVNWCContext.SVNWCSchedule.delete) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE,
"Cannot copy to ''{0}'', as it is scheduled for deletion", nestedWCParent);
SVNErrorManager.error(err, SVNLogType.WC);
}
SVNWCContext.ScheduleInternalInfo schedule = context.getNodeScheduleInternal(nestedWC, true, true);
if (schedule.schedule == SVNWCContext.SVNWCSchedule.delete) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE,
"Cannot copy ''{0}'', as it is scheduled for deletion", nestedWC);
SVNErrorManager.error(err, SVNLogType.WC);
}
File relativePath = baseInfo.reposRelPath;
File parentRelativePath = parentBaseInfo.reposRelPath;
if (relativePath == null || parentRelativePath == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE,
"Cannot copy ''{0}'': cannot resolve its relative path, perhaps it is obstructed", nestedWC);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNPathUtil.isAncestor(relativePath.getPath(), parentRelativePath.getPath())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE,
"Cannot copy path ''{0}'' into its own child ''{1}",
new Object[] { nestedWC, nestedWCParent });
SVNErrorManager.error(err, SVNLogType.WC);
}
if ((schedule.schedule == SVNWCContext.SVNWCSchedule.add && !schedule.copied) ||
context.getNodeUrl(nestedWC) == null) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS,
"Cannot copy or move ''{0}'': it is not in repository yet; " +
"try committing first", nestedWC);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
private void moveWcDb(File sourceWc, File targetWc) throws SVNException {
final File sourceWcDbFile = getWCDbFile(sourceWc);
final File targetWcDbFile = getWCDbFile(targetWc);
SVNFileUtil.ensureDirectoryExists(targetWcDbFile.getParentFile());
SVNFileUtil.rename(sourceWcDbFile, targetWcDbFile);
}
private void copyPristineFiles(File sourceWc, File targetWc, boolean move) throws SVNException {
final File sourcePristineDirectory = getPristineDirectory(sourceWc);
final File targetPristineDirectory = getPristineDirectory(targetWc);
final File[] sourceDirectories = SVNFileListUtil.listFiles(sourcePristineDirectory);
if (sourceDirectories != null) {
for (File sourceDirectory : sourceDirectories) {
final File targetDirectory = new File(targetPristineDirectory, sourceDirectory.getName());
SVNFileUtil.ensureDirectoryExists(targetDirectory);
final File[] sourcePristineFiles = SVNFileListUtil.listFiles(sourceDirectory);
if (sourcePristineFiles != null) {
for (File sourcePristineFile : sourcePristineFiles) {
final File targetPristineFile = new File(targetDirectory, sourcePristineFile.getName());
if (!targetPristineFile.exists()) {
if (move) {
SVNFileUtil.rename(sourcePristineFile, targetPristineFile);
} else {
SVNFileUtil.copyFile(sourcePristineFile, targetPristineFile, false);
}
}
}
}
}
}
SVNFileUtil.deleteAll(sourcePristineDirectory, true);
}
private File getPristineDirectory(File workingCopyDirectory) {
return new File(getAdminDirectory(workingCopyDirectory), "pristine");
}
private File getWCDbFile(File nestedWC) {
return new File(getAdminDirectory(nestedWC), "wc.db");
}
private File getAdminDirectory(File parentWC) {
final String adminDirectoryName = SVNFileUtil.getAdminDirectoryName();
return new File(parentWC, adminDirectoryName);
}
private boolean hasMetadataInParentWc(SVNWCDb wcdb, File nestedWC, File nestedWCParent) throws SVNException {
SVNWCDb.DirParsedInfo parsedInfo = wcdb.obtainWcRoot(nestedWCParent);
SVNWCDbDir wcDbDir = parsedInfo == null ? null : parsedInfo.wcDbDir;
SVNWCDbRoot wcdbRoot = wcDbDir == null ? null : wcDbDir.getWCRoot();
if (wcdbRoot == null) {
return false;
}
try {
wcdb.readInfo(wcdbRoot, new File(SVNPathUtil.getRelativePath(nestedWCParent.getPath(),nestedWC.getPath())));
return true;
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.ENTRY_NOT_FOUND || e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) {
return false;
} else {
throw e;
}
}
}
private boolean copy(SVNWCContext context, Collection<SvnCopySource> sources, File target) throws SVNException {
boolean sleepForTimeStamp = false;
Collection<SvnCopyPair> copyPairs = new ArrayList<SvnCopyPair>();
if (sources.size() > 1) {
if (getOperation().isFailWhenDstExists()) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_MULTIPLE_SOURCES_DISALLOWED);
SVNErrorManager.error(err, SVNLogType.DEFAULT);
}
for (SvnCopySource copySource : sources) {
SvnCopyPair copyPair = new SvnCopyPair();
copyPair.source = copySource.getSource().getFile();
String baseName = copyPair.source.getName();
copyPair.dst = new File(target, baseName);
copyPairs.add(copyPair);
}
} else if (sources.size() == 1) {
SvnCopyPair copyPair = new SvnCopyPair();
SvnCopySource source = sources.iterator().next();
copyPair.source = new File(SVNPathUtil.validateFilePath(source.getSource().getFile().getAbsolutePath()));
copyPair.dst = target;
copyPairs.add(copyPair);
}
for (SvnCopyPair pair : copyPairs) {
File src = pair.source;
File dst = pair.dst;
if (getOperation().isMove() && src.getAbsolutePath().equals(dst.getAbsolutePath())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE,
"Cannot move path ''{0}'' into itself", src);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (SVNWCUtils.isChild(src, dst)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot copy path ''{0}'' into its own child ''{1}''",
src, dst);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
if (getOperation().isMove()) {
for (SvnCopyPair pair : copyPairs) {
File src = pair.source;
try {
Structure<ExternalNodeInfo> externalInfo = SvnWcDbExternals.readExternal(context, src, src, ExternalNodeInfo.kind, ExternalNodeInfo.definingAbsPath);
if (externalInfo.hasValue(ExternalNodeInfo.kind) && externalInfo.get(ExternalNodeInfo.kind) != SVNNodeKind.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CANNOT_MOVE_FILE_EXTERNAL,
"Cannot move the external at ''{0}''; please edit the svn:externals property on ''{1}''.", src, externalInfo.get(ExternalNodeInfo.definingAbsPath));
SVNErrorManager.error(err, SVNLogType.WC);
}
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) {
throw e;
}
}
}
}
verifyPaths(copyPairs, getOperation().isMakeParents(), getOperation().isMove());
if (getOperation().isMove()) {
sleepForTimeStamp = move(copyPairs);
} else {
File dstAncestor = null;
if (copyPairs.size() >= 1) {
dstAncestor = copyPairs.iterator().next().dst;
}
if (copyPairs.size() > 1 && dstAncestor != null) {
dstAncestor = SVNFileUtil.getParentFile(dstAncestor);
}
dstAncestor = dstAncestor.getAbsoluteFile();
dstAncestor = context.acquireWriteLock(dstAncestor, false, true);
try {
for (SvnCopyPair copyPair : copyPairs) {
checkCancelled();
File dstPath = SVNFileUtil.createFilePath(copyPair.dstParent, copyPair.baseName);
sleepForTimeStamp = sleepForTimeStamp || copy(context, copyPair.source, dstPath, getOperation().isMetadataOnly() || getOperation().isVirtual());
}
} finally {
context.releaseWriteLock(dstAncestor);
}
}
return false;
}
private boolean move(Collection<SvnCopyPair> pairs) throws SVNException {
for (SvnCopyPair copyPair : pairs) {
Collection<File> lockPaths = new HashSet<File>();
Collection<File> lockedPaths = new HashSet<File>();
checkCancelled();
File sourceParent = new File(SVNPathUtil.validateFilePath(SVNFileUtil.getParentFile(copyPair.source).getAbsolutePath()));
if (sourceParent.equals(copyPair.dstParent) ||
SVNWCUtils.isChild(sourceParent, copyPair.dstParent)) {
lockPaths.add(sourceParent);
} else if (SVNWCUtils.isChild(copyPair.dstParent, sourceParent)) {
lockPaths.add(copyPair.dstParent);
} else {
lockPaths.add(sourceParent);
lockPaths.add(copyPair.dstParent);
}
try {
for (File file : lockPaths) {
lockedPaths.add(getWcContext().acquireWriteLock(file, false, true));
}
move(getWcContext(), copyPair.source, SVNFileUtil.createFilePath(copyPair.dstParent, copyPair.baseName), getOperation().isMetadataOnly() || getOperation().isVirtual());
} finally {
for (File file : lockedPaths) {
getWcContext().releaseWriteLock(file);
}
}
}
return false;
}
private void verifyPaths(Collection<SvnCopyPair> copyPairs, boolean makeParents, boolean move) throws SVNException {
for (SvnCopyPair copyPair : copyPairs) {
SVNNodeKind dstKind = SvnWcDbCopy.readKind(getWcContext().getDb(), copyPair.dst, false, true);
if (dstKind != SVNNodeKind.NONE) {
SVNWCContext.NodePresence nodePresence = getWcContext().getNodePresence(copyPair.dst, false);
if (nodePresence.isExcluded || nodePresence.isServerExcluded) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Path ''{0}'' exists, but is excluded", copyPair.dst);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
} else {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' already exists", copyPair.dst);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
}
SVNFileType srcType = SVNFileType.getType(copyPair.source);
SVNFileType dstType = SVNFileType.getType(copyPair.dst);
if (!getOperation().isMetadataOnly()) {
if (getOperation().isVirtual()) {
verifyPathsExistenceForVirtualCopy(copyPair.source, copyPair.dst, srcType, dstType, copyPair, move);
} else {
final boolean caseOnlyRename = verifyPaths(srcType, dstType, copyPair, copyPairs.size(), move);
if (caseOnlyRename) {
return;
}
}
}
copyPair.dstParent = new File(SVNPathUtil.validateFilePath(SVNFileUtil.getParentFile(copyPair.dst).getAbsolutePath()));
copyPair.baseName = SVNFileUtil.getFileName(copyPair.dst);
SVNNodeKind dstParentKind = SvnWcDbCopy.readKind(getWcContext().getDb(), copyPair.dstParent, false, true);
if (makeParents && (dstParentKind == SVNNodeKind.NONE || getOperation().isVirtual())) {
SVNFileUtil.ensureDirectoryExists(copyPair.dstParent);
SvnScheduleForAddition add = getOperation().getOperationFactory().createScheduleForAddition();
add.setSingleTarget(SvnTarget.fromFile(copyPair.dstParent));
add.setDepth(getOperation().isVirtual() ? SVNDepth.EMPTY : SVNDepth.INFINITY);
add.setIncludeIgnored(true);
add.setForce(false);
add.setAddParents(true);
add.setSleepForTimestamp(false);
try {
add.run();
} catch (SVNException e) {
if (dstParentKind == SVNNodeKind.NONE) {
SVNFileUtil.deleteAll(copyPair.dstParent, true);
}
throw e;
}
} else if (dstParentKind != SVNNodeKind.DIR) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_PATH_NOT_FOUND, "Path ''{0}'' is not a directory", copyPair.dstParent);
SVNErrorManager.error(err, SVNLogType.WC);
}
dstParentKind = SVNFileType.getNodeKind(SVNFileType.getType(copyPair.dstParent));
if (dstParentKind != SVNNodeKind.DIR) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_MISSING, "Path ''{0}'' is not a directory", copyPair.dstParent);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
}
private boolean verifyPaths(SVNFileType srcType, SVNFileType dstType, SvnCopyPair copyPair, int copyPairsCount, boolean move) throws SVNException {
if (srcType == SVNFileType.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.NODE_UNKNOWN_KIND, "Path ''{0}'' does not exist", copyPair.source);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (dstType != SVNFileType.NONE) {
if (move && copyPairsCount == 1) {
File srcDir = SVNFileUtil.getFileDir(copyPair.source);
File dstDir = SVNFileUtil.getFileDir(copyPair.dst);
if (srcDir.equals(dstDir)) {
// check if it is case-only rename
if (copyPair.source.getName().equalsIgnoreCase(copyPair.dst.getName())) {
copyPair.dstParent = new File(SVNPathUtil.validateFilePath(dstDir.getAbsolutePath()));
copyPair.baseName = SVNFileUtil.getFileName(copyPair.dst);
return true;
}
}
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' already exists", copyPair.dst);
SVNErrorManager.error(err, SVNLogType.WC);
}
return false;
}
private void verifyPathsExistenceForVirtualCopy(File source, File dst, SVNFileType srcType, SVNFileType dstType, SvnCopyPair copyPair, boolean move) throws SVNException {
String opName = move ? "move" : "copy";
if (move && srcType != SVNFileType.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Cannot perform 'virtual' {0}: ''{1}'' still exists", new Object[] {
opName, copyPair.source
});
SVNErrorManager.error(err, SVNLogType.WC);
}
if (dstType == SVNFileType.NONE) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "Cannot perform 'virtual' {0}: ''{1}'' does not exist", new Object[] {
opName, copyPair.dst
});
SVNErrorManager.error(err, SVNLogType.WC);
}
if (dstType == SVNFileType.DIRECTORY) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "Cannot perform 'virtual' {0}: ''{1}'' is a directory", new Object[] {
opName, copyPair.dst
});
SVNErrorManager.error(err, SVNLogType.WC);
}
if (!move && srcType == SVNFileType.DIRECTORY) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "Cannot perform 'virtual' {0}: ''{1}'' is a directory", new Object[] {
opName, copyPair.source
});
SVNErrorManager.error(err, SVNLogType.WC);
}
final SvnStatus dstStatus = getStatus(dst);
if (dstStatus != null &&
(dstStatus.getNodeStatus() != SVNStatusType.STATUS_UNVERSIONED &&
dstStatus.getNodeStatus() != SVNStatusType.STATUS_ADDED &&
dstStatus.getNodeStatus() != SVNStatusType.STATUS_REPLACED)) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_ATTRIBUTE_INVALID, "Cannot perform 'virtual' {0}: ''{1}'' is scheduled neither for addition nor for replacement",
new Object[] {
opName, dst
});
SVNErrorManager.error(err, SVNLogType.WC);
}
final SvnStatus sourceStatus = getStatus(source);
if (sourceStatus == null || sourceStatus.getNodeStatus() == SVNStatusType.STATUS_UNVERSIONED) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "''{0}'' is not under version control", source);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
private SvnStatus getStatus(File interestingFile) throws SVNException {
final String interestingPath = SVNFileUtil.getFilePath(interestingFile);
final SvnStatus[] status2 = new SvnStatus[1];
final SvnOperationFactory operationFactory = getOperation().getOperationFactory();
final SvnGetStatus status = operationFactory.createGetStatus();
status.setDepth(SVNDepth.INFINITY);
status.setRemote(false);
status.setReportAll(true);
status.setReportIgnored(true);
status.setReportExternals(false);
status.setApplicalbeChangelists(null);
status.addTarget(SvnTarget.fromFile(interestingFile.getParentFile()));
status.setReceiver(new ISvnObjectReceiver<SvnStatus>() {
public void receive(SvnTarget target, SvnStatus svnStatus) throws SVNException {
if (svnStatus == null) {
return;
}
final File path = svnStatus.getPath();
if (path == null) {
return;
}
final String statusPath = SVNFileUtil.getFilePath(path);
if (statusPath.equals(interestingPath)) {
status2[0] = svnStatus;
}
}
});
status.run();
return status2[0];
}
public void move(SVNWCContext context, File source, File dst, boolean metadataOnly) throws SVNException {
boolean moveDegradedToCopy = copy(context, source, dst, true);
if (!metadataOnly) {
SVNFileUtil.rename(source, dst);
}
Structure<NodeInfo> nodeInfo = context.getDb().readInfo(source, NodeInfo.kind, NodeInfo.conflicted);
if (nodeInfo.get(NodeInfo.kind) == SVNWCDbKind.Dir) {
removeAllConflictMarkers(context.getDb(), source, dst);
}
if (nodeInfo.is(NodeInfo.conflicted)) {
removeAllConflictMarkers(context.getDb(), source, dst);
}
SvnNgRemove.delete(getWcContext(), source, moveDegradedToCopy ? null : dst, true, false, this);
}
private void removeAllConflictMarkers(ISVNWCDb db, File srcDirAbsPath, File wcDirAbsPath) throws SVNException {
Map<String, ISVNWCDb.SVNWCDbInfo> children = new HashMap<String, ISVNWCDb.SVNWCDbInfo>();
Set<String> conflicts = new HashSet<String>();
db.readChildren(srcDirAbsPath, children, conflicts);
for (Map.Entry<String, ISVNWCDb.SVNWCDbInfo> entry : children.entrySet()) {
String name = entry.getKey();
ISVNWCDb.SVNWCDbInfo info = entry.getValue();
if (info.conflicted) {
removeNodeConflictMarkers(db, SVNFileUtil.createFilePath(srcDirAbsPath, name), SVNFileUtil.createFilePath(wcDirAbsPath, name));
}
if (info.kind == SVNWCDbKind.Dir) {
removeAllConflictMarkers(db, SVNFileUtil.createFilePath(srcDirAbsPath, name), SVNFileUtil.createFilePath(wcDirAbsPath, name));
}
}
}
private void removeNodeConflictMarkers(ISVNWCDb db, File srcAbsPath, File nodeAbsPath) throws SVNException {
SVNSkel conflict = db.readConflict(srcAbsPath);
if (conflict != null) {
File srcDir = SVNFileUtil.getParentFile(srcAbsPath);
File dstDir = SVNFileUtil.getParentFile(nodeAbsPath);
List<File> markers = SvnWcDbConflicts.readConflictMarkers((SVNWCDb) db, srcAbsPath, conflict);
if (markers != null) {
for (File marker : markers) {
File childRelPath = SVNFileUtil.skipAncestor(srcDir, marker);
if (childRelPath != null) {
File childAbsPath = SVNFileUtil.createFilePath(dstDir, childRelPath);
SVNFileUtil.deleteFile(childAbsPath);
}
}
}
}
}
protected boolean copy(SVNWCContext context, File source, File dst, boolean metadataOnly) throws SVNException {
SvnCopy operation = getOperation();
boolean isMove = operation != null && operation.isMove();
boolean allowMixedRevisions = operation == null || operation.isAllowMixedRevisions();
File dstDirectory = SVNFileUtil.getParentFile(dst);
Structure<NodeInfo> srcInfo = null;
try {
srcInfo = context.getDb().readInfo(source, NodeInfo.status, NodeInfo.kind, NodeInfo.reposRelPath,
NodeInfo.reposRootUrl, NodeInfo.reposUuid, NodeInfo.checksum, NodeInfo.conflicted);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "''{0}'' is not under version control", source);
SVNErrorManager.error(err, SVNLogType.WC);
}
throw e;
}
File srcWcRootAbsPath = context.getDb().getWCRoot(source);
ISVNWCDb.SVNWCDbStatus srcStatus = srcInfo.get(NodeInfo.status);
switch (srcStatus) {
case Deleted:
if (!metadataOnly) {
SVNErrorMessage err1 = SVNErrorMessage.create(SVNErrorCode.WC_PATH_UNEXPECTED_STATUS,
"Deleted node ''{0}'' can''t be copied.", source);
SVNErrorManager.error(err1, SVNLogType.WC);
}
break;
case Excluded:
case ServerExcluded:
case NotPresent:
SVNErrorMessage err2 = SVNErrorMessage.create(SVNErrorCode.WC_PATH_NOT_FOUND,
"The node ''{0}'' was not found.", source);
SVNErrorManager.error(err2, SVNLogType.WC);
default:
break;
}
if (isMove && source.equals(srcWcRootAbsPath)) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_PATH_UNEXPECTED_STATUS, "''{0}'' is the root of a working copy and cannot be moved", source);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
File srcReposRelPath = srcInfo.get(NodeInfo.reposRelPath);
if (isMove && srcReposRelPath != null && "".equals(SVNFileUtil.getFilePath(srcReposRelPath))) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_PATH_UNEXPECTED_STATUS, "''{0}'' represents the repository root and cannot be moved", source);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
Structure<NodeInfo> dstDirInfo = null;
try {
dstDirInfo = context.getDb().
readInfo(dstDirectory, NodeInfo.status, NodeInfo.reposRootUrl, NodeInfo.reposUuid);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "''{0}'' is not under version control", dstDirectory);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
}
SVNURL dstReposRootUrl = dstDirInfo.get(NodeInfo.reposRootUrl);
String dstReposUuid = dstDirInfo.get(NodeInfo.reposUuid);
SVNWCDbStatus dstDirStatus = dstDirInfo.get(NodeInfo.status);
dstDirInfo.release();
SVNURL srcReposRootUrl = srcInfo.get(NodeInfo.reposRootUrl);
String srcReposUuid = srcInfo.get(NodeInfo.reposUuid);
File dstWcRootAbsPath = context.getDb().getWCRoot(dstDirectory);
if (srcReposRootUrl == null) {
if (srcStatus == SVNWCDbStatus.Added) {
Structure<AdditionInfo> additionInfo = SvnWcDbShared.scanAddition((SVNWCDb) context.getDb(), source);
srcReposRootUrl = additionInfo.get(AdditionInfo.reposRootUrl);
srcReposUuid = additionInfo.get(AdditionInfo.reposUuid);
additionInfo.release();
} else {
WCDbRepositoryInfo reposInfo = context.getDb().scanBaseRepository(source, RepositoryInfoField.rootUrl, RepositoryInfoField.uuid);
srcReposRootUrl = reposInfo.rootUrl;
srcReposUuid = reposInfo.uuid;
}
}
if (dstReposRootUrl == null) {
if (dstDirStatus == SVNWCDbStatus.Added) {
Structure<AdditionInfo> additionInfo = SvnWcDbShared.scanAddition((SVNWCDb) context.getDb(), dstDirectory);
dstReposRootUrl = additionInfo.get(AdditionInfo.reposRootUrl);
dstReposUuid = additionInfo.get(AdditionInfo.reposUuid);
additionInfo.release();
} else {
WCDbRepositoryInfo reposInfo = context.getDb().scanBaseRepository(dstDirectory, RepositoryInfoField.rootUrl, RepositoryInfoField.uuid);
dstReposRootUrl = reposInfo.rootUrl;
dstReposUuid = reposInfo.uuid;
}
}
if ((srcReposRootUrl != null && dstReposRootUrl != null && !srcReposRootUrl.equals(dstReposRootUrl)) ||
(srcReposUuid != null && dstReposUuid != null && !srcReposUuid.equals(dstReposUuid))) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE,
"Cannot copy to ''{0}'', as it is not from repository ''{1}''; it is from ''{2}''", dst, srcReposRootUrl, dstReposRootUrl);
SVNErrorManager.error(err, SVNLogType.WC);
}
if (dstDirStatus == SVNWCDbStatus.Deleted) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE,
"Cannot copy to ''{0}'', as it is scheduled for deletion", dst);
SVNErrorManager.error(err, SVNLogType.WC);
}
try {
Structure<NodeInfo> dstInfo = context.getDb().readInfo(dst, NodeInfo.status);
SVNWCDbStatus dstStatus = dstInfo.get(NodeInfo.status);
switch (dstStatus) {
case Excluded:
SVNErrorMessage err1 = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS,
"''{0}'' is already under version control but is excluded.", dst);
SVNErrorManager.error(err1, SVNLogType.WC);
break;
case ServerExcluded:
SVNErrorMessage err2 = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS,
"''{0}'' is already under version control", dst);
SVNErrorManager.error(err2, SVNLogType.WC);
break;
case Deleted:
case NotPresent:
break;
default:
if (!metadataOnly) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS,
"There is already a versioned item ''{0}''", dst);
SVNErrorManager.error(err, SVNLogType.WC);
break;
}
}
dstInfo.release();
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) {
throw e;
}
}
if (!metadataOnly) {
SVNFileType dstType = SVNFileType.getType(dst);
if (dstType != SVNFileType.NONE) {
SVNErrorMessage err2 = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS,
"''{0}'' already exists and is in the way", dst);
SVNErrorManager.error(err2, SVNLogType.WC);
}
}
File tmpDir = context.getDb().getWCRootTempDir(dstDirectory);
boolean withinOneWc = srcWcRootAbsPath.equals(dstWcRootAbsPath);
boolean moveDegradedToCopy = false;
if (isMove && !withinOneWc) {
moveDegradedToCopy = true;
isMove = false;
}
if (!withinOneWc) {
SvnWcDbPristines.transferPristine((SVNWCDb)context.getDb(), source, dstWcRootAbsPath);
}
if (srcInfo.get(NodeInfo.kind) == SVNWCDbKind.File || srcInfo.get(NodeInfo.kind) == SVNWCDbKind.Symlink) {
final boolean shouldCopyBaseData = shouldCopyBaseData(context, source, metadataOnly, srcStatus);
if (shouldCopyBaseData && getOperation().isVirtual()) {//we check for "virtual" to preserve current behaviour in this case
copyBaseDataOfFile(context, source, dst);
} else {
copyVersionedFile(context, source, dst, dst, tmpDir, metadataOnly, srcInfo.is(NodeInfo.conflicted), isMove, true);
}
} else {
if (isMove && srcStatus == SVNWCDbStatus.Normal) {
long[] minMaxRevisions = context.getDb().minMaxRevisions(source, false);
long minRevision = minMaxRevisions[0];
long maxRevision = minMaxRevisions[1];
if (SVNRevision.isValidRevisionNumber(minRevision) && SVNRevision.isValidRevisionNumber(maxRevision) && minRevision != maxRevision) {
if (!allowMixedRevisions) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_MIXED_REVISIONS,
"Cannot move mixed-revision subtree ''{0}'' [{1}:{2}]; try updating it first", source, minRevision, maxRevision);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
isMove = false;
moveDegradedToCopy = true;
}
}
if (srcStatus == SVNWCDbStatus.Deleted && metadataOnly) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "Cannot perform 'virtual' {0}: ''{1}'' is a directory", new Object[]{
isMove ? "move" : "copy", source
});
SVNErrorManager.error(err, SVNLogType.WC);
} else {
copyVersionedDirectory(context, source, dst, dst, tmpDir, metadataOnly, isMove, true);
}
}
if (isMove) {
context.getDb().opHandleMoveBack(dst, source, null);
}
context.wqRun(dst);
return moveDegradedToCopy;
}
private boolean shouldCopyBaseData(SVNWCContext context, File source, boolean metadataOnly, SVNWCDbStatus srcStatus) throws SVNException {
if (!metadataOnly) {
return false;
}
if (srcStatus == SVNWCDbStatus.Deleted) {
return true;
}
final SvnStatus svnStatus = SVNStatusEditor17.internalStatus(context, source);
return svnStatus != null && svnStatus.getNodeStatus() == SVNStatusType.STATUS_REPLACED;
}
private void copyBaseDataOfFile(SVNWCContext context, File source, File dst) throws SVNException {
final SVNProperties pristineProps = context.getPristineProps(source);
final ISVNWCDb.WCDbBaseInfo baseInfo = context.getDb().getBaseInfo(source,
ISVNWCDb.WCDbBaseInfo.BaseInfoField.changedAuthor, ISVNWCDb.WCDbBaseInfo.BaseInfoField.changedDate, ISVNWCDb.WCDbBaseInfo.BaseInfoField.changedRev,
ISVNWCDb.WCDbBaseInfo.BaseInfoField.checksum, ISVNWCDb.WCDbBaseInfo.BaseInfoField.revision,
ISVNWCDb.WCDbBaseInfo.BaseInfoField.reposRootUrl, ISVNWCDb.WCDbBaseInfo.BaseInfoField.reposUuid);
final String changedAuthor = baseInfo.changedAuthor;
final SVNDate changedDate = baseInfo.changedDate;
final long changedRev = baseInfo.changedRev;
final SvnChecksum checksum = baseInfo.checksum;
final long revision = baseInfo.revision;
final SVNURL reposRootUrl = baseInfo.reposRootUrl;
final String reposUuid = baseInfo.reposUuid;
context.getDb().opCopyFile(dst, pristineProps, changedRev, changedDate, changedAuthor,
context.getNodeReposRelPath(source.getAbsoluteFile()), reposRootUrl, reposUuid, revision, checksum, false, null, null, null);
final SVNEvent event = SVNEventFactory.createSVNEvent(dst, SVNNodeKind.FILE, null, SVNRepository.INVALID_REVISION, SVNEventAction.COPY, null, null, null);
handleEvent(event);
}
private void copyVersionedDirectory(SVNWCContext wcContext, File srcAbsPath, File dstAbsPath, File dstOpRootAbsPath, File tmpDirAbsPath, boolean metadataOnly, boolean isMove, boolean notify) throws SVNException {
SVNSkel workItems = null;
File dirAbsPath = SVNFileUtil.getParentFile(dstAbsPath);
SVNNodeKind diskKind = SVNNodeKind.UNKNOWN;
if (!metadataOnly) {
CopyToTmpDir copyToTmpDir = copyToTmpDir(srcAbsPath, dstAbsPath, tmpDirAbsPath, false, false);
workItems = copyToTmpDir.workItem;
diskKind = copyToTmpDir.kind;
}
wcContext.getDb().opCopy(srcAbsPath, dstAbsPath, dstOpRootAbsPath, isMove, workItems);
if (notify && wcContext.getEventHandler() != null) {
SVNEvent event = SVNEventFactory.createSVNEvent(dstAbsPath, SVNNodeKind.DIR, null, SVNRepository.INVALID_REVISION, SVNEventAction.ADD, SVNEventAction.ADD, null, null);
if (workItems != null) {
wcContext.wqRun(dirAbsPath);
}
wcContext.getEventHandler().handleEvent(event, UNKNOWN);
}
Set<String> diskChildren = null;
if (!metadataOnly && diskKind == SVNNodeKind.DIR) {
File[] files = SVNFileListUtil.listFiles(srcAbsPath);
if (files != null) {
diskChildren = new HashSet<String>();
for (File file : files) {
diskChildren.add(SVNFileUtil.getFileName(file));
}
} else {
diskChildren = null;
}
} else {
diskChildren = null;
}
Map<String, ISVNWCDb.SVNWCDbInfo> versionedChildren = new HashMap<String, ISVNWCDb.SVNWCDbInfo>();
Set<String> conflictedChildren = new HashSet<String>();
wcContext.getDb().readChildren(srcAbsPath, versionedChildren, conflictedChildren);
for (final Map.Entry<String, ISVNWCDb.SVNWCDbInfo> entry : versionedChildren.entrySet()) {
if (wcContext.getEventHandler() != null) {
wcContext.getEventHandler().checkCancelled();
}
String childName = entry.getKey();
ISVNWCDb.SVNWCDbInfo info = entry.getValue();
File childSrcAbsPath = SVNFileUtil.createFilePath(srcAbsPath, childName);
File childDstAbsPath = SVNFileUtil.createFilePath(dstAbsPath, childName);
if (info.opRoot) {
wcContext.getDb().opCopyShadowedLayer(childSrcAbsPath, childDstAbsPath, isMove);
}
if (info.status == SVNWCDbStatus.Normal || info.status == SVNWCDbStatus.Added) {
if (info.kind == SVNWCDbKind.File) {
if (!info.fileExternal) {
copyVersionedFile(wcContext, childSrcAbsPath, childDstAbsPath, dstOpRootAbsPath, tmpDirAbsPath, metadataOnly, info.conflicted, isMove, false);
}
} else if (info.kind == SVNWCDbKind.Dir) {
copyVersionedDirectory(wcContext, childSrcAbsPath, childDstAbsPath, dstOpRootAbsPath, tmpDirAbsPath, metadataOnly, isMove, false);
} else {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.NODE_UNEXPECTED_KIND, "cannot handle node kind for ''{0}''", childSrcAbsPath);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
} else if (info.status == SVNWCDbStatus.Deleted || info.status == SVNWCDbStatus.NotPresent || info.status == SVNWCDbStatus.Excluded) {
wcContext.getDb().opCopy(childSrcAbsPath, childDstAbsPath, dstOpRootAbsPath, isMove, null);
} else if (info.status == SVNWCDbStatus.Incomplete) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_PATH_UNEXPECTED_STATUS, "Cannot handle status of ''{0}''", childSrcAbsPath);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
} else {
assert (info.status == SVNWCDbStatus.ServerExcluded);
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_PATH_UNEXPECTED_STATUS, "Cannot copy ''{0}'' excluded by server", childSrcAbsPath);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
}
if (diskChildren != null && (info.status == SVNWCDbStatus.Normal || info.status == SVNWCDbStatus.Added)) {
diskChildren.remove(childName);
}
}
if (diskChildren != null && diskChildren.size() > 0) {
List<File> markerFiles = wcContext.getDb().getConflictMarkerFiles(srcAbsPath);
workItems = null;
for (String name : diskChildren) {
name = SVNPathUtil.tail(name);
if (name.equals(SVNFileUtil.getAdminDirectoryName())) {
continue;
}
if (wcContext.getEventHandler() != null) {
wcContext.getEventHandler().checkCancelled();
}
File unverSrcAbsPath = SVNFileUtil.createFilePath(srcAbsPath, name);
File unverDstAbsPath = SVNFileUtil.createFilePath(dstAbsPath, name);
if (markerFiles != null && markerFiles.contains(unverSrcAbsPath)) {
continue;
}
CopyToTmpDir copyToTmpDir = copyToTmpDir(unverSrcAbsPath, unverDstAbsPath, tmpDirAbsPath, true, true);
SVNSkel workItem = copyToTmpDir.workItem;
if (workItem != null) {
workItems = SVNWCContext.wqMerge(workItems, workItem);
}
}
wcContext.getDb().addWorkQueue(dstAbsPath, workItems);
}
}
private void copyVersionedFile(SVNWCContext wcContext, File srcAbsPath, File dstAbsPath, File dstOpRootAbsPath, File tmpDirAbsPath, boolean metadataOnly, boolean conflicted, boolean isMove, boolean notify) throws SVNException {
SVNSkel workItems = null;
if (!metadataOnly) {
File mySrcAbsPath = srcAbsPath;
boolean handleAsUnversioned = false;
if (conflicted) {
SVNSkel conflict = wcContext.getDb().readConflict(srcAbsPath);
File conflictWorking;
try {
Structure<SvnWcDbConflicts.TextConflictInfo> conflictInfoStructure = SvnWcDbConflicts.readTextConflict(wcContext.getDb(), srcAbsPath, conflict);
conflictWorking = conflictInfoStructure.get(SvnWcDbConflicts.TextConflictInfo.mineAbsPath);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_MISSING) {
throw e;
}
conflictWorking = null;
}
if (conflictWorking != null) {
if (SVNFileType.getType(conflictWorking) == SVNFileType.FILE) {
handleAsUnversioned = true;
mySrcAbsPath = conflictWorking;
}
}
}
CopyToTmpDir copyToTmpDir = copyToTmpDir(mySrcAbsPath, dstAbsPath, tmpDirAbsPath, true, handleAsUnversioned);
workItems = copyToTmpDir.workItem;
}
wcContext.getDb().opCopy(srcAbsPath, dstAbsPath, dstOpRootAbsPath, isMove, workItems);
wcContext.wqRun(SVNFileUtil.getParentFile(dstAbsPath));
if (notify) {
if (workItems != null) {
getWcContext().wqRun(dstAbsPath);
}
SVNEvent event = SVNEventFactory.createSVNEvent(dstAbsPath, SVNNodeKind.FILE, null, -1, SVNEventAction.ADD, SVNEventAction.ADD, null, null, 1, 1);
handleEvent(event, -1);
}
}
private CopyToTmpDir copyToTmpDir(File srcAbsPath, File dstAbsPath, File tmpDirAbsPath, boolean fileCopy, boolean unversioned) throws SVNException {
boolean deleteOnClose = false;
CopyToTmpDir copyToTmpDir = new CopyToTmpDir();
copyToTmpDir.workItem = null;
SVNFileType type = SVNFileType.getType(srcAbsPath);
copyToTmpDir.kind = SVNFileType.getNodeKind(type);
boolean isSpecial = type == SVNFileType.SYMLINK;
if (copyToTmpDir.kind == SVNNodeKind.NONE) {
return copyToTmpDir;
} else if (copyToTmpDir.kind == SVNNodeKind.UNKNOWN) {
SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.NODE_UNEXPECTED_KIND, "Source ''{0}'' is unexpected kind", srcAbsPath);
SVNErrorManager.error(errorMessage, SVNLogType.WC);
} else if (copyToTmpDir.kind == SVNNodeKind.DIR) {
deleteOnClose = true;
} else {
deleteOnClose = false;
}
File dstTmpAbsPath = null;
try {
if (fileCopy && !unversioned) {
boolean modified = getWcContext().isTextModified(srcAbsPath, false) || getWcContext().isPropsModified(srcAbsPath);
if (!modified) {
copyToTmpDir.workItem = getWcContext().wqBuildFileInstall(dstAbsPath, null, false, true);
return copyToTmpDir;
}
}
dstTmpAbsPath = copyToTmpDir.kind == SVNNodeKind.DIR ? SVNFileUtil.createUniqueDir(tmpDirAbsPath, SVNFileUtil.getFileName(srcAbsPath), ".tmp", false) : SVNFileUtil.createUniqueFile(tmpDirAbsPath, SVNFileUtil.getFileName(srcAbsPath), ".tmp", false);
if (copyToTmpDir.kind == SVNNodeKind.DIR) {
if (fileCopy) {
SVNFileUtil.copyDirectory(srcAbsPath, dstTmpAbsPath, false, getOperation().getEventHandler());
} else {
SVNFileUtil.ensureDirectoryExists(dstTmpAbsPath);
}
} else if (!isSpecial) {
SVNFileUtil.copyFile(srcAbsPath, dstTmpAbsPath, false, true);
} else {
SVNFileUtil.deleteFile(dstTmpAbsPath);
SVNFileUtil.copySymlink(srcAbsPath, dstTmpAbsPath);
}
if (fileCopy) {
SVNFileUtil.setReadonly(dstTmpAbsPath, false);
}
copyToTmpDir.workItem = getWcContext().wqBuildFileMove(dstAbsPath, dstTmpAbsPath, dstAbsPath);
return copyToTmpDir;
} finally {
if (dstTmpAbsPath != null && deleteOnClose && copyToTmpDir.kind == SVNNodeKind.FILE) {
SVNFileUtil.deleteFile(dstTmpAbsPath);
}
}
}
private static class CopyToTmpDir {
SVNSkel workItem;
SVNNodeKind kind;
}
private static class SvnCopyPair {
File source;
File dst;
File dstParent;
String baseName;
}
}