/******************************************************************************* * Copyright (C) 2008, 2015 Shawn O. Pearce <spearce@spearce.org> and others. * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.egit.core; import java.io.File; import java.io.IOException; import java.net.Authenticator; import java.net.ProxySelector; import java.text.MessageFormat; import java.util.Collection; import java.util.Collections; import java.util.Dictionary; import java.util.HashMap; import java.util.Hashtable; import java.util.LinkedHashMap; import java.util.LinkedHashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.eclipse.core.net.proxy.IProxyService; import org.eclipse.core.resources.IProject; import org.eclipse.core.resources.IResource; import org.eclipse.core.resources.IResourceChangeEvent; import org.eclipse.core.resources.IResourceChangeListener; import org.eclipse.core.resources.IResourceDelta; import org.eclipse.core.resources.IResourceDeltaVisitor; import org.eclipse.core.resources.ResourcesPlugin; import org.eclipse.core.runtime.CoreException; import org.eclipse.core.runtime.IConfigurationElement; import org.eclipse.core.runtime.IExtension; import org.eclipse.core.runtime.IExtensionPoint; import org.eclipse.core.runtime.IExtensionRegistry; import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IRegistryEventListener; import org.eclipse.core.runtime.IStatus; import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; import org.eclipse.core.runtime.Plugin; import org.eclipse.core.runtime.Status; import org.eclipse.core.runtime.jobs.Job; import org.eclipse.core.runtime.preferences.DefaultScope; import org.eclipse.core.runtime.preferences.IEclipsePreferences; import org.eclipse.core.runtime.preferences.InstanceScope; import org.eclipse.egit.core.internal.CoreText; import org.eclipse.egit.core.internal.indexdiff.IndexDiffCache; import org.eclipse.egit.core.internal.job.JobUtil; import org.eclipse.egit.core.internal.trace.GitTraceLocation; import org.eclipse.egit.core.internal.util.ResourceUtil; import org.eclipse.egit.core.op.ConnectProviderOperation; import org.eclipse.egit.core.op.IgnoreOperation; import org.eclipse.egit.core.project.GitProjectData; import org.eclipse.egit.core.project.RepositoryFinder; 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.lib.Constants; import org.eclipse.jgit.merge.MergeStrategy; import org.eclipse.jgit.transport.SshSessionFactory; import org.eclipse.jgit.util.FS; import org.eclipse.jsch.core.IJSchService; import org.eclipse.osgi.service.debug.DebugOptions; import org.eclipse.osgi.service.debug.DebugOptionsListener; import org.eclipse.osgi.util.NLS; import org.eclipse.team.core.RepositoryProvider; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; /** * The plugin class for the org.eclipse.egit.core plugin. This * is a singleton class. */ public class Activator extends Plugin implements DebugOptionsListener { private static Activator plugin; private static String pluginId; private RepositoryCache repositoryCache; private IndexDiffCache indexDiffCache; private RepositoryUtil repositoryUtil; private EGitSecureStore secureStore; private AutoShareProjects shareGitProjectsJob; private IResourceChangeListener preDeleteProjectListener; private IgnoreDerivedResources ignoreDerivedResourcesListener; private MergeStrategyRegistryListener mergeStrategyRegistryListener; /** * @return the singleton {@link Activator} */ public static Activator getDefault() { return plugin; } /** * @return the name of this plugin */ public static String getPluginId() { return pluginId; } /** * Utility to create an error status for this plug-in. * * @param message User comprehensible message * @param thr cause * @return an initialized error status */ public static IStatus error(final String message, final Throwable thr) { return new Status(IStatus.ERROR, getPluginId(), 0, message, thr); } /** * Utility method to log errors in the Egit plugin. * @param message User comprehensible message * @param thr The exception through which we noticed the error */ public static void logError(final String message, final Throwable thr) { getDefault().getLog().log(error(message, thr)); } /** * Log an info message for this plug-in * * @param message */ public static void logInfo(final String message) { getDefault().getLog().log( new Status(IStatus.INFO, getPluginId(), 0, message, null)); } /** * Utility to create a warning status for this plug-in. * * @param message * User comprehensible message * @param thr * cause * @return an initialized warning status */ public static IStatus warning(final String message, final Throwable thr) { return new Status(IStatus.WARNING, getPluginId(), 0, message, thr); } /** * Utility method to log warnings for this plug-in. * * @param message * User comprehensible message * @param thr * The exception through which we noticed the warning */ public static void logWarning(final String message, final Throwable thr) { getDefault().getLog().log(warning(message, thr)); } /** * Construct the {@link Activator} singleton instance */ public Activator() { Activator.setActivator(this); } private static void setActivator(Activator a) { plugin = a; } @Override public void start(final BundleContext context) throws Exception { super.start(context); pluginId = context.getBundle().getSymbolicName(); // we want to be notified about debug options changes Dictionary<String, String> props = new Hashtable<String, String>(4); props.put(DebugOptions.LISTENER_SYMBOLICNAME, pluginId); context.registerService(DebugOptionsListener.class.getName(), this, props); setupSSH(context); setupProxy(context); repositoryCache = new RepositoryCache(); indexDiffCache = new IndexDiffCache(); try { GitProjectData.reconfigureWindowCache(); } catch (RuntimeException e) { logError(CoreText.Activator_ReconfigureWindowCacheError, e); } GitProjectData.attachToWorkspace(); repositoryUtil = new RepositoryUtil(); secureStore = new EGitSecureStore(SecurePreferencesFactory.getDefault()); registerAutoShareProjects(); registerAutoIgnoreDerivedResources(); registerPreDeleteResourceChangeListener(); registerMergeStrategyRegistryListener(); } @SuppressWarnings("unchecked") private void setupSSH(final BundleContext context) { final ServiceReference ssh; ssh = context.getServiceReference(IJSchService.class.getName()); if (ssh != null) { SshSessionFactory.setInstance(new EclipseSshSessionFactory( (IJSchService) context.getService(ssh))); } } @SuppressWarnings("unchecked") private void setupProxy(final BundleContext context) { final ServiceReference proxy; proxy = context.getServiceReference(IProxyService.class.getName()); if (proxy != null) { ProxySelector.setDefault(new EclipseProxySelector( (IProxyService) context.getService(proxy))); Authenticator.setDefault(new EclipseAuthenticator( (IProxyService) context.getService(proxy))); } } private void registerPreDeleteResourceChangeListener() { if (preDeleteProjectListener == null) { preDeleteProjectListener = new IResourceChangeListener() { @Override public void resourceChanged(IResourceChangeEvent event) { IResource resource = event.getResource(); if (resource instanceof IProject) { IProject project = (IProject) resource; if (project.isAccessible()) { if (ResourceUtil.isSharedWithGit(project)) { IResource dotGit = project .findMember(Constants.DOT_GIT); if (dotGit != null && dotGit .getType() == IResource.FOLDER) { GitProjectData.reconfigureWindowCache(); } } } else { // bug 419706: project is closed - use java.io API IPath locationPath = project.getLocation(); if (locationPath != null) { File locationDir = locationPath.toFile(); File dotGit = new File(locationDir, Constants.DOT_GIT); if (dotGit.exists() && dotGit.isDirectory()) { GitProjectData.reconfigureWindowCache(); } } } } } }; ResourcesPlugin.getWorkspace().addResourceChangeListener(preDeleteProjectListener, IResourceChangeEvent.PRE_DELETE); } } @Override public void optionsChanged(DebugOptions options) { // initialize the trace stuff GitTraceLocation.initializeFromOptions(options, isDebugging()); } /** * Provides the 3-way merge strategy to use according to the user's * preferences. The preferred merge strategy is JGit's default merge * strategy unless the user has explicitly chosen a different strategy among * the registered strategies. * * @return The MergeStrategy to use, can be {@code null}, in which case the * default merge strategy should be used as defined by JGit. * @since 4.1 */ public MergeStrategy getPreferredMergeStrategy() { // Get preferences set by user in the UI final IEclipsePreferences prefs = InstanceScope.INSTANCE .getNode(Activator.getPluginId()); String preferredMergeStrategyKey = prefs.get( GitCorePreferences.core_preferredMergeStrategy, null); // Get default preferences, wherever they are defined if (preferredMergeStrategyKey == null || preferredMergeStrategyKey.isEmpty()) { final IEclipsePreferences defaultPrefs = DefaultScope.INSTANCE .getNode(Activator.getPluginId()); preferredMergeStrategyKey = defaultPrefs.get( GitCorePreferences.core_preferredMergeStrategy, null); } if (preferredMergeStrategyKey != null && !preferredMergeStrategyKey.isEmpty() && !GitCorePreferences.core_preferredMergeStrategy_Default .equals(preferredMergeStrategyKey)) { MergeStrategy result = MergeStrategy.get(preferredMergeStrategyKey); if (result != null) { return result; } logError(NLS.bind(CoreText.Activator_invalidPreferredMergeStrategy, preferredMergeStrategyKey), null); } return null; } /** * @return Provides a read-only view of the registered MergeStrategies * available. * @since 4.1 */ public Collection<MergeStrategyDescriptor> getRegisteredMergeStrategies() { if (mergeStrategyRegistryListener == null) { return Collections.emptyList(); } return mergeStrategyRegistryListener.getStrategies(); } private void registerMergeStrategyRegistryListener() { mergeStrategyRegistryListener = new MergeStrategyRegistryListener( Platform.getExtensionRegistry()); Platform.getExtensionRegistry().addListener( mergeStrategyRegistryListener, "org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$ } /** * @return cache for Repository objects */ public RepositoryCache getRepositoryCache() { return repositoryCache; } /** * @return cache for index diffs */ public IndexDiffCache getIndexDiffCache() { return indexDiffCache; } /** * @return the {@link RepositoryUtil} instance */ public RepositoryUtil getRepositoryUtil() { return repositoryUtil; } /** * @return the secure store */ public EGitSecureStore getSecureStore() { return secureStore; } @Override public void stop(final BundleContext context) throws Exception { if (mergeStrategyRegistryListener != null) { Platform.getExtensionRegistry() .removeListener(mergeStrategyRegistryListener); mergeStrategyRegistryListener = null; } if (preDeleteProjectListener != null) { ResourcesPlugin.getWorkspace().removeResourceChangeListener(preDeleteProjectListener); preDeleteProjectListener = null; } if (ignoreDerivedResourcesListener != null) { ResourcesPlugin.getWorkspace().removeResourceChangeListener( ignoreDerivedResourcesListener); ignoreDerivedResourcesListener.stop(); ignoreDerivedResourcesListener = null; } if (shareGitProjectsJob != null) { ResourcesPlugin.getWorkspace().removeResourceChangeListener( shareGitProjectsJob); shareGitProjectsJob.stop(); shareGitProjectsJob = null; } GitProjectData.detachFromWorkspace(); indexDiffCache.dispose(); indexDiffCache = null; repositoryCache.clear(); repositoryCache = null; repositoryUtil.dispose(); repositoryUtil = null; secureStore = null; super.stop(context); plugin = null; } private void registerAutoShareProjects() { shareGitProjectsJob = new AutoShareProjects(); ResourcesPlugin.getWorkspace().addResourceChangeListener( shareGitProjectsJob, IResourceChangeEvent.POST_CHANGE); } private static class AutoShareProjects implements IResourceChangeListener { private static int INTERESTING_CHANGES = IResourceDelta.ADDED | IResourceDelta.OPEN; private final CheckProjectsToShare checkProjectsJob; public AutoShareProjects() { checkProjectsJob = new CheckProjectsToShare(); } private boolean doAutoShare() { IEclipsePreferences d = DefaultScope.INSTANCE.getNode(Activator .getPluginId()); IEclipsePreferences p = InstanceScope.INSTANCE.getNode(Activator .getPluginId()); return p.getBoolean(GitCorePreferences.core_autoShareProjects, d .getBoolean(GitCorePreferences.core_autoShareProjects, true)); } public void stop() { boolean isRunning = !checkProjectsJob.cancel(); Job.getJobManager().cancel(JobFamilies.AUTO_SHARE); try { if (isRunning) { checkProjectsJob.join(); } Job.getJobManager().join(JobFamilies.AUTO_SHARE, new NullProgressMonitor()); } catch (OperationCanceledException e) { // Ignore } catch (InterruptedException e) { logError(e.getLocalizedMessage(), e); } } @Override public void resourceChanged(IResourceChangeEvent event) { if (!doAutoShare()) { return; } try { final Set<IProject> projectCandidates = new LinkedHashSet<>(); event.getDelta().accept(new IResourceDeltaVisitor() { @Override public boolean visit(IResourceDelta delta) throws CoreException { return collectOpenedProjects(delta, projectCandidates); } }); if(!projectCandidates.isEmpty()){ checkProjectsJob.addProjectsToCheck(projectCandidates); } } catch (CoreException e) { Activator.logError(e.getMessage(), e); return; } } /* * This method should not use RepositoryMapping.getMapping(project) or * RepositoryProvider.getProvider(project) which can trigger * RepositoryProvider.map(project) and deadlock current thread. See * https://bugs.eclipse.org/bugs/show_bug.cgi?id=468270 */ private boolean collectOpenedProjects(IResourceDelta delta, Set<IProject> projects) { if (delta.getKind() == IResourceDelta.CHANGED && (delta.getFlags() & INTERESTING_CHANGES) == 0) { return true; } final IResource resource = delta.getResource(); if (resource.getType() == IResource.ROOT) { return true; } if (resource.getType() != IResource.PROJECT) { return false; } if (!resource.isAccessible() || resource.getLocation() == null) { return false; } projects.add((IProject) resource); return false; } } private static class CheckProjectsToShare extends Job { private Object lock = new Object(); private Set<IProject> projectCandidates; public CheckProjectsToShare() { super(CoreText.Activator_AutoShareJobName); this.projectCandidates = new LinkedHashSet<IProject>(); setUser(false); setSystem(true); } public void addProjectsToCheck(Set<IProject> projects) { synchronized (lock) { this.projectCandidates.addAll(projects); if (!projectCandidates.isEmpty()) { schedule(100); } } } @Override protected IStatus run(IProgressMonitor monitor) { Set<IProject> projectsToCheck; synchronized (lock) { projectsToCheck = projectCandidates; projectCandidates = new LinkedHashSet<>(); } if (projectsToCheck.isEmpty()) { return Status.OK_STATUS; } final Map<IProject, File> projects = new HashMap<IProject, File>(); for (IProject project : projectsToCheck) { if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } if (project.isAccessible()) { try { visitConnect(project, projects); } catch (CoreException e) { logError(e.getMessage(), e); } } } if (monitor.isCanceled()) { return Status.CANCEL_STATUS; } if (projects.size() > 0) { ConnectProviderOperation op = new ConnectProviderOperation( projects); op.setRefreshResources(false); JobUtil.scheduleUserJob(op, CoreText.Activator_AutoShareJobName, JobFamilies.AUTO_SHARE); } return Status.OK_STATUS; } private void visitConnect(IProject project, final Map<IProject, File> projects) throws CoreException { if (RepositoryMapping.getMapping(project) != null) { return; } RepositoryProvider provider = RepositoryProvider .getProvider(project); // respect if project is already shared with another // team provider if (provider != null) { return; } RepositoryFinder f = new RepositoryFinder(project); f.setFindInChildren(false); List<RepositoryMapping> mappings = f .find(new NullProgressMonitor()); if (mappings.isEmpty()) { return; } RepositoryMapping m = mappings.get(0); IPath gitDirPath = m.getGitDirAbsolutePath(); if (gitDirPath == null || gitDirPath.segmentCount() == 0) { return; } IPath workingDir = gitDirPath.removeLastSegments(1); // Don't connect "/" or "C:\" if (workingDir.isRoot()) { return; } File userHome = FS.DETECTED.userHome(); if (userHome != null) { Path userHomePath = new Path(userHome.getAbsolutePath()); // Don't connect "/home" or "/home/username" if (workingDir.isPrefixOf(userHomePath)) { return; } } // connect File repositoryDir = gitDirPath.toFile(); projects.put(project, repositoryDir); // If we had more than one mapping: add the last one as // 'configured' repository. We don't want to add submodules, // that would only lead to problems when a configured repository // is deleted. int nofMappings = mappings.size(); if (nofMappings > 1) { IPath lastPath = mappings.get(nofMappings - 1) .getGitDirAbsolutePath(); if (lastPath != null) { repositoryDir = lastPath.toFile(); } } try { Activator.getDefault().getRepositoryUtil() .addConfiguredRepository(repositoryDir); } catch (IllegalArgumentException e) { logError(CoreText.Activator_AutoSharingFailed, e); } } } private void registerAutoIgnoreDerivedResources() { ignoreDerivedResourcesListener = new IgnoreDerivedResources(); ResourcesPlugin.getWorkspace().addResourceChangeListener( ignoreDerivedResourcesListener, IResourceChangeEvent.POST_CHANGE); } /** * @return true if the derived resources should be automatically added to * the .gitignore files */ public static boolean autoIgnoreDerived() { IEclipsePreferences d = DefaultScope.INSTANCE .getNode(Activator.getPluginId()); IEclipsePreferences p = InstanceScope.INSTANCE .getNode(Activator.getPluginId()); return p.getBoolean(GitCorePreferences.core_autoIgnoreDerivedResources, d.getBoolean(GitCorePreferences.core_autoIgnoreDerivedResources, true)); } /** * @return {@code true} if files that get deleted should be automatically * staged * @since 4.6 */ public static boolean autoStageDeletion() { IEclipsePreferences d = DefaultScope.INSTANCE .getNode(Activator.getPluginId()); IEclipsePreferences p = InstanceScope.INSTANCE .getNode(Activator.getPluginId()); boolean autoStageDeletion = p.getBoolean( GitCorePreferences.core_autoStageDeletion, d.getBoolean(GitCorePreferences.core_autoStageDeletion, false)); return autoStageDeletion; } /** * @return {@code true} if files that are moved should be automatically * staged * @since 4.6 */ public static boolean autoStageMoves() { IEclipsePreferences d = DefaultScope.INSTANCE .getNode(Activator.getPluginId()); IEclipsePreferences p = InstanceScope.INSTANCE .getNode(Activator.getPluginId()); boolean autoStageMoves = p.getBoolean( GitCorePreferences.core_autoStageMoves, d.getBoolean(GitCorePreferences.core_autoStageMoves, false)); return autoStageMoves; } private static class IgnoreDerivedResources implements IResourceChangeListener { public void stop() { Job.getJobManager().cancel(JobFamilies.AUTO_IGNORE); try { Job.getJobManager().join(JobFamilies.AUTO_IGNORE, new NullProgressMonitor()); } catch (OperationCanceledException e) { // Ignore } catch (InterruptedException e) { logError(e.getLocalizedMessage(), e); } } @Override public void resourceChanged(IResourceChangeEvent event) { try { IResourceDelta d = event.getDelta(); if (d == null || !autoIgnoreDerived()) { return; } final Set<IPath> toBeIgnored = new LinkedHashSet<IPath>(); d.accept(new IResourceDeltaVisitor() { @Override public boolean visit(IResourceDelta delta) throws CoreException { if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.CHANGED)) == 0) return false; int flags = delta.getFlags(); if ((flags != 0) && ((flags & IResourceDelta.DERIVED_CHANGED) == 0)) return false; final IResource r = delta.getResource(); // don't consider resources contained in a project not // shared with Git team provider if ((r.getProject() != null) && (RepositoryMapping.getMapping(r) == null)) return false; if (r.isTeamPrivateMember()) return false; if (r.isDerived()) { try { IPath location = r.getLocation(); if (RepositoryUtil.canBeAutoIgnored(location)) { toBeIgnored.add(location); } } catch (IOException e) { logError( MessageFormat.format( CoreText.Activator_ignoreResourceFailed, r.getFullPath()), e); } return false; } return true; } }); if (toBeIgnored.size() > 0) JobUtil.scheduleUserJob(new IgnoreOperation(toBeIgnored), CoreText.Activator_autoIgnoreDerivedResources, JobFamilies.AUTO_IGNORE); } catch (CoreException e) { Activator.logError(e.getMessage(), e); return; } } } /** * Describes a MergeStrategy which can be registered with the mergeStrategy * extension point. * * @since 4.1 */ public static class MergeStrategyDescriptor { private final String name; private final String label; private final Class<?> implementedBy; /** * @param name * The referred strategy's name, to use for retrieving the * strategy from MergeRegistry via * {@link MergeStrategy#get(String)} * @param label * The label to display to users so they can select the * strategy they need * @param implementedBy * The class of the MergeStrategy registered through the * mergeStrategy extension point */ public MergeStrategyDescriptor(String name, String label, Class<?> implementedBy) { this.name = name; this.label = label; this.implementedBy = implementedBy; } /** * @return The actual strategy's name, which can be used to retrieve * that actual strategy via {@link MergeStrategy#get(String)}. */ public String getName() { return name; } /** * @return The strategy label, for display purposes. */ public String getLabel() { return label; } /** * @return The class of the MergeStrategy registered through the * mergeStrategy extension point. */ public Class<?> getImplementedBy() { return implementedBy; } } private static class MergeStrategyRegistryListener implements IRegistryEventListener { private Map<String, MergeStrategyDescriptor> strategies; private MergeStrategyRegistryListener(IExtensionRegistry registry) { strategies = new LinkedHashMap<>(); IConfigurationElement[] elements = registry .getConfigurationElementsFor("org.eclipse.egit.core.mergeStrategy"); //$NON-NLS-1$ loadMergeStrategies(elements); } private Collection<MergeStrategyDescriptor> getStrategies() { return Collections.unmodifiableCollection(strategies.values()); } @Override public void added(IExtension[] extensions) { for (IExtension extension : extensions) { loadMergeStrategies(extension.getConfigurationElements()); } } @Override public void added(IExtensionPoint[] extensionPoints) { // Nothing to do here } @Override public void removed(IExtension[] extensions) { for (IExtension extension : extensions) { for (IConfigurationElement element : extension .getConfigurationElements()) { try { Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$ if (ext instanceof MergeStrategy) { MergeStrategy strategy = (MergeStrategy) ext; strategies.remove(strategy.getName()); } } catch (CoreException e) { Activator.logError(CoreText.MergeStrategy_UnloadError, e); } } } } @Override public void removed(IExtensionPoint[] extensionPoints) { // Nothing to do here } private void loadMergeStrategies(IConfigurationElement[] elements) { for (IConfigurationElement element : elements) { try { Object ext = element.createExecutableExtension("class"); //$NON-NLS-1$ if (ext instanceof MergeStrategy) { MergeStrategy strategy = (MergeStrategy) ext; String name = element.getAttribute("name"); //$NON-NLS-1$ if (name == null || name.isEmpty()) { name = strategy.getName(); } if (canRegister(name, strategy)) { if (MergeStrategy.get(name) == null) { MergeStrategy.register(name, strategy); } strategies .put(name, new MergeStrategyDescriptor( name, element.getAttribute("label"), //$NON-NLS-1$ strategy.getClass())); } } } catch (CoreException e) { Activator.logError(CoreText.MergeStrategy_LoadError, e); } } } /** * Checks whether it's possible to register the provided strategy with * the given name * * @param name * Name to use to register the strategy * @param strategy * Strategy to register * @return <code>true</code> if the name is neither null nor empty, no * other strategy is already register for the same name, and the * name is not one of the core JGit strategies. If the given * name is that of a core JGit strategy, the method will return * <code>true</code> only if the strategy is the matching JGit * strategy for that name. */ private boolean canRegister(String name, MergeStrategy strategy) { boolean result = true; if (name == null || name.isEmpty()) { // name is mandatory Activator.logError( NLS.bind(CoreText.MergeStrategy_MissingName, strategy.getClass()), null); result = false; } else if (strategies.containsKey(name)) { // Other strategy already registered for this name Activator.logError(NLS.bind( CoreText.MergeStrategy_DuplicateName, new Object[] { name, strategies.get(name).getImplementedBy(), strategy.getClass() }), null); result = false; } else if (MergeStrategy.get(name) != null && MergeStrategy.get(name) != strategy) { // The name is reserved by a core JGit strategy, and the // provided instance is not that of JGit Activator.logError(NLS.bind( CoreText.MergeStrategy_ReservedName, new Object[] { name, MergeStrategy.get(name).getClass(), strategy.getClass() }), null); result = false; } return result; } } }