Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMatthias Sohn2019-09-23 16:41:17 -0400
committerThomas Wolf2019-10-10 15:57:36 -0400
commit110b54a5ffd44b1151328abbac4cdd8ccbd3ee50 (patch)
tree4ceff340a966a12503439ac73501aced74bea58e
parent9e7e288f0489e8f92883d1ac8d419ae87b6d000e (diff)
downloadegit-110b54a5ffd44b1151328abbac4cdd8ccbd3ee50.tar.gz
egit-110b54a5ffd44b1151328abbac4cdd8ccbd3ee50.tar.xz
egit-110b54a5ffd44b1151328abbac4cdd8ccbd3ee50.zip
Rely on synchronous ResourceRefreshHandler to refresh after checkout
- Convert asynchronous ResourceRefreshJob to a synchronous ResourceRefreshHandler. - Move it to EGit core since it does not depend on the UI. - Remove merging of multiple WorkingTreeChanges and progress monitors since that's not necessary when refreshing synchronously. - Unconditionally refresh resources found, even when in projects not shared with EGit. Bug: 551289 Change-Id: I338e4eff366bf2300e347a1da00c2ab0eee6bc14 Signed-off-by: Matthias Sohn <matthias.sohn@sap.com> Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
-rw-r--r--org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/util/ProjectUtilTest.java14
-rw-r--r--org.eclipse.egit.core/.options5
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java17
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java3
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/ResourceRefreshHandler.java269
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties1
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/trace/GitTraceLocation.java4
-rw-r--r--org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java9
-rwxr-xr-xorg.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java405
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java3
-rw-r--r--org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties1
11 files changed, 304 insertions, 427 deletions
diff --git a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/util/ProjectUtilTest.java b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/util/ProjectUtilTest.java
index 3c221e0af..0bb7de7f2 100644
--- a/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/util/ProjectUtilTest.java
+++ b/org.eclipse.egit.core.test/src/org/eclipse/egit/core/internal/util/ProjectUtilTest.java
@@ -16,10 +16,9 @@ import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
-import static org.mockito.Matchers.any;
-import static org.mockito.Matchers.eq;
+import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
-import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import java.io.ByteArrayInputStream;
@@ -196,15 +195,6 @@ public class ProjectUtilTest extends GitTestCase {
}
@Test
- public void testRefreshValidProjects() throws Exception {
- IProject p = spy(project.getProject());
- IProject[] projects = { p };
- ProjectUtil.refreshValidProjects(projects, new NullProgressMonitor());
- verify(p).refreshLocal(eq(IResource.DEPTH_INFINITE),
- any(IProgressMonitor.class));
- }
-
- @Test
public void testCloseMissingProject() throws Exception {
IProject p = mock(IProject.class);
File projectFile = project.getProject().getLocation()
diff --git a/org.eclipse.egit.core/.options b/org.eclipse.egit.core/.options
index 2a7697f7c..de24ff0de 100644
--- a/org.eclipse.egit.core/.options
+++ b/org.eclipse.egit.core/.options
@@ -4,6 +4,7 @@ org.eclipse.egit.core/debug = false
org.eclipse.egit.core/debug/core = false
# Trace location for the IndexDiffCache
org.eclipse.egit.core/debug/core/indexdiffcache = false
-# Trace location forJSch
+# Trace location for JSch
org.eclipse.egit.core/debug/core/jsch = false
-
+# Trace location for ResourceRefreshJob
+org.eclipse.egit.core/debug/core/refresh = false
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java
index 66238957b..011c64717 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/Activator.java
@@ -57,6 +57,7 @@ import org.eclipse.core.runtime.preferences.InstanceScope;
import org.eclipse.egit.core.internal.CoreText;
import org.eclipse.egit.core.internal.EGitSshdSessionFactory;
import org.eclipse.egit.core.internal.ReportingTypedConfigGetter;
+import org.eclipse.egit.core.internal.ResourceRefreshHandler;
import org.eclipse.egit.core.internal.SshPreferencesMirror;
import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache;
import org.eclipse.egit.core.internal.job.JobUtil;
@@ -70,8 +71,10 @@ import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.core.securestorage.EGitSecureStore;
import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.lib.Config;
import org.eclipse.jgit.lib.Constants;
+import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.lib.StoredConfig;
import org.eclipse.jgit.merge.MergeStrategy;
import org.eclipse.jgit.storage.file.FileBasedConfig;
@@ -117,6 +120,9 @@ public class Activator extends Plugin implements DebugOptionsListener {
private MergeStrategyRegistryListener mergeStrategyRegistryListener;
private IPreferenceChangeListener preferenceChangeListener;
private ServiceTracker<IProxyService, IProxyService> proxyServiceTracker;
+ private ResourceRefreshHandler refreshHandler;
+
+ private ListenerHandle refreshHandle;
/**
* @return the singleton {@link Activator}
@@ -258,6 +264,7 @@ public class Activator extends Plugin implements DebugOptionsListener {
logError(CoreText.Activator_ReconfigureWindowCacheError, e);
}
GitProjectData.attachToWorkspace();
+ setupResourceRefresh();
repositoryUtil = new RepositoryUtil();
@@ -333,6 +340,12 @@ public class Activator extends Plugin implements DebugOptionsListener {
}
}
+ private void setupResourceRefresh() {
+ refreshHandler = new ResourceRefreshHandler();
+ refreshHandle = Repository.getGlobalListenerList()
+ .addWorkingTreeModifiedListener(refreshHandler);
+ }
+
private void registerPreDeleteResourceChangeListener() {
if (preDeleteProjectListener == null) {
preDeleteProjectListener = new IResourceChangeListener() {
@@ -519,6 +532,10 @@ public class Activator extends Plugin implements DebugOptionsListener {
ignoreDerivedResourcesListener.stop();
ignoreDerivedResourcesListener = null;
}
+ if (refreshHandle != null) {
+ refreshHandle.remove();
+ refreshHandle = null;
+ }
if (shareGitProjectsJob != null) {
ResourcesPlugin.getWorkspace().removeResourceChangeListener(
shareGitProjectsJob);
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java
index ef01832a3..4fa3c4aed 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/CoreText.java
@@ -55,6 +55,9 @@ public class CoreText extends NLS {
public static String Activator_ReconfigureWindowCacheError;
/** */
+ public static String Activator_refreshFailed;
+
+ /** */
public static String Activator_SshClientNoJsch;
/** */
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/ResourceRefreshHandler.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/ResourceRefreshHandler.java
new file mode 100644
index 000000000..a563fb47c
--- /dev/null
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/ResourceRefreshHandler.java
@@ -0,0 +1,269 @@
+/*******************************************************************************
+ * Copyright (C) 2018, Thomas Wolf <thomas.wolf@paranor.ch>
+ * Copyright (C) 2019, Matthias Sohn <matthias.sohn@sap.com>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License 2.0
+ * which accompanies this distribution, and is available at
+ * https://www.eclipse.org/legal/epl-2.0/
+ *
+ * SPDX-License-Identifier: EPL-2.0
+ *******************************************************************************/
+package org.eclipse.egit.core.internal;
+
+import java.io.File;
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Stream;
+
+import org.eclipse.core.filesystem.URIUtil;
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspaceRoot;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.OperationCanceledException;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.egit.core.Activator;
+import org.eclipse.egit.core.internal.job.RuleUtil;
+import org.eclipse.egit.core.internal.trace.GitTraceLocation;
+import org.eclipse.jgit.annotations.NonNull;
+import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
+import org.eclipse.jgit.events.WorkingTreeModifiedListener;
+import org.eclipse.jgit.lib.Repository;
+
+/**
+ * Refreshes parts of the workspace changed by JGit operations. This will
+ * not refresh any git-ignored resources since those are not reported in the
+ * {@link WorkingTreeModifiedEvent}.
+ */
+public class ResourceRefreshHandler implements WorkingTreeModifiedListener {
+
+ @Override
+ public void onWorkingTreeModified(WorkingTreeModifiedEvent event) {
+ if (event.isEmpty()) {
+ return;
+ }
+ Repository repo = event.getRepository();
+ if (repo == null || repo.isBare()) {
+ return; // Should never occur
+ }
+ if (GitTraceLocation.REFRESH.isActive()) {
+ GitTraceLocation.getTrace().trace(
+ GitTraceLocation.REFRESH.getLocation(),
+ "Triggered refresh for repo: " + repo); //$NON-NLS-1$
+ }
+ try {
+ refreshRepository(event);
+ } catch (OperationCanceledException oe) {
+ return;
+ } catch (CoreException e) {
+ Activator.error(CoreText.Activator_refreshFailed, e);
+ }
+ }
+
+ private void refreshRepository(WorkingTreeModifiedEvent event)
+ throws CoreException {
+ if (event.isEmpty()) {
+ return; // Should actually not occur
+ }
+ File workTree = event.getRepository().getWorkTree().getAbsoluteFile();
+ Map<IPath, IProject> roots = getProjectLocations(workTree);
+ if (roots.isEmpty()) {
+ // No open projects from this repository in the workspace
+ return;
+ }
+ IPath wt = new Path(workTree.getPath());
+ Map<IResource, Boolean> toRefresh = computeResources(
+ event.getModified(), event.getDeleted(), wt, roots);
+ if (toRefresh.isEmpty()) {
+ return;
+ }
+ if (GitTraceLocation.REFRESH.isActive()) {
+ GitTraceLocation.getTrace().trace(
+ GitTraceLocation.REFRESH.getLocation(),
+ "Refreshing repository " + workTree + ' ' //$NON-NLS-1$
+ + toRefresh.size());
+ }
+ for (Map.Entry<IResource, Boolean> entry : toRefresh.entrySet()) {
+ entry.getKey()
+ .refreshLocal(entry.getValue().booleanValue()
+ ? IResource.DEPTH_INFINITE
+ : IResource.DEPTH_ONE, null);
+ }
+ if (GitTraceLocation.REFRESH.isActive()) {
+ GitTraceLocation.getTrace().trace(
+ GitTraceLocation.REFRESH.getLocation(),
+ "Refreshed repository " + workTree + ' ' //$NON-NLS-1$
+ + toRefresh.size());
+ }
+ }
+
+ private Map<IPath, IProject> getProjectLocations(File workTree) {
+ IProject[] projects = RuleUtil.getProjects(workTree);
+ if (projects == null) {
+ return Collections.emptyMap();
+ }
+ Map<IPath, IProject> result = new HashMap<>();
+ for (IProject project : projects) {
+ if (project.isAccessible()) {
+ IPath path = project.getLocation();
+ if (path != null) {
+ IPath projectFilePath = path.append(
+ IProjectDescription.DESCRIPTION_FILE_NAME);
+ if (projectFilePath.toFile().exists()) {
+ result.put(path, project);
+ }
+ }
+ }
+ }
+ return result;
+ }
+
+ private Map<IResource, Boolean> computeResources(
+ Collection<String> modified, Collection<String> deleted,
+ IPath workTree, Map<IPath, IProject> roots) {
+ // Attempt to minimize the refreshes by returning IContainers if
+ // more than one file in a container has changed.
+ if (GitTraceLocation.REFRESH.isActive()) {
+ GitTraceLocation.getTrace().trace(
+ GitTraceLocation.REFRESH.getLocation(),
+ "Calculating refresh for repository " + workTree + ' ' //$NON-NLS-1$
+ + modified.size() + ' ' + deleted.size());
+ }
+ Set<IPath> fullRefreshes = new HashSet<>();
+ Map<IPath, IFile> handled = new HashMap<>();
+ Map<IResource, Boolean> result = new HashMap<>();
+ Stream.concat(modified.stream(), deleted.stream()).forEach(path -> {
+ IPath filePath = "/".equals(path) ? workTree //$NON-NLS-1$
+ : workTree.append(path);
+ IProject project = roots.get(filePath);
+ if (project != null) {
+ // Eclipse knows this as a project. Make sure it gets
+ // refreshed as such. One can refresh a folder via an IFile,
+ // but not an IProject.
+ handled.put(filePath, null);
+ result.put(project, Boolean.FALSE);
+ return;
+ }
+ if (fullRefreshes.stream()
+ .anyMatch(full -> full.isPrefixOf(filePath))
+ || !roots.keySet().stream()
+ .anyMatch(root -> root.isPrefixOf(filePath))) {
+ // Not in workspace or covered by a full container refresh
+ return;
+ }
+ IPath containerPath;
+ boolean isFile;
+ if (path.endsWith("/")) { //$NON-NLS-1$
+ // It's already a directory
+ isFile = false;
+ containerPath = filePath.removeTrailingSeparator();
+ } else {
+ isFile = true;
+ containerPath = filePath.removeLastSegments(1);
+ }
+ if (!handled.containsKey(containerPath)) {
+ if (!isFile && containerPath != null) {
+ IContainer container = getContainerForLocation(
+ containerPath);
+ if (container != null) {
+ IFile file = handled.get(containerPath);
+ handled.put(containerPath, null);
+ if (file != null) {
+ result.remove(file);
+ }
+ result.put(container, Boolean.FALSE);
+ }
+ } else if (isFile) {
+ // First file in this container. Find the deepest
+ // existing container and record its child.
+ String lastPart = filePath.lastSegment();
+ while (containerPath != null
+ && workTree.isPrefixOf(containerPath)) {
+ IContainer container = getContainerForLocation(
+ containerPath);
+ if (container == null) {
+ lastPart = containerPath.lastSegment();
+ containerPath = containerPath
+ .removeLastSegments(1);
+ isFile = false;
+ continue;
+ }
+ if (container.getType() == IResource.ROOT) {
+ // Missing project... ignore it and anything
+ // beneath. The user or our own branch project
+ // tracker will have to properly add/import the
+ // project.
+ containerPath = containerPath.append(lastPart);
+ fullRefreshes.add(containerPath);
+ handled.put(containerPath, null);
+ } else if (isFile) {
+ IFile file = container
+ .getFile(new Path(lastPart));
+ handled.put(containerPath, file);
+ result.put(file, Boolean.FALSE);
+ } else {
+ // New or deleted folder.
+ container = container
+ .getFolder(new Path(lastPart));
+ containerPath = containerPath.append(lastPart);
+ fullRefreshes.add(containerPath);
+ handled.put(containerPath, null);
+ result.put(container, Boolean.TRUE);
+ }
+ break;
+ }
+ }
+ } else {
+ IFile file = handled.get(containerPath);
+ if (file != null) {
+ // Second file in this container: replace file by
+ // its container.
+ handled.put(containerPath, null);
+ result.remove(file);
+ result.put(file.getParent(), Boolean.FALSE);
+ }
+ // Otherwise we already have this container.
+ }
+ });
+
+ if (GitTraceLocation.REFRESH.isActive()) {
+ GitTraceLocation.getTrace().trace(
+ GitTraceLocation.REFRESH.getLocation(),
+ "Calculated refresh for repository " + workTree); //$NON-NLS-1$
+ }
+ return result;
+ }
+
+ private static IContainer getContainerForLocation(@NonNull IPath location) {
+ IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
+ IContainer dir = root.getContainerForLocation(location);
+ if (dir == null) {
+ return null;
+ }
+ if (isValid(dir)) {
+ return dir;
+ }
+ URI uri = URIUtil.toURI(location);
+ IContainer[] containers = root.findContainersForLocationURI(uri);
+ return Arrays.stream(containers).filter(ResourceRefreshHandler::isValid)
+ .findFirst().orElse(null);
+ }
+
+ private static boolean isValid(@NonNull IResource resource) {
+ return resource.isAccessible()
+ && !resource.isLinked(IResource.CHECK_ANCESTORS);
+ }
+
+} \ No newline at end of file
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties
index 9ef9af42d..80f431855 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/coretext.properties
@@ -13,6 +13,7 @@
###############################################################################
Activator_invalidPreferredMergeStrategy=The configuration of the preferred merge strategy is invalid. It refers to a merge strategy called "{0}" which is not registered. The default recursive strategy will be used instead.
+Activator_refreshFailed=Failed to refresh resources after working tree changed
CherryPickOperation_cherryPicking=Running cherry-pick on commit {0}
CommitFileRevision_pathNotIn=Path {1} not in commit {0}.
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/trace/GitTraceLocation.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/trace/GitTraceLocation.java
index f906e50e6..0f98c45cc 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/trace/GitTraceLocation.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/trace/GitTraceLocation.java
@@ -26,7 +26,9 @@ public enum GitTraceLocation implements ITraceLocation {
/** IndexDiffCache */
INDEXDIFFCACHE("/debug/core/indexdiffcache"), //$NON-NLS-1$
/** JSch logging */
- JSCH("/debug/core/jsch"); //$NON-NLS-1$
+ JSCH("/debug/core/jsch"), //$NON-NLS-1$
+ /** refreshing resources */
+ REFRESH("/debug/core/refresh"); //$NON-NLS-1$
/**
* Initialize the locations
diff --git a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java
index 3c278cce0..7743702a2 100644
--- a/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java
+++ b/org.eclipse.egit.core/src/org/eclipse/egit/core/internal/util/ProjectUtil.java
@@ -150,12 +150,13 @@ public class ProjectUtil {
.append(IProjectDescription.DESCRIPTION_FILE_NAME)
.toOSString();
File projectFile = new File(projectFilePath);
- if (projectFile.exists())
- p.refreshLocal(IResource.DEPTH_INFINITE, progress.newChild(1));
- else if (delete)
+ if (projectFile.exists()) {
+ // refresh is handled by handler for WorkingTreeModifiedEvent
+ } else if (delete) {
p.delete(false, true, progress.newChild(1));
- else
+ } else {
closeMissingProject(p, projectFile, progress.newChild(1));
+ }
}
}
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java
index 92f720629..596eae858 100755
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java
@@ -16,45 +16,24 @@
*******************************************************************************/
package org.eclipse.egit.ui;
-import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
-import java.util.Collections;
import java.util.Dictionary;
-import java.util.HashMap;
-import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
-import java.util.LinkedHashMap;
import java.util.List;
-import java.util.Map;
-import java.util.Set;
-import java.util.stream.Stream;
-import org.eclipse.core.resources.IContainer;
-import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IProject;
-import org.eclipse.core.resources.IProjectDescription;
-import org.eclipse.core.resources.IResource;
-import org.eclipse.core.resources.IWorkspace;
-import org.eclipse.core.resources.IWorkspaceRunnable;
-import org.eclipse.core.resources.ResourcesPlugin;
-import org.eclipse.core.runtime.CoreException;
-import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
-import org.eclipse.core.runtime.OperationCanceledException;
-import org.eclipse.core.runtime.Path;
import org.eclipse.core.runtime.Platform;
import org.eclipse.core.runtime.Status;
-import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.egit.core.JobFamilies;
import org.eclipse.egit.core.RepositoryCache;
import org.eclipse.egit.core.RepositoryUtil;
import org.eclipse.egit.core.internal.job.RuleUtil;
-import org.eclipse.egit.core.internal.util.ResourceUtil;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.ui.internal.ConfigurationChecker;
import org.eclipse.egit.ui.internal.KnownHosts;
@@ -76,7 +55,6 @@ import org.eclipse.jface.util.PropertyChangeEvent;
import org.eclipse.jgit.events.IndexChangedListener;
import org.eclipse.jgit.events.ListenerHandle;
import org.eclipse.jgit.events.WorkingTreeModifiedEvent;
-import org.eclipse.jgit.events.WorkingTreeModifiedListener;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.transport.CredentialsProvider;
import org.eclipse.jgit.treewalk.FileTreeIterator;
@@ -327,8 +305,6 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
private ResourceManager resourceManager;
private RepositoryChangeScanner rcs;
- private ResourceRefreshJob refreshJob;
- private ListenerHandle refreshHandle;
private DebugOptions debugOptions;
private volatile boolean uiIsActive;
@@ -358,7 +334,6 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
RepositoryStateCache.INSTANCE.initialize();
setupRepoChangeScanner();
- setupRepoIndexRefresh();
setupFocusHandling();
setupCredentialsProvider();
ConfigurationChecker.checkConfiguration();
@@ -460,7 +435,6 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
updateUiState();
// 500: give the UI task a chance to update the active state
rcs.schedule(500);
- refreshJob.triggerRefresh();
}
};
Job job = new Job(UIText.Activator_setupFocusListener) {
@@ -516,12 +490,6 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
return debugOptions;
}
- private void setupRepoIndexRefresh() {
- refreshJob = new ResourceRefreshJob();
- refreshHandle = Repository.getGlobalListenerList()
- .addWorkingTreeModifiedListener(refreshJob);
- }
-
/**
* Register for changes made to Team properties.
*
@@ -556,365 +524,6 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
}
/**
- * Refreshes parts of the workspace changed by JGit operations. This will
- * not refresh any git-ignored resources since those are not reported in the
- * {@link WorkingTreeModifiedEvent}.
- */
- static class ResourceRefreshJob extends Job implements
- WorkingTreeModifiedListener {
-
- ResourceRefreshJob() {
- super(UIText.Activator_refreshJobName);
- setUser(false);
- setSystem(true);
- }
-
- /**
- * Internal helper class to record batched accumulated results from
- * several {@link WorkingTreeModifiedEvent}s.
- */
- private static class WorkingTreeChanges {
-
- private final File workTree;
-
- private final Set<String> modified;
-
- private final Set<String> deleted;
-
- public WorkingTreeChanges(WorkingTreeModifiedEvent event) {
- workTree = event.getRepository().getWorkTree()
- .getAbsoluteFile();
- modified = new HashSet<>(event.getModified());
- deleted = new HashSet<>(event.getDeleted());
- }
-
- public File getWorkTree() {
- return workTree;
- }
-
- public Set<String> getModified() {
- return modified;
- }
-
- public Set<String> getDeleted() {
- return deleted;
- }
-
- public boolean isEmpty() {
- return modified.isEmpty() && deleted.isEmpty();
- }
-
- public WorkingTreeChanges merge(WorkingTreeModifiedEvent event) {
- modified.removeAll(event.getDeleted());
- deleted.removeAll(event.getModified());
- modified.addAll(event.getModified());
- deleted.addAll(event.getDeleted());
- return this;
- }
- }
-
- private Map<File, WorkingTreeChanges> repositoriesChanged = new LinkedHashMap<>();
-
- @Override
- public IStatus run(IProgressMonitor monitor) {
- try {
- List<WorkingTreeChanges> changes;
- synchronized (repositoriesChanged) {
- if (repositoriesChanged.isEmpty()) {
- return Status.OK_STATUS;
- }
- changes = new ArrayList<>(repositoriesChanged.values());
- repositoriesChanged.clear();
- }
-
- SubMonitor progress = SubMonitor.convert(monitor,
- changes.size());
- try {
- for (WorkingTreeChanges change : changes) {
- refreshRepository(change, progress.newChild(1));
- }
- } catch (OperationCanceledException oe) {
- return Status.CANCEL_STATUS;
- } catch (CoreException e) {
- handleError(UIText.Activator_refreshFailed, e, false);
- return new Status(IStatus.ERROR, getPluginId(),
- e.getMessage());
- }
-
- if (!monitor.isCanceled()) {
- // re-schedule if we got some changes in the meantime
- synchronized (repositoriesChanged) {
- if (!repositoriesChanged.isEmpty()) {
- schedule(100);
- }
- }
- }
- } finally {
- monitor.done();
- }
- return Status.OK_STATUS;
- }
-
- private void refreshRepository(WorkingTreeChanges changes,
- IProgressMonitor monitor) throws CoreException {
- if (monitor.isCanceled()) {
- throw new OperationCanceledException();
- }
- if (changes.isEmpty()) {
- return; // Should actually not occur
- }
- Map<IPath, IProject> roots = getProjectLocations(
- changes.getWorkTree());
- if (roots.isEmpty()) {
- // No open projects from this repository in the workspace
- return;
- }
- SubMonitor progress = SubMonitor.convert(monitor, 2);
- IPath workTree = new Path(changes.getWorkTree().getPath());
- Map<IResource, Boolean> toRefresh = computeResources(
- changes.getModified(), changes.getDeleted(), workTree,
- roots, progress.newChild(1));
- if (toRefresh.isEmpty()) {
- return;
- }
- IWorkspace workspace = ResourcesPlugin.getWorkspace();
- IWorkspaceRunnable operation = innerMonitor -> {
- SubMonitor innerProgress = SubMonitor.convert(innerMonitor,
- toRefresh.size());
- if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
- GitTraceLocation.getTrace()
- .trace(GitTraceLocation.REPOSITORYCHANGESCANNER
- .getLocation(),
- "Refreshing repository " + workTree + ' ' //$NON-NLS-1$
- + toRefresh.size());
- }
- for (Map.Entry<IResource, Boolean> entry : toRefresh
- .entrySet()) {
- entry.getKey().refreshLocal(entry.getValue().booleanValue()
- ? IResource.DEPTH_INFINITE : IResource.DEPTH_ONE,
- innerProgress.newChild(1));
- }
- if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
- GitTraceLocation.getTrace()
- .trace(GitTraceLocation.REPOSITORYCHANGESCANNER
- .getLocation(),
- "Refreshed repository " + workTree + ' ' //$NON-NLS-1$
- + toRefresh.size());
- }
- };
- // No scheduling rule needed; IResource.refreshLocal() gets its own
- // rule. This workspace operation serves only to batch resource
- // update notifications.
- workspace.run(operation, null, IWorkspace.AVOID_UPDATE,
- progress.newChild(1));
- }
-
- private Map<IPath, IProject> getProjectLocations(File workTree) {
- IProject[] projects = RuleUtil.getProjects(workTree);
- if (projects == null) {
- return Collections.emptyMap();
- }
- Map<IPath, IProject> result = new HashMap<>();
- for (IProject project : projects) {
- if (project.isAccessible()) {
- IPath path = project.getLocation();
- if (path != null) {
- IPath projectFilePath = path.append(
- IProjectDescription.DESCRIPTION_FILE_NAME);
- if (projectFilePath.toFile().exists()) {
- result.put(path, project);
- }
- }
- }
- }
- return result;
- }
-
- private Map<IResource, Boolean> computeResources(
- Set<String> modified, Set<String> deleted, IPath workTree,
- Map<IPath, IProject> roots, IProgressMonitor monitor) {
- // Attempt to minimize the refreshes by returning IContainers if
- // more than one file in a container has changed.
- if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
- GitTraceLocation.getTrace().trace(
- GitTraceLocation.REPOSITORYCHANGESCANNER.getLocation(),
- "Calculating refresh for repository " + workTree + ' ' //$NON-NLS-1$
- + modified.size() + ' ' + deleted.size());
- }
- SubMonitor progress = SubMonitor.convert(monitor,
- modified.size() + deleted.size());
- Set<IPath> fullRefreshes = new HashSet<>();
- Map<IPath, IFile> handled = new HashMap<>();
- Map<IResource, Boolean> result = new HashMap<>();
- Stream.concat(modified.stream(), deleted.stream()).forEach(path -> {
- if (progress.isCanceled()) {
- throw new OperationCanceledException();
- }
- IPath filePath = "/".equals(path) ? workTree //$NON-NLS-1$
- : workTree.append(path);
- IProject project = roots.get(filePath);
- if (project != null) {
- // Eclipse knows this as a project. Make sure it gets
- // refreshed as such. One can refresh a folder via an IFile,
- // but not an IProject.
- handled.put(filePath, null);
- result.put(project, Boolean.FALSE);
- progress.worked(1);
- return;
- }
- if (fullRefreshes.stream()
- .anyMatch(full -> full.isPrefixOf(filePath))
- || !roots.keySet().stream()
- .anyMatch(root -> root.isPrefixOf(filePath))) {
- // Not in workspace or covered by a full container refresh
- progress.worked(1);
- return;
- }
- IPath containerPath;
- boolean isFile;
- if (path.endsWith("/")) { //$NON-NLS-1$
- // It's already a directory
- isFile = false;
- containerPath = filePath.removeTrailingSeparator();
- } else {
- isFile = true;
- containerPath = filePath.removeLastSegments(1);
- }
- if (!handled.containsKey(containerPath)) {
- if (!isFile && containerPath != null) {
- IContainer container = ResourceUtil
- .getContainerForLocation(containerPath, false);
- if (container != null) {
- IFile file = handled.get(containerPath);
- handled.put(containerPath, null);
- if (file != null) {
- result.remove(file);
- }
- result.put(container, Boolean.FALSE);
- }
- } else if (isFile) {
- // First file in this container. Find the deepest
- // existing container and record its non-existing child.
- String lastPart = filePath.lastSegment();
- while (containerPath != null
- && workTree.isPrefixOf(containerPath)) {
- IContainer container = ResourceUtil
- .getContainerForLocation(containerPath,
- false);
- if (container == null) {
- lastPart = containerPath.lastSegment();
- containerPath = containerPath
- .removeLastSegments(1);
- isFile = false;
- continue;
- }
- if (container.getType() == IResource.ROOT) {
- // Missing project... ignore it and anything
- // beneath. The user or our own branch project
- // tracker will have to properly add/import the
- // project.
- containerPath = containerPath.append(lastPart);
- fullRefreshes.add(containerPath);
- handled.put(containerPath, null);
- } else if (isFile) {
- IFile file = container
- .getFile(new Path(lastPart));
- handled.put(containerPath, file);
- result.put(file, Boolean.FALSE);
- } else {
- // New or deleted folder.
- container = container
- .getFolder(new Path(lastPart));
- containerPath = containerPath.append(lastPart);
- fullRefreshes.add(containerPath);
- handled.put(containerPath, null);
- result.put(container, Boolean.TRUE);
- }
- break;
- }
- }
- } else {
- IFile file = handled.get(containerPath);
- if (file != null) {
- // Second file in this container: replace file by
- // its container.
- handled.put(containerPath, null);
- result.remove(file);
- result.put(file.getParent(), Boolean.FALSE);
- }
- // Otherwise we already have this container.
- }
- progress.worked(1);
- });
-
- if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
- GitTraceLocation.getTrace().trace(
- GitTraceLocation.REPOSITORYCHANGESCANNER.getLocation(),
- "Calculated refresh for repository " + workTree); //$NON-NLS-1$
- }
- return result;
- }
-
- @Override
- public void onWorkingTreeModified(WorkingTreeModifiedEvent event) {
- if (Activator.getDefault().getPreferenceStore()
- .getBoolean(UIPreferences.REFRESH_ON_INDEX_CHANGE)) {
- mayTriggerRefresh(event);
- }
- }
-
- /**
- * Record which projects have changes. Initiate a resource refresh job
- * if the user settings allow it.
- *
- * @param event
- * The {@link WorkingTreeModifiedEvent} that triggered this
- * refresh
- */
- private void mayTriggerRefresh(WorkingTreeModifiedEvent event) {
- if (event.isEmpty()) {
- return;
- }
- Repository repo = event.getRepository();
- if (repo == null || repo.isBare()) {
- return; // Should never occur
- }
- File gitDir = repo.getDirectory();
- synchronized (repositoriesChanged) {
- WorkingTreeChanges changes = repositoriesChanged.get(gitDir);
- if (changes == null) {
- repositoriesChanged.put(gitDir,
- new WorkingTreeChanges(event));
- } else {
- changes.merge(event);
- if (changes.isEmpty()) {
- // Actually, this cannot happen.
- repositoriesChanged.remove(gitDir);
- }
- }
- }
- if (!Activator.getDefault().getPreferenceStore()
- .getBoolean(UIPreferences.REFRESH_ONLY_WHEN_ACTIVE)
- || isActive()) {
- triggerRefresh();
- }
- }
-
- /**
- * Figure which projects belong to a repository, add them to a set of
- * project to refresh and schedule the refresh as a job.
- */
- void triggerRefresh() {
- if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
- GitTraceLocation.getTrace().trace(
- GitTraceLocation.REPOSITORYCHANGESCANNER.getLocation(),
- "Triggered refresh"); //$NON-NLS-1$
- }
- schedule();
- }
- }
-
- /**
* A Job that looks at the repository meta data and triggers a refresh of
* the resources in the affected projects.
*/
@@ -1121,10 +730,6 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
@Override
public void stop(final BundleContext context) throws Exception {
RepositoryStateCache.INSTANCE.dispose();
- if (refreshHandle != null) {
- refreshHandle.remove();
- refreshHandle = null;
- }
if (focusListener != null) {
if (PlatformUI.isWorkbenchRunning()) {
@@ -1142,21 +747,13 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
getPreferenceStore().removePropertyChangeListener(rcs);
rcs.setReschedule(false);
rcs.cancel();
- if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
- GitTraceLocation.getTrace().trace(
- GitTraceLocation.REPOSITORYCHANGESCANNER.getLocation(),
- "Trying to cancel " + refreshJob.getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
- }
- refreshJob.cancel();
-
rcs.join();
- refreshJob.join();
-
if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
GitTraceLocation.getTrace().trace(
GitTraceLocation.REPOSITORYCHANGESCANNER.getLocation(),
"Jobs terminated"); //$NON-NLS-1$
}
+
if (resourceManager != null) {
resourceManager.dispose();
resourceManager = null;
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
index a37cc913c..982960196 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/UIText.java
@@ -76,9 +76,6 @@ public class UIText extends NLS {
public static String Activator_refreshingProjects;
/** */
- public static String Activator_refreshJobName;
-
- /** */
public static String Activator_repoScanJobName;
/** */
diff --git a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
index e06b885aa..a0cfe7fce 100644
--- a/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
+++ b/org.eclipse.egit.ui/src/org/eclipse/egit/ui/internal/uitext.properties
@@ -34,7 +34,6 @@ AbstractRebaseCommandHandler_cleanupDialog_text=You have uncommitted changes. Ei
AbstractRebaseCommandHandler_cleanupDialog_title=Cannot Rebase Repository ''{0}''
AbstractReflogCommandHandler_NoInput=Could not get the current input from the Reflog View
Activator_refreshingProjects=Refreshing Git managed projects
-Activator_refreshJobName=Git Repository Refresh
Activator_repoScanJobName=Git Repository Change Scanner
Activator_scanError=An error occurred while scanning for changes. Scanning aborted
Activator_scanningRepositories=Scanning Git repositories for changes

Back to the top