/* * ==================================================================== * 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.InputStream; import java.io.OutputStream; import java.util.Collection; import java.util.Iterator; import java.util.LinkedList; import java.util.Map; import org.tmatesoft.svn.core.SVNCommitInfo; import org.tmatesoft.svn.core.SVNDepth; import org.tmatesoft.svn.core.SVNErrorCode; import org.tmatesoft.svn.core.SVNErrorMessage; import org.tmatesoft.svn.core.SVNException; import org.tmatesoft.svn.core.SVNNodeKind; import org.tmatesoft.svn.core.SVNProperties; import org.tmatesoft.svn.core.SVNProperty; import org.tmatesoft.svn.core.SVNPropertyValue; import org.tmatesoft.svn.core.SVNURL; import org.tmatesoft.svn.core.internal.io.fs.FSRepositoryUtil; import org.tmatesoft.svn.core.internal.util.SVNEncodingUtil; import org.tmatesoft.svn.core.internal.util.SVNHashMap; import org.tmatesoft.svn.core.internal.util.SVNPathUtil; import org.tmatesoft.svn.core.internal.wc.admin.ISVNCleanupHandler; import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminArea; import org.tmatesoft.svn.core.internal.wc.admin.SVNAdminAreaInfo; import org.tmatesoft.svn.core.internal.wc.admin.SVNChecksumInputStream; import org.tmatesoft.svn.core.internal.wc.admin.SVNChecksumOutputStream; 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.SVNRepository; import org.tmatesoft.svn.core.io.diff.SVNDeltaProcessor; import org.tmatesoft.svn.core.io.diff.SVNDiffWindow; 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.SVNStatusType; import org.tmatesoft.svn.util.SVNLogType; /** * @version 1.3 * @author TMate Software Ltd. */ public class SVNUpdateEditor15 implements ISVNUpdateEditor, ISVNCleanupHandler { private String mySwitchURL; private String myTarget; private String myTargetURL; private String myRootURL; private SVNAdminAreaInfo myAdminInfo; private SVNDirectoryInfo myCurrentDirectory; private SVNFileInfo myCurrentFile; private long myTargetRevision; private boolean myIsRootOpen; private boolean myIsTargetDeleted; private boolean myIsUnversionedObstructionsAllowed; private boolean myIsDepthSticky; //File objects private Collection mySkippedPaths; private SVNWCAccess myWCAccess; private SVNDeltaProcessor myDeltaProcessor; private SVNDepth myRequestedDepth; private String[] myExtensionPatterns; private ISVNFileFetcher myFileFetcher; private boolean myIsLockOnDemand; private SVNUpdateEditor15(SVNAdminAreaInfo info, String switchURL, boolean allowUnversionedObstructions, boolean depthIsSticky, SVNDepth depth, String[] preservedExtensions, String targetURL, String rootURL, ISVNFileFetcher fileFetcher, boolean lockOnDemand) { myAdminInfo = info; myWCAccess = info.getWCAccess(); myIsUnversionedObstructionsAllowed = allowUnversionedObstructions; myTarget = info.getTargetName(); mySwitchURL = switchURL; myTargetRevision = -1; myRequestedDepth = depth; myIsDepthSticky = depthIsSticky; myDeltaProcessor = new SVNDeltaProcessor(); myExtensionPatterns = preservedExtensions; myFileFetcher = fileFetcher; myTargetURL = targetURL; myRootURL = rootURL; myIsLockOnDemand = lockOnDemand; if (myTarget != null) { myTargetURL = SVNPathUtil.append(myTargetURL, SVNEncodingUtil.uriEncode(myTarget)); } if ("".equals(myTarget)) { myTarget = null; } } public void targetRevision(long revision) throws SVNException { myTargetRevision = revision; } public long getTargetRevision() { return myTargetRevision; } public void openRoot(long revision) throws SVNException { myIsRootOpen = true; myCurrentDirectory = createDirectoryInfo(null, "", false); myWCAccess.registerCleanupHandler(myCurrentDirectory.getAdminArea(), myCurrentDirectory); if (myTarget == null) { SVNAdminArea adminArea = myCurrentDirectory.getAdminArea(); SVNEntry entry = adminArea.getEntry(adminArea.getThisDirName(), false); if (entry != null) { myCurrentDirectory.myAmbientDepth = entry.getDepth(); myCurrentDirectory.myPreviousRevision = entry.getRevision(); } Map attributes = new SVNHashMap(); attributes.put(SVNProperty.REVISION, Long.toString(myTargetRevision)); attributes.put(SVNProperty.URL, myCurrentDirectory.URL); attributes.put(SVNProperty.INCOMPLETE, Boolean.TRUE.toString()); if (myRootURL != null && SVNPathUtil.isAncestor(myRootURL, myCurrentDirectory.URL)) { attributes.put(SVNProperty.REPOS, myRootURL); } adminArea.modifyEntry(adminArea.getThisDirName(), attributes, true, false); if (mySwitchURL != null) { clearWCProperty(myCurrentDirectory.getAdminArea(), null); } } else if (mySwitchURL != null) { if (myAdminInfo.getTarget() == myAdminInfo.getAnchor()) { clearWCProperty(myAdminInfo.getTarget(), myTarget); } else { clearWCProperty(myAdminInfo.getTarget(), null); } } } public void deleteEntry(String path, long revision) throws SVNException { checkIfPathIsUnderRoot(path); String name = SVNPathUtil.tail(path); SVNAdminArea parentArea = myCurrentDirectory.getAdminArea(); SVNEntry entry = parentArea.getEntry(name, true); if (entry == null) { return; } SVNLog log = myCurrentDirectory.getLog(); SVNProperties attributes = new SVNProperties(); attributes.put(SVNLog.NAME_ATTR, name); log.addCommand(SVNLog.DELETE_ENTRY, attributes, false); SVNNodeKind kind = entry.getKind(); long previousRevision = entry.getRevision(); boolean isDeleted = entry.isDeleted(); SVNURL url = entry.getSVNURL(); if (path.equals(myTarget)) { attributes.put(SVNLog.NAME_ATTR, name); attributes.put(SVNProperty.shortPropertyName(SVNProperty.KIND), kind == SVNNodeKind.DIR ? SVNProperty.KIND_DIR : SVNProperty.KIND_FILE); attributes.put(SVNProperty.shortPropertyName(SVNProperty.REVISION), Long.toString(myTargetRevision)); attributes.put(SVNProperty.shortPropertyName(SVNProperty.DELETED), Boolean.TRUE.toString()); log.addCommand(SVNLog.MODIFY_ENTRY, attributes, false); myIsTargetDeleted = true; } try { myCurrentDirectory.flushLog(); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Error writing log file for ''{0}''", myCurrentDirectory.getPath()); SVNErrorManager.error(err, svne, SVNLogType.WC); } if (myIsLockOnDemand && kind == SVNNodeKind.DIR) { SVNAdminArea childArea = myWCAccess.getAdminArea(parentArea.getFile(name)); if (childArea != null && !childArea.isLocked()) { childArea.lock(false); } } if (mySwitchURL != null && kind == SVNNodeKind.DIR) { SVNAdminArea childArea = myWCAccess.retrieve(parentArea.getFile(name)); try { childArea.removeFromRevisionControl(childArea.getThisDirName(), true, true); } catch (SVNException svne) { handleLeftLocalModificationsError(svne); } } try { myCurrentDirectory.runLogs(); } catch (SVNException svne) { handleLeftLocalModificationsError(svne); } if (isDeleted) { // entry was deleted, but it was already deleted, no need to make a // notification. return; } SVNEvent event = SVNEventFactory.createSVNEvent(parentArea.getFile(name), kind, null, SVNRepository.INVALID_REVISION, SVNEventAction.UPDATE_DELETE, null, null, null); event.setPreviousRevision(previousRevision); event.setPreviousURL(url); myWCAccess.handleEvent(event); } private void handleLeftLocalModificationsError(SVNException originalError) throws SVNException { SVNException error = null; for (error = originalError; error != null;) { if (error.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LEFT_LOCAL_MOD) { break; } error = (error.getCause() instanceof SVNException) ? (SVNException) error.getCause() : null; } if (error != null) { return; } throw originalError; } public void addDir(String path, String copyFromPath, long copyFromRevision) throws SVNException { SVNAdminArea parentArea = myCurrentDirectory.getAdminArea(); SVNDirectoryInfo parentDirectory = myCurrentDirectory; myCurrentDirectory = createDirectoryInfo(myCurrentDirectory, path, true); myCurrentDirectory.myPreviousRevision = -1; if (path.equals(myTarget)) { myCurrentDirectory.myAmbientDepth = myRequestedDepth == SVNDepth.UNKNOWN ? SVNDepth.INFINITY : myRequestedDepth; } else if (myRequestedDepth == SVNDepth.IMMEDIATES || (myRequestedDepth == SVNDepth.UNKNOWN && parentDirectory.myAmbientDepth == SVNDepth.IMMEDIATES)) { myCurrentDirectory.myAmbientDepth = SVNDepth.EMPTY; } else { myCurrentDirectory.myAmbientDepth = SVNDepth.INFINITY; } parentDirectory.flushLog(); checkIfPathIsUnderRoot(path); String name = SVNPathUtil.tail(path); File childDir = parentArea.getFile(name); SVNFileType kind = SVNFileType.getType(childDir); if (kind == SVNFileType.FILE || kind == SVNFileType.UNKNOWN) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add directory ''{0}'': a non-directory object of the same name already exists", path); SVNErrorManager.error(err, SVNLogType.WC); } if (kind == SVNFileType.DIRECTORY) { SVNAdminArea adminArea = null; try { adminArea = SVNWCAccess.newInstance(null).open(childDir, false, 0); } catch (SVNException svne) { if (svne.getErrorMessage().getErrorCode() != SVNErrorCode.WC_NOT_DIRECTORY) { throw svne; } if (myIsUnversionedObstructionsAllowed) { myCurrentDirectory.isExisted = true; } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add directory ''{0}'': an unversioned directory of the same name already exists", myCurrentDirectory.getPath()); SVNErrorManager.error(err, SVNLogType.WC); } } if (adminArea != null) { SVNEntry entry = adminArea.getEntry(adminArea.getThisDirName(), false); if (entry != null && entry.isScheduledForAddition() && !entry.isCopied()) { myCurrentDirectory.isAddExisted = true; } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add directory ''{0}'': a versioned directory of the same name already exists", myCurrentDirectory.getPath()); SVNErrorManager.error(err, SVNLogType.WC); } } } if (SVNFileUtil.getAdminDirectoryName().equals(name)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add directory ''{0}'': object of the same name as the administrative directory", path); SVNErrorManager.error(err, SVNLogType.WC); } if (copyFromPath != null || SVNRevision.isValidRevisionNumber(copyFromRevision)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Failed to add directory ''{0}'': copyfrom arguments not yet supported", path); SVNErrorManager.error(err, SVNLogType.WC); } SVNEntry entry = parentArea.getEntry(name, false); Map attributes = new SVNHashMap(); attributes.put(SVNProperty.KIND, SVNProperty.KIND_DIR); attributes.put(SVNProperty.ABSENT, null); attributes.put(SVNProperty.DELETED, null); boolean force = false; if (myCurrentDirectory.isAddExisted) { attributes.put(SVNProperty.SCHEDULE, null); force = true; } entry = parentArea.modifyEntry(name, attributes, true, force); if (myCurrentDirectory.isAddExisted) { attributes.put(SVNProperty.REVISION, Long.toString(myTargetRevision)); if (mySwitchURL != null) { attributes.put(SVNProperty.URL, myCurrentDirectory.URL); } SVNAdminArea adminArea = myCurrentDirectory.getAdminArea(); adminArea.modifyEntry(adminArea.getThisDirName(), attributes, true, true); } String rootURL = null; if (SVNPathUtil.isAncestor(myRootURL, myCurrentDirectory.URL)) { rootURL = myRootURL; } if (myWCAccess.getAdminArea(childDir) != null) { myWCAccess.closeAdminArea(childDir); } if (SVNWCManager.ensureAdminAreaExists(childDir, myCurrentDirectory.URL, rootURL, null, myTargetRevision, myCurrentDirectory.myAmbientDepth)) { // hack : remove created lock file. SVNFileUtil.deleteFile(new File(childDir, SVNFileUtil.getAdminDirectoryName() + "/lock")); } SVNAdminArea childArea = myWCAccess.open(childDir, true, 0); myWCAccess.registerCleanupHandler(childArea, myCurrentDirectory); if (!myCurrentDirectory.isAddExisted) { SVNEvent event = SVNEventFactory.createSVNEvent(parentArea.getFile(entry.getName()), SVNNodeKind.DIR, null, myTargetRevision, myCurrentDirectory.isExisted ? SVNEventAction.UPDATE_EXISTS : SVNEventAction.UPDATE_ADD, null, null, null); event.setPreviousRevision(myCurrentDirectory.myPreviousRevision); event.setPreviousURL(entry.getSVNURL()); event.setURL(myCurrentDirectory.URL != null ? SVNURL.parseURIEncoded(myCurrentDirectory.URL) : null); myWCAccess.handleEvent(event); } } public void openDir(String path, long revision) throws SVNException { myCurrentDirectory.flushLog(); checkIfPathIsUnderRoot(path); myCurrentDirectory = createDirectoryInfo(myCurrentDirectory, path, false); SVNAdminArea adminArea = myCurrentDirectory.getAdminArea(); myWCAccess.registerCleanupHandler(adminArea, myCurrentDirectory); SVNEntry entry = adminArea.getEntry(adminArea.getThisDirName(), true); if (entry != null) { myCurrentDirectory.myPreviousRevision = entry.getRevision(); myCurrentDirectory.myAmbientDepth = entry.getDepth(); boolean hasPropConflicts = adminArea.hasPropConflict(adminArea.getThisDirName()); if (hasPropConflicts) { myCurrentDirectory.isSkipped = true; Collection skippedPaths = getSkippedPaths(); skippedPaths.add(adminArea.getRoot()); SVNEvent event = SVNEventFactory.createSVNEvent(adminArea.getRoot(), SVNNodeKind.DIR, null, myTargetRevision, SVNStatusType.INAPPLICABLE, SVNStatusType.CONFLICTED, SVNStatusType.LOCK_INAPPLICABLE, SVNEventAction.SKIP, SVNEventAction.UPDATE_UPDATE, null, null); event.setPreviousRevision(entry.getRevision()); event.setURL(entry.getSVNURL()); myWCAccess.handleEvent(event); return; } } else { myCurrentDirectory.myPreviousRevision = -1; } Map attributes = new SVNHashMap(); attributes.put(SVNProperty.REVISION, Long.toString(myTargetRevision)); attributes.put(SVNProperty.URL, myCurrentDirectory.URL); attributes.put(SVNProperty.INCOMPLETE, Boolean.TRUE.toString()); if (myRootURL != null && SVNPathUtil.isAncestor(myRootURL, myCurrentDirectory.URL)) { attributes.put(SVNProperty.REPOS, myRootURL); } entry = adminArea.modifyEntry(adminArea.getThisDirName(), attributes, true, false); if (mySwitchURL != null) { clearWCProperty(myCurrentDirectory.getAdminArea(), null); } } private Collection getSkippedPaths() { if (mySkippedPaths == null) { mySkippedPaths = new LinkedList(); } return mySkippedPaths; } public void absentDir(String path) throws SVNException { absentEntry(path, SVNNodeKind.DIR); } public void absentFile(String path) throws SVNException { absentEntry(path, SVNNodeKind.FILE); } private void absentEntry(String path, SVNNodeKind kind) throws SVNException { String name = SVNPathUtil.tail(path); SVNAdminArea adminArea = myCurrentDirectory.getAdminArea(); SVNEntry entry = adminArea.getEntry(name, false); if (entry != null && entry.isScheduledForAddition()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to mark ''{0}'' absent: item of the same name is already scheduled for addition", path); SVNErrorManager.error(err, SVNLogType.WC); } Map attributes = new SVNHashMap(); attributes.put(SVNProperty.REVISION, Long.toString(myTargetRevision)); attributes.put(SVNProperty.KIND, kind.toString()); attributes.put(SVNProperty.DELETED, null); attributes.put(SVNProperty.ABSENT, Boolean.TRUE.toString()); entry = adminArea.modifyEntry(name, attributes, true, false); } public void changeDirProperty(String name, SVNPropertyValue value) throws SVNException { if (!myCurrentDirectory.isSkipped) { myCurrentDirectory.propertyChanged(name, value); } } private void clearWCProperty(SVNAdminArea adminArea, String target) throws SVNException { if (adminArea == null) { return; } for (Iterator ents = adminArea.entries(false); ents.hasNext();) { SVNEntry entry = (SVNEntry) ents.next(); if (target != null) { if (entry.isFile() && target.equals(entry.getName())) { SVNVersionedProperties props = adminArea.getWCProperties(entry.getName()); props.setPropertyValue(SVNProperty.WC_URL, null); adminArea.saveWCProperties(false); } continue; } if (entry.isFile() || adminArea.getThisDirName().equals(entry.getName())) { SVNVersionedProperties props = adminArea.getWCProperties(entry.getName()); props.setPropertyValue(SVNProperty.WC_URL, null); adminArea.saveWCProperties(false); } else { SVNAdminArea childArea = myAdminInfo.getWCAccess().getAdminArea(adminArea.getFile(entry.getName())); clearWCProperty(childArea, null); } } } public void closeDir() throws SVNException { SVNProperties modifiedWCProps = myCurrentDirectory.getChangedWCProperties(); SVNProperties modifiedEntryProps = myCurrentDirectory.getChangedEntryProperties(); SVNProperties modifiedProps = myCurrentDirectory.getChangedProperties(); SVNStatusType propStatus = SVNStatusType.UNKNOWN; SVNAdminArea adminArea = myCurrentDirectory.getAdminArea(); if (myAdminInfo.isIncomplete(myCurrentDirectory.getPath())) { // delete all props. SVNVersionedProperties oldBaseProps = adminArea.getBaseProperties(adminArea.getThisDirName()); SVNProperties baseMap = oldBaseProps.asMap(); if (modifiedProps == null) { modifiedProps = new SVNProperties(); } for(Iterator names = baseMap.nameSet().iterator(); names.hasNext();) { String name = (String) names.next(); if (!modifiedProps.containsName(name)) { modifiedProps.put(name, SVNPropertyValue.create(null)); } } } if (modifiedWCProps != null || modifiedEntryProps != null || modifiedProps != null) { SVNLog log = myCurrentDirectory.getLog(); if (modifiedProps != null && !modifiedProps.isEmpty()) { if (modifiedProps.containsName(SVNProperty.EXTERNALS)) { String oldExternal = adminArea.getProperties(adminArea.getThisDirName()).getStringPropertyValue(SVNProperty.EXTERNALS); String newExternal = modifiedProps.getStringValue(SVNProperty.EXTERNALS); String path = myCurrentDirectory.getPath(); if (oldExternal == null && newExternal != null) { myAdminInfo.addExternal(path, oldExternal, newExternal); myAdminInfo.addDepth(path, myCurrentDirectory.myAmbientDepth); } else if (oldExternal != null && newExternal == null) { myAdminInfo.addExternal(path, oldExternal, newExternal); myAdminInfo.addDepth(path, myCurrentDirectory.myAmbientDepth); } else if (oldExternal != null && !oldExternal.equals(newExternal)) { myAdminInfo.addExternal(path, oldExternal, newExternal); myAdminInfo.addDepth(path, myCurrentDirectory.myAmbientDepth); } } SVNVersionedProperties oldBaseProps = adminArea.getBaseProperties(adminArea.getThisDirName()); try { propStatus = adminArea.mergeProperties(adminArea.getThisDirName(), oldBaseProps.asMap(), modifiedProps, null, null, true, false, log); } catch (SVNException svne) { SVNErrorMessage err = svne.getErrorMessage().wrap("Couldn't do property merge"); SVNErrorManager.error(err, svne, SVNLogType.WC); } } log.logChangedEntryProperties(adminArea.getThisDirName(), modifiedEntryProps); log.logChangedWCProperties(adminArea.getThisDirName(), modifiedWCProps); } myCurrentDirectory.flushLog(); myCurrentDirectory.runLogs(); maybeBumpDirInfo(myCurrentDirectory); if (!myCurrentDirectory.isSkipped && (myCurrentDirectory.isAddExisted || !myCurrentDirectory.IsAdded)) { if (!(adminArea == myAdminInfo.getAnchor() && !"".equals(myAdminInfo.getTargetName()))) { // skip event for anchor when there is a target. SVNEventAction action = myCurrentDirectory.isAddExisted || myCurrentDirectory.isExisted ? SVNEventAction.UPDATE_EXISTS : SVNEventAction.UPDATE_UPDATE; if (propStatus == SVNStatusType.UNKNOWN && action != SVNEventAction.UPDATE_EXISTS) { action = SVNEventAction.UPDATE_NONE; } SVNEvent event = SVNEventFactory.createSVNEvent(adminArea.getRoot(), SVNNodeKind.DIR, null, myTargetRevision, SVNStatusType.UNKNOWN, propStatus, null, action, null, null, null); event.setPreviousRevision(myCurrentDirectory.myPreviousRevision); event.setURL(myCurrentDirectory.URL != null ? SVNURL.parseURIEncoded(myCurrentDirectory.URL) : null); myWCAccess.handleEvent(event); } } myCurrentDirectory = myCurrentDirectory.Parent; } public SVNCommitInfo closeEdit() throws SVNException { if (myTarget != null && myWCAccess.isMissing(myAdminInfo.getAnchor().getFile(myTarget))) { myCurrentDirectory = createDirectoryInfo(null, "", false); myWCAccess.registerCleanupHandler(myCurrentDirectory.getAdminArea(), myCurrentDirectory); deleteEntry(myTarget, myTargetRevision); } if (!myIsRootOpen) { if (myCurrentDirectory == null) { myCurrentDirectory = createDirectoryInfo(null, "", false); } completeDirectory(myCurrentDirectory); } if (!myIsTargetDeleted) { File targetFile = myTarget != null ? myAdminInfo.getAnchor().getFile(myTarget) : myAdminInfo.getAnchor().getRoot(); SVNWCManager.updateCleanup(targetFile, myWCAccess, mySwitchURL, myRootURL, myTargetRevision, true, mySkippedPaths, myRequestedDepth, myIsLockOnDemand); } return null; } public void addFile(String path, String copyFromPath, long copyFromRevision) throws SVNException { myCurrentFile = addFile(myCurrentDirectory, path, copyFromPath, copyFromRevision); } public void openFile(String path, long revision) throws SVNException { myCurrentFile = openFile(path, myCurrentDirectory); } public void changeFileProperty(String commitPath, String name, SVNPropertyValue value) throws SVNException { changeFileProperty(name, value, myCurrentFile); } public void applyTextDelta(String commitPath, String baseChecksum) throws SVNException { if (myCurrentFile.isSkipped) { return; } myCurrentFile.receivedTextDelta = true; SVNAdminArea adminArea = myCurrentFile.getAdminArea(); SVNEntry entry = adminArea.getEntry(myCurrentFile.Name, false); boolean replaced = entry != null && entry.isScheduledForReplacement(); boolean useRevertBase = replaced; if (useRevertBase) { myCurrentFile.baseFile = adminArea.getFile(SVNAdminUtil.getTextRevertPath(myCurrentFile.Name, false)); myCurrentFile.newBaseFile = adminArea.getFile(SVNAdminUtil.getTextRevertPath(myCurrentFile.Name, true)); } else { myCurrentFile.baseFile = adminArea.getBaseFile(myCurrentFile.Name, false); myCurrentFile.newBaseFile = adminArea.getBaseFile(myCurrentFile.Name, true); } String checksum = entry != null ? entry.getChecksum() : null; if (!replaced && baseChecksum != null && checksum != null && !baseChecksum.equals(checksum)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT_TEXT_BASE, "Checksum mismatch for ''{0}''; expected: ''{1}'', recorded: ''{2}''", new Object[] { myCurrentFile.baseFile, baseChecksum, checksum }); SVNErrorManager.error(err, SVNLogType.WC); } File baseSrcFile = null; if (!myCurrentFile.IsAdded) { baseSrcFile = myCurrentFile.baseFile; } else { if (myCurrentFile.copiedBaseText != null) { baseSrcFile = myCurrentFile.copiedBaseText; } } if (replaced || checksum == null) { checksum = baseChecksum; } File baseTmpFile = myCurrentFile.newBaseFile; if (checksum != null) { myCurrentFile.expectedSrcChecksum = checksum; InputStream baseIS = baseSrcFile != null && baseSrcFile.exists() ? SVNFileUtil.openFileForReading(baseSrcFile) : SVNFileUtil.DUMMY_IN; myCurrentFile.sourceChecksumStream = baseIS != SVNFileUtil.DUMMY_IN ? new SVNChecksumInputStream(baseIS, SVNChecksumInputStream.MD5_ALGORITHM) : null; myDeltaProcessor.applyTextDelta(myCurrentFile.sourceChecksumStream != null ? myCurrentFile.sourceChecksumStream : baseIS, baseTmpFile, true); } else { myDeltaProcessor.applyTextDelta(baseSrcFile, baseTmpFile, true); } } public OutputStream textDeltaChunk(String commitPath, SVNDiffWindow diffWindow) throws SVNException { if (!myCurrentFile.isSkipped) { try { myDeltaProcessor.textDeltaChunk(diffWindow); } catch (SVNException svne) { myDeltaProcessor.textDeltaEnd(); SVNFileUtil.deleteFile(myCurrentFile.newBaseFile); myCurrentFile.newBaseFile = null; throw svne; } } return SVNFileUtil.DUMMY_OUT; } public void textDeltaEnd(String commitPath) throws SVNException { if (!myCurrentFile.isSkipped) { myCurrentFile.Checksum = myDeltaProcessor.textDeltaEnd(); } if (myCurrentFile.expectedSrcChecksum != null) { String actualSourceChecksum = myCurrentFile.sourceChecksumStream != null ? myCurrentFile.sourceChecksumStream.getDigest() : null; if (!myCurrentFile.expectedSrcChecksum.equals(actualSourceChecksum)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_CORRUPT_TEXT_BASE, "Checksum mismatch while updating ''{0}''; expected: ''{1}'', actual: ''{2}''", new Object[] { myCurrentFile.baseFile, myCurrentFile.expectedSrcChecksum, actualSourceChecksum }); SVNErrorManager.error(err, SVNLogType.WC); } } } public void closeFile(String commitPath, String textChecksum) throws SVNException { closeFile(textChecksum, myCurrentFile, myCurrentDirectory); myCurrentFile = null; } public void abortEdit() throws SVNException { } private void checkIfPathIsUnderRoot(String path) throws SVNException { if (SVNFileUtil.isWindows && path != null) { String testPath = path.replace(File.separatorChar, '/'); int ind = -1; while (testPath.length() > 0 && (ind = testPath.indexOf("..")) != -1) { if (ind == 0 || testPath.charAt(ind - 1) == '/') { int i; for (i = ind + 2; i < testPath.length(); i++) { if (testPath.charAt(i) == '.') { continue; } else if (testPath.charAt(i) == '/') { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Path ''{0}'' is not in the working copy", path); SVNErrorManager.error(err, SVNLogType.WC); } else { break; } } if (i == testPath.length()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Path ''{0}'' is not in the working copy", path); SVNErrorManager.error(err, SVNLogType.WC); } testPath = testPath.substring(i); } else { testPath = testPath.substring(ind + 2); } } } } private void maybeBumpDirInfo(SVNDirectoryInfo dirInfo) throws SVNException { while (dirInfo != null) { dirInfo.RefCount--; if (dirInfo.RefCount > 0) { return; } if (!dirInfo.isSkipped) { completeDirectory(dirInfo); } dirInfo = dirInfo.Parent; } } private void completeDirectory(SVNDirectoryInfo dirInfo) throws SVNException { if (dirInfo.Parent == null && myTarget != null) { return; } SVNAdminArea adminArea = dirInfo.getAdminArea(); SVNEntry entry = adminArea.getEntry(adminArea.getThisDirName(), true); if (entry == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.ENTRY_NOT_FOUND, "No ''.'' entry found in ''{0}''", adminArea.getRoot()); SVNErrorManager.error(err, SVNLogType.WC); } entry.setIncomplete(false); if (!myIsDepthSticky && entry.isThisDir() && myRequestedDepth == SVNDepth.IMMEDIATES) { int directoryDepth = SVNPathUtil.getSegmentsCount(dirInfo.getPath()); if (myTarget != null && !"".equals(myTarget)) { directoryDepth--; } if (directoryDepth > 0) { SVNDepth ambientDepth = dirInfo.myAmbientDepth; if (ambientDepth == null || ambientDepth == SVNDepth.UNKNOWN || ambientDepth.compareTo(SVNDepth.EMPTY) > 0) { entry.setIncomplete(true); } } } File target = myAdminInfo.getTarget().getRoot(); if (myIsDepthSticky && (myRequestedDepth == SVNDepth.INFINITY || (adminArea.getRoot().equals(target) && myRequestedDepth.compareTo(entry.getDepth()) > 0))) { entry.setDepth(myRequestedDepth); myAdminInfo.addDepth(dirInfo.getPath(), myRequestedDepth); } for (Iterator ents = adminArea.entries(true); ents.hasNext();) { SVNEntry currentEntry = (SVNEntry) ents.next(); if (currentEntry.isDeleted()) { if (!currentEntry.isScheduledForAddition()) { adminArea.deleteEntry(currentEntry.getName()); } else { Map attributes = new SVNHashMap(); attributes.put(SVNProperty.DELETED, null); adminArea.modifyEntry(currentEntry.getName(), attributes, false, false); } } else if (currentEntry.isAbsent() && currentEntry.getRevision() != myTargetRevision) { adminArea.deleteEntry(currentEntry.getName()); } else if (currentEntry.getKind() == SVNNodeKind.DIR) { if (myWCAccess.isMissing(adminArea.getFile(currentEntry.getName())) && !currentEntry.isAbsent() && !currentEntry.isScheduledForAddition()) { adminArea.deleteEntry(currentEntry.getName()); myWCAccess.handleEvent(SVNEventFactory.createSVNEvent(adminArea.getFile(currentEntry.getName()), currentEntry.getKind(), null, currentEntry.getRevision(), SVNEventAction.UPDATE_DELETE, null, null, null)); } } } adminArea.saveEntries(true); } private SVNFileInfo addFile(SVNDirectoryInfo parent, String path, String copyFromPath, long copyFromRevision) throws SVNException { if (copyFromPath != null || SVNRevision.isValidRevisionNumber(copyFromRevision)) { if (copyFromPath == null || !SVNRevision.isValidRevisionNumber(copyFromRevision)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OP_ON_CWD, "Bad copyfrom arguments received."); SVNErrorManager.error(err, SVNLogType.DEFAULT); } return addFileWithHistory(parent, path, copyFromPath, copyFromRevision); } checkIfPathIsUnderRoot(path); SVNFileInfo info = createFileInfo(parent, path, true); SVNAdminArea adminArea = parent.getAdminArea(); SVNFileType kind = SVNFileType.getType(adminArea.getFile(info.Name)); SVNEntry entry = adminArea.getEntry(info.Name, true); if (kind != SVNFileType.NONE) { if (myIsUnversionedObstructionsAllowed || (entry != null && entry.isScheduledForAddition())) { if (entry != null && entry.isCopied()) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add file ''{0}'': a file of the same name is already scheduled for " + "addition with history", path); SVNErrorManager.error(err, SVNLogType.WC); } if (kind != SVNFileType.FILE) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add file ''{0}'': a non-file object of the same name already exists", path); SVNErrorManager.error(err, SVNLogType.WC); } if (entry != null) { info.isAddExisted = true; } else { info.isExisted = true; } } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add file ''{0}'': object of the same name already exists", path); SVNErrorManager.error(err, SVNLogType.WC); } } return info; } private SVNFileInfo addFileWithHistory(SVNDirectoryInfo parent, String path, String copyFromPath, long copyFromRevision) throws SVNException { SVNFileInfo info = addFile(parent, path, null, SVNRepository.INVALID_REVISION); info.addedWithHistory = true; SVNAdminArea adminArea = parent.getAdminArea(); SVNEntry pathEntry = adminArea.getEntry(adminArea.getThisDirName(), false); SVNEntry srcEntry = null; try { srcEntry = locateCopyFrom(copyFromPath, copyFromRevision, adminArea.getRoot(), pathEntry); } catch (SVNException svne) { if (svne.getErrorMessage().getErrorCode() != SVNErrorCode.WC_COPYFROM_PATH_NOT_FOUND) { throw svne; } } info.copiedBaseText = SVNAdminUtil.createTmpFile(adminArea); SVNProperties baseProperties = null; SVNProperties workingProperties = null; if (srcEntry != null) { SVNAdminArea srcArea = srcEntry.getAdminArea(); String srcTextBasePath = null; if (srcEntry.isScheduledForReplacement() && srcEntry.getCopyFromURL() != null) { srcTextBasePath = SVNAdminUtil.getTextRevertPath(srcEntry.getName(), false); baseProperties = srcArea.getRevertProperties(srcEntry.getName()).asMap(); workingProperties = baseProperties; } else { srcTextBasePath = SVNAdminUtil.getTextBasePath(srcEntry.getName(), false); baseProperties = srcArea.getBaseProperties(srcEntry.getName()).asMap(); workingProperties = srcArea.getProperties(srcEntry.getName()).asMap(); } InputStream srcIS = null; OutputStream copiedBaseOS = null; try { srcIS = SVNFileUtil.openFileForReading(srcArea.getFile(srcTextBasePath)); copiedBaseOS = SVNFileUtil.openFileForWriting(info.copiedBaseText); SVNChecksumOutputStream checksumOS = new SVNChecksumOutputStream(copiedBaseOS, SVNChecksumOutputStream.MD5_ALGORITHM, true); copiedBaseOS = checksumOS; FSRepositoryUtil.copy(srcIS, copiedBaseOS, myWCAccess); info.copiedBaseChecksum = checksumOS.getDigest(); } finally { SVNFileUtil.closeFile(srcIS); SVNFileUtil.closeFile(copiedBaseOS); } if (srcArea.hasTextModifications(srcEntry.getName(), false, true, false)) { info.copiedWorkingText = SVNAdminUtil.createTmpFile(adminArea); SVNFileUtil.copyFile(srcArea.getFile(srcEntry.getName()), info.copiedWorkingText, true); } } else { if (myFileFetcher == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_OP_ON_CWD, "No fetch_func supplied to update_editor."); SVNErrorManager.error(err, SVNLogType.DEFAULT); } baseProperties = new SVNProperties(); OutputStream baseTextOS = null; try { baseTextOS = SVNFileUtil.openFileForWriting(info.copiedBaseText); SVNChecksumOutputStream checksumBaseTextOS = new SVNChecksumOutputStream(baseTextOS, SVNChecksumOutputStream.MD5_ALGORITHM, true); baseTextOS = checksumBaseTextOS; myFileFetcher.fetchFile(copyFromPath, copyFromRevision, baseTextOS, baseProperties); info.copiedBaseChecksum = checksumBaseTextOS.getDigest(); } finally { SVNFileUtil.closeFile(baseTextOS); } workingProperties = baseProperties; } info.copiedBaseProperties = baseProperties.getRegularProperties(); info.copiedWorkingProperties = workingProperties.getRegularProperties(); return info; } private SVNEntry locateCopyFrom(String copyFromPath, long copyFromRevision, File dstDir, SVNEntry dstEntry) throws SVNException { if (dstEntry.getRepositoryRoot() == null || dstEntry.getURL() == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_COPYFROM_PATH_NOT_FOUND, "Destination directory of add-with-history is missing a URL"); SVNErrorManager.error(err, SVNLogType.WC); } dstDir = new File(SVNPathUtil.validateFilePath(dstDir.getAbsolutePath())).getAbsoluteFile(); String dstReposPath = SVNPathUtil.getPathAsChild(dstEntry.getRepositoryRootURL().toDecodedString(), dstEntry.getSVNURL().toDecodedString()); if (dstReposPath == null) { if (dstEntry.getURL().equals(dstEntry.getRepositoryRoot())) { dstReposPath = ""; } else { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_COPYFROM_PATH_NOT_FOUND, "Destination URLs are broken"); SVNErrorManager.error(err, SVNLogType.WC); } } dstReposPath = "/" + dstReposPath; dstReposPath = SVNPathUtil.canonicalizePath(dstReposPath); String copyFromParent = SVNPathUtil.removeTail(copyFromPath); String ancestorPath = SVNPathUtil.getCommonPathAncestor(dstReposPath, copyFromParent); if ("".equals(ancestorPath)) { return null; } int levelsUP = SVNPathUtil.getSegmentsCount(dstReposPath) - SVNPathUtil.getSegmentsCount(ancestorPath); File currentWD = dstDir; for (int i = 0; i < levelsUP && currentWD != null; i++) { currentWD = currentWD.getParentFile(); } if (currentWD == null) { return null; } SVNFileType kind = SVNFileType.getType(currentWD); if (kind != SVNFileType.DIRECTORY) { return null; } SVNWCAccess ancestorAccess = SVNWCAccess.newInstance(null); SVNAdminArea ancestorArea = null; try { ancestorArea = ancestorAccess.open(currentWD, false, 0); } catch (SVNException svne) { if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) { ancestorAccess.close(); return null; } throw svne; } SVNEntry ancestorEntry = ancestorArea.getEntry(ancestorArea.getThisDirName(), false); if (dstEntry.getUUID() != null && ancestorEntry.getUUID() != null && !dstEntry.getUUID().equals(ancestorEntry.getUUID())) { ancestorAccess.close(); return null; } SVNURL ancestorURL = dstEntry.getRepositoryRootURL().appendPath(ancestorPath, false); if (!ancestorURL.equals(ancestorEntry.getSVNURL())) { ancestorAccess.close(); return null; } String extraComponents = SVNPathUtil.getPathAsChild(ancestorPath, copyFromPath); currentWD = new File(currentWD, extraComponents); File currentWDParent = currentWD.getParentFile(); kind = SVNFileType.getType(currentWD); if (kind != SVNFileType.FILE) { ancestorAccess.close(); return null; } try { ancestorAccess.close(); ancestorArea = ancestorAccess.open(currentWDParent, false, 0); } catch (SVNException svne) { if (svne.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_DIRECTORY) { ancestorAccess.close(); return null; } throw svne; } SVNEntry fileEntry = ancestorArea.getEntry(currentWD.getName(), false); if (fileEntry == null) { ancestorAccess.close(); return null; } if (fileEntry.getUUID() != null && dstEntry.getUUID() != null && !fileEntry.getUUID().equals(dstEntry.getUUID())) { ancestorAccess.close(); return null; } SVNURL fileURL = fileEntry.getRepositoryRootURL().appendPath(copyFromPath, false); if (!fileURL.equals(fileEntry.getSVNURL())) { ancestorAccess.close(); return null; } if (!SVNRevision.isValidRevisionNumber(fileEntry.getCommittedRevision()) || !SVNRevision.isValidRevisionNumber(fileEntry.getRevision())) { ancestorAccess.close(); return null; } if (!(fileEntry.getCommittedRevision() <= copyFromRevision && copyFromRevision <= fileEntry.getRevision())) { ancestorAccess.close(); return null; } return fileEntry; } private void changeFileProperty(String name, SVNPropertyValue value, SVNFileInfo fileInfo) { if (!fileInfo.isSkipped) { fileInfo.propertyChanged(name, value); if (myWCAccess.getOptions().isUseCommitTimes() && SVNProperty.COMMITTED_DATE.equals(name)) { fileInfo.CommitTime = value.getString(); if (fileInfo.CommitTime != null) { fileInfo.CommitTime = fileInfo.CommitTime.trim(); } } } } private SVNFileInfo openFile(String path, SVNDirectoryInfo parent) throws SVNException { checkIfPathIsUnderRoot(path); SVNFileInfo info = createFileInfo(parent, path, false); SVNAdminArea adminArea = parent.getAdminArea(); SVNEntry entry = adminArea.getEntry(info.Name, true); if (entry == null) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNVERSIONED_RESOURCE, "File ''{0}'' in directory ''{1}'' is not a versioned resource", new Object[] {info.Name, adminArea.getRoot()}); SVNErrorManager.error(err, SVNLogType.WC); } boolean hasTextConflicts = adminArea.hasTextConflict(info.Name); boolean hasPropConflicts = adminArea.hasPropConflict(info.Name); if (hasTextConflicts || hasPropConflicts) { info.isSkipped = true; Collection skippedPaths = getSkippedPaths(); File file = new File(myAdminInfo.getAnchor().getRoot(), path); skippedPaths.add(file); SVNEvent event = SVNEventFactory.createSVNEvent(adminArea.getFile(info.Name), SVNNodeKind.FILE, null, myTargetRevision, hasTextConflicts ? SVNStatusType.CONFLICTED : SVNStatusType.UNKNOWN, hasPropConflicts ? SVNStatusType.CONFLICTED : SVNStatusType.UNKNOWN, SVNStatusType.LOCK_INAPPLICABLE, SVNEventAction.SKIP, SVNEventAction.UPDATE_UPDATE, null, null); event.setPreviousRevision(entry.getRevision()); event.setURL(entry.getSVNURL()); myWCAccess.handleEvent(event); } return info; } private void closeFile(String textChecksum, SVNFileInfo fileInfo, SVNDirectoryInfo dirInfo) throws SVNException { if (fileInfo.isSkipped) { maybeBumpDirInfo(dirInfo); return; } if (fileInfo.addedWithHistory && !fileInfo.receivedTextDelta) { SVNErrorManager.assertionFailure(fileInfo.baseFile == null && fileInfo.newBaseFile == null && fileInfo.copiedBaseText != null, null, SVNLogType.WC); SVNAdminArea adminArea = fileInfo.getAdminArea(); SVNEntry entry = adminArea.getEntry(fileInfo.Name, false); boolean replaced = entry != null && entry.isScheduledForReplacement(); boolean useRevertBase = replaced && entry.getCopyFromURL() != null; if (useRevertBase) { fileInfo.baseFile = adminArea.getFile(SVNAdminUtil.getTextRevertPath(fileInfo.Name, false)); fileInfo.newBaseFile = adminArea.getFile(SVNAdminUtil.getTextRevertPath(fileInfo.Name, true)); } else { fileInfo.baseFile = adminArea.getBaseFile(fileInfo.Name, false); fileInfo.newBaseFile = adminArea.getBaseFile(fileInfo.Name, true); } SVNFileUtil.copyFile(fileInfo.copiedBaseText, fileInfo.newBaseFile, true); fileInfo.Checksum = fileInfo.copiedBaseChecksum; } // check checksum. String checksum = null; boolean isTextUpdated = fileInfo.newBaseFile != null; if (textChecksum != null && isTextUpdated) { if (fileInfo.Checksum != null && !textChecksum.equals(fileInfo.Checksum)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.CHECKSUM_MISMATCH, "Checksum mismatch for ''{0}''; expected: ''{1}'', actual: ''{2}''", new Object[] {fileInfo.getPath(), textChecksum, fileInfo.Checksum}); SVNErrorManager.error(err, SVNLogType.WC); } checksum = textChecksum; } SVNAdminArea adminArea = fileInfo.getAdminArea(); SVNLog log = dirInfo.getLog(); String name = fileInfo.Name; SVNEntry fileEntry = adminArea.getEntry(name, false); if (fileEntry == null && !fileInfo.IsAdded) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.UNVERSIONED_RESOURCE, "''{0}'' is not under version control", fileInfo.getPath()); SVNErrorManager.error(err, SVNLogType.WC); } long previousRevision = fileEntry != null ? fileEntry.getRevision() : -1; SVNURL previousURL = fileEntry != null ? fileEntry.getSVNURL() : null; // merge props. SVNProperties modifiedWCProps = fileInfo.getChangedWCProperties(); SVNProperties modifiedEntryProps = fileInfo.getChangedEntryProperties(); SVNProperties modifiedProps = fileInfo.getChangedProperties(); String commitTime = fileInfo.CommitTime; SVNProperties command = new SVNProperties(); SVNStatusType textStatus = SVNStatusType.UNCHANGED; SVNStatusType lockStatus = SVNStatusType.LOCK_UNCHANGED; if (myAdminInfo.isIncomplete(fileInfo.getPath()) && fileEntry != null) { // delete all props. SVNVersionedProperties oldBaseProps = adminArea.getBaseProperties(fileEntry.getName()); SVNProperties baseMap = oldBaseProps.asMap(); if (modifiedProps == null) { modifiedProps = new SVNProperties(); } for(Iterator names = baseMap.nameSet().iterator(); names.hasNext();) { String propName = (String) names.next(); if (!modifiedProps.containsName(propName)) { modifiedProps.put(propName, SVNPropertyValue.create(null)); } } } boolean magicPropsChanged = false; if (modifiedProps != null && !modifiedProps.isEmpty()) { magicPropsChanged = modifiedProps.containsName(SVNProperty.EXECUTABLE) || modifiedProps.containsName(SVNProperty.NEEDS_LOCK) || modifiedProps.containsName(SVNProperty.KEYWORDS) || modifiedProps.containsName(SVNProperty.EOL_STYLE) || modifiedProps.containsName(SVNProperty.CHARSET) || modifiedProps.containsName(SVNProperty.SPECIAL); } SVNStatusType propStatus = adminArea.mergeProperties(name, null, fileInfo.copiedBaseProperties, fileInfo.copiedWorkingProperties, modifiedProps, null, null, true, false, log); if (modifiedEntryProps != null) { lockStatus = log.logChangedEntryProperties(name, modifiedEntryProps); } if (modifiedWCProps != null) { log.logChangedWCProperties(name, modifiedWCProps); } boolean isLocallyModified = false; if (fileInfo.copiedWorkingText != null) { isLocallyModified = true; } else if (fileEntry != null && fileEntry.getExternalFilePath() != null && fileEntry.isScheduledForAddition()) { isLocallyModified = false; } else if (!fileInfo.isExisted) { isLocallyModified = adminArea.hasTextModifications(name, false, false, false); } else if (isTextUpdated) { isLocallyModified = adminArea.hasVersionedFileTextChanges(adminArea.getFile(name), fileInfo.newBaseFile, false); } boolean isReplaced = fileEntry != null && fileEntry.isScheduledForReplacement(); SVNProperties logAttributes = new SVNProperties(); if (fileInfo.isAddExisted) { logAttributes.put(SVNLog.FORCE_ATTR, "true"); logAttributes.put(SVNProperty.shortPropertyName(SVNProperty.SCHEDULE), ""); } log.logTweakEntry(name, fileInfo.URL, myTargetRevision); String absDirPath = adminArea.getRoot().getAbsolutePath().replace(File.separatorChar, '/'); String basePath = null; if (fileInfo.baseFile != null) { String absBasePath = fileInfo.baseFile.getAbsolutePath().replace(File.separatorChar, '/'); basePath = absBasePath.substring(absDirPath.length()); if (basePath.startsWith("/")) { basePath = basePath.substring(1); } } String tmpBasePath = null; if (fileInfo.newBaseFile != null) { String absTmpBasePath = fileInfo.newBaseFile.getAbsolutePath().replace(File.separatorChar, '/'); tmpBasePath = absTmpBasePath.substring(absDirPath.length()); if (tmpBasePath.startsWith("/")) { tmpBasePath = tmpBasePath.substring(1); } } SVNStatusType mergeOutcome = SVNStatusType.UNCHANGED; File workingFile = adminArea.getFile(name); boolean deletedCopiedBaseText = false; if (tmpBasePath != null) { textStatus = SVNStatusType.CHANGED; // there is a text to replace the working copy with. if (!isLocallyModified && !isReplaced) { if (fileEntry == null || !fileEntry.isScheduledForDeletion()) { command.put(SVNLog.NAME_ATTR, tmpBasePath); command.put(SVNLog.DEST_ATTR, name); log.addCommand(SVNLog.COPY_AND_TRANSLATE, command, false); command.clear(); } } else { SVNFileType kind = SVNFileType.getType(workingFile); if (kind == SVNFileType.NONE && !fileInfo.addedWithHistory) { command.put(SVNLog.NAME_ATTR, tmpBasePath); command.put(SVNLog.DEST_ATTR, name); log.addCommand(SVNLog.COPY_AND_TRANSLATE, command, false); command.clear(); } else if (!fileInfo.isExisted) { File mergeLeftFile = fileInfo.baseFile; String pathExt = null; if (myExtensionPatterns != null && myExtensionPatterns.length > 0) { int dotInd = name.lastIndexOf('.'); if (dotInd != -1 && dotInd != 0 && dotInd != name.length() - 1) { pathExt = name.substring(dotInd + 1); } if (pathExt != null && !"".equals(pathExt)) { boolean matches = false; for (int i = 0; i < myExtensionPatterns.length; i++) { String extPattern = myExtensionPatterns[i]; matches = DefaultSVNOptions.matches(extPattern, pathExt); if (matches) { break; } } if (!matches) { pathExt = null; } } } boolean deleteLeftMergeFile = false; boolean deleteCopiedBaseText = false; if (fileInfo.isAddExisted && !isReplaced) { deleteLeftMergeFile = true; mergeLeftFile = SVNAdminUtil.createTmpFile(adminArea); } else if (fileInfo.copiedBaseText != null) { deleteLeftMergeFile = deleteCopiedBaseText = true; mergeLeftFile = fileInfo.copiedBaseText; } String absMergeLeftFilePath = mergeLeftFile.getAbsolutePath().replace(File.separatorChar, '/'); String mergeLeftFilePath = absMergeLeftFilePath.substring(absDirPath.length()); if (mergeLeftFilePath.startsWith("/")) { mergeLeftFilePath = mergeLeftFilePath.substring(1); } String leftLabel = null; if (fileInfo.addedWithHistory) { leftLabel = ".copied" + (pathExt != null ? "." + pathExt : ""); } else { leftLabel = ".r" + fileEntry.getRevision() + (pathExt != null ? "." + pathExt : ""); } String rightLabel = ".r" + myTargetRevision + (pathExt != null ? "." + pathExt : ""); String mineLabel = ".mine" + (pathExt != null ? "." + pathExt : ""); // do test merge. mergeOutcome = adminArea.mergeText(name, mergeLeftFile, adminArea.getFile(tmpBasePath), fileInfo.copiedWorkingText, mineLabel, leftLabel, rightLabel, modifiedProps, false, null, log); if (mergeOutcome == SVNStatusType.UNCHANGED) { textStatus = SVNStatusType.MERGED; } if (deleteLeftMergeFile) { command.put(SVNLog.NAME_ATTR, mergeLeftFilePath); log.addCommand(SVNLog.DELETE, command, false); command.clear(); if (deleteCopiedBaseText) { deletedCopiedBaseText = true; } } if (fileInfo.copiedWorkingText != null) { String absCopiedWorkingTextPath = fileInfo.copiedWorkingText.getAbsolutePath().replace(File.separatorChar, '/'); String copiedWorkingTextPath = absCopiedWorkingTextPath.substring(absDirPath.length()); if (copiedWorkingTextPath.startsWith("/")) { copiedWorkingTextPath = copiedWorkingTextPath.substring(1); } command.put(SVNLog.NAME_ATTR, copiedWorkingTextPath); log.addCommand(SVNLog.DELETE, command, false); command.clear(); } } } } else { if (magicPropsChanged && (workingFile.exists() || SVNFileType.getType(workingFile) == SVNFileType.SYMLINK)) { // only props were changed, but we have to retranslate file. // only if wc file exists (may be locally deleted), otherwise no // need to retranslate... String tmpPath = SVNAdminUtil.getTextBasePath(name, true); command.put(SVNLog.NAME_ATTR, name); command.put(SVNLog.DEST_ATTR, tmpPath); log.addCommand(SVNLog.COPY_AND_DETRANSLATE, command, false); command.clear(); command.put(SVNLog.NAME_ATTR, tmpPath); command.put(SVNLog.DEST_ATTR, name); log.addCommand(SVNLog.COPY_AND_TRANSLATE, command, false); command.clear(); } if (lockStatus == SVNStatusType.LOCK_UNLOCKED) { command.put(SVNLog.NAME_ATTR, name); log.addCommand(SVNLog.MAYBE_READONLY, command, false); command.clear(); } } if (tmpBasePath != null) { command.put(SVNLog.NAME_ATTR, tmpBasePath); command.put(SVNLog.DEST_ATTR, basePath); log.addCommand(SVNLog.MOVE, command, false); command.clear(); command.put(SVNLog.NAME_ATTR, basePath); log.addCommand(SVNLog.READONLY, command, false); command.clear(); if (!isReplaced) { logAttributes.put(SVNProperty.shortPropertyName(SVNProperty.CHECKSUM), checksum); } } if (logAttributes.size() > 0) { logAttributes.put(SVNLog.NAME_ATTR, name); log.addCommand(SVNLog.MODIFY_ENTRY, logAttributes, false); } if (!isLocallyModified && (fileInfo.IsAdded || fileEntry.getSchedule() == null)) { if (commitTime != null && !fileInfo.isExisted) { command.put(SVNLog.NAME_ATTR, name); command.put(SVNLog.TIMESTAMP_ATTR, commitTime); log.addCommand(SVNLog.SET_TIMESTAMP, command, false); command.clear(); } if (tmpBasePath != null || magicPropsChanged) { command.put(SVNLog.NAME_ATTR, name); command.put(SVNProperty.shortPropertyName(SVNProperty.TEXT_TIME), SVNLog.WC_TIMESTAMP); log.addCommand(SVNLog.MODIFY_ENTRY, command, false); command.clear(); } command.put(SVNLog.NAME_ATTR, name); command.put(SVNProperty.shortPropertyName(SVNProperty.WORKING_SIZE), SVNLog.WC_WORKING_SIZE); log.addCommand(SVNLog.MODIFY_ENTRY, command, false); command.clear(); } if (fileInfo.copiedBaseText != null && !deletedCopiedBaseText) { String absCopiedBaseTextPath = fileInfo.copiedBaseText.getAbsolutePath().replace(File.separatorChar, '/'); String copiedBaseTextPath = absCopiedBaseTextPath.substring(absDirPath.length()); if (copiedBaseTextPath.startsWith("/")) { copiedBaseTextPath = copiedBaseTextPath.substring(1); } command.put(SVNLog.NAME_ATTR, copiedBaseTextPath); log.addCommand(SVNLog.DELETE, command, false); command.clear(); } // bump. maybeBumpDirInfo(dirInfo); if (mergeOutcome == SVNStatusType.CONFLICTED_UNRESOLVED) { textStatus = SVNStatusType.CONFLICTED_UNRESOLVED; } else if (mergeOutcome == SVNStatusType.CONFLICTED) { textStatus = SVNStatusType.CONFLICTED; } else if (fileInfo.newBaseFile != null) { if (isLocallyModified) { textStatus = SVNStatusType.MERGED; } else { textStatus = SVNStatusType.CHANGED; } } // notify. if ((textStatus != SVNStatusType.UNCHANGED || propStatus != SVNStatusType.UNCHANGED || lockStatus != SVNStatusType.LOCK_UNCHANGED)) { SVNEventAction action = SVNEventAction.UPDATE_UPDATE; if (fileInfo.isExisted || fileInfo.isAddExisted) { if (textStatus != SVNStatusType.CONFLICTED_UNRESOLVED && textStatus != SVNStatusType.CONFLICTED) { action = SVNEventAction.UPDATE_EXISTS; } } else if (fileInfo.IsAdded) { action = SVNEventAction.UPDATE_ADD; } SVNEvent event = SVNEventFactory.createSVNEvent(adminArea.getFile(fileInfo.Name), SVNNodeKind.FILE, null, myTargetRevision, textStatus, propStatus, lockStatus, action, null, null, null); event.setPreviousRevision(previousRevision); event.setPreviousURL(previousURL); event.setURL(fileInfo.URL != null ? SVNURL.parseURIEncoded(fileInfo.URL) : null); myWCAccess.handleEvent(event); } } private SVNFileInfo createFileInfo(SVNDirectoryInfo parent, String path, boolean added) throws SVNException { SVNFileInfo info = new SVNFileInfo(parent, path); info.IsAdded = added; info.Name = SVNPathUtil.tail(path); info.isExisted = false; info.isAddExisted = false; info.isSkipped = false; info.baseFile = null; info.newBaseFile = null; SVNAdminArea adminArea = parent.getAdminArea(); SVNEntry entry = adminArea.getEntry(info.Name, true); if (mySwitchURL != null || entry == null) { info.URL = SVNPathUtil.append(parent.URL, SVNEncodingUtil.uriEncode(info.Name)); } else { info.URL = entry.getURL(); } parent.RefCount++; return info; } private SVNDirectoryInfo createDirectoryInfo(SVNDirectoryInfo parent, String path, boolean added) { SVNDirectoryInfo info = new SVNDirectoryInfo(path); info.Parent = parent; info.IsAdded = added; String name = path != null ? SVNPathUtil.tail(path) : ""; if (mySwitchURL == null) { SVNAdminArea area = null; SVNEntry dirEntry = null; File areaPath = new File(myAdminInfo.getAnchor().getRoot(), info.getPath()); try { area = myWCAccess.getAdminArea(areaPath); if (area != null) { // could be missing. dirEntry = area.getEntry(area.getThisDirName(), false); } } catch (SVNException svne) { // } if (area != null && dirEntry != null) { info.URL = dirEntry.getURL(); } if (info.URL == null && parent != null) { info.URL = SVNPathUtil.append(parent.URL, SVNEncodingUtil.uriEncode(name)); } else if (info.URL == null && parent == null) { info.URL = myTargetURL; } } else { if (parent == null) { info.URL = myTarget == null ? mySwitchURL : SVNPathUtil.removeTail(mySwitchURL); } else { if (myTarget != null && parent.Parent == null) { info.URL = mySwitchURL; } else { info.URL = SVNPathUtil.append(parent.URL, SVNEncodingUtil.uriEncode(name)); } } } info.RefCount = 1; info.isSkipped = false; if (info.Parent != null) { info.Parent.RefCount++; } info.isExisted = false; info.isAddExisted = false; info.log = null; info.myAmbientDepth = SVNDepth.UNKNOWN; return info; } public static SVNUpdateEditor15 createUpdateEditor(SVNAdminAreaInfo info, String switchURL, boolean allowUnversionedObstructions, boolean depthIsSticky, SVNDepth depth, String[] preservedExtensions, ISVNFileFetcher fileFetcher, boolean lockOnDemand) throws SVNException { if (depth == SVNDepth.UNKNOWN) { depthIsSticky = false; } SVNEntry entry = info.getAnchor().getEntry(info.getAnchor().getThisDirName(), false); if (switchURL != null && entry != null && entry.getRepositoryRoot() != null) { if (!SVNPathUtil.isAncestor(entry.getRepositoryRoot(), switchURL)) { SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_INVALID_SWITCH, "''{0}''\nis not the same repository as\n''{1}''", new Object[] { switchURL, entry.getRepositoryRoot() }); SVNErrorManager.error(err, SVNLogType.WC); } } SVNUpdateEditor15 editor = new SVNUpdateEditor15(info, switchURL, allowUnversionedObstructions, depthIsSticky, depth, preservedExtensions, entry != null ? entry.getURL() : null, entry != null ? entry.getRepositoryRoot() : null, fileFetcher, lockOnDemand); info.getTarget().closeEntries(); return editor; } private class SVNEntryInfo { public String URL; public boolean IsAdded; public boolean isExisted; public boolean isAddExisted; public SVNDirectoryInfo Parent; public boolean isSkipped; public long myPreviousRevision; private String myPath; private SVNProperties myChangedProperties; private SVNProperties myChangedEntryProperties; private SVNProperties myChangedWCProperties; protected SVNEntryInfo(String path) { myPath = path; } protected String getPath() { return myPath; } public void propertyChanged(String name, SVNPropertyValue value) { if (name.startsWith(SVNProperty.SVN_ENTRY_PREFIX)) { myChangedEntryProperties = myChangedEntryProperties == null ? new SVNProperties() : myChangedEntryProperties; // trim value of svn:entry property if (value != null) { String strValue = value.getString(); if (strValue != null) { strValue = strValue.trim(); value = SVNPropertyValue.create(strValue); } } myChangedEntryProperties.put(name.substring(SVNProperty.SVN_ENTRY_PREFIX.length()), value); } else if (name.startsWith(SVNProperty.SVN_WC_PREFIX)) { myChangedWCProperties = myChangedWCProperties == null ? new SVNProperties() : myChangedWCProperties; myChangedWCProperties.put(name, value); } else { myChangedProperties = myChangedProperties == null ? new SVNProperties() : myChangedProperties; myChangedProperties.put(name, value); } } public SVNProperties getChangedWCProperties() { return myChangedWCProperties; } public SVNProperties getChangedEntryProperties() { return myChangedEntryProperties; } public SVNProperties getChangedProperties() { return myChangedProperties; } } private class SVNFileInfo extends SVNEntryInfo { public String Name; public String CommitTime; public String Checksum; public String expectedSrcChecksum; public String copiedBaseChecksum; public File baseFile; public File newBaseFile; public boolean addedWithHistory; public boolean receivedTextDelta; private SVNProperties copiedBaseProperties; private SVNProperties copiedWorkingProperties; private File copiedBaseText; private File copiedWorkingText; private SVNChecksumInputStream sourceChecksumStream; public SVNFileInfo(SVNDirectoryInfo parent, String path) { super(path); this.Parent = parent; } public SVNAdminArea getAdminArea() throws SVNException { return Parent.getAdminArea(); } } private class SVNDirectoryInfo extends SVNEntryInfo implements ISVNCleanupHandler { public int RefCount; private SVNLog log; public int LogCount; public SVNDepth myAmbientDepth; public SVNDirectoryInfo(String path) { super(path); } public SVNAdminArea getAdminArea() throws SVNException { String path = getPath(); File file = new File(myAdminInfo.getAnchor().getRoot(), path); SVNAdminArea area = myAdminInfo.getWCAccess().retrieve(file); if (myIsLockOnDemand && area != null && !area.isLocked()) { area.lock(false); area = myAdminInfo.getWCAccess().upgrade(file); } return area; } public SVNLog getLog() throws SVNException { if (log == null) { log = getAdminArea().getLog(); LogCount++; } return log; } public void flushLog() throws SVNException { if (log != null) { log.save(); log = null; } } public void runLogs() throws SVNException { LogCount = 0; getAdminArea().runLogs(); } public void cleanup(SVNAdminArea area) throws SVNException { if (area != null && LogCount > 0) { LogCount = 0; area.runLogs(); } } } public void cleanup(SVNAdminArea area) throws SVNException { area.runLogs(); } }