package org.tmatesoft.svn.core.internal.wc2.ng;
import org.tmatesoft.svn.core.*;
import org.tmatesoft.svn.core.internal.db.SVNSqlJetDb;
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.SVNFileType;
import org.tmatesoft.svn.core.internal.wc.SVNFileUtil;
import org.tmatesoft.svn.core.internal.wc17.SVNWCContext;
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.StructureFields.NodeInfo;
import org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbRevert.RevertInfo;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc2.SvnRevert;
import org.tmatesoft.svn.core.wc2.SvnTarget;
import org.tmatesoft.svn.util.SVNLogType;
import java.io.File;
import java.util.*;
public class SvnNgRevert extends SvnNgOperationRunner<Void, SvnRevert> {
@Override
protected Void run(SVNWCContext context) throws SVNException {
boolean useCommitTimes = getOperation().getOptions().isUseCommitTimes();
for (SvnTarget target : getOperation().getTargets()) {
checkCancelled();
boolean isWcRoot = context.getDb().isWCRoot(target.getFile());
File lockTarget = isWcRoot ? target.getFile() : SVNFileUtil.getParentFile(target.getFile());
File lockRoot = context.acquireWriteLock(lockTarget, false, true);
try {
revert(target.getFile(), getOperation().getDepth(), useCommitTimes, getOperation().getApplicableChangelists());
} catch (SVNException e) {
SVNErrorMessage err = e.getErrorMessage();
if (err.getErrorCode() == SVNErrorCode.ENTRY_NOT_FOUND
|| err.getErrorCode() == SVNErrorCode.UNVERSIONED_RESOURCE
|| err.getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) {
SVNEvent event = SVNEventFactory.createSVNEvent(target.getFile(), SVNNodeKind.NONE, null, -1, SVNEventAction.SKIP, SVNEventAction.REVERT, err, null, -1, -1);
handleEvent(event);
continue;
}
if (!useCommitTimes) {
sleepForTimestamp();
}
throw e;
} finally {
context.releaseWriteLock(lockRoot);
}
}
if (!useCommitTimes) {
sleepForTimestamp();
}
return null;
}
private void revert(File localAbsPath, SVNDepth depth, boolean useCommitTimes, Collection<String> changelists) throws SVNException {
if (changelists != null && changelists.size() > 0) {
revertChangelist(localAbsPath, depth, useCommitTimes, changelists);
return;
}
if (depth == SVNDepth.EMPTY || depth == SVNDepth.INFINITY) {
revert(localAbsPath, depth, useCommitTimes);
return;
}
if (depth == SVNDepth.IMMEDIATES || depth == SVNDepth.FILES) {
revert(localAbsPath, SVNDepth.EMPTY, useCommitTimes);
Set<String> children = ((SVNWCDb) getWcContext().getDb()).getWorkingChildren(localAbsPath);
for (String childName : children) {
File childAbsPath = SVNFileUtil.createFilePath(localAbsPath, childName);
if (depth == SVNDepth.FILES) {
SVNNodeKind childKind = getWcContext().readKind(childAbsPath, true);
if (childKind != SVNNodeKind.FILE) {
continue;
}
}
revert(childAbsPath, SVNDepth.EMPTY, useCommitTimes);
}
return;
}
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OPERATION_DEPTH);
SVNErrorManager.error(err, SVNLogType.WC);
}
private void revertChangelist(File localAbsPath, SVNDepth depth, boolean useCommitTimes, Collection<String> changelists) throws SVNException {
checkCancelled();
if (getWcContext().isChangelistMatch(localAbsPath, changelists)) {
revert(localAbsPath, SVNDepth.EMPTY, useCommitTimes);
}
if (depth == SVNDepth.EMPTY) {
return;
}
if (depth == SVNDepth.FILES || depth == SVNDepth.IMMEDIATES) {
depth = SVNDepth.EMPTY;
}
Set<String> children = ((SVNWCDb) getWcContext().getDb()).getWorkingChildren(localAbsPath);
for (String childName : children) {
File childAbsPath = SVNFileUtil.createFilePath(localAbsPath, childName);
revertChangelist(childAbsPath, depth, useCommitTimes, changelists);
}
}
private void revert(File localAbsPath, SVNDepth depth, boolean useCommitTimes) throws SVNException {
assert depth == SVNDepth.EMPTY || depth == SVNDepth.INFINITY;
File dirAbsPath;
boolean isWcRoot = getWcContext().getDb().isWCRoot(localAbsPath);
if (!isWcRoot) {
dirAbsPath = SVNFileUtil.getFileDir(localAbsPath);
} else {
dirAbsPath = localAbsPath;
}
getWcContext().writeCheck(dirAbsPath);
try {
getWcContext().getDb().opRevert(localAbsPath, depth);
// we should detect that the copy was modified here
// after opRevert() call we won't be able to do that
// final Set<File> modifiedCopiesThatShouldBePreserved = new HashSet<File>();
// populateModifiedCopiesThatShouldBePreserved(localAbsPath, wcRoot, modifiedCopiesThatShouldBePreserved);
restore(getWcContext(), localAbsPath, depth, useCommitTimes, true, getWcContext().getEventHandler());
} finally {
SvnWcDbRevert.dropRevertList(getWcContext(), localAbsPath);
}
}
private void populateModifiedCopiesThatShouldBePreserved(File localAbsPath, File wcRoot, Set<File> modifiedCopiesThatShouldBePreserved) throws SVNException {
if (!getOperation().isPreserveModifiedCopies()) {
return;
}
try {
final ISVNWCDb.WCDbInfo wcDbInfo = getWcContext().getDb().readInfo(localAbsPath, ISVNWCDb.WCDbInfo.InfoField.checksum,
ISVNWCDb.WCDbInfo.InfoField.originalRootUrl, ISVNWCDb.WCDbInfo.InfoField.propsMod,
ISVNWCDb.WCDbInfo.InfoField.lastModTime, ISVNWCDb.WCDbInfo.InfoField.translatedSize,
ISVNWCDb.WCDbInfo.InfoField.kind);
final SVNNodeKind nodeKind = SVNFileType.getNodeKind(SVNFileType.getType(localAbsPath));
if (nodeKind == SVNNodeKind.NONE ) {
//there's nothing to preserve
} else if (nodeKind == SVNNodeKind.DIR) {
if (wcDbInfo.kind == SVNWCDbKind.Dir) {
final List<File> children = getWcContext().getChildrenOfWorkingNode(localAbsPath, false);
for (File child : children) {
populateModifiedCopiesThatShouldBePreserved(child, wcRoot, modifiedCopiesThatShouldBePreserved);
}
}
} else {
if (wcDbInfo.kind != SVNWCDbKind.File) {
//obstruction, nothing to preserve
return;
}
if (wcDbInfo.propsMod) {
modifiedCopiesThatShouldBePreserved.add(localAbsPath);
return;
}
if (wcDbInfo.originalRootUrl != null && wcDbInfo.checksum != null) {
//now we are sure that the file is copied
long fileSize = SVNFileUtil.getFileLength(localAbsPath);
long fileTime = SVNFileUtil.getFileLastModifiedMicros(localAbsPath);
if (wcDbInfo.translatedSize != -1 && wcDbInfo.lastModTime != 0 &&
wcDbInfo.translatedSize == fileSize && wcDbInfo.lastModTime == fileTime) {
//not modified
} else {
SVNWCDb db = (SVNWCDb) getWcContext().getDb();
SVNWCDb.DirParsedInfo dirParsedInfo = db.parseDir(wcRoot, SVNSqlJetDb.Mode.ReadOnly);
File pristineFileName = SvnWcDbPristines.getPristineFileName(dirParsedInfo.wcDbDir.getWCRoot(), wcDbInfo.checksum, false);
if (getWcContext().compareAndVerify(localAbsPath, pristineFileName, true, false, false)) {
modifiedCopiesThatShouldBePreserved.add(localAbsPath);
}
}
}
}
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() != SVNErrorCode.ENTRY_NOT_FOUND && e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) {
throw e;
}
}
}
public static void restore(SVNWCContext context, File localAbsPath, SVNDepth depth, boolean useCommitTimes, boolean revertRoot, ISVNEventHandler notifier) throws SVNException {
restore(context, localAbsPath, depth, useCommitTimes, revertRoot, notifier, Collections.<File>emptySet());
}
public static void restore(SVNWCContext context, File localAbsPath, SVNDepth depth, boolean useCommitTimes, boolean revertRoot, ISVNEventHandler notifier, Set<File> modifiedCopiesThatShouldBePreserved) throws SVNException {
context.checkCancelled();
boolean isWcRoot = context.getDb().isWCRoot(localAbsPath);
if (isWcRoot && !revertRoot) {
if (notifier != null) {
SVNEvent event = SVNEventFactory.createSVNEvent(localAbsPath, SVNNodeKind.UNKNOWN, null, -1, SVNEventAction.UPDATE_SKIP_OBSTRUCTION, SVNEventAction.UPDATE_SKIP_OBSTRUCTION, null, null);
notifier.handleEvent(event, ISVNEventHandler.UNKNOWN);
}
return;
}
Structure<RevertInfo> revertInfo = SvnWcDbRevert.readRevertInfo(context, localAbsPath);
ISVNWCDb.SVNWCDbStatus status = SVNWCDbStatus.Normal;
ISVNWCDb.SVNWCDbKind kind = SVNWCDbKind.Unknown;
long recordedSize = -1;
long recordedTime = 0;
boolean notifyRequired = revertInfo.is(RevertInfo.reverted);
List<File> conflictFiles = revertInfo.get(RevertInfo.markerFiles);
try {
Structure<NodeInfo> nodeInfo = context.getDb().readInfo(localAbsPath, NodeInfo.status, NodeInfo.kind,
NodeInfo.recordedSize, NodeInfo.recordedTime);
status = nodeInfo.get(NodeInfo.status);
kind = nodeInfo.get(NodeInfo.kind);
recordedSize = nodeInfo.lng(NodeInfo.recordedSize);
recordedTime = nodeInfo.lng(NodeInfo.recordedTime);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) {
if (!revertInfo.is(RevertInfo.copiedHere)) {
if (notifyRequired && notifier != null) {
notifier.handleEvent(SVNEventFactory.createSVNEvent(localAbsPath, SVNNodeKind.NONE, null, -1, SVNEventAction.REVERT,
SVNEventAction.REVERT, null, null, -1, -1), -1);
}
if (notifier != null) {
SvnWcDbRevert.notifyRevert(context, localAbsPath, notifier);
}
return;
}
} else {
throw e;
}
}
SVNFileType filetype = SVNFileType.getType(localAbsPath);
SVNNodeKind onDisk = null;
boolean special = false;
if (filetype == SVNFileType.NONE) {
onDisk = SVNNodeKind.NONE;
special = false;
} else {
if (filetype == SVNFileType.FILE || filetype == SVNFileType.SYMLINK) {
onDisk = SVNNodeKind.FILE;
} else if (filetype == SVNFileType.DIRECTORY) {
onDisk = SVNNodeKind.DIR;
} else {
onDisk = SVNNodeKind.UNKNOWN;
}
special = filetype == SVNFileType.SYMLINK;
}
if (revertInfo.is(RevertInfo.copiedHere)) {
if (revertInfo.get(RevertInfo.kind) == SVNWCDbKind.File && onDisk == SVNNodeKind.FILE) {
if (!modifiedCopiesThatShouldBePreserved.contains(localAbsPath)) {
SVNFileUtil.deleteFile(localAbsPath);
}
onDisk = SVNNodeKind.NONE;
} else if (revertInfo.get(RevertInfo.kind) == SVNWCDbKind.Dir && onDisk == SVNNodeKind.DIR) {
boolean removed = restoreCopiedDirectory(context, localAbsPath, true, modifiedCopiesThatShouldBePreserved);
if (removed) {
onDisk = SVNNodeKind.NONE;
}
}
}
if (onDisk != SVNNodeKind.NONE
&& status != SVNWCDbStatus.ServerExcluded
&& status != SVNWCDbStatus.Deleted
&& status != SVNWCDbStatus.Excluded
&& status != SVNWCDbStatus.NotPresent) {
if (onDisk == SVNNodeKind.DIR && kind != SVNWCDbKind.Dir) {
SVNFileUtil.deleteAll(localAbsPath, true, notifier);
onDisk = SVNNodeKind.NONE;
} else if (onDisk == SVNNodeKind.FILE && kind != SVNWCDbKind.File) {
SVNFileUtil.deleteFile(localAbsPath);
onDisk = SVNNodeKind.NONE;
} else if (onDisk == SVNNodeKind.FILE) {
SVNProperties pristineProperties = context.getDb().readPristineProperties(localAbsPath);
boolean modified = false;
String specialProperty = pristineProperties.getStringValue(SVNProperty.SPECIAL);
if (SVNFileUtil.symlinksSupported() && (specialProperty != null) != special) {
SVNFileUtil.deleteFile(localAbsPath);
onDisk = SVNNodeKind.NONE;
} else {
long lastModified = SVNFileUtil.getFileLastModifiedMicros(localAbsPath);
long size = SVNFileUtil.getFileLength(localAbsPath);
if (recordedSize != -1
&& recordedTime != 0
&& recordedSize == size
&& recordedTime == lastModified) {
modified = false;
} else {
modified = context.isTextModified(localAbsPath, true);
}
}
if (modified) {
SVNFileUtil.deleteFile(localAbsPath);
onDisk = SVNNodeKind.NONE;
} else {
if (status == SVNWCDbStatus.Normal) {
boolean isReadOnly = filetype != SVNFileType.SYMLINK && !localAbsPath.canWrite();
boolean needsLock = pristineProperties.getStringValue(SVNProperty.NEEDS_LOCK) != null;
if (needsLock && !isReadOnly) {
SVNFileUtil.setReadonly(localAbsPath, true);
notifyRequired = true;
} else if (!needsLock && isReadOnly) {
SVNFileUtil.setReadonly(localAbsPath, false);
notifyRequired = true;
}
}
}
if (!(SVNFileUtil.isWindows || SVNFileUtil.isOpenVMS) && (!SVNFileUtil.symlinksSupported() || !special)) {
boolean executable = SVNFileUtil.isExecutable(localAbsPath);
boolean executableProperty = pristineProperties.getStringValue(SVNProperty.EXECUTABLE) != null;
if (executableProperty && !executable) {
SVNFileUtil.setExecutable(localAbsPath, true);
notifyRequired = true;
} else if (!executableProperty && executable) {
SVNFileUtil.setExecutable(localAbsPath, false);
notifyRequired = true;
}
}
}
}
if (onDisk == SVNNodeKind.NONE
&& status != SVNWCDbStatus.ServerExcluded
&& status != SVNWCDbStatus.Deleted
&& status != SVNWCDbStatus.Excluded
&& status != SVNWCDbStatus.NotPresent) {
if (kind == SVNWCDbKind.Dir) {
SVNFileUtil.ensureDirectoryExists(localAbsPath);
} else if (kind == SVNWCDbKind.File) {
SVNSkel workItem = context.wqBuildFileInstall(localAbsPath, null, useCommitTimes, true);
context.getDb().addWorkQueue(localAbsPath, workItem);
context.wqRun(localAbsPath);
}
notifyRequired = true;
}
if (conflictFiles != null) {
for (File conflictFile : conflictFiles) {
notifyRequired |= SVNFileUtil.deleteFile(conflictFile);
}
}
if (notifyRequired && notifier != null) {
notifier.handleEvent(SVNEventFactory.createSVNEvent(localAbsPath, SVNNodeKind.NONE, null, -1, SVNEventAction.REVERT,
SVNEventAction.REVERT, null, null, -1, -1), -1);
}
if (depth == SVNDepth.INFINITY && kind == SVNWCDbKind.Dir) {
restoreCopiedDirectory(context, localAbsPath, false, modifiedCopiesThatShouldBePreserved);
Set<String> children = ((SVNWCDb) context.getDb()).getChildrenOfWorkingNode(localAbsPath);
for (String childName : children) {
File childAbsPath = SVNFileUtil.createFilePath(localAbsPath, childName);
restore(context, childAbsPath, depth, useCommitTimes, false, notifier, modifiedCopiesThatShouldBePreserved);
}
}
SvnWcDbRevert.notifyRevert(context, localAbsPath, notifier);
}
private static boolean restoreCopiedDirectory(SVNWCContext context, File localAbsPath, boolean removeSelf, Set<File> modifiedCopiesThatShouldBePreserved) throws SVNException {
boolean selfRemoved = false;
Map<File, SVNWCDbKind> children = SvnWcDbRevert.readRevertCopiedChildren(context, localAbsPath);
for (File child : children.keySet()) {
context.checkCancelled();
SVNWCDbKind childKind = children.get(child);
if (childKind != SVNWCDbKind.File) {
continue;
}
SVNFileType childFileType = SVNFileType.getType(child);
if (childFileType != SVNFileType.FILE && childFileType != SVNFileType.SYMLINK) {
continue;
}
if (!modifiedCopiesThatShouldBePreserved.contains(child)) {
SVNFileUtil.deleteFile(child);
} else {
selfRemoved = false;
removeSelf = false;
}
}
for (File child : children.keySet()) {
context.checkCancelled();
SVNWCDbKind childKind = children.get(child);
if (childKind != SVNWCDbKind.Dir) {
continue;
}
if (!modifiedCopiesThatShouldBePreserved.contains(child)) {
SVNFileUtil.deleteFile(child);
} else {
selfRemoved = false;
removeSelf = false;
}
}
if (removeSelf) {
SVNFileUtil.deleteFile(localAbsPath);
if (SVNFileType.getType(localAbsPath) == SVNFileType.NONE) {
selfRemoved = true;
}
}
return selfRemoved;
}
}