/* * ==================================================================== * Copyright (c) 2004-2012 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.wc; import java.io.File; import java.io.OutputStream; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.TreeMap; import org.tmatesoft.svn.core.SVNCancelException; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNDirEntry; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNMergeInfoInheritance; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager; import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.util.SVNHashMap; import org.tmatesoft.svn.core.internal.util.SVNMergeInfoUtil; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.util.SVNURLUtil; import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea; import org.tmatesoft.svn.core.internal.wc.admin.SVNEntry; import org.tmatesoft.svn.core.internal.wc.admin.SVNLog; import org.tmatesoft.svn.core.internal.wc.admin.SVNVersionedProperties; import org.tmatesoft.svn.core.internal.wc.admin.SVNWCAccess; import org.tmatesoft.svn.core.io.ISVNEditor; import org.tmatesoft.svn.core.io.SVNLocationEntry; import org.tmatesoft.svn.core.io.SVNRepository; import org.tmatesoft.svn.core.wc.ISVNCommitHandler; import org.tmatesoft.svn.core.wc.ISVNCommitParameters; import org.tmatesoft.svn.core.wc.ISVNEventHandler; import org.tmatesoft.svn.core.wc.ISVNExternalsHandler; import org.tmatesoft.svn.core.wc.ISVNOptions; import org.tmatesoft.svn.core.wc.ISVNRepositoryPool; import org.tmatesoft.svn.core.wc.SVNCommitItem; import org.tmatesoft.svn.core.wc.SVNCopySource; 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.wc.SVNWCUtil; import org.tmatesoft.svn.util.SVNDebugLog; import org.tmatesoft.svn.util.SVNLogType; import org.tmatesoft.svn.core.internal.wc16.*; public class SVNCopyDriver extends SVNBasicDelegate { private SVNWCAccess myWCAccess; private boolean myIsDisableLocalModificationsCopying; public void setDisableLocalModificationCopying(boolean disable) { myIsDisableLocalModificationsCopying = disable; } protected SVNCopyDriver(ISVNAuthenticationManager authManager, ISVNOptions options) { super(authManager, options); } protected SVNCopyDriver(ISVNRepositoryPool repositoryPool, ISVNOptions options) { super(repositoryPool, options); } protected void setWCAccess(SVNWCAccess access) { myWCAccess = access; } private SVNWCAccess getWCAccess() { if (myWCAccess == null) { return createWCAccess(); } return myWCAccess; } private SVNAdminArea probeOpen(SVNWCAccess access, File path, boolean writeLock, int depth) throws SVNException { if (access != myWCAccess) { return access.probeOpen(path, writeLock, depth); } return myWCAccess.probeRetrieve(path); } private SVNAdminArea open(SVNWCAccess access, File path, boolean writeLock, boolean stealLock, int depth) throws SVNException { if (access != myWCAccess) { return access.open(path, writeLock, stealLock, depth); } return myWCAccess.retrieve(path); } private void close(SVNWCAccess access) throws SVNException { if (access != myWCAccess) { access.close(); } } protected SVNCopySource[] expandCopySources(SVNCopySource[] sources) throws SVNException { Collection expanded = new ArrayList(sources.length); for (int i = 0; i < sources.length; i++) { SVNCopySource source = sources[i]; if (source.isCopyContents() && source.isURL()) { // get children at revision. SVNRevision pegRevision = source.getPegRevision(); if (!pegRevision.isValid()) { pegRevision = SVNRevision.HEAD; } SVNRevision startRevision = source.getRevision(); if (!startRevision.isValid()) { startRevision = pegRevision; } SVNRepositoryLocation[] locations = getLocations(source.getURL(), null, null, pegRevision, startRevision, SVNRevision.UNDEFINED); SVNRepository repository = createRepository(locations[0].getURL(), null, null, true); long revision = locations[0].getRevisionNumber(); Collection entries = new ArrayList(); repository.getDir("", revision, null, 0, entries); for (Iterator ents = entries.iterator(); ents.hasNext();) { SVNDirEntry entry = (SVNDirEntry) ents.next(); // add new copy source. expanded.add(new SVNCopySource(SVNRevision.UNDEFINED, source.getRevision(), entry.getURL())); } } else { expanded.add(source); } } return (SVNCopySource[]) expanded.toArray(new SVNCopySource[expanded.size()]); } private SVNCommitInfo copyReposToRepos(List copyPairs, boolean makeParents, boolean isMove, String message, SVNProperties revprops, ISVNCommitHandler commitHandler) throws SVNException { List pathInfos = new ArrayList(); Map pathsMap = new SVNHashMap(); for (int i = 0; i < copyPairs.size(); i++) { CopyPathInfo info = new CopyPathInfo(); pathInfos.add(info); } String commonURL = ((CopyPair) copyPairs.get(0)).mySource; String topDstURL = ((CopyPair) copyPairs.get(0)).myDst; for (int i = 1; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); commonURL = SVNPathUtil.getCommonPathAncestor(commonURL, pair.mySource); } if (copyPairs.size() == 1) { commonURL = SVNPathUtil.getCommonPathAncestor(commonURL, topDstURL); } else { commonURL = SVNPathUtil.getCommonPathAncestor(commonURL, SVNPathUtil.removeTail(topDstURL)); } try { SVNURL.parseURIEncoded(commonURL); } catch (SVNException e) { commonURL = null; } if (commonURL == null) { SVNURL url1 = SVNURL.parseURIEncoded(((CopyPair) copyPairs.get(0)).mySource); SVNURL url2 = SVNURL.parseURIEncoded(((CopyPair) copyPairs.get(0)).myDst); SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Source and dest appear not to be in the same repository (src: ''{0}''; dst: ''{1}'')", new Object[] {url1, url2}); SVNErrorManager.error(err, SVNLogType.WC); } SVNRepository nonTopRepos = null; for (int i = 0; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); CopyPathInfo info = (CopyPathInfo) pathInfos.get(i); if (nonTopRepos == null) { nonTopRepos = createRepository(SVNURL.parseURIEncoded(pair.mySource), null, null, true); } if (pair.mySource.equals(pair.myDst)) { info.isResurrection = true; } } /* * Get list of parents that have to be created. start with first 'dst' parent. * This is a list of urls. */ String rootURL = nonTopRepos.getRepositoryRoot(true).toString(); List newDirs = new ArrayList(); SVNURL oldLocation = null; if (makeParents) { CopyPair pair = (CopyPair) copyPairs.get(0); if (!pair.myDst.equals(rootURL)) { oldLocation = nonTopRepos.getLocation(); nonTopRepos.setLocation(SVNURL.parseURIEncoded(pair.myDst).removePathTail(), false); SVNNodeKind kind = nonTopRepos.checkPath("", -1); while (kind == SVNNodeKind.NONE) { newDirs.add(nonTopRepos.getLocation().toString()); nonTopRepos.setLocation(nonTopRepos.getLocation().removePathTail(), false); kind = nonTopRepos.checkPath("", -1); } } } else if (Boolean.getBoolean("svnkit.compatibleHash")) { // XXX: hack for tests to generate error message tests will like. // do not check paths above repository root. CopyPair pair = (CopyPair) copyPairs.get(0); if (!pair.myDst.equals(rootURL)) { oldLocation = nonTopRepos.getLocation(); nonTopRepos.setLocation(SVNURL.parseURIEncoded(pair.myDst).removePathTail(), false); SVNNodeKind kind = nonTopRepos.checkPath("", -1); if (kind == SVNNodeKind.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "''{0}'' path not found", nonTopRepos.getLocation()); SVNErrorManager.error(err, SVNLogType.WC); } } } if (oldLocation != null) { nonTopRepos.setLocation(oldLocation, false); } /* * Check if source is dst child (while dst is not root). */ for (int i = 0; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); CopyPathInfo info = (CopyPathInfo) pathInfos.get(i); if (!pair.myDst.equals(rootURL) && SVNPathUtil.getPathAsChild(pair.myDst, pair.mySource) != null) { info.isResurrection = true; } } long latestRevision = nonTopRepos.getLatestRevision(); for (int i = 0; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); CopyPathInfo info = (CopyPathInfo) pathInfos.get(i); pair.mySourceRevisionNumber = getRevisionNumber(pair.mySourceRevision, nonTopRepos, null); info.mySourceRevisionNumber = pair.mySourceRevisionNumber; SVNRepositoryLocation[] locations = getLocations(SVNURL.parseURIEncoded(pair.mySource), null, null /*optimize topRepos*/, pair.mySourcePegRevision, pair.mySourceRevision, SVNRevision.UNDEFINED); pair.mySource = locations[0].getURL().toString(); // tests: // src is equal to dst if (isMove && pair.mySource.equals(pair.myDst)) { //if ("".equals(srcRelative) && isMove) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot move URL ''{0}'' into itself", SVNURL.parseURIEncoded(pair.mySource)); SVNErrorManager.error(err, SVNLogType.WC); } // src doesn't exist at source revision. nonTopRepos.setLocation(SVNURL.parseURIEncoded(pair.mySource), false); info.mySourceKind = nonTopRepos.checkPath("", pair.mySourceRevisionNumber); if (info.mySourceKind == SVNNodeKind.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "Path ''{0}'' does not exist in revision {1}", new Object[] {SVNURL.parseURIEncoded(pair.mySource), new Long(pair.mySourceRevisionNumber)}); SVNErrorManager.error(err, SVNLogType.WC); } // dst already exists at HEAD. nonTopRepos.setLocation(SVNURL.parseURIEncoded(pair.myDst), false); SVNNodeKind dstKind = nonTopRepos.checkPath("", latestRevision); if (dstKind != SVNNodeKind.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_ALREADY_EXISTS, "Path ''{0}'' already exists", pair.myDst); SVNErrorManager.error(err, SVNLogType.WC); } info.mySource = pair.mySource; info.myDstPath = pair.myDst; } List paths = new ArrayList(copyPairs.size() * 2); List commitItems = new ArrayList(copyPairs.size() * 2); if (makeParents) { for (Iterator newDirsIter = newDirs.iterator(); newDirsIter.hasNext();) { String itemURL = (String) newDirsIter.next(); SVNCommitItem item = new SVNCommitItem(null, SVNURL.parseURIEncoded(itemURL), null, SVNNodeKind.NONE, null, null, true, false, false, false, false, false); commitItems.add(item); } } for (Iterator infos = pathInfos.iterator(); infos.hasNext();) { CopyPathInfo info = (CopyPathInfo) infos.next(); SVNURL itemURL = SVNURL.parseURIEncoded(info.myDstPath); SVNCommitItem item = new SVNCommitItem(null, itemURL, null, SVNNodeKind.NONE, null, null, true, false, false, false, false, false); commitItems.add(item); pathsMap.put(info.myDstPath, info); if (isMove && !info.isResurrection) { itemURL = SVNURL.parseURIEncoded(info.mySource); item = new SVNCommitItem(null, itemURL, null, SVNNodeKind.NONE, null, null, false, true, false, false, false, false); commitItems.add(item); pathsMap.put(info.mySource, info); } } if (makeParents) { for (Iterator newDirsIter = newDirs.iterator(); newDirsIter.hasNext();) { String dirPath = (String) newDirsIter.next(); CopyPathInfo info = new CopyPathInfo(); info.myDstPath = dirPath; info.isDirAdded = true; paths.add(info.myDstPath); pathsMap.put(dirPath, info); } } for (Iterator infos = pathInfos.iterator(); infos.hasNext();) { CopyPathInfo info = (CopyPathInfo) infos.next(); nonTopRepos.setLocation(SVNURL.parseURIEncoded(info.mySource), false); Map mergeInfo = calculateTargetMergeInfo(null, null, SVNURL.parseURIEncoded(info.mySource), info.mySourceRevisionNumber, nonTopRepos, false); if (mergeInfo != null) { info.myMergeInfoProp = SVNMergeInfoUtil.formatMergeInfoToString(mergeInfo, null); } paths.add(info.myDstPath); if (isMove && !info.isResurrection) { // this is too. paths.add(info.mySource); } } SVNCommitItem[] commitables = (SVNCommitItem[]) commitItems.toArray(new SVNCommitItem[commitItems.size()]); message = commitHandler.getCommitMessage(message, commitables); if (message == null) { return SVNCommitInfo.NULL; } message = SVNCommitUtil.validateCommitMessage(message); revprops = commitHandler.getRevisionProperties(message, commitables, revprops == null ? new SVNProperties() : revprops); if (revprops == null) { return SVNCommitInfo.NULL; } SVNPropertiesManager.validateRevisionProperties(revprops); SVNURL topURL = SVNURL.parseURIEncoded((String) paths.get(0)); for(int i = 1; i < paths.size(); i++) { String url = (String) paths.get(i); topURL = SVNURLUtil.getCommonURLAncestor(topURL, SVNURL.parseURIEncoded(url)); } if (paths.contains(topURL.toString())) { topURL = topURL.removePathTail(); } for(int i = 0; i < paths.size(); i++) { String url = (String) paths.get(i); SVNURL svnURL = SVNURL.parseURIEncoded(url); url = SVNPathUtil.getPathAsChild(topURL.getPath(), svnURL.getPath()); paths.set(i, url); CopyPathInfo info = (CopyPathInfo) pathsMap.remove(svnURL.toString()); if (info != null) { if (info.mySource != null) { info.mySourcePath = getPathRelativeToRoot(null, SVNURL.parseURIEncoded(info.mySource), SVNURL.parseURIEncoded(rootURL), null, null); info.mySourceRelativePath = getPathRelativeToSession(SVNURL.parseURIEncoded(info.mySource), topURL, null); } pathsMap.put(url, info); } } nonTopRepos.setLocation(topURL, false); ISVNEditor commitEditor = nonTopRepos.getCommitEditor(message, null, true, revprops, null); ISVNCommitPathHandler committer = new CopyCommitPathHandler(pathsMap, isMove); SVNCommitInfo result = null; try { SVNCommitUtil.driveCommitEditor(committer, paths, commitEditor, latestRevision); result = commitEditor.closeEdit(); } catch (SVNCancelException cancel) { throw cancel; } catch (SVNException e) { SVNErrorMessage err = e.getErrorMessage().wrap("Commit failed (details follow):"); SVNErrorManager.error(err, SVNLogType.DEFAULT); } finally { if (commitEditor != null && result == null) { try { commitEditor.abortEdit(); } catch (SVNException e) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.WC, e); } } } if (result != null && result.getNewRevision() >= 0) { dispatchEvent(SVNEventFactory.createSVNEvent(null, SVNNodeKind.NONE, null, result.getNewRevision(), SVNEventAction.COMMIT_COMPLETED, null, null, null), ISVNEventHandler.UNKNOWN); } return result != null ? result : SVNCommitInfo.NULL; } private String getUUIDFromPath(SVNWCAccess wcAccess, File path) throws SVNException { SVNEntry entry = wcAccess.getVersionedEntry(path, true); String uuid = null; if (entry.getUUID() != null) { uuid = entry.getUUID(); } else if (entry.getURL() != null) { SVNRepository repos = createRepository(entry.getSVNURL(), null, null, false); try { uuid = repos.getRepositoryUUID(true); } finally { repos.closeSession(); } } else { if (wcAccess.isWCRoot(path)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "''{0}'' has no URL", path); SVNErrorManager.error(err, SVNLogType.WC); } uuid = getUUIDFromPath(wcAccess, path.getParentFile()); } return uuid; } private static void postCopyCleanup(SVNAdminArea dir) throws SVNException { SVNPropertiesManager.deleteWCProperties(dir, null, false); SVNFileUtil.setHidden(dir.getAdminDirectory(), true); Map attributes = new SVNHashMap(); boolean save = false; for(Iterator entries = dir.entries(true); entries.hasNext();) { SVNEntry entry = (SVNEntry) entries.next(); boolean deleted = entry.isDeleted(); SVNNodeKind kind = entry.getKind(); boolean force = false; if (entry.getDepth() == SVNDepth.EXCLUDE) { continue; } if (entry.isDeleted()) { force = true; attributes.put(SVNProperty.SCHEDULE, SVNProperty.SCHEDULE_DELETE); attributes.put(SVNProperty.DELETED, null); if (entry.isDirectory()) { attributes.put(SVNProperty.KIND, SVNProperty.KIND_FILE); } } if (entry.getLockToken() != null) { force = true; attributes.put(SVNProperty.LOCK_TOKEN, null); attributes.put(SVNProperty.LOCK_OWNER, null); attributes.put(SVNProperty.LOCK_CREATION_DATE, null); } if (force) { dir.modifyEntry(entry.getName(), attributes, false, force); save = true; } if (!deleted && kind == SVNNodeKind.DIR && !dir.getThisDirName().equals(entry.getName())) { SVNAdminArea childDir = dir.getWCAccess().retrieve(dir.getFile(entry.getName())); postCopyCleanup(childDir); } attributes.clear(); } if (save) { dir.saveEntries(false); } } protected SVNCommitInfo setupCopy(SVNCopySource[] sources, SVNPath dst, boolean isMove, boolean makeParents, String message, SVNProperties revprops, ISVNCommitHandler commitHandler, ISVNCommitParameters commitParameters, ISVNExternalsHandler externalsHandler) throws SVNException { List pairs = new ArrayList(sources.length); for (int i = 0; i < sources.length; i++) { SVNCopySource source = sources[i]; if (source.isURL() && (source.getPegRevision() == SVNRevision.BASE || source.getPegRevision() == SVNRevision.COMMITTED || source.getPegRevision() == SVNRevision.PREVIOUS)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CLIENT_BAD_REVISION, "Revision type requires a working copy path, not URL"); SVNErrorManager.error(err, SVNLogType.WC); } } boolean srcIsURL = sources[0].isURL(); boolean dstIsURL = dst.isURL(); if (sources.length > 1) { for (int i = 0; i < sources.length; i++) { SVNCopySource source = sources[i]; CopyPair pair = new CopyPair(); pair.mySource = source.isURL() ? source.getURL().toString() : source.getFile().getAbsolutePath().replace(File.separatorChar, '/'); pair.setSourceRevisions(source.getPegRevision(), source.getRevision()); if (SVNPathUtil.isURL(pair.mySource) != srcIsURL) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot mix repository and working copy sources"); SVNErrorManager.error(err, SVNLogType.WC); } String baseName = source.getName(); if (srcIsURL && !dstIsURL) { baseName = SVNEncodingUtil.uriDecode(baseName); } pair.myDst = dstIsURL ? dst.getURL().appendPath(baseName, true).toString() : new File(dst.getFile(), baseName).getAbsolutePath().replace(File.separatorChar, '/'); pairs.add(pair); } } else { SVNCopySource source = sources[0]; CopyPair pair = new CopyPair(); pair.mySource = source.isURL() ? source.getURL().toString() : source.getFile().getAbsolutePath().replace(File.separatorChar, '/'); pair.setSourceRevisions(source.getPegRevision(), source.getRevision()); pair.myDst = dstIsURL ? dst.getURL().toString() : dst.getFile().getAbsolutePath().replace(File.separatorChar, '/'); pairs.add(pair); } if (!srcIsURL && !dstIsURL) { for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); String srcPath = pair.mySource; String dstPath = pair.myDst; if (SVNPathUtil.isAncestor(srcPath, dstPath)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot copy path ''{0}'' into its own child ''{1}", new Object[] { srcPath, dstPath }); SVNErrorManager.error(err, SVNLogType.WC); } } } if (isMove && !srcIsURL) { for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); SVNWCAccess wcAccess = getWCAccess(); try { File srcFile = new File(pair.mySource); probeOpen(wcAccess, srcFile, false, 0); SVNEntry entry = wcAccess.getVersionedEntry(new File(pair.mySource), false); if (entry.getExternalFilePath() != null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CANNOT_MOVE_FILE_EXTERNAL, "Cannot move the file external at ''{0}''; please propedit the svn:externals description that created it", srcFile); SVNErrorManager.error(err, SVNLogType.WC); } } finally { close(wcAccess); } } } if (isMove) { if (srcIsURL == dstIsURL) { for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); boolean same; Object p; if (!srcIsURL) { File srcPath = new File(pair.mySource); File dstPath = new File(pair.myDst); same = srcPath.equals(dstPath); p = srcPath; } else { SVNURL srcURL = SVNURL.parseURIEncoded(pair.mySource); SVNURL dstURL = SVNURL.parseURIEncoded(pair.myDst); same = srcURL.getPath().equals(dstURL.getPath()); p = srcURL; } if (same) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot move {1} ''{0}'' into itself", p, srcIsURL ? "URL" : "path"); SVNErrorManager.error(err, SVNLogType.WC); } } } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Moves between the working copy and the repository are not supported"); SVNErrorManager.error(err, SVNLogType.WC); } } else { if (!srcIsURL) { boolean needReposRevision = false; boolean needReposPegRevision = false; for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); if (pair.mySourceRevision != SVNRevision.UNDEFINED && pair.mySourceRevision != SVNRevision.WORKING) { needReposRevision = true; } if (pair.mySourcePegRevision != SVNRevision.UNDEFINED && pair.mySourcePegRevision != SVNRevision.WORKING) { needReposPegRevision = true; } if (needReposRevision || needReposPegRevision) { break; } } if (needReposRevision || needReposPegRevision) { for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); SVNWCAccess wcAccess = getWCAccess(); try { probeOpen(wcAccess, new File(pair.mySource), false, 0); SVNEntry entry = wcAccess.getEntry(new File(pair.mySource), false); SVNURL url = entry.isCopied() ? entry.getCopyFromSVNURL() : entry.getSVNURL(); if (url == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "''{0}'' does not have a URL associated with it", new File(pair.mySource)); SVNErrorManager.error(err, SVNLogType.WC); } pair.mySource = url.toString(); if (!needReposPegRevision || pair.mySourcePegRevision == SVNRevision.BASE) { pair.mySourcePegRevision = entry.isCopied() ? SVNRevision.create(entry.getCopyFromRevision()) : SVNRevision.create(entry.getRevision()); } if (pair.mySourceRevision == SVNRevision.BASE) { pair.mySourceRevision = entry.isCopied() ? SVNRevision.create(entry.getCopyFromRevision()) : SVNRevision.create(entry.getRevision()); } } finally { close(wcAccess); } } srcIsURL = true; } } } if (!srcIsURL && !dstIsURL) { copyWCToWC(pairs, isMove, makeParents); return SVNCommitInfo.NULL; } else if (!srcIsURL && dstIsURL) { //wc2url. return copyWCToRepos(pairs, makeParents, message, revprops, commitHandler, commitParameters, externalsHandler); } else if (srcIsURL && !dstIsURL) { // url2wc. copyReposToWC(pairs, makeParents); return SVNCommitInfo.NULL; } else { return copyReposToRepos(pairs, makeParents, isMove, message, revprops, commitHandler); } } private SVNCommitInfo copyWCToRepos(List copyPairs, boolean makeParents, String message, SVNProperties revprops, ISVNCommitHandler commitHandler, ISVNCommitParameters commitParameters, ISVNExternalsHandler externalsHandler) throws SVNException { String topSrc = ((CopyPair) copyPairs.get(0)).mySource; for (int i = 1; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); topSrc = SVNPathUtil.getCommonPathAncestor(topSrc, pair.mySource); } SVNWCAccess wcAccess = createWCAccess(); SVNCommitInfo info = null; ISVNEditor commitEditor = null; Collection tmpFiles = null; try { SVNAdminArea adminArea = wcAccess.probeOpen(new File(topSrc), false, SVNWCAccess.INFINITE_DEPTH); wcAccess.setAnchor(adminArea.getRoot()); String topDstURL = ((CopyPair) copyPairs.get(0)).myDst; topDstURL = SVNPathUtil.removeTail(topDstURL); for (int i = 1; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); topDstURL = SVNPathUtil.getCommonPathAncestor(topDstURL, pair.myDst); } // should we use also wcAccess here? i do not think so. SVNRepository repos = createRepository(SVNURL.parseURIEncoded(topDstURL), adminArea.getRoot(), wcAccess, true); List newDirs = new ArrayList(); if (makeParents) { String rootURL = topDstURL; SVNNodeKind kind = repos.checkPath("", -1); while(kind == SVNNodeKind.NONE) { newDirs.add(rootURL); rootURL = SVNPathUtil.removeTail(rootURL); repos.setLocation(SVNURL.parseURIEncoded(rootURL), false); kind = repos.checkPath("", -1); } topDstURL = rootURL; } for (int i = 0; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); SVNEntry entry = wcAccess.getEntry(new File(pair.mySource), false); pair.mySourceRevisionNumber = entry.getRevision(); String dstRelativePath = SVNPathUtil.getPathAsChild(topDstURL, pair.myDst); dstRelativePath = SVNEncodingUtil.uriDecode(dstRelativePath); SVNNodeKind kind = repos.checkPath(dstRelativePath, -1); if (kind != SVNNodeKind.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.FS_ALREADY_EXISTS, "Path ''{0}'' already exists", SVNURL.parseURIEncoded(pair.myDst)); SVNErrorManager.error(err, SVNLogType.WC); } } // create commit items list to fetch log messages. List commitItems = new ArrayList(copyPairs.size()); if (makeParents) { for (int i = 0; i < newDirs.size(); i++) { String newDirURL = (String) newDirs.get(i); SVNURL url = SVNURL.parseURIEncoded(newDirURL); SVNCommitItem item = new SVNCommitItem(null, url, null, SVNNodeKind.NONE, null, null, true, false, false, false, false, false); commitItems.add(item); } } for (int i = 0; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); SVNURL url = SVNURL.parseURIEncoded(pair.myDst); SVNCommitItem item = new SVNCommitItem(null, url, null, SVNNodeKind.NONE, null, null, true, false, false, false, false, false); commitItems.add(item); } SVNCommitItem[] commitables = (SVNCommitItem[]) commitItems.toArray(new SVNCommitItem[commitItems.size()]); message = commitHandler.getCommitMessage(message, commitables); if (message == null) { return SVNCommitInfo.NULL; } message = SVNCommitUtil.validateCommitMessage(message); revprops = commitHandler.getRevisionProperties(message, commitables, revprops == null ? new SVNProperties() : revprops); if (revprops == null) { return SVNCommitInfo.NULL; } Map allCommitables = new TreeMap(SVNCommitUtil.FILE_COMPARATOR); repos.setLocation(repos.getRepositoryRoot(true), false); Map pathsToExternalsProps = new SVNHashMap(); for (int i = 0; i < copyPairs.size(); i++) { CopyPair source = (CopyPair) copyPairs.get(i); File srcFile = new File(source.mySource); SVNEntry entry = wcAccess.getVersionedEntry(srcFile, false); SVNAdminArea dirArea; if (entry.isDirectory()) { dirArea = wcAccess.retrieve(srcFile); } else { dirArea = wcAccess.retrieve(srcFile.getParentFile()); } pathsToExternalsProps.clear(); Map sourceCommittables = new HashMap(); SVNCommitUtil.harvestCommitables(sourceCommittables, dirArea, srcFile, null, entry, source.myDst, entry.getURL(), true, false, false, null, SVNDepth.INFINITY, false, null, commitParameters, pathsToExternalsProps); // filter out file externals. // path of the source relative to wcAccess anchor. String basePath = SVNPathUtil.canonicalizePath(wcAccess.getAnchor().getAbsolutePath()); String sourcePath = SVNPathUtil.canonicalizePath(srcFile.getAbsolutePath()); String path = SVNPathUtil.getRelativePath(basePath, sourcePath); SVNCommitUtil.filterOutFileExternals(Collections.singletonList(path), sourceCommittables, wcAccess); allCommitables.putAll(sourceCommittables); SVNCommitItem item = (SVNCommitItem) allCommitables.get(srcFile); SVNURL srcURL = entry.getSVNURL(); Map mergeInfo = calculateTargetMergeInfo(srcFile, wcAccess, srcURL, source.mySourceRevisionNumber, repos, false); Map wcMergeInfo = SVNPropertiesManager.parseMergeInfo(srcFile, entry, false); if (wcMergeInfo != null && mergeInfo != null) { mergeInfo = SVNMergeInfoUtil.mergeMergeInfos(mergeInfo, wcMergeInfo); } else if (mergeInfo == null) { mergeInfo = wcMergeInfo; } if (mergeInfo != null) { String mergeInfoString = SVNMergeInfoUtil.formatMergeInfoToString(mergeInfo, null); setCommitItemProperty(item, SVNProperty.MERGE_INFO, SVNPropertyValue.create(mergeInfoString)); } if (!pathsToExternalsProps.isEmpty()) { LinkedList newExternals = new LinkedList(); for (Iterator pathsIter = pathsToExternalsProps.keySet().iterator(); pathsIter.hasNext();) { File localPath = (File) pathsIter.next(); String externalsPropString = (String) pathsToExternalsProps.get(localPath); SVNExternal[] externals = SVNExternal.parseExternals(localPath.getAbsolutePath(), externalsPropString); boolean introduceVirtualExternalChange = false; newExternals.clear(); for (int k = 0; k < externals.length; k++) { File externalWC = new File(localPath, externals[k].getPath()); SVNEntry externalEntry = null; SVNRevision externalsWCRevision = SVNRevision.UNDEFINED; try { wcAccess.open(externalWC, false, 0); externalEntry = wcAccess.getEntry(externalWC, false); } catch (SVNException svne) { if (svne instanceof SVNCancelException) { throw svne; } } finally { wcAccess.closeAdminArea(externalWC); } if (externalEntry == null) { externalEntry = wcAccess.getEntry(externalWC, false); } if (externalEntry != null && (externalEntry.isThisDir() || externalEntry.getExternalFilePath() != null)) { externalsWCRevision = SVNRevision.create(externalEntry.getRevision()); } SVNEntry ownerEntry = wcAccess.getEntry(localPath, false); SVNURL ownerURL = null; if (ownerEntry != null) { ownerURL = ownerEntry.getSVNURL(); } if (ownerURL == null) { // there is no entry for the directory that has external // property or no url in it? continue; } SVNURL resolvedURL = externals[k].resolveURL(repos.getRepositoryRoot(true), ownerURL); String unresolvedURL = externals[k].getUnresolvedUrl(); if (unresolvedURL != null && !SVNPathUtil.isURL(unresolvedURL) && unresolvedURL.startsWith("../")) { unresolvedURL = SVNURLUtil.getRelativeURL(repos.getRepositoryRoot(true), resolvedURL, true); if (unresolvedURL.startsWith("/")) { unresolvedURL = "^" + unresolvedURL; } else { unresolvedURL = "^/" + unresolvedURL; } } SVNRevision[] revs = externalsHandler.handleExternal( externalWC, resolvedURL, externals[k].getRevision(), externals[k].getPegRevision(), externals[k].getRawValue(), externalsWCRevision); if (revs != null && revs[0].equals(externals[k].getRevision())) { newExternals.add(externals[k].getRawValue()); } else if (revs != null) { SVNExternal newExternal = new SVNExternal(externals[k].getPath(), unresolvedURL, revs[1], revs[0], true, externals[k].isPegRevisionExplicit(), externals[k].isNewFormat()); newExternals.add(newExternal.toString()); if (!introduceVirtualExternalChange) { introduceVirtualExternalChange = true; } } } if (introduceVirtualExternalChange) { String newExternalsProp = ""; for (Iterator externalsIter = newExternals.iterator(); externalsIter.hasNext();) { String external = (String) externalsIter.next(); newExternalsProp += external + '\n'; } SVNCommitItem itemWithExternalsChanges = (SVNCommitItem) allCommitables.get(localPath); if (itemWithExternalsChanges != null) { setCommitItemProperty(itemWithExternalsChanges, SVNProperty.EXTERNALS, SVNPropertyValue.create(newExternalsProp)); } else { SVNAdminArea childArea = wcAccess.retrieve(localPath); String relativePath = childArea.getRelativePath(dirArea); String itemURL = SVNPathUtil.append(source.myDst, SVNEncodingUtil.uriEncode(relativePath)); itemWithExternalsChanges = new SVNCommitItem(localPath, SVNURL.parseURIEncoded(itemURL), null, SVNNodeKind.DIR, null, null, false, false, true, false, false, false); setCommitItemProperty(itemWithExternalsChanges, SVNProperty.EXTERNALS, SVNPropertyValue.create(newExternalsProp)); allCommitables.put(localPath, itemWithExternalsChanges); } } } } } commitItems = new ArrayList(allCommitables.values()); // in case of 'base' commit, remove all 'deletions', mark all other items as non-modified, remove additions and copies from other urls. if (myIsDisableLocalModificationsCopying) { ArrayList harmlessItems = new ArrayList(); for(int i = 0 ; i < commitItems.size(); i++) { SVNCommitItem item = (SVNCommitItem) commitItems.get(i); if (item.isDeleted()) { // deletion or replacement, skip it. continue; } if (item.isAdded()) { if (!item.isCopied()) { // this is just new file or directory continue; } SVNURL copyFromURL = item.getCopyFromURL(); if (copyFromURL == null) { // also skip. continue; } SVNEntry entry = wcAccess.getEntry(item.getFile(), false); if (entry == null) { continue; } SVNURL expectedURL = entry.getSVNURL(); if (!copyFromURL.equals(expectedURL)) { // copied from some other location. continue; } } setCommitItemFlags(item, false, false); harmlessItems.add(item); } commitItems = harmlessItems; } // add parents to commits hash? if (makeParents) { for (int i = 0; i < newDirs.size(); i++) { String newDirURL = (String) newDirs.get(i); SVNURL url = SVNURL.parseURIEncoded(newDirURL); SVNCommitItem item = new SVNCommitItem(null, url, null, SVNNodeKind.NONE, null, null, true, false, false, false, false, false); commitItems.add(item); } } commitables = (SVNCommitItem[]) commitItems.toArray(new SVNCommitItem[commitItems.size()]); for (int i = 0; i < commitables.length; i++) { setCommitItemAccess(commitables[i], wcAccess); } allCommitables = new TreeMap(); SVNURL url = SVNCommitUtil.translateCommitables(commitables, allCommitables); repos = createRepository(url, null, null, true); SVNCommitMediator mediator = new SVNCommitMediator(allCommitables); tmpFiles = mediator.getTmpFiles(); message = SVNCommitUtil.validateCommitMessage(message); SVNURL rootURL = repos.getRepositoryRoot(true); SVNPropertiesManager.validateRevisionProperties(revprops); commitEditor = repos.getCommitEditor(message, null, true, revprops, mediator); info = SVNCommitter.commit(tmpFiles, allCommitables, rootURL.getPath(), commitEditor); commitEditor = null; } catch (SVNCancelException cancel) { throw cancel; } catch (SVNException e) { // wrap error message. SVNErrorMessage err = e.getErrorMessage().wrap("Commit failed (details follow):"); SVNErrorManager.error(err, SVNLogType.WC); } finally { if (tmpFiles != null) { for (Iterator files = tmpFiles.iterator(); files.hasNext();) { File file = (File) files.next(); SVNFileUtil.deleteFile(file); } } if (commitEditor != null && info == null) { // should we hide this exception? try { commitEditor.abortEdit(); } catch (SVNException e) { SVNDebugLog.getDefaultLog().logFine(SVNLogType.WC, e); } } if (wcAccess != null) { wcAccess.close(); } } if (info != null && info.getNewRevision() >= 0) { dispatchEvent(SVNEventFactory.createSVNEvent(null, SVNNodeKind.NONE, null, info.getNewRevision(), SVNEventAction.COMMIT_COMPLETED, null, null, null), ISVNEventHandler.UNKNOWN); } return info != null ? info : SVNCommitInfo.NULL; } private void copyReposToWC(List copyPairs, boolean makeParents) throws SVNException { for (Iterator pairs = copyPairs.iterator(); pairs.hasNext();) { CopyPair pair = (CopyPair) pairs.next(); SVNRepositoryLocation[] locations = getLocations(SVNURL.parseURIEncoded(pair.mySource), null, null, pair.mySourcePegRevision, pair.mySourceRevision, SVNRevision.UNDEFINED); // new String actualURL = locations[0].getURL().toString(); String originalSource = pair.mySource; pair.mySource = actualURL; pair.myOriginalSource = originalSource; } // get src and dst ancestors. String topDst = ((CopyPair) copyPairs.get(0)).myDst; if (copyPairs.size() > 1) { topDst = SVNPathUtil.removeTail(topDst); } String topSrc = ((CopyPair) copyPairs.get(0)).mySource; for(int i = 1; i < copyPairs.size(); i++) { CopyPair pair = (CopyPair) copyPairs.get(i); topSrc = SVNPathUtil.getCommonPathAncestor(topSrc, pair.mySource); } if (copyPairs.size() == 1) { topSrc = SVNPathUtil.removeTail(topSrc); } SVNRepository topSrcRepos = createRepository(SVNURL.parseURIEncoded(topSrc), null, null, false); try { for (Iterator pairs = copyPairs.iterator(); pairs.hasNext();) { CopyPair pair = (CopyPair) pairs.next(); pair.mySourceRevisionNumber = getRevisionNumber(pair.mySourceRevision, topSrcRepos, null); } String reposPath = topSrcRepos.getLocation().toString(); for (Iterator pairs = copyPairs.iterator(); pairs.hasNext();) { CopyPair pair = (CopyPair) pairs.next(); String relativePath = SVNPathUtil.getPathAsChild(reposPath, pair.mySource); relativePath = SVNEncodingUtil.uriDecode(relativePath); SVNNodeKind kind = topSrcRepos.checkPath(relativePath, pair.mySourceRevisionNumber); if (kind == SVNNodeKind.NONE) { SVNErrorMessage err; if (pair.mySourceRevisionNumber >= 0) { err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "Path ''{0}'' not found in revision {1}", new Object[] {SVNURL.parseURIEncoded(pair.mySource), new Long(pair.mySourceRevisionNumber)}); } else { err = SVNErrorMessage.create(SVNErrorCode.FS_NOT_FOUND, "Path ''{0}'' not found in head revision", SVNURL.parseURIEncoded(pair.mySource)); } SVNErrorManager.error(err, SVNLogType.WC); } pair.mySourceKind = kind; SVNFileType dstType = SVNFileType.getType(new File(pair.myDst)); if (dstType != SVNFileType.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' already exists", new File(pair.myDst)); SVNErrorManager.error(err, SVNLogType.WC); } String dstParent = SVNPathUtil.removeTail(pair.myDst); SVNFileType dstParentFileType = SVNFileType.getType(new File(dstParent)); if (makeParents && dstParentFileType == SVNFileType.NONE) { // create parents. addLocalParents(new File(dstParent), getEventDispatcher()); } else if (dstParentFileType != SVNFileType.DIRECTORY) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "Path ''{0}'' is not a directory", dstParent); SVNErrorManager.error(err, SVNLogType.WC); } } SVNWCAccess dstAccess = getWCAccess(); try { probeOpen(dstAccess, new File(topDst), true, 0); for (Iterator pairs = copyPairs.iterator(); pairs.hasNext();) { CopyPair pair = (CopyPair) pairs.next(); SVNEntry dstEntry = dstAccess.getEntry(new File(pair.myDst), true); if (dstEntry != null) { if (dstEntry.getDepth() == SVNDepth.EXCLUDE || dstEntry.isAbsent()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "''{0}'' is already under version control", new File(pair.myDst)); SVNErrorManager.error(err, SVNLogType.WC); } if (!dstEntry.isDirectory() && !dstEntry.isScheduledForDeletion() && !dstEntry.isDeleted()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Entry for ''{0}'' exists (though the working file is missing)", new File(pair.myDst)); SVNErrorManager.error(err, SVNLogType.WC); } } } String srcUUID = null; String dstUUID = null; try { srcUUID = topSrcRepos.getRepositoryUUID(true); } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() != SVNErrorCode.RA_NO_REPOS_UUID) { throw e; } } String dstParent = topDst; if (copyPairs.size() == 1) { dstParent = SVNPathUtil.removeTail(topDst); } try { dstUUID = getUUIDFromPath(dstAccess, new File(dstParent)); } catch (SVNException e) { if (e.getErrorMessage().getErrorCode() != SVNErrorCode.RA_NO_REPOS_UUID) { throw e; } } boolean sameRepos = false; if (srcUUID != null) { sameRepos = srcUUID.equals(dstUUID); } for (Iterator pairs = copyPairs.iterator(); pairs.hasNext();) { CopyPair pair = (CopyPair) pairs.next(); copyReposToWC(pair, sameRepos, topSrcRepos, dstAccess); } } finally { close(dstAccess); } } finally { topSrcRepos.closeSession(); } } private void copyReposToWC(CopyPair pair, boolean sameRepositories, SVNRepository topSrcRepos, SVNWCAccess dstAccess) throws SVNException { long srcRevNum = pair.mySourceRevisionNumber; if (pair.mySourceKind == SVNNodeKind.DIR) { // do checkout String srcURL = pair.myOriginalSource; SVNURL url = SVNURL.parseURIEncoded(srcURL); SVNUpdateClient16 updateClient = new SVNUpdateClient16(getRepositoryPool(), getOptions()); updateClient.setEventHandler(getEventDispatcher()); File dstFile = new File(pair.myDst); SVNRevision srcRevision = pair.mySourceRevision; SVNRevision srcPegRevision = pair.mySourcePegRevision; updateClient.doCheckout(url, dstFile, srcPegRevision, srcRevision, SVNDepth.INFINITY, false); if (sameRepositories) { url = SVNURL.parseURIEncoded(pair.mySource); SVNAdminArea dstArea = dstAccess.open(dstFile, true, SVNWCAccess.INFINITE_DEPTH); SVNEntry dstRootEntry = dstArea.getEntry(dstArea.getThisDirName(), false); if (srcRevision == SVNRevision.HEAD) { srcRevNum = dstRootEntry.getRevision(); } SVNAdminArea dir = dstAccess.getAdminArea(dstFile.getParentFile()); SVNWCManager.add(dstFile, dir, url, srcRevNum, SVNDepth.INFINITY); Map srcMergeInfo = calculateTargetMergeInfo(null, null, url, srcRevNum, topSrcRepos, false); extendWCMergeInfo(dstFile, dstRootEntry, srcMergeInfo, dstAccess); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Source URL ''{0}'' is from foreign repository; leaving it as a disjoint WC", url); SVNErrorManager.error(err, SVNLogType.WC); } } else if (pair.mySourceKind == SVNNodeKind.FILE) { String srcURL = pair.mySource; SVNURL url = SVNURL.parseURIEncoded(srcURL); File dst = new File(pair.myDst); SVNAdminArea dir = dstAccess.getAdminArea(dst.getParentFile()); File tmpFile = SVNAdminUtil.createTmpFile(dir); String path = getPathRelativeToRoot(null, url, null, null, topSrcRepos); SVNProperties props = new SVNProperties(); OutputStream os = null; long revision = -1; try { os = SVNFileUtil.openFileForWriting(tmpFile); revision = topSrcRepos.getFile(path, srcRevNum, props, new SVNCancellableOutputStream(os, this)); props = SVNMergeDriver.filterProperties(props, true, false, false, true); } finally { SVNFileUtil.closeFile(os); } if (srcRevNum < 0) { srcRevNum = revision; } SVNWCManager.addRepositoryFile(dir, dst.getName(), null, tmpFile, props, null, sameRepositories ? pair.mySource : null, sameRepositories ? srcRevNum : -1); SVNEntry entry = dstAccess.getEntry(dst, false); Map mergeInfo = calculateTargetMergeInfo(null, null, url, srcRevNum, topSrcRepos, false); extendWCMergeInfo(dst, entry, mergeInfo, dstAccess); SVNEvent event = SVNEventFactory.createSVNEvent(dst, SVNNodeKind.FILE, null, SVNRepository.INVALID_REVISION, SVNEventAction.COPY, null, null, null); dstAccess.handleEvent(event); sleepForTimeStamp(); } } private void copyWCToWC(List copyPairs, boolean isMove, boolean makeParents) throws SVNException { for (Iterator pairs = copyPairs.iterator(); pairs.hasNext();) { CopyPair pair = (CopyPair) pairs.next(); File source = new File(pair.mySource); SVNFileType srcFileType = SVNFileType.getType(source); if (srcFileType == SVNFileType.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.NODE_UNKNOWN_KIND, "Path ''{0}'' does not exist", source); SVNErrorManager.error(err, SVNLogType.WC); } if (isMove && SVNWCUtil.isWorkingCopyRoot(source)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot move ''{0}'' as it is the root of the working copy", source); SVNErrorManager.error(err, SVNLogType.WC); } SVNFileType dstFileType = SVNFileType.getType(new File(pair.myDst)); if (dstFileType != SVNFileType.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Path ''{0}'' already exists", new File(pair.myDst)); SVNErrorManager.error(err, SVNLogType.WC); } File dstParent = new File(SVNPathUtil.removeTail(pair.myDst)); pair.myBaseName = SVNPathUtil.tail(pair.myDst); SVNFileType dstParentFileType = SVNFileType.getType(dstParent); if (makeParents && dstParentFileType == SVNFileType.NONE) { // create parents. addLocalParents(dstParent, getEventDispatcher()); } else if (dstParentFileType != SVNFileType.DIRECTORY) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_NOT_DIRECTORY, "Path ''{0}'' is not a directory", dstParent); SVNErrorManager.error(err, SVNLogType.WC); } } if (isMove) { moveWCToWC(copyPairs); } else { copyWCToWC(copyPairs); } } protected void copyDisjointWCToWC(File nestedWC) 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); } nestedWC = new File(nestedWC.getAbsolutePath().replace(File.separatorChar, '/')); File nestedWCParent = nestedWC.getParentFile(); 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); } SVNWCAccess parentWCAccess = createWCAccess(); SVNWCAccess nestedWCAccess = createWCAccess(); try { SVNAdminArea parentArea = parentWCAccess.open(nestedWCParent, true, 0); SVNEntry srcEntryInParent = parentWCAccess.getEntry(nestedWC, false); if (srcEntryInParent != null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Entry ''{0}'' already exists in parent directory", nestedWC.getName()); SVNErrorManager.error(err, SVNLogType.WC); } SVNAdminArea nestedArea = nestedWCAccess.open(nestedWC, false, SVNWCAccess.INFINITE_DEPTH); SVNEntry nestedWCThisEntry = nestedWCAccess.getVersionedEntry(nestedWC, false); SVNEntry parentThisEntry = parentWCAccess.getVersionedEntry(nestedWCParent, false); // uuids may be identical while it might be absolutely independent repositories. // subversion uses repos roots comparison for local copies, and uuids comparison for // operations involving ra access. so, I believe we should act similarly here. if (nestedWCThisEntry.getRepositoryRoot() != null && parentThisEntry.getRepositoryRoot() != null && !nestedWCThisEntry.getRepositoryRoot().equals(parentThisEntry.getRepositoryRoot())) { 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, nestedWCThisEntry.getRepositoryRootURL(), parentThisEntry.getRepositoryRootURL() }); SVNErrorManager.error(err, SVNLogType.WC); } if (parentThisEntry.isScheduledForDeletion()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE, "Cannot copy to ''{0}'', as it is scheduled for deletion", nestedWCParent); SVNErrorManager.error(err, SVNLogType.WC); } if (nestedWCThisEntry.isScheduledForDeletion()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE, "Cannot copy ''{0}'', as it is scheduled for deletion", nestedWC); SVNErrorManager.error(err, SVNLogType.WC); } SVNURL nestedWCReposRoot = getReposRoot(nestedWC, null, SVNRevision.WORKING, nestedArea, nestedWCAccess); String nestedWCPath = getPathRelativeToRoot(nestedWC, null, nestedWCReposRoot, nestedWCAccess, null); SVNURL parentReposRoot = getReposRoot(nestedWCParent, null, SVNRevision.WORKING, parentArea, parentWCAccess); String parentPath = getPathRelativeToRoot(nestedWCParent, null, parentReposRoot, parentWCAccess, null); if (SVNPathUtil.isAncestor(nestedWCPath, parentPath)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Cannot copy path ''{0}'' into its own child ''{1}", new Object[] { nestedWCPath, parentPath }); SVNErrorManager.error(err, SVNLogType.WC); } if ((nestedWCThisEntry.isScheduledForAddition() && !nestedWCThisEntry.isCopied()) || nestedWCThisEntry.getURL() == 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); } copyDisjointDir(nestedWC, parentWCAccess, nestedWCParent); parentWCAccess.probeTry(nestedWC, true, SVNWCAccess.INFINITE_DEPTH); } finally { parentWCAccess.close(); nestedWCAccess.close(); sleepForTimeStamp(); } } private void copyDisjointDir(File nestedWC, SVNWCAccess parentAccess, File nestedWCParent) throws SVNException { SVNWCClient16 wcClient = new SVNWCClient16((ISVNAuthenticationManager) null, null); wcClient.setEventHandler(getEventDispatcher()); wcClient.doCleanup(nestedWC); SVNWCAccess nestedWCAccess = createWCAccess(); SVNAdminArea dir; String copyFromURL = null; long copyFromRevision = -1; try { dir = nestedWCAccess.open(nestedWC, true, SVNWCAccess.INFINITE_DEPTH); SVNEntry nestedWCThisEntry = nestedWCAccess.getVersionedEntry(nestedWC, false); postCopyCleanup(dir); if (nestedWCThisEntry.isCopied()) { if (nestedWCThisEntry.getCopyFromURL() != null) { copyFromURL = nestedWCThisEntry.getCopyFromURL(); copyFromRevision = nestedWCThisEntry.getCopyFromRevision(); } Map attributes = new SVNHashMap(); attributes.put(SVNProperty.URL, copyFromURL); dir.modifyEntry(dir.getThisDirName(), attributes, true, false); } else { copyFromURL = nestedWCThisEntry.getURL(); copyFromRevision = nestedWCThisEntry.getRevision(); } } finally { nestedWCAccess.close(); } SVNWCManager.add(nestedWC, parentAccess.getAdminArea(nestedWCParent), SVNURL.parseURIEncoded(copyFromURL), copyFromRevision, SVNDepth.INFINITY); } private void copyWCToWC(List pairs) throws SVNException { // find common ancestor for all dsts. String dstParentPath = null; for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); String dstPath = pair.myDst; if (dstParentPath == null) { dstParentPath = SVNPathUtil.removeTail(pair.myDst); } dstParentPath = SVNPathUtil.getCommonPathAncestor(dstParentPath, dstPath); } SVNWCAccess dstAccess = getWCAccess(); try { open(dstAccess, new File(dstParentPath), true, false, 0); for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); checkCancelled(); SVNWCAccess srcAccess = null; try { // do real copy. File sourceFile = new File(pair.mySource); copyFiles(sourceFile, new File(dstParentPath), dstAccess, pair.myBaseName, false); } finally { if (srcAccess != null && srcAccess != dstAccess) { close(srcAccess); } } } } finally { close(dstAccess); sleepForTimeStamp(); } } private void moveWCToWC(List pairs) throws SVNException { for (Iterator ps = pairs.iterator(); ps.hasNext();) { CopyPair pair = (CopyPair) ps.next(); checkCancelled(); File srcParent = new File(SVNPathUtil.removeTail(pair.mySource)); File dstParent = new File(SVNPathUtil.removeTail(pair.myDst)); File sourceFile = new File(pair.mySource); SVNFileType srcType = SVNFileType.getType(sourceFile); SVNWCAccess srcAccess = createWCAccess(); SVNWCAccess dstAccess = null; try { srcAccess.open(srcParent, true, srcType == SVNFileType.DIRECTORY ? -1 : 0); if (srcParent.equals(dstParent)) { dstAccess = srcAccess; } else { String srcParentPath = srcParent.getAbsolutePath().replace(File.separatorChar, '/'); srcParentPath = SVNPathUtil.validateFilePath(srcParentPath); String dstParentPath = dstParent.getAbsolutePath().replace(File.separatorChar, '/'); dstParentPath = SVNPathUtil.validateFilePath(dstParentPath); if (srcType == SVNFileType.DIRECTORY && SVNPathUtil.isAncestor(srcParentPath, dstParentPath)) { dstAccess = srcAccess; if (dstAccess.getAdminArea(dstParent) == null) { dstAccess.open(dstParent, true, 0); } } else { dstAccess = createWCAccess(); dstAccess.open(dstParent, true, 0); } } copyFiles(sourceFile, dstParent, dstAccess, pair.myBaseName, true); // delete src. SVNWCManager.delete(srcAccess, srcAccess.getAdminArea(srcParent), sourceFile, true, true); } finally { if (dstAccess != null && dstAccess != srcAccess) { dstAccess.close(); } srcAccess.close(); } } sleepForTimeStamp(); } private void copyFiles(File src, File dstParent, SVNWCAccess dstAccess, String dstName, boolean move) throws SVNException { SVNWCAccess srcAccess = getWCAccess(); try { probeOpen(srcAccess, src, false, -1); SVNEntry dstEntry = dstAccess.getVersionedEntry(dstParent, false); SVNEntry srcEntry = srcAccess.getVersionedEntry(src, false); if (srcEntry.getRepositoryRoot() != null && dstEntry.getRepositoryRoot() != null && !srcEntry.getRepositoryRoot().equals(dstEntry.getRepositoryRoot())) { 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[] {dstParent, srcEntry.getRepositoryRootURL(), dstEntry.getRepositoryRootURL()}); SVNErrorManager.error(err, SVNLogType.WC); } if (dstEntry.isScheduledForDeletion()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SCHEDULE, "Cannot copy to ''{0}'', as it is scheduled for deletion", dstParent); SVNErrorManager.error(err, SVNLogType.WC); } SVNFileType srcType = SVNFileType.getType(src); if (srcType == SVNFileType.FILE || srcType == SVNFileType.SYMLINK) { if (srcEntry.isScheduledForAddition() && !(srcEntry.isCopied() && srcEntry.getCopyFromURL() != null)) { copyAddedFileAdm(src, srcAccess, dstAccess, dstParent, dstName, true); } else { copyFileAdm(src, srcAccess, dstParent, dstAccess, dstName, move); } } else if (srcType == SVNFileType.DIRECTORY) { if (srcEntry.isScheduledForAddition() && !(srcEntry.isCopied() && srcEntry.getCopyFromURL() != null)) { copyAddedDirAdm(src, srcAccess, dstParent, dstAccess, dstName, true); } else { copyDirAdm(src, srcAccess, dstAccess, dstParent, dstName); } } } finally { close(srcAccess); } } private void copyFileAdm(File src, SVNWCAccess srcAccess, File dstParent, SVNWCAccess dstAccess, String dstName, boolean move) throws SVNException { File dst = new File(dstParent, dstName); SVNFileType dstType = SVNFileType.getType(dst); if (dstType != SVNFileType.NONE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "''{0}'' already exists and is in the way", dst); SVNErrorManager.error(err, SVNLogType.WC); } SVNEntry dstEntry = dstAccess.getEntry(dst, false); if (dstEntry != null && !dstEntry.isScheduledForDeletion()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "There is already a versioned item ''{0}''", dst); SVNErrorManager.error(err, SVNLogType.WC); } SVNEntry srcEntry = srcAccess.getVersionedEntry(src, false); if ((srcEntry.isScheduledForAddition() && !srcEntry.isCopied()) || srcEntry.getURL() == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Cannot copy or move ''{0}'': it is not in repository yet; " + "try committing first", src); SVNErrorManager.error(err, SVNLogType.WC); } String copyFromURL; long copyFromRevision; SVNAdminArea srcDir = srcAccess.getAdminArea(src.getParentFile()); if (srcEntry.isCopied()) { // get cf info - one of the parents has to keep that. SVNLocationEntry location = determineCopyFromInfo(src, srcAccess, srcEntry, dstEntry); copyFromURL = location.getPath(); copyFromRevision = location.getRevision(); } else { copyFromURL = srcEntry.getURL(); copyFromRevision = srcEntry.getRevision(); } String changelistName = srcEntry.getChangelistName(); // copy base file. File srcBaseFile = new File(src.getParentFile(), SVNAdminUtil.getTextBasePath(src.getName(), false)); File dstBaseFile = new File(dstParent, SVNAdminUtil.getTextBasePath(dstName, true)); SVNFileUtil.copyFile(srcBaseFile, dstBaseFile, false); SVNVersionedProperties srcBaseProps = srcDir.getBaseProperties(src.getName()); SVNVersionedProperties srcWorkingProps = srcDir.getProperties(src.getName()); SVNAdminArea dstDir = dstAccess.getAdminArea(dstParent); if (srcWorkingProps.getPropertyValue(SVNProperty.SPECIAL) != null) { SVNWCManager.addRepositoryFile(dstDir, dstName, null, srcBaseFile, srcBaseProps.asMap(), srcWorkingProps.asMap(), copyFromURL, copyFromRevision); String sourceLinkTarget = SVNFileUtil.getSymlinkName(src); String targetLinkTarget = SVNFileUtil.getSymlinkName(dst); if (sourceLinkTarget != null && !sourceLinkTarget.equals(targetLinkTarget)) { SVNFileUtil.deleteFile(dst); SVNFileUtil.createSymlink(dst, sourceLinkTarget); } } else { File tmpWCFile = SVNAdminUtil.createTmpFile(dstDir); SVNFileUtil.copyFile(src, tmpWCFile, false); SVNWCManager.addRepositoryFile(dstDir, dstName, tmpWCFile, null, srcBaseProps.asMap(), srcWorkingProps.asMap(), copyFromURL, copyFromRevision); } if (changelistName != null && move) { dstEntry = dstAccess.getEntry(dst, false); if (dstEntry != null) { SVNLog log = dstDir.getLog(); SVNProperties command = new SVNProperties(); command.put(SVNProperty.shortPropertyName(SVNProperty.CHANGELIST), changelistName); log.logChangedEntryProperties(dstName, command); log.save(); dstDir.runLogs(); } } SVNEvent event = SVNEventFactory.createSVNEvent(dst, SVNNodeKind.FILE, null, SVNRepository.INVALID_REVISION, SVNEventAction.COPY, null, null, null); dstAccess.handleEvent(event); } private void copyAddedFileAdm(File src, SVNWCAccess srcAccess, SVNWCAccess dstAccess, File dstParent, String dstName, boolean isAdded) throws SVNException { File dst = new File(dstParent, dstName); SVNFileType srcType = SVNFileType.getType(src); if (srcType == SVNFileType.SYMLINK) { String linkName = SVNFileUtil.getSymlinkName(src); SVNFileUtil.createSymlink(dst, linkName); } else { SVNFileUtil.copyFile(src, dst, false); } if (isAdded) { SVNWCManager.add(dst, dstAccess.getAdminArea(dstParent), null, SVNRepository.INVALID_REVISION, SVNDepth.INFINITY); copyProps(src, dst, srcAccess, dstAccess); } } private void copyDirAdm(File src, SVNWCAccess srcAccess, SVNWCAccess dstAccess, File dstParent, String dstName) throws SVNException { File dst = new File(dstParent, dstName); SVNEntry srcEntry = srcAccess.getVersionedEntry(src, false); if ((srcEntry.isScheduledForAddition() && !srcEntry.isCopied()) || srcEntry.getURL() == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_EXISTS, "Cannot copy or move ''{0}'': it is not in repository yet; " + "try committing first", src); SVNErrorManager.error(err, SVNLogType.WC); } SVNFileUtil.copyDirectory(src, dst, true, getEventDispatcher()); SVNWCClient16 wcClient = new SVNWCClient16((ISVNAuthenticationManager) null, null); wcClient.setEventHandler(getEventDispatcher()); wcClient.doCleanup(dst); SVNWCAccess tgtAccess = getWCAccess(); SVNAdminArea dir; String copyFromURL = null; long copyFromRevision = -1; try { dir = open(tgtAccess, dst, true, false, -1); postCopyCleanup(dir); if (srcEntry.isCopied()) { SVNEntry dstEntry = dstAccess.getEntry(dst, false); SVNLocationEntry info = determineCopyFromInfo(src, srcAccess, srcEntry, dstEntry); copyFromURL = info.getPath(); copyFromRevision = info.getRevision(); Map attributes = new SVNHashMap(); attributes.put(SVNProperty.URL, copyFromURL); dir.modifyEntry(dir.getThisDirName(), attributes, true, false); } else { copyFromURL = srcEntry.getURL(); copyFromRevision = srcEntry.getRevision(); } } finally { close(tgtAccess); } SVNWCManager.add(dst, dstAccess.getAdminArea(dstParent), SVNURL.parseURIEncoded(copyFromURL), copyFromRevision, SVNDepth.INFINITY); } private void copyAddedDirAdm(File src, SVNWCAccess srcAccess, File dstParent, SVNWCAccess dstParentAccess, String dstName, boolean isAdded) throws SVNException { File dst = new File(dstParent, dstName); if (!isAdded) { SVNFileUtil.copyDirectory(src, dst, true, getEventDispatcher()); } else { checkCancelled(); dst.mkdirs(); SVNWCManager.add(dst, dstParentAccess.getAdminArea(dstParent), null, SVNRepository.INVALID_REVISION, SVNDepth.INFINITY); copyProps(src, dst, srcAccess, dstParentAccess); SVNAdminArea srcChildArea = srcAccess.retrieve(src); File[] entries = SVNFileListUtil.listFiles(src); for (int i = 0; entries != null && i < entries.length; i++) { checkCancelled(); File fsEntry = entries[i]; String name = fsEntry.getName(); if (SVNFileUtil.getAdminDirectoryName().equals(name)) { continue; } SVNEntry entry = srcChildArea.getEntry(name, true); if (fsEntry.isDirectory()) { copyAddedDirAdm(fsEntry, srcAccess, dst, dstParentAccess, name, entry != null); } else if (fsEntry.isFile()) { copyAddedFileAdm(fsEntry, srcAccess, dstParentAccess, dst, name, entry != null); } } } } private void copyProps(File src, File dst, SVNWCAccess srcAccess, SVNWCAccess dstAccess) throws SVNException { SVNEntry srcEntry = srcAccess.getVersionedEntry(src, false); SVNAdminArea srcArea = srcEntry.getAdminArea(); SVNVersionedProperties srcProps = srcArea.getProperties(srcEntry.getName()); Collection propNames = srcProps.getPropertyNames(null); for (Iterator propNamesIter = propNames.iterator(); propNamesIter.hasNext();) { String propName = (String) propNamesIter.next(); SVNPropertyValue propValue = srcProps.getPropertyValue(propName); SVNPropertiesManager.setProperty(dstAccess, dst, propName, propValue, false); } } private SVNLocationEntry determineCopyFromInfo(File src, SVNWCAccess srcAccess, SVNEntry srcEntry, SVNEntry dstEntry) throws SVNException { String url; long rev; if (srcEntry.getCopyFromURL() != null) { url = srcEntry.getCopyFromURL(); rev = srcEntry.getCopyFromRevision(); } else { SVNLocationEntry info = getCopyFromInfoFromParent(src, srcAccess); url = info.getPath(); rev = info.getRevision(); } if (dstEntry != null && rev == dstEntry.getRevision() && url.equals(dstEntry.getCopyFromURL())) { url = null; rev = -1; } return new SVNLocationEntry(rev, url); } private SVNLocationEntry getCopyFromInfoFromParent(File file, SVNWCAccess access) throws SVNException { File parent = file.getParentFile(); String rest = file.getName(); String url = null; long rev = -1; while (parent != null && url == null) { try { SVNEntry entry = access.getVersionedEntry(parent, false); url = entry.getCopyFromURL(); rev = entry.getCopyFromRevision(); } catch (SVNException e) { SVNWCAccess wcAccess = SVNWCAccess.newInstance(null); try { probeOpen(wcAccess, parent, false, -1); SVNEntry entry = wcAccess.getVersionedEntry(parent, false); url = entry.getCopyFromURL(); rev = entry.getCopyFromRevision(); } finally { close(wcAccess); } } if (url != null) { url = SVNPathUtil.append(url, SVNEncodingUtil.uriEncode(rest)); } else { rest = SVNPathUtil.append(parent.getName(), rest); parent = parent.getParentFile(); } } return new SVNLocationEntry(rev, url); } private void addLocalParents(File path, ISVNEventHandler handler) throws SVNException { boolean created = path.mkdirs(); SVNWCClient16 wcClient = new SVNWCClient16((ISVNAuthenticationManager) null, null); try { wcClient.setEventHandler(handler); wcClient.doAdd(path, false, false, true, SVNDepth.EMPTY, true, true); } catch (SVNException e) { if (created) { SVNFileUtil.deleteAll(path, true); } throw e; } } private void extendWCMergeInfo(File path, SVNEntry entry, Map mergeInfo, SVNWCAccess access) throws SVNException { Map wcMergeInfo = SVNPropertiesManager.parseMergeInfo(path, entry, false); if (wcMergeInfo != null && mergeInfo != null) { wcMergeInfo = SVNMergeInfoUtil.mergeMergeInfos(wcMergeInfo, mergeInfo); } else if (wcMergeInfo == null) { wcMergeInfo = mergeInfo; } SVNPropertiesManager.recordWCMergeInfo(path, wcMergeInfo, access); } private Map calculateTargetMergeInfo(File srcFile, SVNWCAccess access, SVNURL srcURL, long srcRevision, SVNRepository repository, boolean noReposAccess) throws SVNException { boolean isLocallyAdded = false; SVNEntry entry = null; SVNURL url = null; if (access != null) { entry = access.getVersionedEntry(srcFile, false); if (entry.isScheduledForAddition() && !entry.isCopied()) { isLocallyAdded = true; } else { if (entry.getCopyFromURL() != null) { url = entry.getCopyFromSVNURL(); srcRevision = entry.getCopyFromRevision(); } else if (entry.getURL() != null) { url = entry.getSVNURL(); srcRevision = entry.getRevision(); } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_MISSING_URL, "Entry for ''{0}'' has no URL", srcFile); SVNErrorManager.error(err, SVNLogType.WC); } } } else { url = srcURL; } Map targetMergeInfo = null; if (!isLocallyAdded) { String mergeInfoPath; if (!noReposAccess) { SVNRepository repos = repository; if (repos == null) { repos = createRepository(url, null, false); } SVNURL oldLocation = null; try { mergeInfoPath = getPathRelativeToSession(url, null, repos); if (mergeInfoPath == null) { oldLocation = repos.getLocation(); repos.setLocation(url, false); mergeInfoPath = ""; } targetMergeInfo = getReposMergeInfo(repos, mergeInfoPath, srcRevision, SVNMergeInfoInheritance.INHERITED, true); } finally { if (repository == null) { repos.closeSession(); } else if (oldLocation != null) { repos.setLocation(oldLocation, false); } } } else { targetMergeInfo = getWCMergeInfo(srcFile, entry, null, SVNMergeInfoInheritance.INHERITED, false, new boolean[1]); } } return targetMergeInfo; } private static class CopyCommitPathHandler implements ISVNCommitPathHandler { private Map myPathInfos; private boolean myIsMove; public CopyCommitPathHandler(Map pathInfos, boolean isMove) { myPathInfos = pathInfos; myIsMove = isMove; } public boolean handleCommitPath(String commitPath, ISVNEditor commitEditor) throws SVNException { CopyPathInfo pathInfo = (CopyPathInfo) myPathInfos.get(commitPath); boolean doAdd = false; boolean doDelete = false; if (pathInfo.isDirAdded) { commitEditor.addDir(commitPath, null, SVNRepository.INVALID_REVISION); return true; } if (pathInfo.isResurrection) { if (!myIsMove) { doAdd = true; } } else { if (myIsMove) { if (commitPath.equals(pathInfo.mySourceRelativePath)) { doDelete = true; } else { doAdd = true; } } else { doAdd = true; } } if (doDelete) { commitEditor.deleteEntry(commitPath, -1); } boolean closeDir = false; if (doAdd) { SVNPathUtil.checkPathIsValid(commitPath); if (pathInfo.mySourceKind == SVNNodeKind.DIR) { commitEditor.addDir(commitPath, pathInfo.mySourcePath, pathInfo.mySourceRevisionNumber); if (pathInfo.myMergeInfoProp != null) { commitEditor.changeDirProperty(SVNProperty.MERGE_INFO, SVNPropertyValue.create(pathInfo.myMergeInfoProp)); } closeDir = true; } else { commitEditor.addFile(commitPath, pathInfo.mySourcePath, pathInfo.mySourceRevisionNumber); if (pathInfo.myMergeInfoProp != null) { commitEditor.changeFileProperty(commitPath, SVNProperty.MERGE_INFO, SVNPropertyValue.create(pathInfo.myMergeInfoProp)); } commitEditor.closeFile(commitPath, null); } } return closeDir; } } private static class CopyPathInfo { public String mySourceRelativePath; public boolean isDirAdded; public boolean isResurrection; public SVNNodeKind mySourceKind; public String mySource; public String mySourcePath; public String myDstPath; public String myMergeInfoProp; public long mySourceRevisionNumber; } private static class CopyPair { public String mySource; public String myOriginalSource; public SVNNodeKind mySourceKind; public SVNRevision mySourceRevision; public SVNRevision mySourcePegRevision; public long mySourceRevisionNumber; public String myBaseName; public String myDst; public void setSourceRevisions(SVNRevision pegRevision, SVNRevision revision) { if (pegRevision == SVNRevision.UNDEFINED) { if (SVNPathUtil.isURL(mySource)) { pegRevision = SVNRevision.HEAD; } else { pegRevision = SVNRevision.WORKING; } } if (revision == SVNRevision.UNDEFINED) { revision = pegRevision; } mySourceRevision = revision; mySourcePegRevision = pegRevision; } } }