Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorThomas Wolf2019-10-11 15:34:40 -0400
committerThomas Wolf2019-10-12 07:43:56 -0400
commit4eb4faf6079dde8e19f38fa9e677147c5145470e (patch)
tree6001bc388b75a87cee87fc10476e2ee7e2ab3928 /org.eclipse.egit.ui
parent110b54a5ffd44b1151328abbac4cdd8ccbd3ee50 (diff)
downloadegit-4eb4faf6079dde8e19f38fa9e677147c5145470e.tar.gz
egit-4eb4faf6079dde8e19f38fa9e677147c5145470e.tar.xz
egit-4eb4faf6079dde8e19f38fa9e677147c5145470e.zip
[regression] Fix resource refreshes after git operations
The asynchronous RepositoryChangeScanner must _not_ trigger synchronous refreshes. It uses a non-resource scheduling rule and thus must not perform any workspace operations. Re-introduce the ResourceRefreshJob but let it use the new ResourceRefreshHandler. Re-add merging of events, and re-introduce IProgressMonitor so that the UI bundle can cancel its jobs when being stopped. Bug: 551289 Change-Id: I961ff20355baf7e3d88d2ca222b937f7ffd43fb6 Signed-off-by: Thomas Wolf <thomas.wolf@paranor.ch>
Diffstat (limited to 'org.eclipse.egit.ui')
-rwxr-xr-xorg.eclipse.egit.ui/src/org/eclipse/egit/ui/Activator.java186
-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
3 files changed, 184 insertions, 6 deletions
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 596eae858..a19ff49e7 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,23 +16,33 @@
*******************************************************************************/
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.Collection;
import java.util.Dictionary;
+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 org.eclipse.core.resources.IProject;
+import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.OperationCanceledException;
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.ResourceRefreshHandler;
import org.eclipse.egit.core.internal.job.RuleUtil;
import org.eclipse.egit.core.project.RepositoryMapping;
import org.eclipse.egit.ui.internal.ConfigurationChecker;
@@ -305,6 +315,8 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
private ResourceManager resourceManager;
private RepositoryChangeScanner rcs;
+
+ private ResourceRefreshJob refreshJob;
private DebugOptions debugOptions;
private volatile boolean uiIsActive;
@@ -535,8 +547,12 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
private int interval;
+ private final ResourceRefreshJob refresher;
+
private final RepositoryCache repositoryCache;
+ private Collection<WorkingTreeModifiedEvent> events;
+
private final IndexChangedListener listener = event -> {
if (event.isInternal()) {
return;
@@ -601,12 +617,15 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
if (directories.isEmpty()) {
return;
}
- repository
- .fireEvent(new WorkingTreeModifiedEvent(directories, null));
+ WorkingTreeModifiedEvent evt = new WorkingTreeModifiedEvent(
+ directories, null);
+ evt.setRepository(repository);
+ events.add(evt);
};
- RepositoryChangeScanner() {
+ public RepositoryChangeScanner(ResourceRefreshJob refresher) {
super(UIText.Activator_repoScanJobName);
+ this.refresher = refresher;
setRule(new RepositoryCacheRule());
setSystem(true);
setUser(false);
@@ -625,7 +644,7 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
return doReschedule;
}
- void setReschedule(boolean reschedule){
+ public void setReschedule(boolean reschedule) {
doReschedule = reschedule;
}
@@ -649,6 +668,7 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
monitor.beginTask(UIText.Activator_scanningRepositories,
repos.length);
try {
+ events = new ArrayList<>();
for (Repository repo : repos) {
if (monitor.isCanceled()) {
break;
@@ -676,12 +696,16 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
}
monitor.worked(1);
}
+ if (!monitor.isCanceled()) {
+ refresher.trigger(events);
+ }
+ events.clear();
} catch (IOException e) {
if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
GitTraceLocation.getTrace().trace(
GitTraceLocation.REPOSITORYCHANGESCANNER
.getLocation(),
- "Stopped rescheduling " + getName() + "job"); //$NON-NLS-1$ //$NON-NLS-2$
+ "Stopped rescheduling " + getName() + " job"); //$NON-NLS-1$ //$NON-NLS-2$
}
return createErrorStatus(UIText.Activator_scanError, e);
} finally {
@@ -722,8 +746,155 @@ 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}.
+ */
+ private static class ResourceRefreshJob extends Job {
+
+ public 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) {
+ if (progress.isCanceled()) {
+ return Status.CANCEL_STATUS;
+ }
+ ResourceRefreshHandler handler = new ResourceRefreshHandler();
+ handler.refreshRepository(new WorkingTreeModifiedEvent(
+ change.getModified(), change.getDeleted()),
+ change.getWorkTree(), 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;
+ }
+
+ /**
+ * Record which projects have changes. Initiate a resource refresh job
+ * if the user settings allow it.
+ *
+ * @param events
+ * The {@link WorkingTreeModifiedEvent}s that triggered this
+ * refresh
+ */
+ public void trigger(Collection<WorkingTreeModifiedEvent> events) {
+ boolean haveChanges = false;
+ for (WorkingTreeModifiedEvent event : events) {
+ if (event.isEmpty()) {
+ continue;
+ }
+ Repository repo = event.getRepository();
+ if (repo == null || repo.isBare()) {
+ continue; // 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);
+ }
+ }
+ }
+ haveChanges = true;
+ }
+ if (haveChanges) {
+ schedule();
+ }
+ }
+ }
+
private void setupRepoChangeScanner() {
- rcs = new RepositoryChangeScanner();
+ refreshJob = new ResourceRefreshJob();
+ rcs = new RepositoryChangeScanner(refreshJob);
getPreferenceStore().addPropertyChangeListener(rcs);
}
@@ -747,7 +918,10 @@ public class Activator extends AbstractUIPlugin implements DebugOptionsListener
getPreferenceStore().removePropertyChangeListener(rcs);
rcs.setReschedule(false);
rcs.cancel();
+ refreshJob.cancel();
+
rcs.join();
+ refreshJob.join();
if (GitTraceLocation.REPOSITORYCHANGESCANNER.isActive()) {
GitTraceLocation.getTrace().trace(
GitTraceLocation.REPOSITORYCHANGESCANNER.getLocation(),
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 982960196..a37cc913c 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,6 +76,9 @@ 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 a0cfe7fce..e06b885aa 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,6 +34,7 @@ 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