[EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Read in all relevant ignore files: all .gitignore files in the projects,
all .gitignore files between the checkout directory and the project
directories, the info/exclude ignore files of the repositories, the
repository core.excludesfile ignore files and the global
core.excludesfile ignore file.

Signed-off-by: Ferry Huberts <ferry.huberts@xxxxxxxxxx>
---
 .../src/org/spearce/egit/core/ignores/Exclude.java |  158 ++++++
 .../spearce/egit/core/ignores/GitIgnoreData.java   |  139 +++++
 .../org/spearce/egit/core/ignores/IgnoreFile.java  |   82 +++
 .../egit/core/ignores/IgnoreFileOutside.java       |  543 ++++++++++++++++++++
 .../egit/core/ignores/IgnoreProjectCache.java      |  201 ++++++++
 .../egit/core/ignores/IgnoreRepositoryCache.java   |  308 +++++++++++
 .../spearce/egit/core/project/GitProjectData.java  |    5 +
 7 files changed, 1436 insertions(+), 0 deletions(-)
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java

diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java
new file mode 100644
index 0000000..c4c48e9
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java
@@ -0,0 +1,158 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@xxxxxxxxxx>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+package org.spearce.egit.core.ignores;
+
+import java.util.regex.Pattern;
+
+/**
+ * This class describes an ignore pattern in the same way as git does, with some
+ * extra information to support Eclipse specific functionality.
+ * 
+ * The git definition can be found in the source file dir.h, within the
+ * exclude_list structure definition. The code can be found in the source file
+ * dir.c:excluded_1
+ */
+class Exclude {
+	/** the pattern to match */
+	private String pattern = null;
+
+	/**
+	 * the directory in which the pattern is anchored, relative to the checkout
+	 * directory and with a trailing slash (except when in the checkout
+	 * directory, in which case it will be an empty string). Slashes are in Unix
+	 * format: forward slashes
+	 */
+	private String base = null;
+
+	/**
+	 * true when the resource must be excluded when matched, false in case of a
+	 * negative pattern: when it must be included
+	 */
+	private boolean to_exclude = true;
+
+	/** true when the resource must be a directory */
+	private boolean mustBeDir = false;
+
+	/** true when the pattern does not contain directories */
+	private boolean noDir = false;
+
+	/** true when the resource must end with pattern.substring(1) */
+	private boolean endsWith = false;
+
+	/** true when the pattern has no wildcards */
+	private boolean noWildcard = false;
+
+	/*
+	 * Extra Information
+	 */
+
+	/**
+	 * the full path name of the ignore file. Stored so that a user can ask
+	 * 'which pattern in which ignore file makes this resource be ignored?'
+	 */
+	private String ignoreFileAbsolutePath = null;
+
+	/**
+	 * the line number of the pattern in the ignore file. Stored for the same
+	 * reason as the ignoreFileFullPath field
+	 */
+	private int lineNumber = 0;
+
+	/**
+	 * Constructor. See the git source file dir.c, method add_exclude
+	 * 
+	 * @param pattern
+	 *            the pattern to match
+	 * @param base
+	 *            the directory in which the pattern is anchored, relative to
+	 *            the checkout directory and with a trailing slash (except when
+	 *            in the checkout directory, in which case it will be an empty
+	 *            string). Slashes are in Unix format: forward slashes
+	 * @param ignoreFileAbsolutePath
+	 *            the full path name of the ignore file. Stored so that a user
+	 *            can ask 'which pattern in which ignore file makes this
+	 *            resource be ignored?'
+	 * @param lineNumber
+	 *            the line number of the pattern in the ignore file. Stored for
+	 *            the same reason as the ignoreFileFullPath field
+	 */
+	Exclude(final String pattern, final String base,
+			final String ignoreFileAbsolutePath, final int lineNumber) {
+		this.pattern = pattern;
+		this.base = base;
+
+		this.to_exclude = !this.pattern.startsWith("!");
+		if (!this.to_exclude) {
+			this.pattern = this.pattern.substring(1);
+		}
+
+		this.mustBeDir = this.pattern.endsWith("/");
+		if (this.mustBeDir) {
+			this.pattern = this.pattern.substring(0, this.pattern.length() - 1);
+		}
+		this.noDir = !this.pattern.contains("/");
+		this.noWildcard = no_wildcard(this.pattern);
+		this.endsWith = ((this.pattern.charAt(0) == '*') && no_wildcard(this.pattern
+				.substring(1)));
+
+		this.ignoreFileAbsolutePath = ignoreFileAbsolutePath;
+		this.lineNumber = lineNumber;
+	}
+
+	/*
+	 * Private Methods
+	 */
+
+	private static Pattern wildcardPattern = Pattern
+			.compile("^.*[\\*\\?\\[\\{].*$");
+
+	/* dir.c::no_wildcard */
+	private boolean no_wildcard(final String string) {
+		return !wildcardPattern.matcher(string).matches();
+	}
+
+	/*
+	 * Getters / Setters
+	 */
+
+	public String getIgnoreFileAbsolutePath() {
+		return ignoreFileAbsolutePath;
+	}
+
+	public int getLineNumber() {
+		return lineNumber;
+	}
+
+	/**
+	 * @return the base
+	 */
+	public String getBase() {
+		return base;
+	}
+
+	/**
+	 * @return the noDir
+	 */
+	public boolean isNoDir() {
+		return noDir;
+	}
+
+	/**
+	 * @return the endsWith
+	 */
+	public boolean isEndsWith() {
+		return endsWith;
+	}
+
+	/**
+	 * @return the noWildcard
+	 */
+	public boolean isNoWildcard() {
+		return noWildcard;
+	}
+}
\ No newline at end of file
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java
new file mode 100644
index 0000000..401a378
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java
@@ -0,0 +1,139 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@xxxxxxxxxx>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+package org.spearce.egit.core.ignores;
+
+import java.util.HashMap;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourcesPlugin;
+import org.eclipse.core.runtime.CoreException;
+import org.spearce.egit.core.project.RepositoryMapping;
+import org.spearce.jgit.lib.Repository;
+
+/**
+ * This class provides management of ignore data. It deals with .gitignore
+ * files, the .git/info/exclude file, and core.excludefile settings. It also
+ * deals with with changes to those files.
+ * 
+ * The git code for ignores can be found in its files dir.{h,c}.
+ * 
+ * See the file Documentation/gitignore.txt in the git repository for a
+ * description of how ignores work.
+ */
+public class GitIgnoreData {
+
+	/*
+	 * Ignore Data Cache
+	 */
+
+	private static HashMap<Repository, IgnoreRepositoryCache> repositories = new HashMap<Repository, IgnoreRepositoryCache>();
+
+	/**
+	 * Retrieve a repository mapping from the repositories cache. When the
+	 * repository is not yet in the cache then create a new mapping for it and
+	 * store it in the cache first.
+	 * 
+	 * @param repository
+	 *            the repository to retrieve from the repositories cache
+	 * @return the repository mapping in the cache
+	 */
+	private static IgnoreRepositoryCache getRepositoryFromCache(
+			final Repository repository) {
+		IgnoreRepositoryCache cache = repositories.get(repository);
+		if (cache == null) {
+			cache = new IgnoreRepositoryCache(repository);
+			repositories.put(repository, cache);
+		}
+		return cache;
+	}
+
+	/*
+	 * Public Methods
+	 */
+
+	/**
+	 * This method must be invoked upon shutdown of the plugin. It empties the
+	 * ignore cache.
+	 */
+	public synchronized static void clear() {
+		for (final IgnoreRepositoryCache projectCache : repositories.values()) {
+			projectCache.clear();
+		}
+		repositories.clear();
+	}
+
+	/**
+	 * @param project
+	 *            the project to remove the ignores for
+	 */
+	public synchronized static void uncacheProject(final IResource project) {
+		if ((project == null) || (!(project instanceof IProject))) {
+			return;
+		}
+
+		final RepositoryMapping mapping = RepositoryMapping.getMapping(project);
+		if (mapping == null) {
+			return;
+		}
+
+		final Repository repository = mapping.getRepository();
+		final IgnoreRepositoryCache ignoreData = repositories.get(repository);
+		if (ignoreData == null) {
+			return;
+		}
+
+		ignoreData.uncacheProject((IProject) project);
+	}
+
+	/**
+	 * This method must be invoked upon startup. It goes through all files in
+	 * the workspace and picks up all .gitignore files, parses them so that the
+	 * plugin knows what to ignore. It also goes up the directory tree from the
+	 * project root directories to look for .gitignore files.
+	 */
+	public synchronized static void importWorkspaceIgnores() {
+		final IWorkspace workSpace = ResourcesPlugin.getWorkspace();
+		final IProject[] projects = workSpace.getRoot().getProjects();
+		for (final IProject project : projects) {
+			final RepositoryMapping mapping = RepositoryMapping
+					.getMapping(project);
+			if (mapping != null) {
+				try {
+					final IResource[] projectChildren = project.members();
+					final IgnoreRepositoryCache ignoreRepositoryCache = getRepositoryFromCache(mapping
+							.getRepository());
+					ignoreRepositoryCache.importProjectIgnores(project,
+							projectChildren);
+					ignoreRepositoryCache.importProjectIgnoresOutside(project);
+				} catch (final CoreException e) {
+					/* swallow */
+				}
+			}
+		}
+
+		for (final IgnoreRepositoryCache repositoryCache : repositories
+				.values()) {
+			repositoryCache.importRepositoryInfoExclude();
+			repositoryCache.importRepositoryCoreExclude();
+		}
+
+		/*
+		 * FIXME: also do the file `git config --global --get
+		 * core.excludesfile`, is this already covered? RepositoryConfig
+		 * globalConfig = new RepositoryConfig(null, new File(FS.userHome(),
+		 * ".gitconfig"));
+		 */
+
+		/*
+		 * System.out.println("== GitIgnoreData.repositories ==\n" +
+		 * repositories.toString());
+		 */
+	}
+}
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java
new file mode 100644
index 0000000..78dd71d
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java
@@ -0,0 +1,82 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@xxxxxxxxxx>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+package org.spearce.egit.core.ignores;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.util.LinkedList;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This class implements ignore file helpers.
+ */
+class IgnoreFile {
+	/**
+	 * This method parses an ignore file.
+	 * 
+	 * @param ignoreFileBaseDir
+	 *            the directory of the ignore file, relative to the checkout
+	 *            directory and with a trailing slash. Slashes are in Unix
+	 *            format: forward slashes
+	 * @param ignoreFile
+	 *            the .gitignore file
+	 * @return returns a set of Excludes that reflects the ignore patterns.
+	 */
+	static LinkedList<Exclude> parseIgnoreFile(final String ignoreFileBaseDir,
+			final IFile ignoreFile) {
+		final LinkedList<Exclude> excludes = new LinkedList<Exclude>();
+
+		/* make sure that the resource is synchronized */
+		try {
+			if (!ignoreFile.isSynchronized(IResource.DEPTH_ZERO)) {
+				ignoreFile.refreshLocal(IResource.DEPTH_ZERO, null);
+			}
+		} catch (final Exception e) {
+			return excludes;
+		}
+
+		String base = ignoreFileBaseDir;
+		if (base.equals("/")) {
+			base = "";
+		}
+		final String ignoreFileName = ignoreFile.getLocation().toOSString();
+		BufferedReader txtIn = null;
+		int lineNumber = 0;
+		try {
+			txtIn = new BufferedReader(new InputStreamReader(ignoreFile
+					.getContents()));
+			String line;
+			while ((line = txtIn.readLine()) != null) {
+				lineNumber++;
+				line = line.trim();
+				if (!line.startsWith("#") && (line.length() > 0)) {
+					excludes.add(new Exclude(line, base, ignoreFileName,
+							lineNumber));
+				}
+			}
+		} catch (final CoreException e) {
+			/* swallow */
+		} catch (final IOException e) {
+			/* swallow */
+		} finally {
+			try {
+				if (txtIn != null) {
+					txtIn.close();
+					txtIn = null;
+				}
+			} catch (final IOException e1) {
+				/* swallow */
+			}
+		}
+		return excludes;
+	}
+}
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java
new file mode 100644
index 0000000..8ad5a48
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java
@@ -0,0 +1,543 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@xxxxxxxxxx>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+package org.spearce.egit.core.ignores;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.net.URI;
+import java.util.Map;
+
+import org.eclipse.core.resources.IContainer;
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFileState;
+import org.eclipse.core.resources.IMarker;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IProjectDescription;
+import org.eclipse.core.resources.IResourceProxy;
+import org.eclipse.core.resources.IResourceProxyVisitor;
+import org.eclipse.core.resources.IResourceVisitor;
+import org.eclipse.core.resources.IWorkspace;
+import org.eclipse.core.resources.ResourceAttributes;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.IStatus;
+import org.eclipse.core.runtime.Path;
+import org.eclipse.core.runtime.QualifiedName;
+import org.eclipse.core.runtime.Status;
+import org.eclipse.core.runtime.content.IContentDescription;
+import org.eclipse.core.runtime.jobs.ISchedulingRule;
+import org.spearce.jgit.lib.Repository;
+
+/**
+ * This class is only used to be able to store the .gitignore files in the
+ * ignore cache that are outside the projects (up in the checkout directory tree
+ * from the project root directory)
+ */
+class IgnoreFileOutside implements IFile {
+	private String relativeDir = null;
+
+	private String relativePath = null;
+
+	private String resourceBaseName = null;
+
+	private String absoluteDir = null;
+
+	private String fullPath = null;
+
+	private File fullPathFile = null;
+
+	private long lastModificationTime = 0L;
+
+	/**
+	 * Constructor
+	 * 
+	 * @param repository
+	 *            the repository. when null then the relativeDir parameter is
+	 *            taken to be an absolute path
+	 * @param directory
+	 *            the directory in which the pattern is anchored, relative to
+	 *            the checkout directory and with a trailing slash (except when
+	 *            in the checkout directory, in which case it will be an empty
+	 *            string). Slashes are in Unix format: forward slashes. When
+	 *            repository is null then this is taken to be an absolute
+	 *            directory with slashes in platform format.
+	 * @param resourceBasename
+	 *            the name of the ignore file.
+	 */
+	IgnoreFileOutside(final Repository repository, final String directory,
+			final String resourceBasename) {
+		if ((directory == null) || (resourceBasename == null)) {
+			throw new IllegalArgumentException("Can not handle NULL values: "
+					+ directory + ", " + resourceBasename);
+		}
+
+		this.relativeDir = directory.replaceAll("/", File.separator);
+		this.resourceBaseName = resourceBasename
+				.replaceAll("/", File.separator);
+		this.relativePath = this.relativeDir + this.resourceBaseName;
+
+		String repoRoot = "";
+		if (repository != null) {
+			repoRoot = repository.getWorkDir().getAbsolutePath()
+					+ File.separator;
+		}
+		this.absoluteDir = repoRoot + this.relativeDir;
+		this.fullPath = this.absoluteDir + this.resourceBaseName;
+		this.fullPathFile = new File(this.fullPath);
+	}
+
+	/* used interface methods */
+
+	public boolean exists() {
+		return this.fullPathFile.exists();
+	}
+
+	public InputStream getContents() throws CoreException {
+		try {
+			return new FileInputStream(fullPath);
+		} catch (final FileNotFoundException e) {
+			/* FIXME: use actual plugin id */
+			throw new CoreException(new Status(IStatus.WARNING, "git plugin", e
+					.getLocalizedMessage()));
+		}
+	}
+
+	public String getName() {
+		return resourceBaseName;
+	}
+
+	public IPath getProjectRelativePath() {
+		return new Path(relativePath);
+	}
+
+	public boolean isSynchronized(final int depth) {
+		return (lastModificationTime == this.fullPathFile.lastModified());
+	}
+
+	public void refreshLocal(final int depth, final IProgressMonitor monitor)
+			throws CoreException {
+		lastModificationTime = this.fullPathFile.lastModified();
+		return;
+	}
+
+	public IPath getLocation() {
+		return new Path(fullPath);
+	}
+
+	/*
+	 * Overridden Methods
+	 */
+
+	@Override
+	public boolean equals(final Object obj) {
+		if (!(obj instanceof IgnoreFileOutside)) {
+			throw new IllegalArgumentException("Wrong type");
+		}
+		return ((IgnoreFileOutside) obj).fullPath.equals(this.fullPath);
+	}
+
+	@Override
+	public int hashCode() {
+		return fullPath.hashCode();
+	}
+
+	@Override
+	public String toString() {
+		return fullPath;
+	}
+
+	/*
+	 * Unused Interface Methods
+	 */
+
+	public void appendContents(final InputStream source, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void appendContents(final InputStream source, final boolean force,
+			final boolean keepHistory, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void create(final InputStream source, final boolean force,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void create(final InputStream source, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void createLink(final IPath localLocation, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+
+		/** not used */
+	}
+
+	public void createLink(final URI location, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void delete(final boolean force, final boolean keepHistory,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public String getCharset() throws CoreException {
+		return null;
+	}
+
+	public String getCharset(final boolean checkImplicit) throws CoreException {
+		return null;
+	}
+
+	public String getCharsetFor(final Reader reader) throws CoreException {
+		return null;
+	}
+
+	public IContentDescription getContentDescription() throws CoreException {
+		return null;
+	}
+
+	public InputStream getContents(final boolean force) throws CoreException {
+		return null;
+	}
+
+	public int getEncoding() throws CoreException {
+		return 0;
+	}
+
+	public IPath getFullPath() {
+		return null;
+	}
+
+	public IFileState[] getHistory(final IProgressMonitor monitor)
+			throws CoreException {
+		return null;
+	}
+
+	public boolean isReadOnly() {
+		return false;
+	}
+
+	public void move(final IPath destination, final boolean force,
+			final boolean keepHistory, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void setCharset(final String newCharset) throws CoreException {
+		/** not used */
+	}
+
+	public void setCharset(final String newCharset,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void setContents(final InputStream source, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void setContents(final IFileState source, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void setContents(final InputStream source, final boolean force,
+			final boolean keepHistory, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void setContents(final IFileState source, final boolean force,
+			final boolean keepHistory, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void accept(final IResourceVisitor visitor) throws CoreException {
+		/** not used */
+	}
+
+	public void accept(final IResourceProxyVisitor visitor,
+			final int memberFlags) throws CoreException {
+		/** not used */
+	}
+
+	public void accept(final IResourceVisitor visitor, final int depth,
+			final boolean includePhantoms) throws CoreException {
+		/** not used */
+	}
+
+	public void accept(final IResourceVisitor visitor, final int depth,
+			final int memberFlags) throws CoreException {
+		/** not used */
+	}
+
+	public void clearHistory(final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void copy(final IPath destination, final boolean force,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void copy(final IPath destination, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void copy(final IProjectDescription description,
+			final boolean force, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void copy(final IProjectDescription description,
+			final int updateFlags, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public IMarker createMarker(final String type) throws CoreException {
+		return null;
+	}
+
+	public IResourceProxy createProxy() {
+		return null;
+	}
+
+	public void delete(final boolean force, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void delete(final int updateFlags, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void deleteMarkers(final String type, final boolean includeSubtypes,
+			final int depth) throws CoreException {
+		/** not used */
+	}
+
+	public IMarker findMarker(final long id) throws CoreException {
+		return null;
+	}
+
+	public IMarker[] findMarkers(final String type,
+			final boolean includeSubtypes, final int depth)
+			throws CoreException {
+		return null;
+	}
+
+	public int findMaxProblemSeverity(final String type,
+			final boolean includeSubtypes, final int depth)
+			throws CoreException {
+		return 0;
+	}
+
+	public String getFileExtension() {
+		return null;
+	}
+
+	public long getLocalTimeStamp() {
+		return 0;
+	}
+
+	public URI getLocationURI() {
+		return null;
+	}
+
+	public IMarker getMarker(final long id) {
+		return null;
+	}
+
+	public long getModificationStamp() {
+		return 0;
+	}
+
+	public IContainer getParent() {
+		return null;
+	}
+
+	public Map getPersistentProperties() throws CoreException {
+		return null;
+	}
+
+	public String getPersistentProperty(final QualifiedName key)
+			throws CoreException {
+		return null;
+	}
+
+	public IProject getProject() {
+		return null;
+	}
+
+	public IPath getRawLocation() {
+		return null;
+	}
+
+	public URI getRawLocationURI() {
+		return null;
+	}
+
+	public ResourceAttributes getResourceAttributes() {
+		return null;
+	}
+
+	public Map getSessionProperties() throws CoreException {
+		return null;
+	}
+
+	public Object getSessionProperty(final QualifiedName key)
+			throws CoreException {
+		return null;
+	}
+
+	public int getType() {
+		return 0;
+	}
+
+	public IWorkspace getWorkspace() {
+		return null;
+	}
+
+	public boolean isAccessible() {
+		return false;
+	}
+
+	public boolean isDerived() {
+		return false;
+	}
+
+	public boolean isDerived(final int options) {
+		return false;
+	}
+
+	public boolean isHidden() {
+		return false;
+	}
+
+	public boolean isLinked() {
+		return false;
+	}
+
+	public boolean isLinked(final int options) {
+		return false;
+	}
+
+	public boolean isLocal(final int depth) {
+		return false;
+	}
+
+	public boolean isPhantom() {
+		return false;
+	}
+
+	public boolean isTeamPrivateMember() {
+		return false;
+	}
+
+	public void move(final IPath destination, final boolean force,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void move(final IPath destination, final int updateFlags,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void move(final IProjectDescription description,
+			final int updateFlags, final IProgressMonitor monitor)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void move(final IProjectDescription description,
+			final boolean force, final boolean keepHistory,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public void revertModificationStamp(final long value) throws CoreException {
+		/** not used */
+	}
+
+	public void setDerived(final boolean isDerived) throws CoreException {
+		/** not used */
+	}
+
+	public void setHidden(final boolean isHidden) throws CoreException {
+		/** not used */
+	}
+
+	public void setLocal(final boolean flag, final int depth,
+			final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public long setLocalTimeStamp(final long value) throws CoreException {
+		return 0;
+	}
+
+	public void setPersistentProperty(final QualifiedName key,
+			final String value) throws CoreException {
+		/** not used */
+	}
+
+	public void setReadOnly(final boolean readOnly) {
+		/** not used */
+	}
+
+	public void setResourceAttributes(final ResourceAttributes attributes)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void setSessionProperty(final QualifiedName key, final Object value)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void setTeamPrivateMember(final boolean isTeamPrivate)
+			throws CoreException {
+		/** not used */
+	}
+
+	public void touch(final IProgressMonitor monitor) throws CoreException {
+		/** not used */
+	}
+
+	public Object getAdapter(final Class adapter) {
+		return null;
+	}
+
+	public boolean contains(final ISchedulingRule rule) {
+		return false;
+	}
+
+	public boolean isConflicting(final ISchedulingRule rule) {
+		return false;
+	}
+
+}
\ No newline at end of file
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java
new file mode 100644
index 0000000..fe2f529
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java
@@ -0,0 +1,201 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@xxxxxxxxxx>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+package org.spearce.egit.core.ignores;
+
+import java.util.HashMap;
+import java.util.LinkedList;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.runtime.CoreException;
+
+/**
+ * This class implements a cache of ignore patterns for an Eclipse project: it
+ * holds a list of ignore patterns, stored in 'ignoreFiles' against the ignore
+ * file handle. We also keep an ignoreFilesIndex of the ignore file handle
+ * stored against the directory names (relative to the 'checkoutDir') of the
+ * ignore ignoreFiles in 'ignoreFilesIndex'. We use this to access the cache and
+ * retrieve the list of ignore patterns. This is because when trying to
+ * determine whether a resource is ignored we must first try the ignore file in
+ * the directory of the resource, and if it doesn't match, then in the directory
+ * up from that, and so on, al the way up to the checkout directory.
+ */
+class IgnoreProjectCache {
+	/**
+	 * the directory of the project, relative to the checkout, with a trailing
+	 * slash, except when in the checkout directory in which case it will be
+	 * empty
+	 */
+	private String projectDirInCheckout = null;
+
+	/**
+	 * Map used to find .gitignore ignore files in ignoreFiles. key=directory
+	 * path of the .gitignore file, relative to the checkout directory,
+	 * value=IFile to use to ignoreFilesIndex ignoreFiles
+	 */
+	private final HashMap<String, IFile> ignoreFilesIndex = new HashMap<String, IFile>();
+
+	/**
+	 * Map with .gitignore ignore files and their exclude patterns.
+	 * key=.gitignore file handle, value=list with its ignore patterns
+	 */
+	private final HashMap<IFile, LinkedList<Exclude>> ignoreFiles = new HashMap<IFile, LinkedList<Exclude>>();
+
+	/*
+	 * Constructors
+	 */
+
+	/**
+	 * Constructor
+	 * 
+	 * @param projectDirInCheckout
+	 *            the directory of the project, relative to the checkout
+	 */
+	IgnoreProjectCache(final String projectDirInCheckout) {
+		if (projectDirInCheckout == null) {
+			throw new ExceptionInInitializerError(
+					"NULL is not a valid project directory");
+		}
+
+		this.projectDirInCheckout = projectDirInCheckout;
+		if (!this.projectDirInCheckout.isEmpty()
+				&& !this.projectDirInCheckout.endsWith("/")) {
+			this.projectDirInCheckout = this.projectDirInCheckout.concat("/");
+		}
+	}
+
+	/*
+	 * Methods
+	 */
+
+	synchronized void clear() {
+		ignoreFilesIndex.clear();
+
+		for (final LinkedList<Exclude> excludeList : ignoreFiles.values()) {
+			excludeList.clear();
+		}
+		ignoreFiles.clear();
+	}
+
+	synchronized void importProjectIgnores(final IResource[] projectChildren) {
+		if (projectChildren == null) {
+			return;
+		}
+
+		for (final IResource projectChild : projectChildren) {
+			if (projectChild != null) {
+				if (projectChild instanceof IFile) {
+					if (projectChild.getName().equals(".gitignore")) {
+						String projectRelativeDir = projectChild
+								.getProjectRelativePath().removeLastSegments(1)
+								.toString();
+						if (!projectRelativeDir.isEmpty()) {
+							projectRelativeDir = projectRelativeDir + "/";
+						}
+						final String ignoreFileBaseDir = projectDirInCheckout
+								+ projectRelativeDir;
+						processIgnoreFile((IFile) projectChild,
+								ignoreFileBaseDir, IResourceDelta.ADDED,
+								IResourceDelta.CONTENT);
+					}
+				} else if (projectChild instanceof IFolder) {
+					try {
+						importProjectIgnores(((IFolder) projectChild).members());
+					} catch (final CoreException e) {
+						/* swallow */
+					}
+				} else {
+					/* FIXME: signal an error */
+					System.out.println("Unhandled resource type in"
+							+ " processResourceForIgnoreChild: "
+							+ projectChild.getClass().getName());
+				}
+			}
+		}
+	}
+
+	/**
+	 * This method parses a .gitignore file and stores the patterns for use by
+	 * the plugin. It is shared between startup of the workspace and changes to
+	 * the workspace.
+	 * 
+	 * @param ignoreFile
+	 *            the .gitignore file
+	 * @param ignoreFileBaseDir
+	 *            the directory of the ignore file, relative to the checkout
+	 *            directory and with a trailing slash (except when in the
+	 *            checkout directory, in which case it will be an empty string).
+	 *            Slashes are in Unix format: forward slashes
+	 * @param changeKind
+	 *            the kind of the change
+	 * @param changeFlags
+	 *            further information on the change
+	 */
+	synchronized void processIgnoreFile(final IFile ignoreFile,
+			final String ignoreFileBaseDir, final int changeKind,
+			final int changeFlags) {
+		if ((ignoreFile == null) || (changeKind == IResourceDelta.NO_CHANGE)) {
+			return;
+		}
+
+		if (((changeKind & IResourceDelta.ADDED) == IResourceDelta.ADDED)
+				|| ((changeKind & IResourceDelta.ADDED_PHANTOM) == IResourceDelta.ADDED_PHANTOM)
+				|| (((changeKind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED) && ((changeFlags & IResourceDelta.CONTENT) == IResourceDelta.CONTENT))) {
+			ignoreFiles.put(ignoreFile, IgnoreFile.parseIgnoreFile(
+					ignoreFileBaseDir, ignoreFile));
+			ignoreFilesIndex.put(ignoreFileBaseDir, ignoreFile);
+		} else if (((changeKind & IResourceDelta.REMOVED) == IResourceDelta.REMOVED)
+				|| ((changeKind & IResourceDelta.REMOVED_PHANTOM) == IResourceDelta.REMOVED_PHANTOM)) {
+			ignoreFiles.remove(ignoreFile);
+			ignoreFilesIndex.remove(ignoreFileBaseDir);
+		} else {
+			System.out.println("Unhandled change combination kind/flags: "
+					+ changeKind + "/" + changeFlags);
+		}
+
+		// int changeKind = projectChild.getKind();
+		// int changeFlags = projectChild.getFlags();
+		// switch (changeKind) {
+		// case IResourceDelta.ADDED:
+		// case IResourceDelta.ADDED_PHANTOM:
+		// if ((changeFlags & IResourceDelta.MOVED_FROM) ==
+		// IResourceDelta.MOVED_FROM) {
+		// /*
+		// * The resource has moved: getMovedToPath will
+		// * return the path of where it was moved to.
+		// */
+		// break;
+		// }
+		//
+		// /* simply parse the content and process it */
+		// break;
+		//
+		// case IResourceDelta.REMOVED:
+		// case IResourceDelta.REMOVED_PHANTOM:
+		// /* remove the patterns from the file */
+		// break;
+		//
+		// case IResourceDelta.CHANGED:
+		// /* this one is more involved, also have to deal with moved
+		// ignoreFiles */
+		// if ((changeFlags & IResourceDelta.REPLACED) ==
+		// IResourceDelta.REPLACED) {
+		// /*
+		// * The resource has moved: getMovedToPath will
+		// * return the path of where it was moved to.
+		// */
+		// }
+		// break;
+		//
+		// default:
+		// break;
+		// }
+	}
+}
\ No newline at end of file
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java
new file mode 100644
index 0000000..4b6bf61
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java
@@ -0,0 +1,308 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@xxxxxxxxxx>
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * See LICENSE for the full license text, also available.
+ *******************************************************************************/
+package org.spearce.egit.core.ignores;
+
+import java.io.File;
+import java.util.HashMap;
+
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+import org.eclipse.core.resources.IResourceDelta;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
+import org.eclipse.core.runtime.Path;
+import org.spearce.egit.core.project.RepositoryMapping;
+import org.spearce.jgit.lib.Repository;
+import org.spearce.jgit.lib.RepositoryConfig;
+
+/**
+ * This class manages the ignore data for a single repository (and its
+ * checkout). It implements the model that a single repository can contain
+ * multiple projects.
+ * 
+ * It contains a cache that holds ignore data on a per-project basis and it also
+ * contains a cache that holds ignore data that does not belong to any project
+ * but still belongs to the repository. The latter cache we need because Eclipse
+ * will not send change events for those files; we have to re-read these files
+ * every time there is a change.
+ */
+class IgnoreRepositoryCache {
+	/** the repository */
+	private Repository repository = null;
+
+	/** the checkout directory for the repository */
+	private String checkoutDir = null;
+
+	/** the cache that holds ignore data on a per-project basis */
+	private final HashMap<IProject, IgnoreProjectCache> projects = new HashMap<IProject, IgnoreProjectCache>();
+
+	/** cache that holds ignore data that does not belong to any project */
+	private final IgnoreProjectCache outside = new IgnoreProjectCache("");
+
+	/** cache that holds ignore data of the .git/info/exclude files */
+	private final IgnoreProjectCache infoExclude = new IgnoreProjectCache("");
+
+	/** cache that holds ignore data of the core exclude setting */
+	private final IgnoreProjectCache coreExcludes = new IgnoreProjectCache("");
+
+	/** the .git/info/exclude file */
+	private IgnoreFileOutside infoExcludesFile = null;
+
+	/** the core.excludes file setting from the config */
+	private IgnoreFileOutside coreExcludesFile = null;
+
+	/** the core.excludesfile setting */
+	private String coreExcludesSetting = null;
+
+	/**
+	 * Retrieve a project mapping from the projects cache. When the project is
+	 * not yet in the cache then create a new mapping for it and store it in the
+	 * cache first.
+	 * 
+	 * @param project
+	 *            the project to retrieve from the projects cache
+	 * @return the project mapping in the cache
+	 */
+	private IgnoreProjectCache getProjectFromCache(final IProject project) {
+		IgnoreProjectCache cache = projects.get(project);
+		if (cache == null) {
+			cache = new IgnoreProjectCache(RepositoryMapping
+					.getMapping(project).getRepoRelativePath(project));
+			projects.put(project, cache);
+		}
+		return cache;
+	}
+
+	/*
+	 * Constructors
+	 */
+
+	/**
+	 * Constructor
+	 * 
+	 * @param repository
+	 *            the repository
+	 */
+	IgnoreRepositoryCache(final Repository repository) {
+		if (repository == null) {
+			throw new ExceptionInInitializerError(
+					"NULL is not a valid repository");
+		}
+		this.repository = repository;
+		this.checkoutDir = repository.getWorkDir().getAbsolutePath();
+	}
+
+	/*
+	 * Methods
+	 */
+
+	synchronized void clear() {
+		for (final IgnoreProjectCache projectCache : projects.values()) {
+			projectCache.clear();
+		}
+		projects.clear();
+		outside.clear();
+		infoExclude.clear();
+		coreExcludes.clear();
+	}
+
+	/**
+	 * @param project
+	 *            the project for which to remove the ignore data
+	 */
+	synchronized void uncacheProject(final IProject project) {
+		if (project == null) {
+			return;
+		}
+
+		final IgnoreProjectCache cache = projects.get(project);
+		if (cache == null) {
+			return;
+		}
+
+		/*
+		 * FIXME: remove everything that is not 'outside' to remaining projects
+		 */
+
+		cache.clear();
+
+		projects.remove(project);
+	}
+
+	/**
+	 * Process all project children: look for .gitignore files and read in the
+	 * ignore patterns.
+	 * 
+	 * @param project
+	 *            the project
+	 * @param projectChildren
+	 *            the project children
+	 */
+	synchronized void importProjectIgnores(final IProject project,
+			final IResource[] projectChildren) {
+		if ((project == null) || (projectChildren == null)) {
+			return;
+		}
+
+		final IgnoreProjectCache projectCache = getProjectFromCache(project);
+		projectCache.importProjectIgnores(projectChildren);
+	}
+
+	/*
+	 * walk directory tree up looking for .gitignore files until in the checkout
+	 * directory
+	 */
+	synchronized boolean importProjectIgnoresOutside(final IProject project) {
+		boolean changes = false;
+
+		String projectDirectory = RepositoryMapping.getMapping(project)
+				.getRepoRelativePath(project);
+		while (!projectDirectory.isEmpty()) {
+			final int pos = projectDirectory.lastIndexOf('/');
+			if (pos < 0) {
+				projectDirectory = "";
+			} else {
+				projectDirectory = projectDirectory.substring(0, pos) + "/";
+			}
+
+			final IgnoreFileOutside ignoreFile = new IgnoreFileOutside(
+					repository, projectDirectory, ".gitignore");
+			if (!ignoreFile.isSynchronized(0)) {
+				changes = true;
+				String projectRelativeDir = ignoreFile.getProjectRelativePath()
+						.removeLastSegments(1).toString();
+				if (!projectRelativeDir.isEmpty()) {
+					projectRelativeDir = projectRelativeDir + "/";
+				}
+				final String ignoreFileBaseDir = projectDirectory
+						+ projectRelativeDir;
+				outside.processIgnoreFile(ignoreFile, ignoreFileBaseDir,
+						IResourceDelta.ADDED, IResourceDelta.CONTENT);
+			}
+		}
+
+		return changes;
+	}
+
+	synchronized boolean importRepositoryInfoExclude() {
+		readRepositoryInfoExcludesFile();
+
+		if ((infoExcludesFile != null) && !infoExcludesFile.isSynchronized(0)) {
+			try {
+				infoExcludesFile.refreshLocal(IResource.DEPTH_ZERO, null);
+			} catch (final CoreException e) {
+				/* swallow */
+			}
+			infoExclude.clear();
+			infoExclude.processIgnoreFile(infoExcludesFile, "",
+					IResourceDelta.ADDED, IResourceDelta.CONTENT);
+			return true;
+		}
+		return false;
+	}
+
+	synchronized boolean importRepositoryCoreExclude() {
+		readRepositoryCoreExcludesSetting();
+		if ((coreExcludesFile != null) && !coreExcludesFile.isSynchronized(0)) {
+			try {
+				coreExcludesFile.refreshLocal(IResource.DEPTH_ZERO, null);
+			} catch (final CoreException e) {
+				/* swallow */
+			}
+			coreExcludes.clear();
+			coreExcludes.processIgnoreFile(coreExcludesFile, "",
+					IResourceDelta.ADDED, IResourceDelta.CONTENT);
+			return true;
+		}
+		return false;
+	}
+
+	/*
+	 * Private Methods
+	 */
+
+	private boolean readRepositoryInfoExcludesFile() {
+		boolean changes = false;
+
+		if (infoExcludesFile == null) {
+			infoExcludesFile = new IgnoreFileOutside(this.repository, "",
+					".git/info/exclude");
+			changes = true;
+			if (!infoExcludesFile.exists()) {
+				infoExcludesFile = null;
+				changes = false;
+			}
+		} else {
+			if (!infoExcludesFile.exists()) {
+				infoExcludesFile = null;
+				changes = true;
+			}
+		}
+
+		return changes;
+	}
+
+	private boolean readRepositoryCoreExcludesSetting() {
+		final RepositoryConfig config = repository.getConfig();
+		if (config == null) {
+			return false;
+		}
+
+		boolean changes = false;
+		String newCoreExcludesSetting = config.getString("core", null,
+				"excludesfile");
+		if (newCoreExcludesSetting != null) {
+			/* FIXME check this! both for per-repo and global */
+			if (!newCoreExcludesSetting.equals(coreExcludesSetting)) {
+				changes = true;
+
+				final IPath newCoreExcludesSettingPath = new Path(
+						newCoreExcludesSetting);
+				newCoreExcludesSetting = newCoreExcludesSettingPath
+						.toOSString();
+				if (!newCoreExcludesSettingPath.isAbsolute()) {
+					newCoreExcludesSetting = repository.getWorkDir()
+							.getAbsolutePath().toString()
+							+ File.separator + newCoreExcludesSetting;
+				}
+
+				if (coreExcludesFile != null) {
+					coreExcludesFile = null;
+				}
+
+				final int pos = newCoreExcludesSetting
+						.lastIndexOf(File.separatorChar);
+				final String directory = newCoreExcludesSetting.substring(0,
+						pos);
+				final String resourceBasename = newCoreExcludesSetting
+						.substring(pos + 1);
+
+				coreExcludes.clear();
+				coreExcludesSetting = newCoreExcludesSetting;
+				coreExcludesFile = new IgnoreFileOutside(null, directory,
+						resourceBasename);
+			}
+		} else {
+			changes = (coreExcludesSetting != null);
+			coreExcludesSetting = null;
+			coreExcludesFile = null;
+		}
+		return changes;
+	}
+	
+	/*
+	 * Getters / Setters
+	 */
+
+	/**
+	 * @return the checkoutDir
+	 */
+	public String getCheckoutDir() {
+		return checkoutDir;
+	}
+}
\ No newline at end of file
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java b/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java
index 31d5483..414bd83 100644
--- a/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java
@@ -40,6 +40,7 @@
 import org.spearce.egit.core.CoreText;
 import org.spearce.egit.core.GitCorePreferences;
 import org.spearce.egit.core.GitProvider;
+import org.spearce.egit.core.ignores.GitIgnoreData;
 import org.spearce.jgit.lib.Repository;
 import org.spearce.jgit.lib.WindowCache;
 import org.spearce.jgit.lib.WindowCacheConfig;
@@ -64,9 +65,11 @@ public void resourceChanged(final IResourceChangeEvent event) {
 			switch (event.getType()) {
 			case IResourceChangeEvent.PRE_CLOSE:
 				uncache((IProject) event.getResource());
+				GitIgnoreData.uncacheProject(event.getResource());
 				break;
 			case IResourceChangeEvent.PRE_DELETE:
 				delete((IProject) event.getResource());
+				GitIgnoreData.uncacheProject(event.getResource());
 				break;
 			default:
 				break;
@@ -84,6 +87,7 @@ public void resourceChanged(final IResourceChangeEvent event) {
 	 */
 	public static void attachToWorkspace(final boolean includeChange) {
 		trace("attachToWorkspace - addResourceChangeListener");
+		GitIgnoreData.importWorkspaceIgnores();
 		ResourcesPlugin.getWorkspace().addResourceChangeListener(
 				rcl,
 				(includeChange ? IResourceChangeEvent.POST_CHANGE : 0)
@@ -97,6 +101,7 @@ public static void attachToWorkspace(final boolean includeChange) {
 	public static void detachFromWorkspace() {
 		trace("detachFromWorkspace - removeResourceChangeListener");
 		ResourcesPlugin.getWorkspace().removeResourceChangeListener(rcl);
+		GitIgnoreData.clear();
 	}
 
 	/**
-- 
1.6.0.6

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html

[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]

  Powered by Linux