Commit bd820dbf authored by agurov's avatar agurov

Limit SVN status cache refresh depth when possible (bug 473161)

https://bugs.eclipse.org/bugs/show_bug.cgi?id=473161

git-svn-id: https://dev.eclipse.org/svnroot/technology/org.eclipse.subversive/trunk@21495 ee007c2a-0a25-0410-9ab9-bf268980928c
parent 5f27d069
......@@ -38,6 +38,7 @@ import org.eclipse.team.svn.core.operation.local.AbstractWorkingCopyOperation;
import org.eclipse.team.svn.core.operation.local.RefreshResourcesOperation;
import org.eclipse.team.svn.core.resource.ILocalResource;
import org.eclipse.team.svn.core.resource.IRepositoryLocation;
import org.eclipse.team.svn.core.resource.IResourceProvider;
import org.eclipse.team.svn.core.svnstorage.SVNRemoteStorage;
import org.eclipse.team.svn.core.utility.FileUtility;
import org.eclipse.team.svn.core.utility.ProgressMonitorUtility;
......@@ -75,101 +76,120 @@ public class FileReplaceListener implements IResourceChangeListener {
}
protected void processResources(IResource []resources) {
AbstractWorkingCopyOperation mainOp = new AbstractWorkingCopyOperation("Operation_FileReplaceListener", SVNMessages.class, resources) { //$NON-NLS-1$
protected void runImpl(IProgressMonitor monitor) throws Exception {
for (IResource file : this.operableData()) {
if (monitor.isCanceled()) {
return;
}
FileReplaceListenerOperation mainOp = new FileReplaceListenerOperation(resources);
CompositeOperation cmpOp = new CompositeOperation(mainOp.getId(), mainOp.getMessagesClass());
cmpOp.add(mainOp);
cmpOp.add(new RefreshResourcesOperation(mainOp, IResource.DEPTH_ZERO, RefreshResourcesOperation.REFRESH_CHANGES));
ProgressMonitorUtility.doTaskScheduledDefault(cmpOp);
}
private class FileReplaceListenerOperation
extends AbstractWorkingCopyOperation
implements IResourceProvider {
private ArrayList<IResource> processedResources;
public FileReplaceListenerOperation(IResource []resources) {
super("Operation_FileReplaceListener", SVNMessages.class, resources);//$NON-NLS-1$
this.processedResources = new ArrayList<IResource>();
}
public IResource[] getResources() {
return this.processedResources.toArray(new IResource[this.processedResources.size()]);
}
protected void runImpl(IProgressMonitor monitor) throws Exception {
IResource []resources = this.operableData();
SVNRemoteStorage.instance().refreshLocalResources(resources, IResource.DEPTH_ZERO);
for (IResource file : resources) {
if (monitor.isCanceled()) {
return;
}
ILocalResource local = SVNRemoteStorage.instance().asLocalResource(file);
if (!IStateFilter.SF_PREREPLACEDREPLACED.accept(local)) {
continue;
}
IResource parent = file.getParent();
ILocalResource localParent = SVNRemoteStorage.instance().asLocalResource(parent);
if (IStateFilter.SF_DELETED.accept(localParent)) {
continue;
}
File originalFile = new File(FileUtility.getWorkingCopyPath(file));
File tmpFile = new File(originalFile + ".svntmp"); //$NON-NLS-1$
ILocalResource local = SVNRemoteStorage.instance().asLocalResource(file);
if (!IStateFilter.SF_PREREPLACEDREPLACED.accept(local)) {
continue;
}
IResource parent = file.getParent();
ILocalResource localParent = SVNRemoteStorage.instance().asLocalResource(parent);
if (IStateFilter.SF_DELETED.accept(localParent)) {
continue;
}
File originalFile = new File(FileUtility.getWorkingCopyPath(file));
File tmpFile = new File(originalFile + ".svntmp"); //$NON-NLS-1$
try {
IRepositoryLocation location = SVNRemoteStorage.instance().getRepositoryLocation(file);
ISVNConnector proxy = location.acquireSVNProxy();
FileOutputStream oStream = null;
try {
IRepositoryLocation location = SVNRemoteStorage.instance().getRepositoryLocation(file);
ISVNConnector proxy = location.acquireSVNProxy();
FileOutputStream oStream = null;
try {
oStream = new FileOutputStream(tmpFile);
proxy.streamFileContent(new SVNEntryRevisionReference(originalFile.getAbsolutePath(), null, SVNRevision.BASE), 8192, oStream, new SVNProgressMonitor(this, monitor, null));
if (this.equals(originalFile, tmpFile)) {
originalFile.delete();
proxy.revert(originalFile.getAbsolutePath(), SVNDepth.EMPTY, null, new SVNProgressMonitor(this, monitor, null));
}
}
finally {
if (oStream != null) {
try {oStream.close();} catch (IOException ex) {}
}
location.releaseSVNProxy(proxy);
oStream = new FileOutputStream(tmpFile);
proxy.streamFileContent(new SVNEntryRevisionReference(originalFile.getAbsolutePath(), null, SVNRevision.BASE), 8192, oStream, new SVNProgressMonitor(this, monitor, null));
if (this.equals(originalFile, tmpFile)) {
this.processedResources.add(file);
originalFile.delete();
proxy.revert(originalFile.getAbsolutePath(), SVNDepth.EMPTY, null, new SVNProgressMonitor(this, monitor, null));
}
}
finally {
if (!originalFile.exists()) {
tmpFile.renameTo(originalFile);
}
else {
tmpFile.delete();
if (oStream != null) {
try {oStream.close();} catch (IOException ex) {}
}
location.releaseSVNProxy(proxy);
}
}
}
protected boolean equals(File src1, File src2) throws IOException {
long len = src1.length();
if (len == src2.length()) {
FileInputStream stream1 = null;
FileInputStream stream2 = null;
try {
stream1 = new FileInputStream(src1);
stream2 = new FileInputStream(src2);
int bufSize = len < 8192 ? (int)len : 8192;
byte []buffer1 = new byte[bufSize];
byte []buffer2 = new byte[bufSize];
int rem = (int)(len % bufSize);
for (int off = 0; off < len; off += bufSize) {
stream1.read(buffer1);
stream2.read(buffer2);
if (!Arrays.equals(buffer1, buffer2)) {
return false;
}
}
if (rem != 0) {
buffer1 = new byte[rem];
buffer2 = new byte[rem];
stream1.read(buffer1);
stream2.read(buffer2);
if (!Arrays.equals(buffer1, buffer2)) {
return false;
}
}
return true;
}
finally {
if (!originalFile.exists()) {
tmpFile.renameTo(originalFile);
}
finally {
if (stream1 != null) {
try {stream1.close();} catch (IOException ex) {}
else {
tmpFile.delete();
}
}
}
}
protected boolean equals(File src1, File src2) throws IOException {
long len = src1.length();
if (len == src2.length()) {
FileInputStream stream1 = null;
FileInputStream stream2 = null;
try {
stream1 = new FileInputStream(src1);
stream2 = new FileInputStream(src2);
int bufSize = len < 8192 ? (int)len : 8192;
byte []buffer1 = new byte[bufSize];
byte []buffer2 = new byte[bufSize];
int rem = (int)(len % bufSize);
for (int off = 0; off < len; off += bufSize) {
stream1.read(buffer1);
stream2.read(buffer2);
if (!Arrays.equals(buffer1, buffer2)) {
return false;
}
if (stream2 != null) {
try {stream2.close();} catch (IOException ex) {}
}
if (rem != 0) {
buffer1 = new byte[rem];
buffer2 = new byte[rem];
stream1.read(buffer1);
stream2.read(buffer2);
if (!Arrays.equals(buffer1, buffer2)) {
return false;
}
}
return true;
}
finally {
if (stream1 != null) {
try {stream1.close();} catch (IOException ex) {}
}
if (stream2 != null) {
try {stream2.close();} catch (IOException ex) {}
}
}
return false;
}
};
CompositeOperation cmpOp = new CompositeOperation(mainOp.getId(), mainOp.getMessagesClass());
cmpOp.add(mainOp);
cmpOp.add(new RefreshResourcesOperation(resources));
ProgressMonitorUtility.doTaskScheduledDefault(cmpOp);
return false;
}
}
}
......@@ -11,7 +11,6 @@
package org.eclipse.team.svn.core;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
......@@ -55,6 +54,7 @@ public class ResourceChangeListener implements IResourceChangeListener, ISavePar
ProgressMonitorUtility.doTaskScheduledDefault(new AbstractActionOperation("Operation_ResourcesChanged", SVNMessages.class) { //$NON-NLS-1$
protected void runImpl(IProgressMonitor monitor) throws Exception {
final Set<IResource> modified = new HashSet<IResource>();
final int []depth = new int[] {IResource.DEPTH_ZERO};
event.getDelta().accept(new IResourceDeltaVisitor() {
public boolean visit(IResourceDelta delta) throws CoreException {
IResource resource = delta.getResource();
......@@ -66,22 +66,26 @@ public class ResourceChangeListener implements IResourceChangeListener, ISavePar
return false;
}
IResource toAdd = null;
IResource svnFolder = FileUtility.getSVNFolder(resource);
if (svnFolder != null) {
toAdd = svnFolder.getParent();
return false;
}
if (delta.getKind() == IResourceDelta.ADDED || delta.getKind() == IResourceDelta.REMOVED) {
if (FileUtility.isSVNInternals(resource)) {
IContainer parent = resource.getParent();
modified.add(parent);
if (parent.exists()) {
modified.addAll(Arrays.asList(parent.members()));
}
return false;
}
modified.add(resource);
toAdd = resource;
}
else if (delta.getKind() == IResourceDelta.CHANGED) {
int flags = delta.getFlags();
if (resource instanceof IContainer && (flags & ResourceChangeListener.INTERESTING_CHANGES) != 0 ||
resource instanceof IFile && (flags & (ResourceChangeListener.INTERESTING_CHANGES | IResourceDelta.CONTENT)) != 0) {
modified.add(resource);
toAdd = resource;
}
}
if (toAdd != null) {
modified.add(toAdd);
if (toAdd.getType() != IResource.FILE) {
depth[0] = IResource.DEPTH_INFINITE;
}
}
......@@ -90,11 +94,13 @@ public class ResourceChangeListener implements IResourceChangeListener, ISavePar
}, IContainer.INCLUDE_TEAM_PRIVATE_MEMBERS);
// reset statuses only for changed resources, but notify regarding all and including parents
IResource []resources = modified.toArray(new IResource[modified.size()]);
SVNRemoteStorage.instance().scheduleRefresh(
resources, IResource.DEPTH_INFINITE,
new ResourceStatesChangedEvent(FileUtility.getPathNodes(resources), IResource.DEPTH_ZERO, ResourceStatesChangedEvent.PATH_NODES),
new ResourceStatesChangedEvent(resources, IResource.DEPTH_ZERO, ResourceStatesChangedEvent.CHANGED_NODES));
if (modified.size() > 0) {
IResource []resources = modified.toArray(new IResource[modified.size()]);
SVNRemoteStorage.instance().scheduleRefresh(
resources, depth[0],
new ResourceStatesChangedEvent(FileUtility.getPathNodes(resources), IResource.DEPTH_ZERO, ResourceStatesChangedEvent.PATH_NODES),
new ResourceStatesChangedEvent(resources, IResource.DEPTH_ZERO, ResourceStatesChangedEvent.CHANGED_NODES));
}
}
});
}
......
......@@ -127,9 +127,21 @@ public class RefreshResourcesOperation extends AbstractWorkingCopyOperation {
protected void runImpl(IProgressMonitor monitor) throws Exception {
IResource []original = this.operableData();
if (original.length == 0) {
return;
}
int tDepth = this.depth;
if (tDepth != IResource.DEPTH_ZERO) {
boolean allFiles = true; // reduce specified refresh depth in case all are files
for (int i = 0; i < original.length; i++) {
allFiles &= original[i].getType() == IResource.FILE;
}
if (allFiles) {
tDepth = IResource.DEPTH_ZERO;
}
}
final IResource []resources = this.depth == IResource.DEPTH_INFINITE ? FileUtility.shrinkChildNodes(original) : original;
final boolean isPriorToSVN17 = SVNUtility.isPriorToSVN17();
if (this.refreshType != RefreshResourcesOperation.REFRESH_CACHE) {
ResourcesPlugin.getWorkspace().run(new IWorkspaceRunnable() {
public void run(IProgressMonitor monitor) throws CoreException {
......@@ -162,7 +174,7 @@ public class RefreshResourcesOperation extends AbstractWorkingCopyOperation {
IResource []roots = FileUtility.getPathNodes(resources);
SVNRemoteStorage.instance().fireResourceStatesChangedEvent(new ResourceStatesChangedEvent(roots, IResource.DEPTH_ZERO, ResourceStatesChangedEvent.PATH_NODES));
SVNRemoteStorage.instance().fireResourceStatesChangedEvent(new ResourceStatesChangedEvent(original, this.depth, ResourceStatesChangedEvent.CHANGED_NODES));
SVNRemoteStorage.instance().fireResourceStatesChangedEvent(new ResourceStatesChangedEvent(original, tDepth, ResourceStatesChangedEvent.CHANGED_NODES));
}
if (this.ignoreNestedProjects) {
......
......@@ -136,6 +136,8 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
protected long lastMonitorTime;
protected Map<IResource, File> changeMonitorMap;
protected int suggestedLoadDepth = IResource.DEPTH_INFINITE;
protected ISchedulingRule notifyLock = new ISchedulingRule() {
public boolean isConflicting(ISchedulingRule rule) {
return rule == this;
......@@ -400,7 +402,7 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
Map map = (Map)this.localResources.get(container);
if (map == null) {
this.loadLocalResourcesSubTree(container, false);
this.loadLocalResourcesSubTree(container, IResource.DEPTH_ONE);
map = (Map)this.localResources.get(container);
}
Set retVal = null;
......@@ -417,7 +419,7 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
return this.asLocalResource(resource);
}
// null resource and workspace root shouldn't be provided
if (resource == null || resource.getProject() == null) {
if (resource == null || resource.getProject() == null || !resource.getProject().isAccessible()) {
return this.wrapUnexistingResource(resource, IStateFilter.ST_INTERNAL_INVALID, 0);
}
ILocalResource retVal = this.getCachedResource(resource);
......@@ -456,40 +458,39 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
}
public ILocalResource asLocalResource(IResource resource) {
return this.asLocalResourceImpl(resource, true);
return this.asLocalResourceImpl(resource, this.suggestedLoadDepth);
}
protected ILocalResource asLocalResourceImpl(IResource resource, boolean recurse) {
protected ILocalResource asLocalResourceImpl(IResource resource, int depth) {
// null resource and workspace root shouldn't be provided
if (resource == null || resource.getProject() == null || !resource.getProject().isAccessible()) {
return this.wrapUnexistingResource(resource, IStateFilter.ST_INTERNAL_INVALID, 0);
}
ILocalResource local = this.getCachedResource(resource);
if (local == null) {
synchronized (this) {
local = this.getCachedResource(resource);
if (local == null) {
try {
local = this.loadLocalResourcesSubTree(resource, recurse);
}
catch (RuntimeException ex) {
throw ex;
}
catch (SVNConnectorException ex) {
return this.wrapUnexistingResource(resource, IStateFilter.ST_INTERNAL_INVALID, 0);
}
catch (Exception e) {
throw new RuntimeException(e);
}
synchronized (this) {
ILocalResource local = this.getCachedResource(resource);
if (local == null) {
try {
local = this.loadLocalResourcesSubTree(resource, depth);
}
catch (RuntimeException ex) {
throw ex;
}
catch (SVNConnectorException ex) {
return this.wrapUnexistingResource(resource, IStateFilter.ST_INTERNAL_INVALID, 0);
}
catch (Exception e) {
throw new RuntimeException(e);
}
}
return local;
}
return local;
}
public synchronized void refreshLocalResources(IResource []resources, int depth) {
this.suggestedLoadDepth = IResource.DEPTH_ONE;
if (depth == IResource.DEPTH_INFINITE) {
resources = FileUtility.shrinkChildNodes(resources);
this.suggestedLoadDepth = IResource.DEPTH_INFINITE;
}
for (int i = 0; i < resources.length; i++) {
this.refreshLocalResourceImpl(resources[i], depth);
......@@ -680,7 +681,7 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
this.switchedToUrls.remove(resource.getFullPath());
}
protected ILocalResource loadLocalResourcesSubTree(final IResource resource, boolean recurse) throws Exception {
protected ILocalResource loadLocalResourcesSubTree(final IResource resource, int depth) throws Exception {
IConnectedProjectInformation provider = (IConnectedProjectInformation)RepositoryProvider.getProvider(resource.getProject(), SVNTeamPlugin.NATURE_ID);
if (provider == null || FileUtility.isSVNInternals(resource)) {
return this.wrapUnexistingResource(resource, IStateFilter.ST_INTERNAL_INVALID, 0);
......@@ -690,25 +691,25 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
if (!isCacheEnabled) {
this.localResources.clear();
}
recurse &= isCacheEnabled;
depth = isCacheEnabled ? depth : IResource.DEPTH_ZERO;
ILocalResource retVal = null;
boolean isLinked = FileUtility.isLinked(resource);
IResource parent = resource.getParent();
boolean parentExists = parent != null && parent.isAccessible();
if (parentExists && !isLinked) {
ILocalResource parentLocal = this.asLocalResourceImpl(parent, false);
ILocalResource parentLocal = this.asLocalResourceImpl(parent, IResource.DEPTH_ONE);
if (parentLocal == null || !SVNRemoteStorage.SF_NONSVN.accept(parentLocal) ||
IStateFilter.SF_UNVERSIONED_EXTERNAL.accept(parentLocal)) {
retVal = this.loadLocalResourcesSubTreeSVNImpl(provider, resource, recurse);
retVal = this.loadLocalResourcesSubTreeSVNImpl(provider, resource, depth);
}
}
return (retVal == null || IStateFilter.SF_UNVERSIONED.accept(retVal) && !IStateFilter.SF_IGNORED.accept(retVal))
? this.loadUnversionedSubtree(resource, isLinked, recurse) : retVal;
? this.loadUnversionedSubtree(resource, isLinked, depth) : retVal;
}
protected ILocalResource loadUnversionedSubtree(final IResource resource, final boolean isLinked, boolean recurse) throws Exception {
protected ILocalResource loadUnversionedSubtree(final IResource resource, final boolean isLinked, int depth) throws Exception {
final ILocalResource []tmp = new ILocalResource[1];
/*
* Performance optimization: make an assumption that if resource is unversioned external then all its unversioned
......@@ -751,7 +752,7 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
}
return true;
}
}, recurse ? IResource.DEPTH_INFINITE : IResource.DEPTH_ONE, false);
}, depth, false);
return tmp[0] != null ? tmp[0] : this.wrapUnexistingResource(resource, IStateFilter.ST_INTERNAL_INVALID, 0);
}
......@@ -796,7 +797,7 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
return false;
}
protected ILocalResource loadLocalResourcesSubTreeSVNImpl(IConnectedProjectInformation provider, IResource resource, boolean recurse) throws Exception {
protected ILocalResource loadLocalResourcesSubTreeSVNImpl(IConnectedProjectInformation provider, IResource resource, int depth) throws Exception {
IProject project = resource.getProject();
IResource target = resource.getType() == IResource.FILE ? resource.getParent() : resource;
......@@ -825,7 +826,9 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
}
IRepositoryResource baseResource = provider.getRepositoryResource();
int offsetFromRoot = resourcePath.segmentCount() - wcPath.segmentCount();
SVNChangeStatus []statuses = this.getStatuses(resourcePath.toString(), offsetFromRoot < 1 || !CoreExtensionsManager.instance().getOptionProvider().isSVNCacheEnabled() ? SVNDepth.IMMEDIATES : SVNDepth.INFINITY);
SVNDepth svnDepth = depth == IResource.DEPTH_ZERO ? SVNDepth.EMPTY : (depth == IResource.DEPTH_ONE ? SVNDepth.IMMEDIATES : SVNDepth.INFINITY);
svnDepth = offsetFromRoot < 1 || !CoreExtensionsManager.instance().getOptionProvider().isSVNCacheEnabled() ? SVNDepth.IMMEDIATES : svnDepth;
SVNChangeStatus []statuses = this.getStatuses(resourcePath.toString(), svnDepth);
String desiredUrl = this.makeUrl(target, baseResource);
SVNChangeStatus [][]loadTargets = new SVNChangeStatus[1][];
ILocalResource retVal = this.fillCache(statuses, desiredUrl, resource, subPathStart, requestedPath, loadTargets);
......@@ -836,7 +839,7 @@ public class SVNRemoteStorage extends AbstractSVNStorage implements IRemoteStora
}
statuses = loadTargets[0];
if (retVal != null && hasSVNMeta && statuses.length > 1 && recurse && CoreExtensionsManager.instance().getOptionProvider().isSVNCacheEnabled()) {
if (retVal != null && hasSVNMeta && statuses.length > 1 && depth != IResource.DEPTH_ZERO && CoreExtensionsManager.instance().getOptionProvider().isSVNCacheEnabled()) {
this.scheduleStatusesFetch(statuses, target);
}
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment