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; } }