diff options
Diffstat (limited to 'org.eclipse.egit.ui')
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 596eae8586..a19ff49e72 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 9829601965..a37cc913ca 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 a0cfe7fceb..e06b885aa5 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 |