package org.tmatesoft.svn.core.internal.wc2.ng; import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.Map; import org.tmatesoft.svn.core.*; import org.tmatesoft.svn.core.internal.util.SVNDate; import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.util.SVNSkel; import org.tmatesoft.svn.core.internal.util.SVNURLUtil; import org.tmatesoft.svn.core.internal.wc.*; import org.tmatesoft.svn.core.internal.wc.admin.SVNTranslator; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext.SVNWCNodeReposInfo; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext.UniqueFileInfo; import org.tmatesoft.svn.core.internal.wc17.SVNWCContext.WritableBaseInfo; import org.tmatesoft.svn.core.internal.wc17.SVNWCUtils; import org.tmatesoft.svn.core.internal.wc17.db.ISVNWCDb; 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.Structure; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.NodeInfo; import org.tmatesoft.svn.core.internal.wc17.db.StructureFields.NodeOriginInfo; import org.tmatesoft.svn.core.internal.wc17.db.SvnWcDbCopy; import org.tmatesoft.svn.core.internal.wc2.SvnRepositoryAccess; import org.tmatesoft.svn.core.internal.wc2.SvnRepositoryAccess.LocationsInfo; import org.tmatesoft.svn.core.internal.wc2.SvnRepositoryAccess.RevisionsPair; import org.tmatesoft.svn.core.internal.wc2.SvnWcGeneration; import org.tmatesoft.svn.core.io.ISVNEditor; import org.tmatesoft.svn.core.io.ISVNReporter; import org.tmatesoft.svn.core.io.ISVNReporterBaton; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor; import org.tmatesoft.svn.core.io.diff.SVNDiffWindow; 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.wc.SVNRevision; import org.tmatesoft.svn.core.wc2.*; import org.tmatesoft.svn.util.SVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; public class SvnNgReposToWcCopy extends SvnNgOperationRunner<Void, SvnCopy> { @Override public boolean isApplicable(SvnCopy operation, SvnWcGeneration wcGeneration) throws SVNException { return areAllSourcesRemote(operation) && operation.getFirstTarget().isLocal(); } private boolean areAllSourcesRemote(SvnCopy operation) { for(SvnCopySource source : operation.getSources()) { if (source.getSource().isFile()) { if (operation.isMove()) { return false; } if (isLocalRevision(source.getRevision()) && isLocalRevision(source.getSource().getResolvedPegRevision())) { return false; } } } return true; } private boolean isLocalRevision(SVNRevision revision) { return revision == SVNRevision.WORKING || revision == SVNRevision.UNDEFINED; } @Override protected Void run(SVNWCContext context) throws SVNException { if (getOperation().isMove()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Moves between the working copy and the repository are not supported"); SVNErrorManager.error(err, SVNLogType.WC); } Collection<SvnCopySource> sources = expandCopySources(getOperation().getSources()); Collection<SvnCopyPair> copyPairs = new ArrayList<SvnNgReposToWcCopy.SvnCopyPair>(); boolean srcsAreUrls = sources.iterator().next().getSource().isURL(); if (sources.size() > 1) { for (SvnCopySource copySource : sources) { SvnCopyPair copyPair = new SvnCopyPair(); String baseName; if (copySource.getSource().isFile()) { copyPair.sourceFile = copySource.getSource().getFile(); baseName = copyPair.sourceFile.getName(); if (srcsAreUrls) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot mix repository and working copy sources"); SVNErrorManager.error(err, SVNLogType.WC); } // resolve peg rev which might be 'WORKING' or 'BASE'. if (copySource.getSource().getResolvedPegRevision() == SVNRevision.WORKING) { Structure<RevisionsPair> pair = getRepositoryAccess().getRevisionNumber(null, copySource.getSource(), copySource.getSource().getResolvedPegRevision(), null); copyPair.sourcePegRevision = SVNRevision.create(pair.lng(RevisionsPair.revNumber)); pair.release(); } else { copyPair.sourcePegRevision = copySource.getSource().getResolvedPegRevision(); } copyPair.sourceRevision = copySource.getRevision(); } else { copyPair.source = copySource.getSource().getURL(); baseName = SVNPathUtil.tail(copyPair.source.getPath()); copyPair.sourcePegRevision = copySource.getSource().getResolvedPegRevision(); copyPair.sourceRevision = copySource.getRevision(); } copyPair.dst = new File(getFirstTarget(), baseName); copyPairs.add(copyPair); } } else if (sources.size() == 1) { SvnCopyPair copyPair = new SvnCopyPair(); SvnCopySource source = sources.iterator().next(); String baseName; if (source.getSource().isFile()) { copyPair.sourceFile = source.getSource().getFile(); baseName = copyPair.sourceFile.getName(); } else { copyPair.source = source.getSource().getURL(); baseName = SVNPathUtil.tail(copyPair.source.getPath()); } if (source.getSource().getResolvedPegRevision() == SVNRevision.WORKING) { Structure<RevisionsPair> pair = getRepositoryAccess().getRevisionNumber(null, source.getSource(), source.getSource().getResolvedPegRevision(), null); copyPair.sourcePegRevision = SVNRevision.create(pair.lng(RevisionsPair.revNumber)); pair.release(); } else { copyPair.sourcePegRevision = source.getSource().getResolvedPegRevision(); } copyPair.sourceRevision = source.getRevision(); copyPair.dst = getFirstTarget(); if (!getOperation().isFailWhenDstExists() && SVNFileType.getType(copyPair.dst) != SVNFileType.NONE) { copyPair.dst = new File(copyPair.dst, baseName); } copyPairs.add(copyPair); } if (!srcsAreUrls) { for (SvnCopyPair pair : copyPairs) { File src = pair.sourceFile; File dst = pair.dst; 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); } Structure<NodeOriginInfo> no = getWcContext().getNodeOrigin(src, true, NodeOriginInfo.reposRelpath, NodeOriginInfo.reposRootUrl, NodeOriginInfo.revision); if (no.get(NodeOriginInfo.reposRelpath) != null) { pair.source = no.<SVNURL>get(NodeOriginInfo.reposRootUrl).appendPath(SVNFileUtil.getFilePath(no.<File>get(NodeOriginInfo.reposRelpath)), false); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "''{0}'' does not have an URL associated with it", src); SVNErrorManager.error(err, SVNLogType.WC); } if (pair.sourcePegRevision == SVNRevision.BASE) { pair.sourcePegRevision = SVNRevision.create(no.lng(NodeOriginInfo.revision)); } if (pair.sourceRevision == SVNRevision.BASE) { pair.sourceRevision = SVNRevision.create(no.lng(NodeOriginInfo.revision)); } } } return copy(copyPairs, getOperation().isMakeParents(), getOperation().isIgnoreExternals()); } protected Collection<SvnCopySource> expandCopySources(Collection<SvnCopySource> sources) throws SVNException { Collection<SvnCopySource> expanded = new ArrayList<SvnCopySource>(sources.size()); for (SvnCopySource source : sources) { if (source.isCopyContents() && source.getSource().isURL()) { // get children at revision. SVNRevision pegRevision = source.getSource().getResolvedPegRevision(); if (!pegRevision.isValid()) { pegRevision = SVNRevision.HEAD; } SVNRevision startRevision = source.getRevision(); if (!startRevision.isValid()) { startRevision = pegRevision; } final SVNRepository svnRepository = getRepositoryAccess().createRepository(source.getSource().getURL(), null, true); final Structure<LocationsInfo> locations = getRepositoryAccess().getLocations(svnRepository, source.getSource(), pegRevision, startRevision, SVNRevision.UNDEFINED); long revision = locations.lng(LocationsInfo.startRevision); Collection<SVNDirEntry> entries = new ArrayList<SVNDirEntry>(); svnRepository.getDir("", revision, null, 0, entries); for (Iterator<SVNDirEntry> ents = entries.iterator(); ents.hasNext();) { SVNDirEntry entry = (SVNDirEntry) ents.next(); // add new copy source. expanded.add(SvnCopySource.create(SvnTarget.fromURL(entry.getURL()), source.getRevision())); } } else { expanded.add(source); } } return expanded; } private Void copy(Collection<SvnCopyPair> copyPairs, boolean makeParents, boolean ignoreExternals) throws SVNException { for (SvnCopyPair pair : copyPairs) { Structure<LocationsInfo> locations = getRepositoryAccess().getLocations(null, pair.sourceFile != null ? SvnTarget.fromFile(pair.sourceFile) : SvnTarget.fromURL(pair.source), pair.sourcePegRevision, pair.sourceRevision, SVNRevision.UNDEFINED); pair.sourceOriginal = pair.source; pair.source = locations.get(LocationsInfo.startUrl); locations.release(); } SVNURL topSrcUrl = getCommonCopyAncestor(copyPairs); File topDst = getCommonCopyDst(copyPairs); if (copyPairs.size() == 1) { topSrcUrl = topSrcUrl.removePathTail(); topDst = SVNFileUtil.getParentFile(topDst); } SVNRepository repository = getRepositoryAccess().createRepository(topSrcUrl, null); Structure<RevisionsPair> revisionPair = null; for (SvnCopyPair pair : copyPairs) { revisionPair = getRepositoryAccess().getRevisionNumber(repository, SvnTarget.fromURL(pair.source), pair.sourceRevision, revisionPair); pair.revNum = revisionPair.lng(RevisionsPair.revNumber); } for (SvnCopyPair pair : copyPairs) { String relativePath = SVNURLUtil.getRelativeURL(topSrcUrl, pair.source, false); relativePath = SVNEncodingUtil.uriDecode(relativePath); SVNNodeKind sourceKind = repository.checkPath(relativePath, pair.revNum); if (sourceKind == SVNNodeKind.NONE) { if (pair.revNum >= 0) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "Path ''{0}'' not found in revision ''{1}''", pair.source, pair.revNum); SVNErrorManager.error(err, SVNLogType.WC); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "Path ''{0}'' not found in HEAD revision", pair.source); SVNErrorManager.error(err, SVNLogType.WC); } } pair.srcKind = sourceKind; SVNFileType dstType = SVNFileType.getType(pair.dst); if (dstType != SVNFileType.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' already exists", pair.dst); SVNErrorManager.error(err, SVNLogType.WC); } File dstParent = SVNFileUtil.getParentFile(pair.dst); dstType = SVNFileType.getType(dstParent); if (getOperation().isMakeParents() && dstType == SVNFileType.NONE) { SVNFileUtil.ensureDirectoryExists(dstParent); SvnScheduleForAddition add = getOperation().getOperationFactory().createScheduleForAddition(); add.setSingleTarget(SvnTarget.fromFile(dstParent)); add.setDepth(SVNDepth.INFINITY); add.setIncludeIgnored(true); add.setForce(false); add.setAddParents(true); add.setSleepForTimestamp(false); try { add.run(); } catch (SVNException e) { SVNFileUtil.deleteAll(dstParent, true); throw e; } } else if (dstType != SVNFileType.DIRECTORY) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' in not a directory", dstParent); SVNErrorManager.error(err, SVNLogType.WC); } } // now do real copy File locked = getWcContext().acquireWriteLock(topDst, false, true); try { return copy(copyPairs, topDst, ignoreExternals, repository); } finally { getWcContext().releaseWriteLock(locked); } } private Void copy(Collection<SvnCopyPair> copyPairs, File topDst, boolean ignoreExternals, SVNRepository repository) throws SVNException { for (SvnCopyPair pair : copyPairs) { SVNNodeKind dstKind = SvnWcDbCopy.readKind(getWcContext().getDb(), pair.dst, false, true); if (dstKind != SVNNodeKind.NONE) { SVNWCContext.NodePresence nodePresence = getWcContext().getNodePresence(pair.dst, false); boolean isExcluded = nodePresence.isExcluded; boolean isServerExcluded = nodePresence.isServerExcluded; if (isExcluded || isServerExcluded) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Path ''{0}'' exists, but is excluded", pair.dst); SVNErrorManager.error(errorMessage, SVNLogType.WC); } else { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' already exists", pair.dst); SVNErrorManager.error(errorMessage, SVNLogType.WC); } } dstKind = SVNFileType.getNodeKind(SVNFileType.getType(pair.dst)); if (dstKind != SVNNodeKind.NONE) { if (getOperation().isMove() && copyPairs.size() == 1 && SVNFileUtil.getParentFile(pair.sourceFile).equals(SVNFileUtil.getParentFile(pair.dst))) { try { boolean caseRenamed = pair.sourceFile.getCanonicalPath().equals(pair.dst.getCanonicalPath()); if (caseRenamed) { continue; } } catch (IOException e) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e); SVNErrorManager.error(errorMessage, SVNLogType.WC); } } SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' already exists as unversioned node", pair.dst); SVNErrorManager.error(errorMessage, SVNLogType.WC); } File dstParentAbsPath = SVNFileUtil.getParentFile(pair.dst); SVNNodeKind dstParentKind = SvnWcDbCopy.readKind(getWcContext().getDb(), dstParentAbsPath, false, true); if (getOperation().isMakeParents() && dstParentKind == SVNNodeKind.NONE) { SvnScheduleForAddition scheduleForAddition = getOperation().getOperationFactory().createScheduleForAddition(); scheduleForAddition.setMkDir(true); SvnNgAdd svnNgAdd = new SvnNgAdd(); svnNgAdd.setWcContext(getWcContext()); svnNgAdd.setOperation(scheduleForAddition); svnNgAdd.addFromDisk(dstParentAbsPath, null, true); } else if (dstParentKind != SVNNodeKind.DIR) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_PATH_NOT_FOUND, "Path ''{0}'' is not a directory", dstParentAbsPath); SVNErrorManager.error(errorMessage, SVNLogType.WC); } dstParentKind = SVNFileType.getNodeKind(SVNFileType.getType(dstParentAbsPath)); if (dstParentKind != SVNNodeKind.DIR) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_MISSING, "Path ''{0}'' is not a directory", dstParentAbsPath); SVNErrorManager.error(errorMessage, SVNLogType.WC); } } boolean sameRepositories = false; try { String sourceUuid = repository.getRepositoryUUID(true); SVNWCNodeReposInfo info = getWcContext().getNodeReposInfo(topDst); String dstUuid = info != null ? info.reposUuid : null; sameRepositories = sourceUuid != null && dstUuid != null && sourceUuid.equals(dstUuid); } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() != SVNErrorCode.RA_NO_REPOS_UUID) { throw e; } } for (SvnCopyPair pair : copyPairs) { copy(pair, sameRepositories, ignoreExternals, repository); } sleepForTimestamp(); return null; } private long copy(final SvnCopyPair pair, boolean sameRepositories, boolean ignoreExternals, SVNRepository repository) throws SVNException { ISVNEventHandler eventHandler = getOperation().getEventHandler(); if (!sameRepositories && eventHandler != null) { SVNEvent event = SVNEventFactory.createSVNEvent(pair.sourceFile, pair.srcKind, null, -1, SVNEventAction.FOREIGN_COPY_BEGIN, SVNEventAction.FOREIGN_COPY_BEGIN, null, null); event.setURL(pair.source); eventHandler.handleEvent(event, UNKNOWN); } long rev = -1; SVNURL oldLocation = repository.getLocation(); repository.setLocation(pair.source, false); if (pair.srcKind == SVNNodeKind.DIR) { if (sameRepositories) { File dstParent = SVNFileUtil.getParentFile(pair.dst); final File dstPath = SVNFileUtil.createUniqueFile(dstParent, pair.dst.getName(), ".tmp", false); try { SVNFileUtil.deleteFile(dstPath); SVNFileUtil.ensureDirectoryExists(dstPath); SvnCheckout co = getOperation().getOperationFactory().createCheckout(); co.setSingleTarget(SvnTarget.fromFile(dstPath)); co.setSource(SvnTarget.fromURL(pair.sourceOriginal, pair.sourcePegRevision)); co.setRevision(pair.sourceRevision); co.setIgnoreExternals(ignoreExternals); co.setDepth(SVNDepth.INFINITY); co.setAllowUnversionedObstructions(false); co.setSleepForTimestamp(false); final ISVNEventHandler oldHandler = getWcContext().getEventHandler(); getWcContext().pushEventHandler(new ISVNEventHandler() { public void checkCancelled() throws SVNCancelException { if (oldHandler != null) { oldHandler.checkCancelled(); } } public void handleEvent(SVNEvent event, double progress) throws SVNException { File path = event.getFile(); if (path != null) { path = SVNWCUtils.skipAncestor(dstPath, path); if (path != null) { path = new File(pair.dst, path.getPath()); event.setFile(path); } else if (dstPath.equals(event.getFile())) { event.setFile(pair.dst); } } if (oldHandler != null) { oldHandler.handleEvent(event, progress); } } }); try { rev = co.run(); } finally { getWcContext().popEventHandler(); } eventHandler = getWcContext().getEventHandler(); getWcContext().setEventHandler(null); new SvnNgWcToWcCopy().copy(getWcContext(), dstPath, pair.dst, true); getWcContext().setEventHandler(eventHandler); File dstLock = getWcContext().acquireWriteLock(dstPath, false, true); try { getWcContext().removeFromRevisionControl(dstPath, false, false); } finally { try { getWcContext().releaseWriteLock(dstLock); } catch (SVNException e) { } } SVNFileUtil.rename(dstPath, pair.dst); } finally { SVNFileUtil.deleteAll(dstPath, true); } } else { copyForeign(pair.source, pair.dst, pair.sourcePegRevision, pair.sourceRevision, SVNDepth.INFINITY, false, true); sleepForTimestamp(); return rev; } } else { String relativePath = ""; File tmpDir = getWcContext().getDb().getWCRootTempDir(pair.dst); UniqueFileInfo ufInfo = SVNWCContext.openUniqueFile(tmpDir, true); SVNProperties newProperties = new SVNProperties(); try { pair.revNum = repository.getFile(relativePath, pair.revNum, newProperties, ufInfo.stream); } finally { SVNFileUtil.closeFile(ufInfo.stream); } InputStream newContents = SVNFileUtil.openFileForReading(ufInfo.path); try { addFileToWc(getWcContext(), pair.dst, newContents, null, newProperties, null, sameRepositories ? pair.source : null, sameRepositories ? pair.revNum : -1); } finally { SVNFileUtil.closeFile(newContents); } } Map<String, SVNMergeRangeList> mergeInfo = calculateTargetMergeInfo(pair.source, pair.revNum, repository); repository.setLocation(oldLocation, false); String mergeInfoProperty = getWcContext().getProperty(pair.dst, SVNProperty.MERGE_INFO); Map<String, SVNMergeRangeList> wcMergeInfo = null; if (mergeInfoProperty != null) { wcMergeInfo = SVNMergeInfoUtil.parseMergeInfo(new StringBuffer(mergeInfoProperty), null); } if (wcMergeInfo != null && mergeInfo != null) { wcMergeInfo = SVNMergeInfoUtil.mergeMergeInfos(wcMergeInfo, mergeInfo); } else if (wcMergeInfo == null) { wcMergeInfo = mergeInfo; } String extendedMergeInfoValue = null; if (wcMergeInfo != null) { extendedMergeInfoValue = SVNMergeInfoUtil.formatMergeInfoToString(wcMergeInfo, null); } SvnNgPropertiesManager.setProperty(getWcContext(), pair.dst, SVNProperty.MERGE_INFO, extendedMergeInfoValue != null ? SVNPropertyValue.create(extendedMergeInfoValue) : null, SVNDepth.EMPTY, true, null, null); SVNEvent event = SVNEventFactory.createSVNEvent(pair.dst, pair.srcKind, null, pair.revNum, SVNEventAction.COPY, SVNEventAction.COPY, null, null, 1, 1); handleEvent(event); return rev; } private void copyForeign(SVNURL url, File dstAbsPath, SVNRevision pegRevision, SVNRevision revision, SVNDepth depth, boolean makeParents, boolean alreadyLocked) throws SVNException { assert SVNFileUtil.isAbsolute(dstAbsPath); Structure<SvnRepositoryAccess.RepositoryInfo> repositoryInfoStructure = getRepositoryAccess().createRepositoryFor(SvnTarget.fromURL(url), pegRevision, revision, null); SVNRepository repository = repositoryInfoStructure.get(SvnRepositoryAccess.RepositoryInfo.repository); long locRev = repositoryInfoStructure.lng(SvnRepositoryAccess.RepositoryInfo.revision); SVNNodeKind kind = repository.checkPath("", locRev); if (kind != SVNNodeKind.FILE && kind != SVNNodeKind.DIR) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.ILLEGAL_TARGET, "''{0}'' is not a valid location inside a repository", url); SVNErrorManager.error(errorMessage, SVNLogType.WC); } SVNNodeKind wcKind = getWcContext().readKind(dstAbsPath, true); if (wcKind != SVNNodeKind.NONE) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "''{0}'' is already under version control", dstAbsPath); SVNErrorManager.error(errorMessage, SVNLogType.WC); } File dirAbsPath = SVNFileUtil.getParentFile(dstAbsPath); wcKind = getWcContext().readKind(dirAbsPath, false); if (wcKind == SVNNodeKind.NONE) { if (makeParents) { SvnScheduleForAddition scheduleForAddition = getOperation().getOperationFactory().createScheduleForAddition(); scheduleForAddition.setMkDir(true); scheduleForAddition.setAddParents(true); scheduleForAddition.setSingleTarget(SvnTarget.fromFile(dirAbsPath)); scheduleForAddition.run(); } wcKind = getWcContext().readKind(dirAbsPath, false); } if (wcKind != SVNNodeKind.DIR) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "Can't add ''{0}'', because no parent directory is found", dstAbsPath); SVNErrorManager.error(errorMessage, SVNLogType.WC); } if (kind == SVNNodeKind.FILE) { SVNProperties props = new SVNProperties(); SVNProperties regularProps = new SVNProperties(); OutputStream outputStream = SVNFileUtil.openFileForWriting(dstAbsPath); try { repository.getFile("", locRev, props, outputStream); } finally { SVNFileUtil.closeFile(outputStream); } SvnNgPropertiesManager.categorizeProperties(props, regularProps, null, null); regularProps.remove(SVNProperty.MERGE_INFO); if (!alreadyLocked) { final File lockRoot = getWcContext().acquireWriteLock(dirAbsPath, false, true); try { SvnNgAdd svnNgAdd = new SvnNgAdd(); svnNgAdd.setWcContext(getWcContext()); svnNgAdd.setRepositoryAccess(getRepositoryAccess()); svnNgAdd.addFromDisk(dstAbsPath, regularProps, true); } finally { getWcContext().releaseWriteLock(lockRoot); } } else { SvnNgAdd svnNgAdd = new SvnNgAdd(); svnNgAdd.setWcContext(getWcContext()); svnNgAdd.setRepositoryAccess(getRepositoryAccess()); svnNgAdd.addFromDisk(dstAbsPath, regularProps, true); } } else { if (!alreadyLocked) { final File lockRoot = getWcContext().acquireWriteLock(dirAbsPath, false, true); try { copyForeignDir(repository, locRev, dstAbsPath, depth); } finally { getWcContext().releaseWriteLock(lockRoot); } } else { copyForeignDir(repository, locRev, dstAbsPath, depth); } } } private void copyForeignDir(SVNRepository repository, final long locRev, File dstAbsPath, final SVNDepth depth) throws SVNException { ISVNEditor editor = new SVNCopyForeignEditor(dstAbsPath, getWcContext()); editor = SVNCancellableEditor.newInstance(editor, getWcContext().getEventHandler(), SVNDebugLog.getDefaultLog()); repository.update(locRev, "", SVNDepth.INFINITY, false, new ISVNReporterBaton() { public void report(ISVNReporter reporter) throws SVNException { reporter.setPath("", null, locRev, depth, true); reporter.finishReport(); } }, editor); } private SVNURL getCommonCopyAncestor(Collection<SvnCopyPair> copyPairs) { SVNURL ancestor = null; for (SvnCopyPair svnCopyPair : copyPairs) { if (ancestor == null) { ancestor = svnCopyPair.source; continue; } ancestor = SVNURLUtil.getCommonURLAncestor(ancestor, svnCopyPair.source); } return ancestor; } private File getCommonCopyDst(Collection<SvnCopyPair> copyPairs) { String ancestor = null; for (SvnCopyPair svnCopyPair : copyPairs) { String path = svnCopyPair.dst.getAbsolutePath().replace(File.separatorChar, '/'); if (ancestor == null) { ancestor = path; continue; } ancestor = SVNPathUtil.getCommonPathAncestor(ancestor, path); } return new File(ancestor); } public static void addFileToWc(SVNWCContext context, File path, InputStream newBaseContents, InputStream newContents, SVNProperties newBaseProps, SVNProperties newProps, SVNURL copyFromURL, long copyFromRev) throws SVNException { context.writeCheck(path); SVNWCDbStatus status = null; try { Structure<NodeInfo> ni = context.getDb().readInfo(path, NodeInfo.status); status = ni.get(NodeInfo.status); ni.release(); switch (status) { case NotPresent: case Deleted: break; default: SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Node ''{0}'' exists", path); SVNErrorManager.error(err, SVNLogType.WC); break; } } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() != SVNErrorCode.WC_PATH_NOT_FOUND) { throw e; } } File dirPath = SVNFileUtil.getParentFile(path); Structure<NodeInfo> ni = context.getDb().readInfo(dirPath, NodeInfo.status, NodeInfo.kind); status = ni.get(NodeInfo.status); ISVNWCDb.SVNWCDbKind kind = ni.get(NodeInfo.kind); ni.release(); switch (status) { case Normal: case Added: break; case Deleted: SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Can''t add ''{0}'' to a parent directory scheduled for deletion", path); SVNErrorManager.error(err, SVNLogType.WC); default: SVNErrorMessage err2 = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Can''t find parent directory''s node while trying to add ''{0}''", path); SVNErrorManager.error(err2, SVNLogType.WC); break; } if (kind != SVNWCDbKind.Dir) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Can''t schedule an addition of ''{0}'' below a not-directory node", path); SVNErrorManager.error(err, SVNLogType.WC); } SVNURL originalURL = null; File originalReposPath = null; String originalUuid = null; if (copyFromURL != null) { SVNWCNodeReposInfo reposInfo = context.getNodeReposInfo(dirPath); if (!SVNURLUtil.isAncestor(reposInfo.reposRootUrl, copyFromURL)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Copyfrom-url ''{0}'' has different repository root than ''{0}''", copyFromURL, reposInfo.reposRootUrl); SVNErrorManager.error(err, SVNLogType.WC); } originalUuid = reposInfo.reposUuid; originalURL = reposInfo.reposRootUrl; String reposPath = SVNURLUtil.getRelativeURL(originalURL, copyFromURL, false); if (reposPath.startsWith("/")) { reposPath = reposPath.substring("/".length()); } originalReposPath = new File(reposPath); } else { copyFromRev = -1; } SVNProperties regularProps = new SVNProperties(); SVNProperties entryProps = new SVNProperties(); SvnNgPropertiesManager.categorizeProperties(newBaseProps, regularProps, entryProps, null); newBaseProps = regularProps; long changedRev = entryProps.containsName(SVNProperty.COMMITTED_REVISION) ? Long.parseLong(entryProps.getStringValue(SVNProperty.COMMITTED_REVISION)) : - 1; String changedAuthor = entryProps.getStringValue(SVNProperty.LAST_AUTHOR); SVNDate changedDate = entryProps.containsName(SVNProperty.COMMITTED_REVISION) ? SVNDate.parseDate(entryProps.getStringValue(SVNProperty.COMMITTED_DATE)) : new SVNDate(0, 0); WritableBaseInfo wbInfo = context.openWritableBase(path, true, true); try { SVNTranslator.copy(newBaseContents, wbInfo.stream); } catch (IOException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } finally { SVNFileUtil.closeFile(wbInfo.stream); } File sourcePath = null; if (newContents != null) { File tempDir = context.getDb().getWCRootTempDir(path); UniqueFileInfo ufInfo = SVNWCContext.openUniqueFile(tempDir, true); try { SVNTranslator.copy(newContents, ufInfo.stream); } catch (IOException e) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.IO_ERROR, e); SVNErrorManager.error(err, SVNLogType.WC); } finally { SVNFileUtil.closeFile(ufInfo.stream); } sourcePath = ufInfo.path; } if (copyFromURL != null) { context.getDb().installPristine(wbInfo.tempBaseAbspath, wbInfo.getSHA1Checksum(), wbInfo.getMD5Checksum()); } if (newContents == null && copyFromURL == null) { sourcePath = wbInfo.tempBaseAbspath; } boolean recordFileInfo = newContents == null; SVNSkel wi = context.wqBuildFileInstall(path, sourcePath, false, recordFileInfo); wi = context.wqMerge(wi, null); if (sourcePath != null) { SVNSkel remove = context.wqBuildFileRemove(path, sourcePath); wi = context.wqMerge(wi, remove); } context.getDb().opCopyFile(path, newBaseProps, changedRev, changedDate, changedAuthor, originalReposPath, originalURL, originalUuid, copyFromRev, copyFromURL != null ? wbInfo.getSHA1Checksum() : null, false, null, null, null); context.getDb().opSetProps(path, newProps, null, false, wi); context.wqRun(dirPath); } private Map<String, SVNMergeRangeList> calculateTargetMergeInfo(SVNURL srcURL, long srcRevision, SVNRepository repository) throws SVNException { SVNURL url = null; url = srcURL; Map<String, SVNMergeRangeList> targetMergeInfo = null; String mergeInfoPath; SVNRepository repos = repository; if (repos == null) { repos = getRepositoryAccess().createRepository(url, null, false); } SVNURL oldLocation = null; try { mergeInfoPath = getRepositoryAccess().getPathRelativeToSession(url, null, repos); if (mergeInfoPath == null) { oldLocation = repos.getLocation(); repos.setLocation(url, false); mergeInfoPath = ""; } targetMergeInfo = getRepositoryAccess().getReposMergeInfo(repos, mergeInfoPath, srcRevision, SVNMergeInfoInheritance.INHERITED, true); } finally { if (repository == null) { repos.closeSession(); } else if (oldLocation != null) { repos.setLocation(oldLocation, false); } } return targetMergeInfo; } private static class SvnCopyPair { SVNNodeKind srcKind; long revNum; SVNURL sourceOriginal; File sourceFile; SVNURL source; SVNRevision sourceRevision; SVNRevision sourcePegRevision; File dst; } private class SVNCopyForeignEditor implements ISVNEditor { private SVNCopyForeignEditor(File anchorAbsPath, SVNWCContext context) { this.anchorAbsPath = anchorAbsPath; this.context = context; } private class DirectoryBaton { DirectoryBaton parentBaton; File localAbsPath; boolean created; SVNProperties properties; public DirectoryBaton(DirectoryBaton directoryBaton) { parentBaton = directoryBaton; } } private class FileBaton { DirectoryBaton parentBaton; File localAbsPath; SVNProperties properties; boolean writing; String checksum; public FileBaton(DirectoryBaton directoryBaton) { parentBaton = directoryBaton; } } private final File anchorAbsPath; private DirectoryBaton currentDirectory; private FileBaton currentFile; private final SVNWCContext context; SVNDeltaProcessor svnDeltaProcessor = new SVNDeltaProcessor(); public void targetRevision(long revision) throws SVNException { } public void openRoot(long revision) throws SVNException { currentDirectory = new DirectoryBaton(null); currentDirectory.localAbsPath = anchorAbsPath; SVNFileUtil.ensureDirectoryExists(anchorAbsPath); } public void deleteEntry(String path, long revision) throws SVNException { } public void absentDir(String path) throws SVNException { } public void absentFile(String path) throws SVNException { } public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException { currentDirectory = new DirectoryBaton(currentDirectory); currentDirectory.localAbsPath = SVNFileUtil.createFilePath(anchorAbsPath, path); if (!SVNPathUtil.isAncestor(SVNFileUtil.getFilePath(anchorAbsPath), SVNFileUtil.getFilePath(currentDirectory.localAbsPath))) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Path ''{0}'' is not in the working copy", path); SVNErrorManager.error(errorMessage, SVNLogType.WC); } SVNFileUtil.ensureDirectoryExists(currentDirectory.localAbsPath); } public void openDir(String path, long revision) throws SVNException { } public void changeDirProperty(String name, SVNPropertyValue value) throws SVNException { if (!SVNProperty.isRegularProperty(name) && SVNProperty.MERGE_INFO.equals(name)) { return; } if (!currentDirectory.created) { if (currentDirectory.properties == null) { currentDirectory.properties = new SVNProperties(); } if (value != null) { currentDirectory.properties.put(name, value); } } else { SvnNgPropertiesManager.setProperty(context, currentDirectory.localAbsPath, name, value, SVNDepth.EMPTY, false, null, null); } } public void closeDir() throws SVNException { ensureAdded(currentDirectory); } private void ensureAdded(DirectoryBaton currentDirectory) throws SVNException { if (currentDirectory.created) { return; } if (currentDirectory.parentBaton != null) { ensureAdded(currentDirectory.parentBaton); } currentDirectory.created = true; SvnNgAdd svnNgAdd = new SvnNgAdd(); svnNgAdd.setWcContext(context); svnNgAdd.setOperation(getOperation().getOperationFactory().createScheduleForAddition()); svnNgAdd.addFromDisk(currentDirectory.localAbsPath, currentDirectory.properties, true); } public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException { currentFile = new FileBaton(currentDirectory); currentFile.localAbsPath = SVNFileUtil.createFilePath(anchorAbsPath, path); if (!SVNPathUtil.isAncestor(SVNFileUtil.getFilePath(anchorAbsPath), SVNFileUtil.getFilePath(currentFile.localAbsPath))) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Path ''{0}'' is not in the working copy", path); SVNErrorManager.error(errorMessage, SVNLogType.WC); } } public void openFile(String path, long revision) throws SVNException { } public void changeFileProperty(String path, String propertyName, SVNPropertyValue propertyValue) throws SVNException { if (!SVNProperty.isRegularProperty(propertyName) && SVNProperty.MERGE_INFO.equals(propertyName)) { return; } if (currentFile.properties == null) { currentFile.properties = new SVNProperties(); } if (propertyValue != null) { currentFile.properties.put(propertyName, propertyValue); } } public void closeFile(String path, String textChecksum) throws SVNException { ensureAdded(currentFile.parentBaton); if (textChecksum != null && currentFile.checksum != null) { if (!textChecksum.equals(currentFile.checksum)) { SVNErrorMessage errorMessage = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH, "Checksum mismatch for ''{0}''", currentFile.localAbsPath); SVNErrorManager.error(errorMessage, SVNLogType.WC); } } SvnNgAdd svnNgAdd = new SvnNgAdd(); svnNgAdd.setWcContext(context); svnNgAdd.setOperation(getOperation().getOperationFactory().createScheduleForAddition()); svnNgAdd.addFromDisk(currentFile.localAbsPath, currentFile.properties, true); } public SVNCommitInfo closeEdit() throws SVNException { return null; } public void abortEdit() throws SVNException { } public void applyTextDelta(String path, String baseChecksum) throws SVNException { assert !currentFile.writing; currentFile.writing = true; svnDeltaProcessor.applyTextDelta(SVNFileUtil.DUMMY_IN, SVNFileUtil.openFileForWriting(currentFile.localAbsPath), true); } public OutputStream textDeltaChunk(String path, SVNDiffWindow diffWindow) throws SVNException { return svnDeltaProcessor.textDeltaChunk(diffWindow); } public void textDeltaEnd(String path) throws SVNException { currentFile.checksum = svnDeltaProcessor.textDeltaEnd(); } } }