Hide $GIT_DIR and friends from JGit
[egit/eclipse.git] / org.eclipse.egit.core / src / org / eclipse / egit / core / Activator.java
blob53e3d6647f1ebcb3545a0d14310150f488b5417d
1 /*******************************************************************************
2 * Copyright (C) 2008, 2015 Shawn O. Pearce <spearce@spearce.org> and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License 2.0
5 * which accompanies this distribution, and is available at
6 * https://www.eclipse.org/legal/epl-2.0/
8 * SPDX-License-Identifier: EPL-2.0
9 *******************************************************************************/
10 package org.eclipse.egit.core;
12 import java.io.File;
13 import java.io.IOException;
14 import java.lang.reflect.InvocationTargetException;
15 import java.net.Authenticator;
16 import java.net.ProxySelector;
17 import java.text.MessageFormat;
18 import java.util.Collection;
19 import java.util.Collections;
20 import java.util.Dictionary;
21 import java.util.HashMap;
22 import java.util.Hashtable;
23 import java.util.LinkedHashMap;
24 import java.util.LinkedHashSet;
25 import java.util.List;
26 import java.util.Map;
27 import java.util.Set;
29 import org.eclipse.core.net.proxy.IProxyService;
30 import org.eclipse.core.resources.IProject;
31 import org.eclipse.core.resources.IResource;
32 import org.eclipse.core.resources.IResourceChangeEvent;
33 import org.eclipse.core.resources.IResourceChangeListener;
34 import org.eclipse.core.resources.IResourceDelta;
35 import org.eclipse.core.resources.IResourceDeltaVisitor;
36 import org.eclipse.core.resources.ResourcesPlugin;
37 import org.eclipse.core.runtime.CoreException;
38 import org.eclipse.core.runtime.IConfigurationElement;
39 import org.eclipse.core.runtime.IExtension;
40 import org.eclipse.core.runtime.IExtensionPoint;
41 import org.eclipse.core.runtime.IExtensionRegistry;
42 import org.eclipse.core.runtime.IPath;
43 import org.eclipse.core.runtime.IProgressMonitor;
44 import org.eclipse.core.runtime.IRegistryEventListener;
45 import org.eclipse.core.runtime.IStatus;
46 import org.eclipse.core.runtime.NullProgressMonitor;
47 import org.eclipse.core.runtime.OperationCanceledException;
48 import org.eclipse.core.runtime.Path;
49 import org.eclipse.core.runtime.Platform;
50 import org.eclipse.core.runtime.Plugin;
51 import org.eclipse.core.runtime.Status;
52 import org.eclipse.core.runtime.jobs.Job;
53 import org.eclipse.core.runtime.preferences.DefaultScope;
54 import org.eclipse.core.runtime.preferences.IEclipsePreferences;
55 import org.eclipse.core.runtime.preferences.InstanceScope;
56 import org.eclipse.egit.core.internal.CoreText;
57 import org.eclipse.egit.core.internal.ReportingTypedConfigGetter;
58 import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache;
59 import org.eclipse.egit.core.internal.job.JobUtil;
60 import org.eclipse.egit.core.internal.trace.GitTraceLocation;
61 import org.eclipse.egit.core.internal.util.ResourceUtil;
62 import org.eclipse.egit.core.op.ConnectProviderOperation;
63 import org.eclipse.egit.core.op.IgnoreOperation;
64 import org.eclipse.egit.core.project.GitProjectData;
65 import org.eclipse.egit.core.project.RepositoryFinder;
66 import org.eclipse.egit.core.project.RepositoryMapping;
67 import org.eclipse.egit.core.securestorage.EGitSecureStore;
68 import org.eclipse.equinox.security.storage.SecurePreferencesFactory;
69 import org.eclipse.jgit.lib.Config;
70 import org.eclipse.jgit.lib.Constants;
71 import org.eclipse.jgit.merge.MergeStrategy;
72 import org.eclipse.jgit.storage.file.FileBasedConfig;
73 import org.eclipse.jgit.transport.SshSessionFactory;
74 import org.eclipse.jgit.util.FS;
75 import org.eclipse.jgit.util.SystemReader;
76 import org.eclipse.jsch.core.IJSchService;
77 import org.eclipse.osgi.service.debug.DebugOptions;
78 import org.eclipse.osgi.service.debug.DebugOptionsListener;
79 import org.eclipse.osgi.util.NLS;
80 import org.eclipse.team.core.RepositoryProvider;
81 import org.osgi.framework.BundleContext;
82 import org.osgi.framework.ServiceReference;
84 /**
85 * The plugin class for the org.eclipse.egit.core plugin. This
86 * is a singleton class.
88 public class Activator extends Plugin implements DebugOptionsListener {
89 private static Activator plugin;
90 private static String pluginId;
91 private RepositoryCache repositoryCache;
92 private IndexDiffCache indexDiffCache;
93 private RepositoryUtil repositoryUtil;
94 private EGitSecureStore secureStore;
95 private AutoShareProjects shareGitProjectsJob;
96 private IResourceChangeListener preDeleteProjectListener;
97 private IgnoreDerivedResources ignoreDerivedResourcesListener;
98 private MergeStrategyRegistryListener mergeStrategyRegistryListener;
101 * @return the singleton {@link Activator}
103 public static Activator getDefault() {
104 return plugin;
108 * @return the name of this plugin
110 public static String getPluginId() {
111 return pluginId;
115 * Utility to create an error status for this plug-in.
117 * @param message User comprehensible message
118 * @param thr cause
119 * @return an initialized error status
121 public static IStatus error(final String message, final Throwable thr) {
122 return new Status(IStatus.ERROR, getPluginId(), 0, message, thr);
126 * Utility method to log errors in the Egit plugin.
127 * @param message User comprehensible message
128 * @param thr The exception through which we noticed the error
130 public static void logError(final String message, final Throwable thr) {
131 getDefault().getLog().log(error(message, thr));
135 * Log an info message for this plug-in
137 * @param message
139 public static void logInfo(final String message) {
140 getDefault().getLog().log(
141 new Status(IStatus.INFO, getPluginId(), 0, message, null));
145 * Utility to create a warning status for this plug-in.
147 * @param message
148 * User comprehensible message
149 * @param thr
150 * cause
151 * @return an initialized warning status
153 public static IStatus warning(final String message, final Throwable thr) {
154 return new Status(IStatus.WARNING, getPluginId(), 0, message, thr);
158 * Utility method to log warnings for this plug-in.
160 * @param message
161 * User comprehensible message
162 * @param thr
163 * The exception through which we noticed the warning
165 public static void logWarning(final String message, final Throwable thr) {
166 getDefault().getLog().log(warning(message, thr));
170 * Construct the {@link Activator} singleton instance
172 public Activator() {
173 Activator.setActivator(this);
176 private static void setActivator(Activator a) {
177 plugin = a;
180 @Override
181 public void start(final BundleContext context) throws Exception {
183 super.start(context);
185 SystemReader.setInstance(
186 new EclipseSystemReader(SystemReader.getInstance()));
187 pluginId = context.getBundle().getSymbolicName();
189 Config.setTypedConfigGetter(new ReportingTypedConfigGetter());
190 // we want to be notified about debug options changes
191 Dictionary<String, String> props = new Hashtable<>(4);
192 props.put(DebugOptions.LISTENER_SYMBOLICNAME, pluginId);
193 context.registerService(DebugOptionsListener.class.getName(), this,
194 props);
196 setupSSH(context);
197 setupProxy(context);
199 repositoryCache = new RepositoryCache();
200 indexDiffCache = new IndexDiffCache();
201 try {
202 GitProjectData.reconfigureWindowCache();
203 } catch (RuntimeException e) {
204 logError(CoreText.Activator_ReconfigureWindowCacheError, e);
206 GitProjectData.attachToWorkspace();
208 repositoryUtil = new RepositoryUtil();
210 secureStore = new EGitSecureStore(SecurePreferencesFactory.getDefault());
212 registerAutoShareProjects();
213 registerAutoIgnoreDerivedResources();
214 registerPreDeleteResourceChangeListener();
215 registerMergeStrategyRegistryListener();
216 registerBuiltinLFS();
219 @SuppressWarnings("unchecked")
220 private void setupSSH(final BundleContext context) {
221 final ServiceReference ssh;
223 ssh = context.getServiceReference(IJSchService.class.getName());
224 if (ssh != null) {
225 SshSessionFactory.setInstance(new EclipseSshSessionFactory(
226 (IJSchService) context.getService(ssh)));
230 @SuppressWarnings("unchecked")
231 private void setupProxy(final BundleContext context) {
232 final ServiceReference proxy;
234 proxy = context.getServiceReference(IProxyService.class.getName());
235 if (proxy != null) {
236 ProxySelector.setDefault(new EclipseProxySelector(
237 (IProxyService) context.getService(proxy)));
238 Authenticator.setDefault(new EclipseAuthenticator(
239 (IProxyService) context.getService(proxy)));
243 private void registerPreDeleteResourceChangeListener() {
244 if (preDeleteProjectListener == null) {
245 preDeleteProjectListener = new IResourceChangeListener() {
247 @Override
248 public void resourceChanged(IResourceChangeEvent event) {
249 IResource resource = event.getResource();
250 if (resource instanceof IProject) {
251 IProject project = (IProject) resource;
252 if (project.isAccessible()) {
253 if (ResourceUtil.isSharedWithGit(project)) {
254 IResource dotGit = project
255 .findMember(Constants.DOT_GIT);
256 if (dotGit != null && dotGit
257 .getType() == IResource.FOLDER) {
258 GitProjectData.reconfigureWindowCache();
261 } else {
262 // bug 419706: project is closed - use java.io API
263 IPath locationPath = project.getLocation();
264 if (locationPath != null) {
265 File locationDir = locationPath.toFile();
266 File dotGit = new File(locationDir,
267 Constants.DOT_GIT);
268 if (dotGit.exists() && dotGit.isDirectory()) {
269 GitProjectData.reconfigureWindowCache();
276 ResourcesPlugin.getWorkspace().addResourceChangeListener(preDeleteProjectListener, IResourceChangeEvent.PRE_DELETE);
280 private void registerBuiltinLFS() {
281 if (Platform.getBundle("org.eclipse.jgit.lfs") != null) { //$NON-NLS-1$
282 Class<?> lfs;
283 try {
284 lfs = Class.forName("org.eclipse.jgit.lfs.BuiltinLFS"); //$NON-NLS-1$
285 if (lfs != null) {
286 lfs.getMethod("register").invoke(null); //$NON-NLS-1$
288 } catch (ClassNotFoundException | IllegalAccessException
289 | IllegalArgumentException | InvocationTargetException
290 | NoSuchMethodException | SecurityException e1) {
291 logWarning(CoreText.Activator_noBuiltinLfsSupportDetected, e1);
296 @Override
297 public void optionsChanged(DebugOptions options) {
298 // initialize the trace stuff
299 GitTraceLocation.initializeFromOptions(options, isDebugging());
303 * Provides the 3-way merge strategy to use according to the user's
304 * preferences. The preferred merge strategy is JGit's default merge
305 * strategy unless the user has explicitly chosen a different strategy among
306 * the registered strategies.
308 * @return The MergeStrategy to use, can be {@code null}, in which case the
309 * default merge strategy should be used as defined by JGit.
310 * @since 4.1
312 public MergeStrategy getPreferredMergeStrategy() {
313 // Get preferences set by user in the UI
314 final IEclipsePreferences prefs = InstanceScope.INSTANCE
315 .getNode(Activator.getPluginId());
316 String preferredMergeStrategyKey = prefs.get(
317 GitCorePreferences.core_preferredMergeStrategy, null);
319 // Get default preferences, wherever they are defined
320 if (preferredMergeStrategyKey == null
321 || preferredMergeStrategyKey.isEmpty()) {
322 final IEclipsePreferences defaultPrefs = DefaultScope.INSTANCE
323 .getNode(Activator.getPluginId());
324 preferredMergeStrategyKey = defaultPrefs.get(
325 GitCorePreferences.core_preferredMergeStrategy, null);
327 if (preferredMergeStrategyKey != null
328 && !preferredMergeStrategyKey.isEmpty()
329 && !GitCorePreferences.core_preferredMergeStrategy_Default
330 .equals(preferredMergeStrategyKey)) {
331 MergeStrategy result = MergeStrategy.get(preferredMergeStrategyKey);
332 if (result != null) {
333 return result;
335 logError(NLS.bind(CoreText.Activator_invalidPreferredMergeStrategy,
336 preferredMergeStrategyKey), null);
338 return null;
342 * @return Provides a read-only view of the registered MergeStrategies
343 * available.
344 * @since 4.1
346 public Collection<MergeStrategyDescriptor> getRegisteredMergeStrategies() {
347 if (mergeStrategyRegistryListener == null) {
348 return Collections.emptyList();
350 return mergeStrategyRegistryListener.getStrategies();
353 private void registerMergeStrategyRegistryListener() {
354 mergeStrategyRegistryListener = new MergeStrategyRegistryListener(
355 Platform.getExtensionRegistry());
356 Platform.getExtensionRegistry().addListener(
357 mergeStrategyRegistryListener,
358 "org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$
362 * @return cache for Repository objects
364 public RepositoryCache getRepositoryCache() {
365 return repositoryCache;
369 * @return cache for index diffs
371 public IndexDiffCache getIndexDiffCache() {
372 return indexDiffCache;
376 * @return the {@link RepositoryUtil} instance
378 public RepositoryUtil getRepositoryUtil() {
379 return repositoryUtil;
383 * @return the secure store
385 public EGitSecureStore getSecureStore() {
386 return secureStore;
389 @Override
390 public void stop(final BundleContext context) throws Exception {
391 if (mergeStrategyRegistryListener != null) {
392 Platform.getExtensionRegistry()
393 .removeListener(mergeStrategyRegistryListener);
394 mergeStrategyRegistryListener = null;
396 if (preDeleteProjectListener != null) {
397 ResourcesPlugin.getWorkspace().removeResourceChangeListener(preDeleteProjectListener);
398 preDeleteProjectListener = null;
400 if (ignoreDerivedResourcesListener != null) {
401 ResourcesPlugin.getWorkspace().removeResourceChangeListener(
402 ignoreDerivedResourcesListener);
403 ignoreDerivedResourcesListener.stop();
404 ignoreDerivedResourcesListener = null;
406 if (shareGitProjectsJob != null) {
407 ResourcesPlugin.getWorkspace().removeResourceChangeListener(
408 shareGitProjectsJob);
409 shareGitProjectsJob.stop();
410 shareGitProjectsJob = null;
412 GitProjectData.detachFromWorkspace();
413 indexDiffCache.dispose();
414 indexDiffCache = null;
415 repositoryCache.clear();
416 repositoryCache = null;
417 repositoryUtil.dispose();
418 repositoryUtil = null;
419 secureStore = null;
420 Config.setTypedConfigGetter(null);
421 super.stop(context);
422 plugin = null;
425 private void registerAutoShareProjects() {
426 shareGitProjectsJob = new AutoShareProjects();
427 ResourcesPlugin.getWorkspace().addResourceChangeListener(
428 shareGitProjectsJob, IResourceChangeEvent.POST_CHANGE);
431 private static class AutoShareProjects implements IResourceChangeListener {
433 private static int INTERESTING_CHANGES = IResourceDelta.ADDED
434 | IResourceDelta.OPEN;
436 private final CheckProjectsToShare checkProjectsJob;
438 public AutoShareProjects() {
439 checkProjectsJob = new CheckProjectsToShare();
442 private boolean doAutoShare() {
443 IEclipsePreferences d = DefaultScope.INSTANCE.getNode(Activator
444 .getPluginId());
445 IEclipsePreferences p = InstanceScope.INSTANCE.getNode(Activator
446 .getPluginId());
447 return p.getBoolean(GitCorePreferences.core_autoShareProjects, d
448 .getBoolean(GitCorePreferences.core_autoShareProjects,
449 true));
452 public void stop() {
453 boolean isRunning = !checkProjectsJob.cancel();
454 Job.getJobManager().cancel(JobFamilies.AUTO_SHARE);
455 try {
456 if (isRunning) {
457 checkProjectsJob.join();
459 Job.getJobManager().join(JobFamilies.AUTO_SHARE,
460 new NullProgressMonitor());
461 } catch (OperationCanceledException e) {
462 // Ignore
463 } catch (InterruptedException e) {
464 logError(e.getLocalizedMessage(), e);
468 @Override
469 public void resourceChanged(IResourceChangeEvent event) {
470 if (!doAutoShare()) {
471 return;
473 try {
474 final Set<IProject> projectCandidates = new LinkedHashSet<>();
475 event.getDelta().accept(new IResourceDeltaVisitor() {
476 @Override
477 public boolean visit(IResourceDelta delta)
478 throws CoreException {
479 return collectOpenedProjects(delta,
480 projectCandidates);
483 if(!projectCandidates.isEmpty()){
484 checkProjectsJob.addProjectsToCheck(projectCandidates);
486 } catch (CoreException e) {
487 Activator.logError(e.getMessage(), e);
488 return;
493 * This method should not use RepositoryMapping.getMapping(project) or
494 * RepositoryProvider.getProvider(project) which can trigger
495 * RepositoryProvider.map(project) and deadlock current thread. See
496 * https://bugs.eclipse.org/bugs/show_bug.cgi?id=468270
498 private boolean collectOpenedProjects(IResourceDelta delta,
499 Set<IProject> projects) {
500 if (delta.getKind() == IResourceDelta.CHANGED
501 && (delta.getFlags() & INTERESTING_CHANGES) == 0) {
502 return true;
504 final IResource resource = delta.getResource();
505 if (resource.getType() == IResource.ROOT) {
506 return true;
508 if (resource.getType() != IResource.PROJECT) {
509 return false;
511 if (!resource.isAccessible() || resource.getLocation() == null) {
512 return false;
514 projects.add((IProject) resource);
515 return false;
520 private static class CheckProjectsToShare extends Job {
521 private Object lock = new Object();
523 private Set<IProject> projectCandidates;
525 public CheckProjectsToShare() {
526 super(CoreText.Activator_AutoShareJobName);
527 this.projectCandidates = new LinkedHashSet<>();
528 setUser(false);
529 setSystem(true);
532 public void addProjectsToCheck(Set<IProject> projects) {
533 synchronized (lock) {
534 this.projectCandidates.addAll(projects);
535 if (!projectCandidates.isEmpty()) {
536 schedule(100);
541 @Override
542 protected IStatus run(IProgressMonitor monitor) {
543 Set<IProject> projectsToCheck;
544 synchronized (lock) {
545 projectsToCheck = projectCandidates;
546 projectCandidates = new LinkedHashSet<>();
548 if (projectsToCheck.isEmpty()) {
549 return Status.OK_STATUS;
552 final Map<IProject, File> projects = new HashMap<>();
553 for (IProject project : projectsToCheck) {
554 if (monitor.isCanceled()) {
555 return Status.CANCEL_STATUS;
557 if (project.isAccessible()) {
558 try {
559 visitConnect(project, projects);
560 } catch (CoreException e) {
561 logError(e.getMessage(), e);
565 if (monitor.isCanceled()) {
566 return Status.CANCEL_STATUS;
568 if (projects.size() > 0) {
569 ConnectProviderOperation op = new ConnectProviderOperation(
570 projects);
571 op.setRefreshResources(false);
572 JobUtil.scheduleUserJob(op,
573 CoreText.Activator_AutoShareJobName,
574 JobFamilies.AUTO_SHARE);
576 return Status.OK_STATUS;
579 private void visitConnect(IProject project,
580 final Map<IProject, File> projects) throws CoreException {
582 if (RepositoryMapping.getMapping(project) != null) {
583 return;
585 RepositoryProvider provider = RepositoryProvider
586 .getProvider(project);
587 // respect if project is already shared with another
588 // team provider
589 if (provider != null) {
590 return;
592 RepositoryFinder f = new RepositoryFinder(project);
593 f.setFindInChildren(false);
594 List<RepositoryMapping> mappings = f
595 .find(new NullProgressMonitor());
596 if (mappings.isEmpty()) {
597 return;
599 RepositoryMapping m = mappings.get(0);
600 IPath gitDirPath = m.getGitDirAbsolutePath();
601 if (gitDirPath == null || gitDirPath.segmentCount() == 0) {
602 return;
605 IPath workingDir = gitDirPath.removeLastSegments(1);
606 // Don't connect "/" or "C:\"
607 if (workingDir.isRoot()) {
608 return;
611 File userHome = FS.DETECTED.userHome();
612 if (userHome != null) {
613 Path userHomePath = new Path(userHome.getAbsolutePath());
614 // Don't connect "/home" or "/home/username"
615 if (workingDir.isPrefixOf(userHomePath)) {
616 return;
620 // connect
621 File repositoryDir = gitDirPath.toFile();
622 projects.put(project, repositoryDir);
624 // If we had more than one mapping: add the last one as
625 // 'configured' repository. We don't want to add submodules,
626 // that would only lead to problems when a configured repository
627 // is deleted.
628 int nofMappings = mappings.size();
629 if (nofMappings > 1) {
630 IPath lastPath = mappings.get(nofMappings - 1)
631 .getGitDirAbsolutePath();
632 if (lastPath != null) {
633 repositoryDir = lastPath.toFile();
636 try {
637 Activator.getDefault().getRepositoryUtil()
638 .addConfiguredRepository(repositoryDir);
639 } catch (IllegalArgumentException e) {
640 logError(CoreText.Activator_AutoSharingFailed, e);
645 private void registerAutoIgnoreDerivedResources() {
646 ignoreDerivedResourcesListener = new IgnoreDerivedResources();
647 ResourcesPlugin.getWorkspace().addResourceChangeListener(
648 ignoreDerivedResourcesListener,
649 IResourceChangeEvent.POST_CHANGE);
653 * @return true if the derived resources should be automatically added to
654 * the .gitignore files
656 public static boolean autoIgnoreDerived() {
657 IEclipsePreferences d = DefaultScope.INSTANCE
658 .getNode(Activator.getPluginId());
659 IEclipsePreferences p = InstanceScope.INSTANCE
660 .getNode(Activator.getPluginId());
661 return p.getBoolean(GitCorePreferences.core_autoIgnoreDerivedResources,
662 d.getBoolean(GitCorePreferences.core_autoIgnoreDerivedResources,
663 true));
667 * @return {@code true} if files that get deleted should be automatically
668 * staged
669 * @since 4.6
671 public static boolean autoStageDeletion() {
672 IEclipsePreferences d = DefaultScope.INSTANCE
673 .getNode(Activator.getPluginId());
674 IEclipsePreferences p = InstanceScope.INSTANCE
675 .getNode(Activator.getPluginId());
676 boolean autoStageDeletion = p.getBoolean(
677 GitCorePreferences.core_autoStageDeletion,
678 d.getBoolean(GitCorePreferences.core_autoStageDeletion, false));
679 return autoStageDeletion;
683 * @return {@code true} if files that are moved should be automatically
684 * staged
685 * @since 4.6
687 public static boolean autoStageMoves() {
688 IEclipsePreferences d = DefaultScope.INSTANCE
689 .getNode(Activator.getPluginId());
690 IEclipsePreferences p = InstanceScope.INSTANCE
691 .getNode(Activator.getPluginId());
692 boolean autoStageMoves = p.getBoolean(
693 GitCorePreferences.core_autoStageMoves,
694 d.getBoolean(GitCorePreferences.core_autoStageMoves, false));
695 return autoStageMoves;
697 private static class IgnoreDerivedResources implements
698 IResourceChangeListener {
700 public void stop() {
701 Job.getJobManager().cancel(JobFamilies.AUTO_IGNORE);
702 try {
703 Job.getJobManager().join(JobFamilies.AUTO_IGNORE,
704 new NullProgressMonitor());
705 } catch (OperationCanceledException e) {
706 // Ignore
707 } catch (InterruptedException e) {
708 logError(e.getLocalizedMessage(), e);
712 @Override
713 public void resourceChanged(IResourceChangeEvent event) {
714 try {
715 IResourceDelta d = event.getDelta();
716 if (d == null || !autoIgnoreDerived()) {
717 return;
720 final Set<IPath> toBeIgnored = new LinkedHashSet<>();
722 d.accept(new IResourceDeltaVisitor() {
724 @Override
725 public boolean visit(IResourceDelta delta)
726 throws CoreException {
727 if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.CHANGED)) == 0)
728 return false;
729 int flags = delta.getFlags();
730 if ((flags != 0)
731 && ((flags & IResourceDelta.DERIVED_CHANGED) == 0))
732 return false;
734 final IResource r = delta.getResource();
735 // don't consider resources contained in a project not
736 // shared with Git team provider
737 if ((r.getProject() != null)
738 && (RepositoryMapping.getMapping(r) == null))
739 return false;
740 if (r.isTeamPrivateMember())
741 return false;
743 if (r.isDerived()) {
744 try {
745 IPath location = r.getLocation();
746 if (RepositoryUtil.canBeAutoIgnored(location)) {
747 toBeIgnored.add(location);
749 } catch (IOException e) {
750 logError(
751 MessageFormat.format(
752 CoreText.Activator_ignoreResourceFailed,
753 r.getFullPath()), e);
755 return false;
757 return true;
760 if (toBeIgnored.size() > 0)
761 JobUtil.scheduleUserJob(new IgnoreOperation(toBeIgnored),
762 CoreText.Activator_autoIgnoreDerivedResources,
763 JobFamilies.AUTO_IGNORE);
764 } catch (CoreException e) {
765 Activator.logError(e.getMessage(), e);
766 return;
772 * Describes a MergeStrategy which can be registered with the mergeStrategy
773 * extension point.
775 * @since 4.1
777 public static class MergeStrategyDescriptor {
778 private final String name;
780 private final String label;
782 private final Class<?> implementedBy;
785 * @param name
786 * The referred strategy's name, to use for retrieving the
787 * strategy from MergeRegistry via
788 * {@link MergeStrategy#get(String)}
789 * @param label
790 * The label to display to users so they can select the
791 * strategy they need
792 * @param implementedBy
793 * The class of the MergeStrategy registered through the
794 * mergeStrategy extension point
796 public MergeStrategyDescriptor(String name, String label,
797 Class<?> implementedBy) {
798 this.name = name;
799 this.label = label;
800 this.implementedBy = implementedBy;
804 * @return The actual strategy's name, which can be used to retrieve
805 * that actual strategy via {@link MergeStrategy#get(String)}.
807 public String getName() {
808 return name;
812 * @return The strategy label, for display purposes.
814 public String getLabel() {
815 return label;
819 * @return The class of the MergeStrategy registered through the
820 * mergeStrategy extension point.
822 public Class<?> getImplementedBy() {
823 return implementedBy;
827 private static class MergeStrategyRegistryListener implements
828 IRegistryEventListener {
830 private Map<String, MergeStrategyDescriptor> strategies;
832 private MergeStrategyRegistryListener(IExtensionRegistry registry) {
833 strategies = new LinkedHashMap<>();
834 IConfigurationElement[] elements = registry
835 .getConfigurationElementsFor("org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$
836 loadMergeStrategies(elements);
839 private Collection<MergeStrategyDescriptor> getStrategies() {
840 return Collections.unmodifiableCollection(strategies.values());
843 @Override
844 public void added(IExtension[] extensions) {
845 for (IExtension extension : extensions) {
846 loadMergeStrategies(extension.getConfigurationElements());
850 @Override
851 public void added(IExtensionPoint[] extensionPoints) {
852 // Nothing to do here
855 @Override
856 public void removed(IExtension[] extensions) {
857 for (IExtension extension : extensions) {
858 for (IConfigurationElement element : extension
859 .getConfigurationElements()) {
860 try {
861 Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$
862 if (ext instanceof MergeStrategy) {
863 MergeStrategy strategy = (MergeStrategy) ext;
864 strategies.remove(strategy.getName());
866 } catch (CoreException e) {
867 Activator.logError(CoreText.MergeStrategy_UnloadError,
874 @Override
875 public void removed(IExtensionPoint[] extensionPoints) {
876 // Nothing to do here
879 private void loadMergeStrategies(IConfigurationElement[] elements) {
880 for (IConfigurationElement element : elements) {
881 try {
882 Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$
883 if (ext instanceof MergeStrategy) {
884 MergeStrategy strategy = (MergeStrategy) ext;
885 String name = element.getAttribute("name"); //$NON-NLS-1$
886 if (name == null || name.isEmpty()) {
887 name = strategy.getName();
889 if (canRegister(name, strategy)) {
890 if (MergeStrategy.get(name) == null) {
891 MergeStrategy.register(name, strategy);
893 strategies
894 .put(name,
895 new MergeStrategyDescriptor(
896 name,
897 element.getAttribute("label"), //$NON-NLS-1$
898 strategy.getClass()));
901 } catch (CoreException e) {
902 Activator.logError(CoreText.MergeStrategy_LoadError, e);
908 * Checks whether it's possible to register the provided strategy with
909 * the given name
911 * @param name
912 * Name to use to register the strategy
913 * @param strategy
914 * Strategy to register
915 * @return <code>true</code> if the name is neither null nor empty, no
916 * other strategy is already register for the same name, and the
917 * name is not one of the core JGit strategies. If the given
918 * name is that of a core JGit strategy, the method will return
919 * <code>true</code> only if the strategy is the matching JGit
920 * strategy for that name.
922 private boolean canRegister(String name, MergeStrategy strategy) {
923 boolean result = true;
924 if (name == null || name.isEmpty()) {
925 // name is mandatory
926 Activator.logError(
927 NLS.bind(CoreText.MergeStrategy_MissingName,
928 strategy.getClass()), null);
929 result = false;
930 } else if (strategies.containsKey(name)) {
931 // Other strategy already registered for this name
932 Activator.logError(NLS.bind(
933 CoreText.MergeStrategy_DuplicateName, new Object[] {
934 name, strategies.get(name).getImplementedBy(),
935 strategy.getClass() }), null);
936 result = false;
937 } else if (MergeStrategy.get(name) != null
938 && MergeStrategy.get(name) != strategy) {
939 // The name is reserved by a core JGit strategy, and the
940 // provided instance is not that of JGit
941 Activator.logError(NLS.bind(
942 CoreText.MergeStrategy_ReservedName, new Object[] {
943 name, MergeStrategy.get(name).getClass(),
944 strategy.getClass() }), null);
945 result = false;
947 return result;
952 * A system reader that hides certain global git environment variables from
953 * JGit.
955 private static class EclipseSystemReader extends SystemReader {
958 * Hide these variables lest JGit tries to use them for different
959 * repositories.
961 private static final String[] HIDDEN_VARIABLES = {
962 Constants.GIT_DIR_KEY, Constants.GIT_WORK_TREE_KEY,
963 Constants.GIT_OBJECT_DIRECTORY_KEY,
964 Constants.GIT_INDEX_FILE_KEY,
965 Constants.GIT_ALTERNATE_OBJECT_DIRECTORIES_KEY };
967 private final SystemReader delegate;
969 public EclipseSystemReader(SystemReader delegate) {
970 this.delegate = delegate;
973 @Override
974 public String getenv(String variable) {
975 String result = delegate.getenv(variable);
976 if (result == null) {
977 return result;
979 boolean isWin = isWindows();
980 for (String gitvar : HIDDEN_VARIABLES) {
981 if (isWin && gitvar.equalsIgnoreCase(variable)
982 || !isWin && gitvar.equals(variable)) {
983 return null;
986 return result;
989 @Override
990 public String getHostname() {
991 return delegate.getHostname();
994 @Override
995 public String getProperty(String key) {
996 return delegate.getProperty(key);
999 @Override
1000 public FileBasedConfig openUserConfig(Config parent, FS fs) {
1001 return delegate.openUserConfig(parent, fs);
1004 @Override
1005 public FileBasedConfig openSystemConfig(Config parent, FS fs) {
1006 return delegate.openSystemConfig(parent, fs);
1009 @Override
1010 public long getCurrentTime() {
1011 return delegate.getCurrentTime();
1014 @Override
1015 public int getTimezone(long when) {
1016 return delegate.getTimezone(when);