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
;
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
;
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
;
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() {
108 * @return the name of this plugin
110 public static String
getPluginId() {
115 * Utility to create an error status for this plug-in.
117 * @param message User comprehensible message
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
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.
148 * User comprehensible message
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.
161 * User comprehensible message
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
173 Activator
.setActivator(this);
176 private static void setActivator(Activator a
) {
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,
199 repositoryCache
= new RepositoryCache();
200 indexDiffCache
= new IndexDiffCache();
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());
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());
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() {
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();
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
,
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$
284 lfs
= Class
.forName("org.eclipse.jgit.lfs.BuiltinLFS"); //$NON-NLS-1$
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
);
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.
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) {
335 logError(NLS
.bind(CoreText
.Activator_invalidPreferredMergeStrategy
,
336 preferredMergeStrategyKey
), null);
342 * @return Provides a read-only view of the registered MergeStrategies
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() {
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;
420 Config
.setTypedConfigGetter(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
445 IEclipsePreferences p
= InstanceScope
.INSTANCE
.getNode(Activator
447 return p
.getBoolean(GitCorePreferences
.core_autoShareProjects
, d
448 .getBoolean(GitCorePreferences
.core_autoShareProjects
,
453 boolean isRunning
= !checkProjectsJob
.cancel();
454 Job
.getJobManager().cancel(JobFamilies
.AUTO_SHARE
);
457 checkProjectsJob
.join();
459 Job
.getJobManager().join(JobFamilies
.AUTO_SHARE
,
460 new NullProgressMonitor());
461 } catch (OperationCanceledException e
) {
463 } catch (InterruptedException e
) {
464 logError(e
.getLocalizedMessage(), e
);
469 public void resourceChanged(IResourceChangeEvent event
) {
470 if (!doAutoShare()) {
474 final Set
<IProject
> projectCandidates
= new LinkedHashSet
<>();
475 event
.getDelta().accept(new IResourceDeltaVisitor() {
477 public boolean visit(IResourceDelta delta
)
478 throws CoreException
{
479 return collectOpenedProjects(delta
,
483 if(!projectCandidates
.isEmpty()){
484 checkProjectsJob
.addProjectsToCheck(projectCandidates
);
486 } catch (CoreException e
) {
487 Activator
.logError(e
.getMessage(), e
);
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) {
504 final IResource resource
= delta
.getResource();
505 if (resource
.getType() == IResource
.ROOT
) {
508 if (resource
.getType() != IResource
.PROJECT
) {
511 if (!resource
.isAccessible() || resource
.getLocation() == null) {
514 projects
.add((IProject
) resource
);
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
<>();
532 public void addProjectsToCheck(Set
<IProject
> projects
) {
533 synchronized (lock
) {
534 this.projectCandidates
.addAll(projects
);
535 if (!projectCandidates
.isEmpty()) {
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()) {
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(
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) {
585 RepositoryProvider provider
= RepositoryProvider
586 .getProvider(project
);
587 // respect if project is already shared with another
589 if (provider
!= null) {
592 RepositoryFinder f
= new RepositoryFinder(project
);
593 f
.setFindInChildren(false);
594 List
<RepositoryMapping
> mappings
= f
595 .find(new NullProgressMonitor());
596 if (mappings
.isEmpty()) {
599 RepositoryMapping m
= mappings
.get(0);
600 IPath gitDirPath
= m
.getGitDirAbsolutePath();
601 if (gitDirPath
== null || gitDirPath
.segmentCount() == 0) {
605 IPath workingDir
= gitDirPath
.removeLastSegments(1);
606 // Don't connect "/" or "C:\"
607 if (workingDir
.isRoot()) {
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
)) {
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
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();
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
,
667 * @return {@code true} if files that get deleted should be automatically
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
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
{
701 Job
.getJobManager().cancel(JobFamilies
.AUTO_IGNORE
);
703 Job
.getJobManager().join(JobFamilies
.AUTO_IGNORE
,
704 new NullProgressMonitor());
705 } catch (OperationCanceledException e
) {
707 } catch (InterruptedException e
) {
708 logError(e
.getLocalizedMessage(), e
);
713 public void resourceChanged(IResourceChangeEvent event
) {
715 IResourceDelta d
= event
.getDelta();
716 if (d
== null || !autoIgnoreDerived()) {
720 final Set
<IPath
> toBeIgnored
= new LinkedHashSet
<>();
722 d
.accept(new IResourceDeltaVisitor() {
725 public boolean visit(IResourceDelta delta
)
726 throws CoreException
{
727 if ((delta
.getKind() & (IResourceDelta
.ADDED
| IResourceDelta
.CHANGED
)) == 0)
729 int flags
= delta
.getFlags();
731 && ((flags
& IResourceDelta
.DERIVED_CHANGED
) == 0))
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))
740 if (r
.isTeamPrivateMember())
745 IPath location
= r
.getLocation();
746 if (RepositoryUtil
.canBeAutoIgnored(location
)) {
747 toBeIgnored
.add(location
);
749 } catch (IOException e
) {
751 MessageFormat
.format(
752 CoreText
.Activator_ignoreResourceFailed
,
753 r
.getFullPath()), e
);
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
);
772 * Describes a MergeStrategy which can be registered with the mergeStrategy
777 public static class MergeStrategyDescriptor
{
778 private final String name
;
780 private final String label
;
782 private final Class
<?
> implementedBy
;
786 * The referred strategy's name, to use for retrieving the
787 * strategy from MergeRegistry via
788 * {@link MergeStrategy#get(String)}
790 * The label to display to users so they can select the
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
) {
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() {
812 * @return The strategy label, for display purposes.
814 public String
getLabel() {
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());
844 public void added(IExtension
[] extensions
) {
845 for (IExtension extension
: extensions
) {
846 loadMergeStrategies(extension
.getConfigurationElements());
851 public void added(IExtensionPoint
[] extensionPoints
) {
852 // Nothing to do here
856 public void removed(IExtension
[] extensions
) {
857 for (IExtension extension
: extensions
) {
858 for (IConfigurationElement element
: extension
859 .getConfigurationElements()) {
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
,
875 public void removed(IExtensionPoint
[] extensionPoints
) {
876 // Nothing to do here
879 private void loadMergeStrategies(IConfigurationElement
[] elements
) {
880 for (IConfigurationElement element
: elements
) {
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
);
895 new MergeStrategyDescriptor(
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
912 * Name to use to register the 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()) {
927 NLS
.bind(CoreText
.MergeStrategy_MissingName
,
928 strategy
.getClass()), null);
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);
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);
952 * A system reader that hides certain global git environment variables from
955 private static class EclipseSystemReader
extends SystemReader
{
958 * Hide these variables lest JGit tries to use them for different
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
;
974 public String
getenv(String variable
) {
975 String result
= delegate
.getenv(variable
);
976 if (result
== null) {
979 boolean isWin
= isWindows();
980 for (String gitvar
: HIDDEN_VARIABLES
) {
981 if (isWin
&& gitvar
.equalsIgnoreCase(variable
)
982 || !isWin
&& gitvar
.equals(variable
)) {
990 public String
getHostname() {
991 return delegate
.getHostname();
995 public String
getProperty(String key
) {
996 return delegate
.getProperty(key
);
1000 public FileBasedConfig
openUserConfig(Config parent
, FS fs
) {
1001 return delegate
.openUserConfig(parent
, fs
);
1005 public FileBasedConfig
openSystemConfig(Config parent
, FS fs
) {
1006 return delegate
.openSystemConfig(parent
, fs
);
1010 public long getCurrentTime() {
1011 return delegate
.getCurrentTime();
1015 public int getTimezone(long when
) {
1016 return delegate
.getTimezone(when
);