/* * ==================================================================== * Copyright (c) 2004-2010 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.wc17; import java.io.File; import java.util.HashSet; import java.util.Map; import java.util.Set; 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.SVNURL; import org.tmatesoft.svn.core.internal.util.SVNSkel; 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.db.ISVNWCDb.SVNWCDbKind; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbLock; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.SVNWCDbStatus; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbBaseInfo; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbBaseInfo.BaseInfoField; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbInfo; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb.WCDbInfo.InfoField; 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.SVNWCDb; import org.tmatesoft.svn.core.internal.wc17.db.SVNWCDb.DirParsedInfo; import org.tmatesoft.svn.core.internal.wc17.db.SVNWCDbRoot; import org.tmatesoft.svn.core.internal.wc17.db.Structure; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.AdditionInfo; import org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbShared; import org.tmatesoft.svn.core.io.ISVNReporter; import org.tmatesoft.svn.core.io.ISVNReporterBaton; import org.tmatesoft.svn.core.wc.SVNConflictChoice; import org.tmatesoft.svn.core.wc.SVNEventAction; import org.tmatesoft.svn.core.wc2.SvnChecksum; import org.tmatesoft.svn.util.ISVNDebugLog; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNReporter17 implements ISVNReporterBaton { private final File path; private SVNWCContext wcContext; private SVNDepth depth; private final boolean isRestoreFiles; private final boolean isUseDepthCompatibilityTrick; private final boolean isHonorDepthExclude; private boolean isUseCommitTimes; private int reportedFilesCount; private int totalFilesCount; public SVNReporter17(File path, SVNWCContext wcContext, boolean restoreFiles, boolean useDepthCompatibilityTrick, SVNDepth depth, boolean lockOnDemand, boolean isStatus, boolean isHonorDepthExclude, boolean isUseCommitTimes, ISVNDebugLog log) { this.path = path; this.wcContext = wcContext; this.isRestoreFiles = restoreFiles; this.isUseDepthCompatibilityTrick = useDepthCompatibilityTrick; this.depth = depth; this.isHonorDepthExclude = isHonorDepthExclude; this.isUseCommitTimes = isUseCommitTimes; } public int getReportedFilesCount() { return reportedFilesCount; } public int getTotalFilesCount() { return totalFilesCount; } public void report(ISVNReporter reporter) throws SVNException { assert (SVNWCDb.isAbsolute(path)); SVNWCDbStatus status = null; SVNWCDbKind target_kind = null; long target_rev = 0; File repos_relpath = null; SVNURL repos_root = null; SVNDepth target_depth = SVNDepth.UNKNOWN; SVNWCDbLock target_lock = null; boolean start_empty; SVNErrorMessage err = null; try { final WCDbBaseInfo baseInfo = wcContext.getDb().getBaseInfo(path, BaseInfoField.status, BaseInfoField.kind, BaseInfoField.revision, BaseInfoField.reposRelPath, BaseInfoField.reposRootUrl, BaseInfoField.depth, BaseInfoField.lock); status = baseInfo.status; target_kind = baseInfo.kind; target_rev = baseInfo.revision; repos_relpath = baseInfo.reposRelPath; repos_root = baseInfo.reposRootUrl; target_depth = baseInfo.depth; target_lock = baseInfo.lock; } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) { throw e; } err = e.getErrorMessage(); } if (err != null || (status != SVNWCDbStatus.Normal && status != SVNWCDbStatus.Incomplete)) { if (depth == SVNDepth.UNKNOWN) { depth = SVNDepth.INFINITY; } reporter.setPath("", null, 0, depth, false); reporter.deletePath(""); reporter.finishReport(); return; } if (target_depth == SVNDepth.UNKNOWN) { target_depth = SVNDepth.INFINITY; } start_empty = status == SVNWCDbStatus.Incomplete; if (isUseDepthCompatibilityTrick && target_depth.compareTo(SVNDepth.IMMEDIATES) <= 0 && depth.compareTo(target_depth) > 0) { start_empty = true; } SVNFileType diskType = SVNFileType.UNKNOWN; if (isRestoreFiles) { diskType = SVNFileType.getType(path); } if (isRestoreFiles && diskType == SVNFileType.NONE) { // restore node WCDbInfo wInfo = null; SVNWCDbKind wrkKind; SVNWCDbStatus wrkStatus; SvnChecksum checksum; try { wInfo = wcContext.getDb().readInfo(path, InfoField.status, InfoField.kind, InfoField.checksum); wrkStatus = wInfo.status; wrkKind = wInfo.kind; checksum = wInfo.checksum; } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) { wrkStatus = SVNWCDbStatus.NotPresent; wrkKind = SVNWCDbKind.File; checksum = null; } else { throw e; } } if ((wrkStatus == SVNWCDbStatus.Normal || wrkStatus == SVNWCDbStatus.Added || wrkStatus == SVNWCDbStatus.Incomplete) && (wrkKind == SVNWCDbKind.Dir || checksum != null)) { restoreNode(wcContext, path, wrkKind, target_rev, isUseCommitTimes); } } try { SVNDepth reportDepth = target_depth; if (isHonorDepthExclude && depth != SVNDepth.UNKNOWN && depth.compareTo(target_depth) < 0) { reportDepth = depth; } reporter.setPath("", null, target_rev, reportDepth, start_empty); if (target_kind == SVNWCDbKind.Dir) { if (depth != SVNDepth.EMPTY) { DirParsedInfo rootInfo = ((SVNWCDb) wcContext.getDb()).obtainWcRoot(path); reportRevisionsAndDepths(rootInfo.wcDbDir.getWCRoot(), path, rootInfo.localRelPath, SVNFileUtil.createFilePath(""), target_rev, repos_relpath, repos_root, reportDepth, reporter, isRestoreFiles, depth, start_empty); } } else if (target_kind == SVNWCDbKind.Symlink || target_kind == SVNWCDbKind.File) { String base = SVNFileUtil.getFileName(path); File parentPath = SVNFileUtil.getParentFile(path); WCDbBaseInfo parentInfo = wcContext.getDb().getBaseInfo(parentPath, BaseInfoField.reposRelPath); if (!repos_relpath.equals(SVNFileUtil.createFilePath(parentInfo.reposRelPath, base))) { SVNURL url = SVNWCUtils.join(repos_root, repos_relpath); reporter.linkPath(url, "", target_lock != null ? target_lock.token : null, target_rev, SVNDepth.INFINITY, false); } else if (target_lock != null) { reporter.setPath("", target_lock.token, target_rev, SVNDepth.INFINITY, false); } } reporter.finishReport(); } catch (SVNException e) { // abort report try { reporter.abortReport(); } catch (SVNException inner) { e.getErrorMessage().setChildErrorMessage(inner.getErrorMessage()); } throw e; } } public static boolean restoreNode(SVNWCContext context, File local_abspath, SVNWCDbKind kind, long target_rev, boolean useCommitTimes) throws SVNException { boolean restored = false; /* * Currently we can only restore files, but we will be able to restore * directories after we move to a single database and pristine store. */ if (kind == SVNWCDbKind.File || kind == SVNWCDbKind.Symlink) { /* ... recreate file from text-base, and ... */ restoreFile(context, local_abspath, useCommitTimes, true); restored = true; } else if (kind == SVNWCDbKind.Dir) { /* Recreating a directory is just a mkdir */ local_abspath.mkdirs(); restored = true; } if (restored) { /* ... report the restoration to the caller. */ if (context.getEventHandler() != null) { context.getEventHandler().handleEvent(SVNEventFactory.createSVNEvent(local_abspath, SVNNodeKind.FILE, null, target_rev, SVNEventAction.RESTORE, null, null, null), 0); } } return restored; } private void reportRevisionsAndDepths(SVNWCDbRoot root, File dirPath, File dirLocalRelPath, File reportRelPath, long dirRev, File dirReposRelPath, SVNURL dirReposRoot, SVNDepth dirDepth, ISVNReporter reporter, boolean restoreFiles, SVNDepth depth, boolean reportEverything) throws SVNException { Map<String, WCDbBaseInfo> baseChildren = wcContext.getDb().getBaseChildrenMap(root, dirLocalRelPath, true); Set<String> dirEntries = null; if (restoreFiles) { dirEntries = new HashSet<String>(); File[] list = SVNFileListUtil.listFiles(dirPath); if (list != null) { for (File file : list) { dirEntries.add(SVNFileUtil.getFileName(file)); } } else { SVNFileType fileType = SVNFileType.getType(dirPath); if (fileType != SVNFileType.DIRECTORY) { restoreFiles = false; } } } for (String child : baseChildren.keySet()) { boolean thisSwitched = false; wcContext.checkCancelled(); String thisReportRelpath = SVNFileUtil.getFilePath(SVNFileUtil.createFilePath(reportRelPath, child)); File thisAbsPath = SVNFileUtil.createFilePath(dirPath, child); WCDbBaseInfo ths = baseChildren.get(child); if (ths.updateRoot) { continue; } if (ths.status == SVNWCDbStatus.Excluded) { if (isHonorDepthExclude) { reporter.setPath(thisReportRelpath, null, dirRev, SVNDepth.EXCLUDE, false); } else { if (!reportEverything) { reporter.deletePath(thisReportRelpath); } } continue; } if (ths.status == SVNWCDbStatus.ServerExcluded || ths.status == SVNWCDbStatus.NotPresent) { if (!reportEverything) { reporter.deletePath(thisReportRelpath); } continue; } if (restoreFiles && !dirEntries.contains(child)) { WCDbInfo wInfo = null; SVNWCDbKind wrkKind; SVNWCDbStatus wrkStatus; SvnChecksum checksum; try { wInfo = wcContext.getDb().readInfo(thisAbsPath, InfoField.status, InfoField.kind, InfoField.checksum); wrkStatus = wInfo.status; wrkKind = wInfo.kind; checksum = wInfo.checksum; } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_PATH_NOT_FOUND) { wrkStatus = SVNWCDbStatus.NotPresent; wrkKind = SVNWCDbKind.File; checksum = null; } else { throw e; } } if ((wrkStatus == SVNWCDbStatus.Normal || wrkStatus == SVNWCDbStatus.Added || wrkStatus == SVNWCDbStatus.Incomplete) && (wrkKind == SVNWCDbKind.Dir || checksum != null)) { if (SVNFileType.getType(thisAbsPath) == SVNFileType.NONE) { restoreNode(wcContext, thisAbsPath, wrkKind, dirRev, isUseCommitTimes); } } } if (ths.reposRelPath == null) { ths.reposRelPath = SVNFileUtil.createFilePath(dirReposRelPath, child); } else { String childName = SVNWCUtils.getPathAsChild(dirReposRelPath, ths.reposRelPath); if (childName == null || !child.equals(childName)) { thisSwitched = true; } } if (ths.depth == SVNDepth.UNKNOWN) { ths.depth = SVNDepth.INFINITY; } if (ths.kind == SVNWCDbKind.File || ths.kind == SVNWCDbKind.Symlink) { if (reportEverything) { if (thisSwitched) { SVNURL url = SVNWCUtils.join(dirReposRoot, ths.reposRelPath); reporter.linkPath(url, thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, ths.depth, false); } else { reporter.setPath(thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, ths.depth, false); } } else if (thisSwitched) { SVNURL url = SVNWCUtils.join(dirReposRoot, ths.reposRelPath); reporter.linkPath(url, thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, ths.depth, false); } else if (ths.revision != dirRev || ths.lock != null || dirDepth == SVNDepth.EMPTY) { reporter.setPath(thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, ths.depth, false); } } else if (ths.kind == SVNWCDbKind.Dir && (depth == SVNDepth.UNKNOWN || depth.compareTo(SVNDepth.FILES) > 0)) { boolean isIncomplete = ths.status == SVNWCDbStatus.Incomplete; boolean startEmpty = isIncomplete; SVNDepth reportDepth = ths.depth; if (!depth.isRecursive()) { reportDepth = SVNDepth.EMPTY; } if (isIncomplete && ths.revision < 0) { ths.revision = dirRev; } if (isUseDepthCompatibilityTrick && ths.depth.compareTo(SVNDepth.FILES) <= 0 && depth.compareTo(ths.depth) > 0) { startEmpty = true; } if (reportEverything) { if (thisSwitched) { SVNURL url = SVNWCUtils.join(dirReposRoot, ths.reposRelPath); reporter.linkPath(url, thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, reportDepth, startEmpty); } else { reporter.setPath(thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, reportDepth, startEmpty); } } else if (thisSwitched) { SVNURL url = SVNWCUtils.join(dirReposRoot, ths.reposRelPath); reporter.linkPath(url, thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, reportDepth, startEmpty); } else if (ths.revision != dirRev || isIncomplete || ths.lock != null || dirDepth == SVNDepth.EMPTY || dirDepth == SVNDepth.FILES || (dirDepth == SVNDepth.IMMEDIATES && ths.depth != SVNDepth.EMPTY) || (ths.depth.compareTo(SVNDepth.INFINITY) < 0 && depth.isRecursive())) { reporter.setPath(thisReportRelpath, ths.lock != null ? ths.lock.token : null, ths.revision, reportDepth, startEmpty); } if (depth.isRecursive()) { File reposRelPath = ths.reposRelPath; if (reposRelPath == null) { reposRelPath = SVNFileUtil.createFilePath(dirReposRelPath, child); } reportRevisionsAndDepths(root, thisAbsPath, SVNFileUtil.createFilePath(dirLocalRelPath, child), SVNFileUtil.createFilePath(thisReportRelpath), ths.revision, reposRelPath, dirReposRoot, ths.depth, reporter, restoreFiles, depth, startEmpty); } } } } private static void restoreFile(SVNWCContext context, File localAbsPath, boolean useCommitTimes, boolean removeTextConflicts) throws SVNException { SVNSkel workItem = context.wqBuildFileInstall(localAbsPath, null, useCommitTimes, true); context.getDb().addWorkQueue(localAbsPath, workItem); context.wqRun(localAbsPath); if (removeTextConflicts) { resolveTextConflict(context, localAbsPath); } } private static void resolveTextConflict(SVNWCContext context, File localAbsPath) throws SVNException { context.resolveConflictOnNode(localAbsPath, true, false, SVNConflictChoice.MERGED); } }