/*
* ====================================================================
* 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.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
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.ISVNEntryHandler;
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.diff.SVNDeltaProcessor;
import org.tmatesoft.svn.core.io.diff.SVNDiffWindow;
import org.tmatesoft.svn.core.wc.ISVNEventHandler;
import org.tmatesoft.svn.core.wc.SVNConflictAction;
import org.tmatesoft.svn.core.wc.SVNConflictReason;
import org.tmatesoft.svn.core.wc.SVNEvent;
import org.tmatesoft.svn.core.wc.SVNEventAction;
import org.tmatesoft.svn.core.wc.SVNOperation;
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNStatusType;
import org.tmatesoft.svn.core.wc.SVNTreeConflictDescription;
import org.tmatesoft.svn.util.SVNLogType;
/**
* @version 1.3
* @author TMate Software Ltd.
*/
public class SVNUpdateEditor 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 mySkippedTrees;
private Collection myDeletedTrees;
private SVNWCAccess myWCAccess;
private SVNDeltaProcessor myDeltaProcessor;
private SVNDepth myRequestedDepth;
private String[] myExtensionPatterns;
private ISVNFileFetcher myFileFetcher;
private boolean myIsLockOnDemand;
private SVNUpdateEditor(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;
}
}
private Collection getSkippedTrees() {
if (mySkippedTrees == null) {
mySkippedTrees = new LinkedList();
}
return mySkippedTrees;
}
private Collection getDeletedTrees() {
if (myDeletedTrees == null) {
myDeletedTrees = new LinkedList();
}
return myDeletedTrees;
}
private void addSkippedTree(File path) {
getSkippedTrees().add(path);
}
private void addDeletedTree(File path) {
getDeletedTrees().add(path);
}
private boolean inSkippedTree(File path) {
while (path != null && !path.equals(myAdminInfo.getAnchor().getRoot())) {
if (getSkippedTrees().contains(path)) {
return true;
}
path = path.getParentFile();
}
return false;
}
private boolean inDeletedTree(File path, boolean includeRoot) {
if (!includeRoot) {
path = path.getParentFile();
}
while (path != null && !path.equals(myAdminInfo.getAnchor().getRoot())) {
if (getDeletedTrees().contains(path)) {
return true;
}
path = path.getParentFile();
}
return false;
}
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.wasIncomplete = entry.isIncomplete();
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);
}
}
}
private void doDeleteEntry(String path, SVNAdminArea parentArea, SVNDirectoryInfo parent, SVNURL theirURL) throws SVNException {
File fullPath = myAdminInfo.getAnchor().getFile(path);
String name = SVNPathUtil.tail(path);
SVNEntry entry = myWCAccess.getVersionedEntry(fullPath, true);
parent.anyChangesInChildren = true;
if (entry.getDepth() == SVNDepth.EXCLUDE) {
parentArea.deleteEntry(name);
parentArea.saveEntries(true);
if (path.equals(myTarget)) {
myIsTargetDeleted = true;
}
return;
}
SVNNodeKind kind = entry.getKind();
long previousRevision = entry.getRevision();
SVNURL url = entry.getSVNURL();
if (inSkippedTree(fullPath) && !inDeletedTree(fullPath, true)) {
return;
}
File victim = alreadyInTreeConflict(fullPath);
if (victim != null) {
addSkippedTree(fullPath);
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, entry.getKind(), null, myTargetRevision, SVNEventAction.SKIP, SVNEventAction.UPDATE_DELETE, null, null);
event.setPreviousRevision(previousRevision);
event.setPreviousURL(url);
myWCAccess.handleEvent(event);
return;
}
SVNLog log = parent == null ? parentArea.getLog() : parent.getLog();
SVNTreeConflictDescription treeConflict;
if (kind == SVNNodeKind.DIR && myWCAccess.isMissing(fullPath) && (entry.isScheduledForDeletion() || entry.isScheduledForReplacement())) {
treeConflict = null;
} else {
treeConflict = checkTreeConflict(fullPath, entry, parentArea, log, SVNConflictAction.DELETE, SVNNodeKind.NONE, theirURL);
}
if (treeConflict != null) {
addSkippedTree(fullPath);
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, entry.getKind(), null, myTargetRevision, SVNEventAction.TREE_CONFLICT, SVNEventAction.UPDATE_DELETE, null, null);
event.setPreviousRevision(entry.getRevision());
event.setPreviousURL(entry.getSVNURL());
myWCAccess.handleEvent(event);
if (treeConflict.getConflictReason() == SVNConflictReason.EDITED) {
if (parent != null) {
parent.flushLog();
parent.runLogs();
} else {
if (log != null) {
log.save();
parentArea.runLogs();
}
}
scheduleExistingEntryForReAdd(entry, fullPath, theirURL, true);
return;
} else if (treeConflict.getConflictReason() == SVNConflictReason.DELETED) {
// The item does not exist locally (except perhaps as a skeleton
// directory tree) because it was already scheduled for delete.
// We must complete the deletion, leaving the tree conflict info
// as the only difference from a normal deletion.
//
// Fall through to the normal "delete" code path.
if (entry.isScheduledForReplacement()) {
if (parent != null) {
parent.flushLog();
parent.runLogs();
} else {
if (log != null) {
log.save();
parentArea.runLogs();
}
}
scheduleExistingEntryForReAdd(entry, fullPath, theirURL, false);
return;
}
} else {
SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.UNSUPPORTED_FEATURE, "Unexpected tree conflict reason");
SVNErrorManager.error(error, SVNLogType.WC);
}
}
log = parent == null ? parentArea.getLog() : parent.getLog();
SVNProperties attributes = new SVNProperties();
attributes.put(SVNLog.NAME_ATTR, name);
log.addCommand(SVNLog.DELETE_ENTRY, attributes, false);
attributes.clear();
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 {
if (parent != null) {
parent.flushLog();
} else {
if (log != null) {
log.save();
}
}
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage().wrap("Error writing log file for ''{0}''", parent.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));
if (childArea != null) {
try {
childArea.removeFromRevisionControl(childArea.getThisDirName(), true, false);
} catch (SVNException svne) {
handleLeftLocalModificationsError(svne);
}
}
}
try {
if (parent != null) {
parent.runLogs();
} else {
parentArea.runLogs();
}
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage().wrap("Error running log file for ''{0}''", parentArea.getRoot());
SVNErrorManager.error(err, svne, SVNLogType.WC);
}
if (treeConflict == null && !inDeletedTree(fullPath, true)) {
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, kind, null, myTargetRevision, SVNEventAction.UPDATE_DELETE, null, null, null);
event.setPreviousRevision(previousRevision);
event.setPreviousURL(url);
myWCAccess.handleEvent(event);
}
}
private File alreadyInTreeConflict(File path) throws SVNException {
File ancestor = path;
List ancestors = new ArrayList();
SVNWCAccess access = SVNWCAccess.newInstance(myWCAccess);
try {
access.probeOpen(ancestor, false, 0);
SVNEntry entry = access.getEntry(path, true);
if (entry != null) {
ancestors.add(ancestor);
}
} finally {
access.close();
}
ancestor = ancestor.getParentFile();
access = SVNWCAccess.newInstance(myWCAccess);
try {
while (ancestor != null) {
SVNAdminArea adminArea = access.probeOpen(ancestor, false, 0);
if (adminArea == null) {
break;
}
boolean isWCRoot = access.isWCRoot(ancestor);
if (isWCRoot) {
break;
}
ancestors.add(ancestor);
ancestor = ancestor.getParentFile();
}
} finally {
access.close();
}
for (int i = ancestors.size() - 1; i >= 0; i--) {
ancestor = (File) ancestors.get(i);
SVNTreeConflictDescription treeConflict = access.getTreeConflict(ancestor);
if (treeConflict != null) {
return ancestor;
}
}
return null;
}
private SVNTreeConflictDescription checkTreeConflict(File path, SVNEntry entry, SVNAdminArea parentArea, SVNLog log, SVNConflictAction action, SVNNodeKind theirKind, SVNURL theirURL)
throws SVNException {
boolean allModsAreDeletes = false;
boolean isSubtreeOfLocallyDeleted = inDeletedTree(path, false);
SVNConflictReason reason = null;
if (action == SVNConflictAction.EDIT) {
if ((entry.isScheduledForDeletion() || entry.isScheduledForReplacement()) && !isSubtreeOfLocallyDeleted) {
reason = SVNConflictReason.DELETED;
}
} else if (action == SVNConflictAction.ADD) {
if (entry != null && entry.getExternalFilePath() == null) {
reason = SVNConflictReason.ADDED;
}
} else if (action == SVNConflictAction.DELETE) {
if (entry.isScheduledForDeletion() || entry.isScheduledForReplacement()) {
if (!isSubtreeOfLocallyDeleted) {
reason = SVNConflictReason.DELETED;
}
} else {
boolean modified = false;
if (entry.isFile()) {
modified = entryHasLocalModifications(parentArea, path, SVNNodeKind.FILE, entry.getSchedule());
if (entry.isScheduledForDeletion()) {
allModsAreDeletes = true;
}
} else if (entry.isDirectory()) {
SVNAdminArea adminArea = myWCAccess.probeRetrieve(path);
if (adminArea.getRoot().equals(path)) {
boolean[] allEditsAreDeletes = new boolean[1];
modified = treeHasLocalModifications(adminArea, allEditsAreDeletes);
allModsAreDeletes = allEditsAreDeletes[0];
}
}
if (modified) {
if (allModsAreDeletes) {
reason = SVNConflictReason.DELETED;
} else {
reason = SVNConflictReason.EDITED;
}
}
}
}
if (reason != null) {
SVNNodeKind leftKind = entry.isScheduledForAddition() ? SVNNodeKind.NONE : entry.isScheduledForDeletion() ? SVNNodeKind.UNKNOWN : entry.getKind();
SVNURL repoRoot = entry.getRepositoryRootURL();
String repoPath = SVNPathUtil.getPathAsChild(repoRoot.getPath(), entry.getSVNURL().getPath());
repoPath = repoPath != null ? repoPath : "/";
SVNConflictVersion srcLeftVersion = new SVNConflictVersion(repoRoot, repoPath, entry.getRevision(), leftKind);
if (mySwitchURL != null) {
if (theirURL != null) {
repoPath = SVNPathUtil.getPathAsChild(repoRoot.getPath(), theirURL.getPath());
} else {
SVNURL switchURL = SVNURL.parseURIEncoded(mySwitchURL);
repoPath = SVNPathUtil.getPathAsChild(repoRoot.getPath(), switchURL.getPath());
}
}
SVNConflictVersion srcRightVersion = new SVNConflictVersion(repoRoot, repoPath, myTargetRevision, theirKind);
SVNTreeConflictDescription treeConflict = new SVNTreeConflictDescription(path, entry.getKind(), action, reason, mySwitchURL != null ? SVNOperation.SWITCH : SVNOperation.UPDATE,
srcLeftVersion, srcRightVersion);
SVNProperties command = getTreeConflictCreationAttributes(parentArea, treeConflict);
log.addCommand(SVNLog.ADD_TREE_CONFLICT, command, false);
return treeConflict;
}
return null;
}
public boolean treeHasLocalModifications(SVNAdminArea adminArea, final boolean[] allModsAreDeletes) throws SVNException {
final boolean[] modified = new boolean[] {
false
};
if (allModsAreDeletes != null) {
allModsAreDeletes[0] = true;
}
final ISVNEntryHandler entryHandler = new ISVNEntryHandler() {
public void handleEntry(File path, SVNEntry entry) throws SVNException {
boolean hasModifications;
SVNAdminArea entryArea;
try {
entryArea = myWCAccess.probeRetrieve(path);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_NOT_LOCKED) {
ISVNEventHandler eventHandler = myWCAccess.getEventHandler();
myWCAccess.setEventHandler(null);
try {
entryArea = myWCAccess.open(path, false, -1);
} finally {
myWCAccess.setEventHandler(eventHandler);
}
} else {
throw e;
}
}
hasModifications = entryHasLocalModifications(entryArea, path, entry.getKind(), entry.getSchedule());
if (hasModifications) {
modified[0] = true;
if (!entry.isScheduledForDeletion()) {
allModsAreDeletes[0] = false;
}
}
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
SVNErrorManager.error(error, SVNLogType.WC);
}
};
adminArea.walkThisDirectory(entryHandler, false, SVNDepth.INFINITY);
return modified[0];
}
private boolean entryHasLocalModifications(SVNAdminArea adminArea, File path, SVNNodeKind kind, String schedule) throws SVNException {
boolean modified;
if (schedule != null) {
modified = true;
} else {
boolean textModified = false;
if (kind == SVNNodeKind.FILE) {
textModified = adminArea.hasTextModifications(path.getName(), false);
}
boolean propsModified;
if (kind == SVNNodeKind.FILE) {
propsModified = adminArea.hasPropModifications(path.getName());
} else if (path.equals(adminArea.getRoot())) {
propsModified = adminArea.hasPropModifications(adminArea.getThisDirName());
} else {
SVNAdminArea tmpArea = myWCAccess.probeRetrieve(path);
if (tmpArea.getRoot().equals(path)) {
propsModified = tmpArea.hasPropModifications(tmpArea.getThisDirName());
} else {
propsModified = tmpArea.hasPropConflict(path.getName());
}
}
modified = textModified || propsModified;
}
return modified;
}
private void scheduleExistingEntryForReAdd(final SVNEntry entry, final File path, SVNURL theirURL, boolean modifyCopyFrom) throws SVNException {
final File parentPath = path.getParentFile();
String entryName = path.getName();
Map attributes = new SVNHashMap();
attributes.put(SVNProperty.URL, theirURL.toString());
attributes.put(SVNProperty.SCHEDULE, SVNProperty.SCHEDULE_ADD);
if (modifyCopyFrom) {
attributes.put(SVNProperty.COPYFROM_URL, entry.getURL());
attributes.put(SVNProperty.COPYFROM_REVISION, String.valueOf(entry.getRevision()));
attributes.put(SVNProperty.COPIED, Boolean.TRUE.toString());
}
if (myIsLockOnDemand && entry.isDirectory()) {
SVNAdminArea area = myWCAccess.getAdminArea(path);
if (area != null && !area.isLocked()) {
area.lock(false);
}
}
final SVNAdminArea adminArea = myWCAccess.retrieve(entry.isDirectory() ? path : parentPath);
adminArea.modifyEntry(entry.isDirectory() ? adminArea.getThisDirName() : entryName, attributes, true, true);
if (entry.isDirectory()) {
ISVNEntryHandler entryHandler = new ISVNEntryHandler() {
public void handleEntry(File ePath, SVNEntry e) throws SVNException {
if (!path.equals(ePath)) {
SVNAdminArea eArea;
if (myIsLockOnDemand && e.isDirectory() && !adminArea.getThisDirName().equals(e.getName())) {
SVNAdminArea childArea = myWCAccess.getAdminArea(ePath);
if (childArea != null && !childArea.isLocked()) {
childArea.lock(false);
}
}
if (adminArea.getThisDirName().equals(e.getName())) {
eArea = myWCAccess.retrieve(ePath);
} else {
eArea = myWCAccess.retrieve(ePath.getParentFile());
}
if (e.getSchedule() == null) {
Map eAttrs = new SVNHashMap();
eAttrs.put(SVNProperty.COPIED, Boolean.TRUE.toString());
eArea.modifyEntry(e.getName(), eAttrs, true, false);
}
}
}
public void handleError(File path, SVNErrorMessage error) throws SVNException {
SVNErrorManager.error(error, SVNLogType.WC);
}
};
adminArea.walkThisDirectory(entryHandler, false, SVNDepth.INFINITY);
SVNAdminArea parentArea = myWCAccess.retrieve(parentPath);
parentArea.getVersionedEntry(parentArea.getThisDirName(), true);
parentArea.modifyEntry(entryName, attributes, true, true);
}
}
public void deleteEntry(String path, long revision) throws SVNException {
checkIfPathIsUnderRoot(path);
String name = SVNPathUtil.tail(path);
SVNURL url = SVNURL.parseURIEncoded(myCurrentDirectory.URL);
SVNURL theirURL = url.appendPath(name, false);
checkIfPathIsUnderRoot(path);
doDeleteEntry(path, myCurrentDirectory.getAdminArea(), myCurrentDirectory, theirURL);
}
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 {
File fullPath = myAdminInfo.getAnchor().getFile(path);
String name = SVNPathUtil.tail(path);
boolean isLocallyDeleted = inDeletedTree(fullPath, true);
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);
if (inSkippedTree(fullPath) && !isLocallyDeleted) {
return;
}
File victim = alreadyInTreeConflict(fullPath);
if (victim != null) {
addSkippedTree(fullPath);
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, SVNNodeKind.DIR, null, myTargetRevision, SVNEventAction.SKIP, SVNEventAction.UPDATE_ADD, null, null);
myWCAccess.handleEvent(event);
}
SVNFileType kind = SVNFileType.getType(fullPath);
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);
} else if (kind == SVNFileType.NONE) {
SVNEntry entryInParent = parentArea.getEntry(name, false);
if (entryInParent != null && entryInParent.isDirectory() && entryInParent.isScheduledForDeletion()) {
addDeletedTree(fullPath);
isLocallyDeleted = true;
}
}
if (kind == SVNFileType.DIRECTORY) {
SVNAdminArea adminArea = null;
try {
adminArea = SVNWCAccess.newInstance(null).open(fullPath, 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) {
try {
SVNEntry entry = adminArea.getEntry(adminArea.getThisDirName(), false);
SVNEntry parentEntry = parentArea.getEntry(parentArea.getThisDirName(), false);
SVNEntry entryInParent = (SVNEntry) parentArea.getEntries().get(name);
if (entry != null && parentEntry != null && entry.getUUID() != null && parentEntry.getUUID() != null && !entry.getUUID().equals(parentEntry.getUUID())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "UUID mismatch: existing directory ''{0}'' was checked out from a different repository",
myCurrentDirectory.getPath());
SVNErrorManager.error(err, SVNLogType.WC);
}
if (entry != null && mySwitchURL == null && myCurrentDirectory.URL != null && !myCurrentDirectory.URL.equals(entry.getURL())) {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "URL ''{0}'' of existing directory ''{1}'' does not match expected URL ''{2}''", new Object[] {
entry.getURL(), myCurrentDirectory.getPath(), myCurrentDirectory.URL
});
SVNErrorManager.error(err, SVNLogType.WC);
}
if (entryInParent == null) {
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 (entry != null && (entry.isScheduledForAddition() || entry.isScheduledForReplacement()) && !entry.isCopied()) {
myCurrentDirectory.isAddExisted = true;
} else {
SVNURL theirURL = SVNURL.parseURIEncoded(myCurrentDirectory.URL);
SVNTreeConflictDescription treeConflict = checkTreeConflict(fullPath, entry, parentArea, parentDirectory.getLog(), SVNConflictAction.ADD, SVNNodeKind.DIR, theirURL);
try {
parentDirectory.flushLog();
} catch (SVNException svne) {
SVNErrorMessage err = svne.getErrorMessage().wrap("Error writing log file for ''{0}''", parentDirectory.getPath());
SVNErrorManager.error(err, svne, SVNLogType.WC);
}
if (treeConflict != null) {
addSkippedTree(fullPath);
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, SVNNodeKind.DIR, null, myTargetRevision, SVNEventAction.TREE_CONFLICT, SVNEventAction.UPDATE_ADD, null, null);
if (entry != null) {
event.setPreviousRevision(entry.getRevision());
event.setPreviousURL(entry.getSVNURL());
}
myWCAccess.handleEvent(event);
return;
}
}
} finally {
adminArea.getWCAccess().close();
}
}
}
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 (myRootURL != null && SVNPathUtil.isAncestor(myRootURL, myCurrentDirectory.URL)) {
rootURL = myRootURL;
}
SVNWCManager.ensureAdminAreaExists(fullPath, myCurrentDirectory.URL, rootURL, null, myTargetRevision, myCurrentDirectory.myAmbientDepth);
SVNAdminArea childArea = null;
if (!myAdminInfo.getAnchor().getRoot().equals(fullPath)) {
ISVNEventHandler eventHandler = myWCAccess.getEventHandler();
try {
myWCAccess.setEventHandler(null);
childArea = myWCAccess.open(fullPath, true, 0);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LOCKED) {
childArea = myWCAccess.retrieve(fullPath);
} else {
throw e;
}
} finally {
myWCAccess.setEventHandler(eventHandler);
}
}
if (isLocallyDeleted) {
attributes.clear();
attributes.put(SVNProperty.SCHEDULE, SVNProperty.SCHEDULE_DELETE);
parentArea.modifyEntry(name, attributes, true, false);
childArea = myWCAccess.retrieve(fullPath);
childArea.modifyEntry(childArea.getThisDirName(), attributes, true, false);
}
try {
childArea = myWCAccess.open(fullPath, true, 0);
} catch (SVNException e) {
if (e.getErrorMessage().getErrorCode() == SVNErrorCode.WC_LOCKED) {
childArea = myWCAccess.retrieve(fullPath);
} else {
throw e;
}
}
if (childArea != null) {
myWCAccess.registerCleanupHandler(childArea, myCurrentDirectory);
}
if (!myCurrentDirectory.isAddExisted && !isLocallyDeleted) {
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);
SVNDirectoryInfo parentInfo = myCurrentDirectory;
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();
myCurrentDirectory.wasIncomplete = entry.isIncomplete();
} else {
myCurrentDirectory.myPreviousRevision = -1;
}
File fullPath = myAdminInfo.getAnchor().getFile(path);
if (inSkippedTree(fullPath) && !inDeletedTree(fullPath, true)) {
myCurrentDirectory.isSkipped = true;
return;
}
SVNTreeConflictDescription treeConflict;
File victim = alreadyInTreeConflict(fullPath);
if (victim != null) {
treeConflict = null;
} else {
//originally there were no tree conflict
SVNURL theirURL = SVNURL.parseURIEncoded(myCurrentDirectory.URL);
treeConflict = checkTreeConflict(fullPath, entry, parentInfo.getAdminArea(), parentInfo.getLog(), SVNConflictAction.EDIT, SVNNodeKind.DIR, theirURL);
if (treeConflict != null && treeConflict.getConflictAction() == SVNConflictAction.EDIT) {
//now tree conflict is created
myCurrentDirectory.treeConflictCreationAttributes = getTreeConflictCreationAttributes(parentInfo.getAdminArea(), treeConflict);
}
}
if (treeConflict != null && treeConflict.getConflictReason() == SVNConflictReason.DELETED && !inDeletedTree(fullPath, true)) {
addDeletedTree(fullPath);
}
boolean hasPropConflicts = adminArea.hasPropConflict(adminArea.getThisDirName());
if (victim != null || treeConflict != null || hasPropConflicts) {
if (!inDeletedTree(fullPath, true)) {
myCurrentDirectory.isSkipped = true;
}
addSkippedTree(fullPath);
if (!inDeletedTree(fullPath, false)) {
SVNEventAction eventAction = hasPropConflicts ? SVNEventAction.SKIP : SVNEventAction.TREE_CONFLICT;
SVNStatusType propStatus = hasPropConflicts ? SVNStatusType.CONFLICTED : null;
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, SVNNodeKind.DIR, null, myTargetRevision, null, propStatus, null, eventAction, SVNEventAction.UPDATE_UPDATE, null, null);
event.setPreviousRevision(myCurrentDirectory.myPreviousRevision);
if (myCurrentDirectory.URL != null) {
event.setURL(SVNURL.parseURIEncoded(myCurrentDirectory.URL));
}
if (entry != null) {
event.setPreviousURL(entry.getSVNURL());
}
myWCAccess.handleEvent(event);
}
if (hasPropConflicts || (treeConflict != null && treeConflict.getConflictReason() != SVNConflictReason.DELETED)) {
return;
}
}
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);
}
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 {
File fullPath = myAdminInfo.getAnchor().getFile(myCurrentDirectory.getPath());
if (inSkippedTree(fullPath) && !inDeletedTree(fullPath, true)) {
maybeBumpDirInfo(myCurrentDirectory);
myCurrentDirectory = myCurrentDirectory.Parent;
return;
}
SVNProperties modifiedWCProps = myCurrentDirectory.getChangedWCProperties();
SVNProperties modifiedEntryProps = myCurrentDirectory.getChangedEntryProperties();
SVNProperties modifiedProps = myCurrentDirectory.getChangedProperties();
if (!myCurrentDirectory.anyChangesInChildren &&
(modifiedProps == null || modifiedProps.isEmpty())) {
// a workaround for SVN issue 3525 on the client side
// a situation is possible that openDir/openFile/closeFile/closeDir invocation describes no real changes
// (except maybe entry- and wc- properties)
// in this case we should make sure no tree conflict is created
if (myCurrentDirectory.treeConflictCreationAttributes != null) {
SVNDirectoryInfo parentDirectoryInfo = myCurrentDirectory.Parent;
if (parentDirectoryInfo != null) {
SVNLog log = parentDirectoryInfo.getLog();
log.deleteCommandsByNameAndAttributes(SVNLog.ADD_TREE_CONFLICT, myCurrentDirectory.treeConflictCreationAttributes, false);
}
}
} else {
SVNDirectoryInfo parentDirectoryInfo = myCurrentDirectory.Parent;
if (parentDirectoryInfo != null) {
parentDirectoryInfo.anyChangesInChildren = true;
}
}
SVNStatusType propStatus = SVNStatusType.UNKNOWN;
SVNAdminArea adminArea = myCurrentDirectory.getAdminArea();
if (myCurrentDirectory.wasIncomplete) {
// 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);
handleDuplicateEntries();
if (!myCurrentDirectory.isSkipped && (myCurrentDirectory.isAddExisted || !myCurrentDirectory.IsAdded) && !inDeletedTree(fullPath, true)) {
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;
}
private void handleDuplicateEntries() throws SVNException {
if (!SVNFileUtil.isCaseInsensitiveFS()) {
return;
}
SVNAdminArea area = myCurrentDirectory.getAdminArea();
Map entries = area.getEntries();
Set duplicates = new HashSet();
Set entryNames = new HashSet();
for (Iterator names = entries.keySet().iterator(); names.hasNext();) {
String name = (String) names.next();
SVNEntry entry = (SVNEntry) entries.get(name);
if (entry == null || entry.getKind() != SVNNodeKind.FILE || entry.isHidden()) {
continue;
}
name = name.toLowerCase();
if (!duplicates.add(name)) {
entryNames.add(name);
}
}
if (entryNames.isEmpty()) {
return;
}
SVNLog log = null;
for (Iterator names = entries.keySet().iterator(); names.hasNext();) {
myCurrentDirectory.flushLog();
log = myCurrentDirectory.getLog();
String name = (String) names.next();
SVNEntry entry = (SVNEntry) entries.get(name);
if (entry == null || entry.getKind() != SVNNodeKind.FILE || entry.isHidden()) {
continue;
}
if (entryNames.contains(name.toLowerCase())) {
SVNProperties command = new SVNProperties();
command.put(SVNLog.NAME_ATTR, name);
command.put(SVNProperty.shortPropertyName(SVNProperty.ABSENT), Boolean.TRUE.toString());
command.put(SVNProperty.shortPropertyName(SVNProperty.CHECKSUM), "nameconflict");
log.addCommand(SVNLog.MODIFY_ENTRY, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, SVNAdminUtil.getTextBasePath(name, false));
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, SVNAdminUtil.getTextRevertPath(name, false));
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, SVNAdminUtil.getPropBasePath(name, SVNNodeKind.FILE, false));
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, SVNAdminUtil.getPropPath(name, SVNNodeKind.FILE, false));
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, SVNAdminUtil.getPropRevertPath(name, SVNNodeKind.FILE, false));
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
command.put(SVNLog.NAME_ATTR, entry.getName());
log.addCommand(SVNLog.DELETE, command, false);
command.clear();
SVNEvent event = SVNEventFactory.createSVNEvent(area.getFile(name), SVNNodeKind.FILE, null, myTargetRevision, SVNEventAction.UPDATE_DELETE, null, null, null);
myWCAccess.handleEvent(event);
}
}
myCurrentDirectory.flushLog();
myCurrentDirectory.runLogs();
}
public SVNCommitInfo closeEdit() throws SVNException {
if (myTarget != null && myWCAccess.isMissing(myAdminInfo.getAnchor().getFile(myTarget))) {
myCurrentDirectory = createDirectoryInfo(null, "", false);
myWCAccess.registerCleanupHandler(myCurrentDirectory.getAdminArea(), myCurrentDirectory);
doDeleteEntry(myTarget, myCurrentDirectory.getAdminArea(), myCurrentDirectory, null);
}
if (!myIsRootOpen) {
if (myCurrentDirectory == null) {
myCurrentDirectory = createDirectoryInfo(null, "", false);
}
completeDirectory(myCurrentDirectory);
}
if (!myIsTargetDeleted) {
File targetFile = myTarget != null ? myAdminInfo.getAnchor().getFile(myTarget) : myAdminInfo.getAnchor().getRoot();
getSkippedTrees().removeAll(getDeletedTrees());
SVNWCManager.updateCleanup(targetFile, myWCAccess, mySwitchURL, myRootURL, myTargetRevision, true, getSkippedTrees(), 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 {
File file = myAdminInfo.getAnchor().getFile(dirInfo.getPath());
if (inSkippedTree(file) && !inDeletedTree(file, true)) {
return;
}
if (dirInfo.Parent == null && myTarget != null) {
if (myIsDepthSticky || myTarget != null) {
SVNAdminArea adminArea = dirInfo.getAdminArea();
SVNEntry entry = adminArea.getEntry(myTarget, true);
if (entry != null && entry.getDepth() == SVNDepth.EXCLUDE) {
File target = myAdminInfo.getAnchor().getFile(myTarget);
SVNAdminArea targetArea = myWCAccess.getAdminArea(target);
if (targetArea == null && entry.isDirectory()) {
doDeleteEntry(myTarget, myAdminInfo.getAnchor(), null, null);
} else {
entry.setDepth(SVNDepth.INFINITY);
adminArea.saveEntries(false);
}
}
}
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 (currentEntry.getDepth() == SVNDepth.EXCLUDE) {
if (myIsDepthSticky && myRequestedDepth.compareTo(SVNDepth.IMMEDIATES) >= 0) {
currentEntry.setDepth(SVNDepth.INFINITY);
}
} else 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 {
File fullPath = myAdminInfo.getAnchor().getFile(path);
boolean isLocallyDeleted = inDeletedTree(fullPath, true);
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);
}
}
SVNFileInfo info = createFileInfo(myCurrentDirectory, path, true);
if (inSkippedTree(fullPath) && !isLocallyDeleted) {
info.isSkipped = true;
return info;
}
info.isDeleted = isLocallyDeleted;
checkIfPathIsUnderRoot(path);
SVNAdminArea adminArea = parent.getAdminArea();
SVNFileType fileType = SVNFileType.getType(adminArea.getFile(info.name));
SVNEntry entry = adminArea.getEntry(info.name, false);
File victim = alreadyInTreeConflict(fullPath);
if (victim != null) {
info.isSkipped = true;
addSkippedTree(fullPath);
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, SVNNodeKind.FILE, null, myTargetRevision, SVNEventAction.SKIP, SVNEventAction.UPDATE_ADD, null, null);
if (entry != null) {
event.setPreviousRevision(entry.getRevision());
event.setPreviousURL(entry.getSVNURL());
}
myWCAccess.handleEvent(event);
return info;
}
if (fileType == SVNFileType.DIRECTORY || fileType == SVNFileType.UNKNOWN) {
SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add file ''{0}'': a non-file object of the same name already exists", path);
SVNErrorManager.error(error, SVNLogType.WC);
}
if (entry != null && fileType == SVNFileType.NONE) {
if (entry.isDirectory() && entry.isScheduledForAddition()) {
// special case of missing not yet versioned directory scheduled
// for addition.
// remove this entry, no chance to restore anything from the
// directory.
adminArea.deleteEntry(entry.getName());
adminArea.saveEntries(false);
entry = null;
}
}
if (entry == null && (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK)) {
if (myIsUnversionedObstructionsAllowed) {
info.isExisted = true;
} else {
SVNErrorMessage err = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "Failed to add file ''{0}'': an unversioned file of the same name already exists", path);
SVNErrorManager.error(err, SVNLogType.WC);
}
}
if (entry != null) {
SVNEntry parentEntry = adminArea.getEntry(adminArea.getThisDirName(), false);
if (entry.getUUID() != null && !entry.getUUID().equals(parentEntry.getUUID())) {
SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "UUID mismatch: existing file ''{0}'' was checked out from a different repository", fullPath);
SVNErrorManager.error(error, SVNLogType.WC);
}
if (mySwitchURL == null) {
boolean equals = info.URL == entry.getURL() || (info.URL != null && info.URL.equals(entry.getURL()));
if (!equals) {
SVNErrorMessage error = SVNErrorMessage.create(SVNErrorCode.WC_OBSTRUCTED_UPDATE, "URL ''{0}'' of existing file ''{1}'' does not match expected URL ''{2}''", new Object[] {
entry.getURL(), fullPath, info.URL
});
SVNErrorManager.error(error, SVNLogType.WC);
}
}
}
if (entry != null && (fileType == SVNFileType.FILE || fileType == SVNFileType.SYMLINK)) {
if ((entry.isScheduledForAddition() || entry.isScheduledForReplacement()) && !entry.isCopied()) {
info.isAddExisted = true;
} else {
SVNLog log = parent.getLog();
SVNURL theirURL = SVNURL.parseURIEncoded(info.URL);
SVNTreeConflictDescription treeConflict = checkTreeConflict(fullPath, entry, adminArea, log, SVNConflictAction.ADD, SVNNodeKind.FILE, theirURL);
if (treeConflict != null) {
addSkippedTree(fullPath);
info.isSkipped = true;
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, SVNNodeKind.FILE, null, myTargetRevision, SVNEventAction.TREE_CONFLICT, SVNEventAction.UPDATE_ADD, null, null);
event.setPreviousRevision(entry.getRevision());
event.setPreviousURL(entry.getSVNURL());
myWCAccess.handleEvent(event);
return info;
}
}
}
if (copyFromPath != null && !info.isSkipped) {
return addFileWithHistory(parent, info, copyFromPath, copyFromRevision);
}
return info;
}
private SVNFileInfo addFileWithHistory(SVNDirectoryInfo parent, SVNFileInfo info, String copyFromPath, long copyFromRevision) throws SVNException {
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();
}
}
}
}
public SVNFileInfo openFile(String path, SVNDirectoryInfo parent) throws SVNException {
checkIfPathIsUnderRoot(path);
File fullPath = myAdminInfo.getAnchor().getFile(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 isLocallyDeleted = inDeletedTree(fullPath, true);
if (inSkippedTree(fullPath) && !isLocallyDeleted) {
info.isSkipped = true;
return info;
}
File victim = alreadyInTreeConflict(fullPath);
SVNTreeConflictDescription treeConflict = null;
if (victim == null) {
//tree conflict didn't exist before
SVNLog log = parent.getLog();
SVNURL theirURL = SVNURL.parseURIEncoded(info.URL);
treeConflict = checkTreeConflict(fullPath, entry, adminArea, log, SVNConflictAction.EDIT, SVNNodeKind.FILE, theirURL);
if (treeConflict != null && treeConflict.getConflictAction() == SVNConflictAction.EDIT) {
//now tree conflict is created
info.treeConflictCreationAttributes = getTreeConflictCreationAttributes(adminArea, treeConflict);
}
}
String name = SVNPathUtil.tail(path);
boolean hasTextConflicts = adminArea.hasTextConflict(name);
boolean hasPropConflicts = adminArea.hasPropConflict(name);
if (treeConflict != null && treeConflict.getConflictReason() == SVNConflictReason.DELETED && !isLocallyDeleted) {
addDeletedTree(fullPath);
isLocallyDeleted = true;
}
info.isDeleted = isLocallyDeleted;
if (victim != null || treeConflict != null || hasTextConflicts || hasPropConflicts) {
if (!isLocallyDeleted) {
info.isSkipped = true;
}
addSkippedTree(fullPath);
if (!inDeletedTree(fullPath, false)) {
SVNEventAction eventAction = treeConflict != null ? SVNEventAction.TREE_CONFLICT : SVNEventAction.SKIP;
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, SVNNodeKind.FILE, null, myTargetRevision, hasTextConflicts ? SVNStatusType.CONFLICTED : SVNStatusType.UNKNOWN,
hasPropConflicts ? SVNStatusType.CONFLICTED : SVNStatusType.UNKNOWN, SVNStatusType.LOCK_INAPPLICABLE, eventAction, SVNEventAction.UPDATE_UPDATE, null, null);
event.setPreviousRevision(entry.getRevision());
event.setURL(entry.getSVNURL());
myWCAccess.handleEvent(event);
}
}
return info;
}
private SVNProperties getTreeConflictCreationAttributes(SVNAdminArea adminArea, SVNTreeConflictDescription treeConflict) throws SVNException {
Map conflicts = new SVNHashMap();
conflicts.put(treeConflict.getPath(), treeConflict);
String conflictData = SVNTreeConflictUtil.getTreeConflictData(conflicts);
SVNProperties command = new SVNProperties();
command.put(SVNLog.NAME_ATTR, adminArea.getThisDirName());
command.put(SVNLog.DATA_ATTR, conflictData);
return command;
}
private void closeFile(String textChecksum, SVNFileInfo fileInfo, SVNDirectoryInfo dirInfo) throws SVNException {
if (fileInfo.isSkipped) {
maybeBumpDirInfo(dirInfo);
return;
}
if (!fileInfo.receivedTextDelta &&
(fileInfo.getChangedProperties() == null || fileInfo.getChangedProperties().isEmpty())) {
//only "fake" changes received
// a workaround for SVN issue 3525 on the client side
if (fileInfo.treeConflictCreationAttributes != null) {
dirInfo.getLog().deleteCommandsByNameAndAttributes(SVNLog.ADD_TREE_CONFLICT, fileInfo.treeConflictCreationAttributes, false);
}
} else {
dirInfo.anyChangesInChildren = true;
}
File fullPath = myAdminInfo.getAnchor().getFile(fileInfo.getPath());
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 (isReplaced) {
// do nothing.
} else if (!isLocallyModified) {
if (!fileInfo.isDeleted) {
command.put(SVNLog.NAME_ATTR, tmpBasePath);
command.put(SVNLog.DEST_ATTR, name);
log.addCommand(SVNLog.COPY_AND_TRANSLATE, command, false);
command.clear();
}
// if (fileEntry == null || !fileEntry.isScheduledForDeletion())
// {
// }
} 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) {
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;
File mergeLeftFile = fileInfo.baseFile;
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();
logAttributes.put(SVNProperty.shortPropertyName(SVNProperty.CHECKSUM), checksum);
}
if (fileInfo.isDeleted && !isReplaced) {
logAttributes.put(SVNProperty.shortPropertyName(SVNProperty.SCHEDULE), SVNProperty.SCHEDULE_DELETE);
}
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) && !fileInfo.isDeleted) {
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 || fileInfo.treeConficted) && !inDeletedTree(fullPath, true)) {
SVNEventAction action = SVNEventAction.UPDATE_UPDATE;
SVNEventAction expectedAction = action;
if (fileInfo.treeConficted) {
action = SVNEventAction.TREE_CONFLICT;
} else if (fileInfo.isExisted || fileInfo.isAddExisted) {
if (textStatus != SVNStatusType.CONFLICTED_UNRESOLVED && textStatus != SVNStatusType.CONFLICTED) {
action = SVNEventAction.UPDATE_EXISTS;
expectedAction = action;
}
} else if (fileInfo.IsAdded) {
action = SVNEventAction.UPDATE_ADD;
expectedAction = action;
}
SVNEvent event = SVNEventFactory.createSVNEvent(fullPath, SVNNodeKind.FILE, null, myTargetRevision, textStatus, propStatus, lockStatus, action, expectedAction, 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.treeConficted = false;
info.isDeleted = 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;
info.wasIncomplete = false;
return info;
}
public static SVNUpdateEditor 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);
}
}
SVNUpdateEditor editor = new SVNUpdateEditor(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;
// a workaround for SVN issue 3525 on the client side
// in short: because of some bug, all SVN servers of version < 1.6.17
// may send only entry properties change in update editor (instead of sending nothing)
// if a client working with such a buggy server, it should ignore those changes
// instead of a tree conflict creation
//
// the variable keeps tree conflict creation command to remove in closeFile
protected SVNProperties treeConflictCreationAttributes;
protected SVNEntryInfo(String path) {
myPath = path;
this.treeConflictCreationAttributes = null;
}
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;
private boolean treeConficted;
// Set if this file is locally deleted or is being added
// within a locally deleted tree.
private boolean isDeleted;
// The checksum for the file located at newBaseFile.
// private String actualChecksum;
// If this file was added with history, this is the checksum of the
// text base (see copied_text_base). May be NULL if unknown.
// private String copiedBaseChecksum;
public SVNFileInfo(SVNDirectoryInfo parent, String path) {
super(path);
this.Parent = parent;
this.isDeleted = false;
}
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 boolean wasIncomplete;
public boolean anyChangesInChildren;
public SVNDirectoryInfo(String path) {
super(path);
this.anyChangesInChildren = false;
}
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();
}
}