git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
@ 2009-03-26 21:34 Ferry Huberts
  2009-03-26 21:34 ` [EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup Ferry Huberts
                   ` (3 more replies)
  0 siblings, 4 replies; 16+ messages in thread
From: Ferry Huberts @ 2009-03-26 21:34 UTC (permalink / raw)
  To: git; +Cc: Shawn O. Pearce, Robin Rosenberg, Ferry Huberts

This is the first - early - code that adds ignore functionality to EGit.
Currently it reads in all ignore patterns upon workspace startup into an
ignore cache. From this cache the ignore state of a resource is evaluated
in the same fashion as git does.

The code does not yet react to changes in ignore files but I'm planning to add
that soon and I can share a lot of code for that.

I send this code to receive feedback and to give you insight into what I'm
doing with it. I'm new both to EGit programming and Eclipse programming so
there might be things that could be done more elegantly :-)

A few notes:
- The patches are rebased on the current master (e3440623)
- The order of the patches must be re-arranged, but that is rather easy. The
  correct order - once finished - would be:
    Build up the ignore patterns cache upon workspace startup.
    Use the ignore patterns cache to determine ignores
    Enable the ignore handling of the plugin
    Optimise ignore evaluation
    Do not set .git as a Team ignore pattern
- The core.excludesfile code is currently untested, the other code seems to be
  in a good state.
- There are a few FIXMEs in the code with questions and tasks. It's a work in
  progress and these will disappear.

Ferry Huberts (5):
  Build up the ignore patterns cache upon workspace startup.
  Enable the ignore handling of the plugin
  Optimise ignore evaluation
  Do not set .git as a Team ignore pattern
  Use the ignore patterns cache to determine ignores

 org.spearce.egit.core/META-INF/MANIFEST.MF         |    1 +
 org.spearce.egit.core/plugin.xml                   |    6 -
 .../src/org/spearce/egit/core/ignores/DType.java   |   44 ++
 .../src/org/spearce/egit/core/ignores/Exclude.java |  243 +++++++++
 .../spearce/egit/core/ignores/GitIgnoreData.java   |  180 +++++++
 .../org/spearce/egit/core/ignores/IgnoreFile.java  |   82 +++
 .../egit/core/ignores/IgnoreFileOutside.java       |  543 ++++++++++++++++++++
 .../egit/core/ignores/IgnoreProjectCache.java      |  245 +++++++++
 .../egit/core/ignores/IgnoreRepositoryCache.java   |  358 +++++++++++++
 .../org/spearce/egit/core/op/TrackOperation.java   |    7 +-
 .../spearce/egit/core/project/GitProjectData.java  |    8 +
 .../decorators/DecoratableResourceAdapter.java     |   11 +-
 org.spearce.jgit/META-INF/MANIFEST.MF              |    1 +
 13 files changed, 1712 insertions(+), 17 deletions(-)
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java
 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

^ permalink raw reply	[flat|nested] 16+ messages in thread

* [EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup.
  2009-03-26 21:34 [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Ferry Huberts
@ 2009-03-26 21:34 ` Ferry Huberts
  2009-03-26 21:34   ` [EGIT] [PATCH RFC v1 2/5] Enable the ignore handling of the plugin Ferry Huberts
  2009-03-29  9:23 ` [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Robin Rosenberg
                   ` (2 subsequent siblings)
  3 siblings, 1 reply; 16+ messages in thread
From: Ferry Huberts @ 2009-03-26 21:34 UTC (permalink / raw)
  To: git; +Cc: Shawn O. Pearce, Robin Rosenberg, Ferry Huberts

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@pelagic.nl>
---
 .../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@pelagic.nl>
+ *
+ * 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@pelagic.nl>
+ *
+ * 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@pelagic.nl>
+ *
+ * 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@pelagic.nl>
+ *
+ * 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@pelagic.nl>
+ *
+ * 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@pelagic.nl>
+ *
+ * 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

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [EGIT] [PATCH RFC v1 2/5] Enable the ignore handling of the plugin
  2009-03-26 21:34 ` [EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup Ferry Huberts
@ 2009-03-26 21:34   ` Ferry Huberts
  2009-03-26 21:34     ` [EGIT] [PATCH RFC v1 3/5] Optimise ignore evaluation Ferry Huberts
  0 siblings, 1 reply; 16+ messages in thread
From: Ferry Huberts @ 2009-03-26 21:34 UTC (permalink / raw)
  To: git; +Cc: Shawn O. Pearce, Robin Rosenberg, Ferry Huberts

Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
---
 org.spearce.egit.core/META-INF/MANIFEST.MF         |    1 +
 .../org/spearce/egit/core/op/TrackOperation.java   |    6 +++---
 .../decorators/DecoratableResourceAdapter.java     |    4 ++--
 3 files changed, 6 insertions(+), 5 deletions(-)

diff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF
index 20df15f..546ea72 100644
--- a/org.spearce.egit.core/META-INF/MANIFEST.MF
+++ b/org.spearce.egit.core/META-INF/MANIFEST.MF
@@ -13,6 +13,7 @@ Require-Bundle: org.eclipse.core.runtime,
  org.eclipse.core.filesystem,
  org.eclipse.ui
 Export-Package: org.spearce.egit.core,
+ org.spearce.egit.core.ignores;x-friends:="org.spearce.egit.ui",
  org.spearce.egit.core.internal.storage;x-friends:="org.spearce.egit.ui",
  org.spearce.egit.core.internal.util;x-friends:="org.spearce.egit.ui",
  org.spearce.egit.core.op,
diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java
index 29b4344..4a4b93c 100644
--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java
@@ -24,9 +24,9 @@
 import org.eclipse.core.runtime.IAdaptable;
 import org.eclipse.core.runtime.IProgressMonitor;
 import org.eclipse.core.runtime.NullProgressMonitor;
-import org.eclipse.team.core.Team;
 import org.spearce.egit.core.Activator;
 import org.spearce.egit.core.CoreText;
+import org.spearce.egit.core.ignores.GitIgnoreData;
 import org.spearce.egit.core.project.RepositoryMapping;
 import org.spearce.jgit.lib.GitIndex;
 import org.spearce.jgit.lib.GitIndex.Entry;
@@ -96,12 +96,12 @@ public boolean visit(IResource resource) throws CoreException {
 									// by explicitly selecting and invoking track on it.
 									if (resource.getType() == IResource.FILE) {
 										Entry entry = index.getEntry(repoPath);
-										if (!Team.isIgnoredHint(resource) || entry != null && entry.isAssumedValid()) {
+										if (!GitIgnoreData.isIgnored(resource) || ((entry != null) && entry.isAssumedValid())) {
 											entry = index.add(rm.getWorkDir(), new File(rm.getWorkDir(), repoPath));
 											entry.setAssumeValid(false);
 										}
 									}
-									if (Team.isIgnoredHint(resource))
+									if (GitIgnoreData.isIgnored(resource))
 										return false;
 
 								} catch (IOException e) {
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
index 5c68d5b..4c740d5 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
@@ -22,10 +22,10 @@
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IWorkspaceRoot;
 import org.eclipse.jface.preference.IPreferenceStore;
-import org.eclipse.team.core.Team;
 import org.spearce.egit.core.AdaptableFileTreeIterator;
 import org.spearce.egit.core.ContainerTreeIterator;
 import org.spearce.egit.core.ContainerTreeIterator.ResourceEntry;
+import org.spearce.egit.core.ignores.GitIgnoreData;
 import org.spearce.egit.core.project.RepositoryMapping;
 import org.spearce.egit.ui.Activator;
 import org.spearce.egit.ui.UIPreferences;
@@ -363,7 +363,7 @@ private static boolean timestampMatches(DirCacheEntry indexEntry,
 
 	private static boolean isIgnored(IResource resource) {
 		// TODO: Also read ignores from .git/info/excludes et al.
-		return Team.isIgnoredHint(resource);
+		return GitIgnoreData.isIgnored(resource);
 	}
 
 	public String getName() {
-- 
1.6.0.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [EGIT] [PATCH RFC v1 3/5] Optimise ignore evaluation
  2009-03-26 21:34   ` [EGIT] [PATCH RFC v1 2/5] Enable the ignore handling of the plugin Ferry Huberts
@ 2009-03-26 21:34     ` Ferry Huberts
  2009-03-26 21:34       ` [EGIT] [PATCH RFC v1 4/5] Do not set .git as a Team ignore pattern Ferry Huberts
  0 siblings, 1 reply; 16+ messages in thread
From: Ferry Huberts @ 2009-03-26 21:34 UTC (permalink / raw)
  To: git; +Cc: Shawn O. Pearce, Robin Rosenberg, Ferry Huberts

Do not call GitIgnoreData.isIgnored(resource) multiple times when
not needed.

Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
---
 .../org/spearce/egit/core/op/TrackOperation.java   |    5 +++--
 .../decorators/DecoratableResourceAdapter.java     |    9 ++-------
 2 files changed, 5 insertions(+), 9 deletions(-)

diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java b/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java
index 4a4b93c..a64c1dd 100644
--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java
@@ -94,14 +94,15 @@ public boolean visit(IResource resource) throws CoreException {
 									// first. If a resource within a ignored folder is marked
 									// we ignore it here, i.e. there is no way to unmark it expect
 									// by explicitly selecting and invoking track on it.
+									boolean ignored = GitIgnoreData.isIgnored(resource);
 									if (resource.getType() == IResource.FILE) {
 										Entry entry = index.getEntry(repoPath);
-										if (!GitIgnoreData.isIgnored(resource) || ((entry != null) && entry.isAssumedValid())) {
+										if (!ignored || ((entry != null) && entry.isAssumedValid())) {
 											entry = index.add(rm.getWorkDir(), new File(rm.getWorkDir(), repoPath));
 											entry.setAssumeValid(false);
 										}
 									}
-									if (GitIgnoreData.isIgnored(resource))
+									if (ignored)
 										return false;
 
 								} catch (IOException e) {
diff --git a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
index 4c740d5..7b48fd8 100644
--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java
@@ -118,7 +118,7 @@ private void extractResourceProperties(TreeWalk treeWalk) {
 		if (resourceEntry == null)
 			return;
 
-		if (isIgnored(resourceEntry.getResource())) {
+		if (GitIgnoreData.isIgnored(resourceEntry.getResource())) {
 			ignored = true;
 			return;
 		}
@@ -261,7 +261,7 @@ public boolean shouldBeRecursive() {
 
 	private void extractContainerProperties(TreeWalk treeWalk) throws IOException {
 
-		if (isIgnored(resource)) {
+		if (GitIgnoreData.isIgnored(resource)) {
 			ignored = true;
 			return;
 		}
@@ -361,11 +361,6 @@ private static boolean timestampMatches(DirCacheEntry indexEntry,
 		}
 	}
 
-	private static boolean isIgnored(IResource resource) {
-		// TODO: Also read ignores from .git/info/excludes et al.
-		return GitIgnoreData.isIgnored(resource);
-	}
-
 	public String getName() {
 		return resource.getName();
 	}
-- 
1.6.0.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [EGIT] [PATCH RFC v1 4/5] Do not set .git as a Team ignore pattern
  2009-03-26 21:34     ` [EGIT] [PATCH RFC v1 3/5] Optimise ignore evaluation Ferry Huberts
@ 2009-03-26 21:34       ` Ferry Huberts
  2009-03-26 21:34         ` [EGIT] [PATCH RFC v1 5/5] Use the ignore patterns cache to determine ignores Ferry Huberts
  0 siblings, 1 reply; 16+ messages in thread
From: Ferry Huberts @ 2009-03-26 21:34 UTC (permalink / raw)
  To: git; +Cc: Shawn O. Pearce, Robin Rosenberg, Ferry Huberts

The .git ignore pattern is only valid in the context that it matches
a .git directory that is actually a repository.

Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
---
 org.spearce.egit.core/plugin.xml |    6 ------
 1 files changed, 0 insertions(+), 6 deletions(-)

diff --git a/org.spearce.egit.core/plugin.xml b/org.spearce.egit.core/plugin.xml
index 77ecebf..ff24ac7 100644
--- a/org.spearce.egit.core/plugin.xml
+++ b/org.spearce.egit.core/plugin.xml
@@ -11,10 +11,4 @@
 		id="org.spearce.egit.core.GitProvider">
 	</repository>
   </extension>
-  <extension
-        point="org.eclipse.team.core.ignore">
-     <ignore
-           enabled="true"
-           pattern=".git"/>
-  </extension>   
 </plugin>
-- 
1.6.0.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* [EGIT] [PATCH RFC v1 5/5] Use the ignore patterns cache to determine ignores
  2009-03-26 21:34       ` [EGIT] [PATCH RFC v1 4/5] Do not set .git as a Team ignore pattern Ferry Huberts
@ 2009-03-26 21:34         ` Ferry Huberts
  0 siblings, 0 replies; 16+ messages in thread
From: Ferry Huberts @ 2009-03-26 21:34 UTC (permalink / raw)
  To: git; +Cc: Shawn O. Pearce, Robin Rosenberg, Ferry Huberts

Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>
---
 .../src/org/spearce/egit/core/ignores/DType.java   |   44 ++++++
 .../src/org/spearce/egit/core/ignores/Exclude.java |  143 ++++++++++++++++----
 .../spearce/egit/core/ignores/GitIgnoreData.java   |   43 ++++++-
 .../egit/core/ignores/IgnoreProjectCache.java      |   44 ++++++
 .../egit/core/ignores/IgnoreRepositoryCache.java   |   68 ++++++++--
 .../spearce/egit/core/project/GitProjectData.java  |    3 +
 org.spearce.jgit/META-INF/MANIFEST.MF              |    1 +
 7 files changed, 307 insertions(+), 39 deletions(-)
 create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java

diff --git a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java
new file mode 100644
index 0000000..5661dca
--- /dev/null
+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java
@@ -0,0 +1,44 @@
+/*******************************************************************************
+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>
+ *
+ * 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 org.eclipse.core.resources.IFile;
+import org.eclipse.core.resources.IFolder;
+import org.eclipse.core.resources.IProject;
+import org.eclipse.core.resources.IResource;
+
+/**
+ * This class describes a file type in the same way as git does.
+ * 
+ * The git definition can be found in the source file cache.h. The code can be
+ * found in the source file dir.c:get_dtype
+ */
+enum DType {
+	DT_UNKNOWN, DT_DIR, DT_REG, DT_LINK;
+
+	/*
+	 * Interface Methods
+	 */
+
+	static DType get(final IResource resource) {
+		if (resource == null) {
+			return DT_UNKNOWN;
+		}
+		if (resource instanceof IFile) {
+			return DT_REG;
+		}
+		if ((resource instanceof IFolder) || (resource instanceof IProject)) {
+			return DT_DIR;
+		}
+		if (resource.isLinked()) {
+			return DT_LINK;
+		}
+
+		return DT_UNKNOWN;
+	}
+}
\ No newline at end of file
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
index c4c48e9..eaddd17 100644
--- 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
@@ -9,6 +9,9 @@
 
 import java.util.regex.Pattern;
 
+import org.spearce.jgit.errors.InvalidPatternException;
+import org.spearce.jgit.fnmatch.FileNameMatcher;
+
 /**
  * This class describes an ignore pattern in the same way as git does, with some
  * extra information to support Eclipse specific functionality.
@@ -105,6 +108,110 @@ Exclude(final String pattern, final String base,
 	}
 
 	/*
+	 * Interface Methods
+	 */
+
+	/**
+	 * Tries to match a given resource to the Exclude
+	 * 
+	 * @param pathName
+	 *            the full path of the resource, relative to the checkout
+	 *            directory
+	 * @param baseName
+	 *            the baseName of the resource
+	 * @param resourceType
+	 *            the type of the resource
+	 * @return true when the resource matches this Exclude, false otherwise
+	 * 
+	 */
+	boolean isMatch(final String pathName, final String baseName,
+			final DType resourceType) {
+		/* this is needed to make the exact same match as git does */
+		String xbase = pathName;
+		final int pos = xbase.lastIndexOf('/');
+		if (pos < 0) {
+			xbase = "";
+		} else {
+			xbase = xbase.substring(0, pos + 1);
+		}
+
+		if (mustBeDir && (resourceType != DType.DT_DIR)) {
+			return false;
+		}
+
+		if (noDir) {
+			/* pattern does not contain directories. dir.c: match basename */
+			if (noWildcard) {
+				/* pattern does not contain directories and has no wildcards */
+				if (baseName.equals(pattern)) {
+					return to_exclude;
+				}
+			} else if (endsWith) {
+				/*
+				 * pattern does not contain directories and resource must end
+				 * with pattern.substring(1)
+				 */
+				if (baseName.endsWith(pattern.substring(1))) {
+					return to_exclude;
+				}
+			} else {
+				/*
+				 * pattern does not contain directories, has wildcards, and does
+				 * not end with pattern.substring(1)
+				 */
+				try {
+					final FileNameMatcher matcher = new FileNameMatcher(
+							pattern, null);
+					matcher.append(baseName);
+					if (matcher.isMatch()) {
+						return to_exclude;
+					}
+				} catch (final InvalidPatternException e) {
+					return false;
+				}
+			}
+		} else {
+			/*
+			 * pattern contains directories. dir.c: match with FNM_PATHNAME:
+			 * exclude (e.g. 'this.pattern') has base (baselen long) implicitly
+			 * in front of it.
+			 */
+			final int baselen = base.length();
+			String matchPattern = this.pattern;
+			if (matchPattern.startsWith("/")) {
+				matchPattern = matchPattern.substring(1);
+			}
+
+			if ((pathName.length() < baselen)
+					|| ((baselen > 0) && (pathName.charAt(baselen - 1) != '/'))
+					|| !pathName.substring(0, baselen).equals(xbase)) {
+				return false;
+			}
+
+			final String remainingResourceName = pathName.substring(baselen);
+			if (noWildcard) {
+				/* pattern contains directories and has no wildcards */
+				if (remainingResourceName.equals(matchPattern)) {
+					return to_exclude;
+				}
+			} else {
+				/* pattern contains directories and has wildcards */
+				try {
+					final FileNameMatcher matcher = new FileNameMatcher(
+							matchPattern, Character.valueOf('/'));
+					matcher.append(remainingResourceName);
+					if (matcher.isMatch()) {
+						return to_exclude;
+					}
+				} catch (final InvalidPatternException e) {
+					return false;
+				}
+			}
+		}
+		return false;
+	}
+
+	/*
 	 * Private Methods
 	 */
 
@@ -119,40 +226,18 @@ private boolean no_wildcard(final String string) {
 	/*
 	 * 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
+	 * @return the ignoreFileAbsolutePath
 	 */
-	public boolean isEndsWith() {
-		return endsWith;
+	public String getIgnoreFileAbsolutePath() {
+		return ignoreFileAbsolutePath;
 	}
 
 	/**
-	 * @return the noWildcard
+	 * @return the lineNumber
 	 */
-	public boolean isNoWildcard() {
-		return noWildcard;
+	public int getLineNumber() {
+		return lineNumber;
 	}
 }
\ 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
index 401a378..48cf454 100644
--- 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
@@ -14,6 +14,7 @@
 import org.eclipse.core.resources.IWorkspace;
 import org.eclipse.core.resources.ResourcesPlugin;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.team.core.Team;
 import org.spearce.egit.core.project.RepositoryMapping;
 import org.spearce.jgit.lib.Repository;
 
@@ -128,7 +129,7 @@ public synchronized static void importWorkspaceIgnores() {
 		 * 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"));
+		 * ".gitconfig")); RepositoryConfig.openUserConfig
 		 */
 
 		/*
@@ -136,4 +137,44 @@ public synchronized static void importWorkspaceIgnores() {
 		 * repositories.toString());
 		 */
 	}
+
+	/**
+	 * @param resource
+	 *            the resource to check
+	 * @return null when not matched, the matching Exclude otherwise the
+	 *         resource
+	 */
+	synchronized static Exclude isResourceExcluded(final IResource resource) {
+		if (resource == null) {
+			return null;
+		}
+
+		final RepositoryMapping mapping = RepositoryMapping
+				.getMapping(resource);
+		if (mapping == null) {
+			return null;
+		}
+
+		final IgnoreRepositoryCache cache = repositories.get(mapping
+				.getRepository());
+		if (cache == null) {
+			return null;
+		}
+
+		/* FIXME: also check global core.excludesfile, is this already covered? */
+
+		return cache.isIgnored(resource, mapping);
+	}
+
+	/**
+	 * @param resource
+	 * @return true when the resource is ignored
+	 */
+	public synchronized static boolean isIgnored(final IResource resource) {
+		if (isResourceExcluded(resource) != null) {
+			return true;
+		}
+
+		return Team.isIgnoredHint(resource);
+	}
 }
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
index fe2f529..7f35240 100644
--- 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
@@ -15,6 +15,7 @@
 import org.eclipse.core.resources.IResource;
 import org.eclipse.core.resources.IResourceDelta;
 import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IPath;
 
 /**
  * This class implements a cache of ignore patterns for an Eclipse project: it
@@ -198,4 +199,47 @@ synchronized void processIgnoreFile(final IFile ignoreFile,
 		// break;
 		// }
 	}
+
+	synchronized Exclude isIgnored(final String pathName,
+			final String baseName, final DType dType,
+			final IPath deepestDirectory) {
+		IPath searchDir = deepestDirectory;
+		String lookupKey = (searchDir.isEmpty() ? searchDir.toString()
+				: searchDir.toString() + "/");
+		final boolean result = false;
+		searcher: while (!result) {
+			/* look for the first ignore file up in the tree */
+			while (!ignoreFilesIndex.containsKey(lookupKey)) {
+				searchDir = searchDir.removeLastSegments(1);
+				if (searchDir.isEmpty()) {
+					break searcher;
+				}
+				lookupKey = (searchDir.isEmpty() ? searchDir.toString()
+						: searchDir.toString() + "/");
+			}
+			final IFile ignoreFile = ignoreFilesIndex.get(lookupKey);
+
+			/* when found then try to match the resource to those patterns */
+			if (ignoreFile != null) {
+				final LinkedList<Exclude> excludeList = ignoreFiles
+						.get(ignoreFile);
+				for (int i = excludeList.size() - 1; i >= 0; i--) {
+					final Exclude x = excludeList.get(i);
+					if (x.isMatch(pathName, baseName, dType)) {
+						return x;
+					}
+				}
+			}
+
+			if (searchDir.isEmpty()) {
+				break searcher;
+			}
+			searchDir = searchDir.removeLastSegments(1);
+			lookupKey = (searchDir.isEmpty() ? searchDir.toString() : searchDir
+					.toString()
+					+ "/");
+		}
+
+		return null;
+	}
 }
\ 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
index 4b6bf61..9bfb197 100644
--- 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
@@ -35,7 +35,7 @@
 	/** the repository */
 	private Repository repository = null;
 
-	/** the checkout directory for the repository */
+	/** the checkout directory for the repository, full path, platform specific */
 	private String checkoutDir = null;
 
 	/** the cache that holds ignore data on a per-project basis */
@@ -59,6 +59,9 @@
 	/** the core.excludesfile setting */
 	private String coreExcludesSetting = null;
 
+	/** the exclude for the repository itself */
+	private Exclude repositoryExclude = 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
@@ -95,6 +98,8 @@ IgnoreRepositoryCache(final Repository repository) {
 		}
 		this.repository = repository;
 		this.checkoutDir = repository.getWorkDir().getAbsolutePath();
+		this.repositoryExclude = new Exclude("/.git/", "", checkoutDir
+				+ "/.git/config", 1);
 	}
 
 	/*
@@ -295,14 +300,59 @@ private boolean readRepositoryCoreExcludesSetting() {
 		return changes;
 	}
 	
-	/*
-	 * Getters / Setters
-	 */
+	synchronized Exclude isIgnored(final IResource resource,
+			final RepositoryMapping mapping) {
+		final String pathName = mapping.getRepoRelativePath(resource);
+		IPath deepestDirectory = new Path(pathName).removeLastSegments(1);
+		final String baseName = resource.getName();
+		final DType dType = DType.get(resource);
+
+		/* the repository directory is always ignored */
+		if (this.repositoryExclude.isMatch(pathName, resource.getName(), DType
+				.get(resource))) {
+			return this.repositoryExclude;
+		}
 
-	/**
-	 * @return the checkoutDir
-	 */
-	public String getCheckoutDir() {
-		return checkoutDir;
+		Exclude exclude = null;
+
+		/* check project excludes */
+		final IProject project = resource.getProject();
+		if (project != null) {
+			final IgnoreProjectCache cache = projects.get(project);
+			if (cache != null) {
+				exclude = cache.isIgnored(pathName, baseName, dType,
+						deepestDirectory);
+				if (exclude != null) {
+					return exclude;
+				}
+			}
+			deepestDirectory = new Path(mapping.getRepoRelativePath(project))
+					.removeLastSegments(1);
+		} else {
+			deepestDirectory = new Path("");
+		}
+		
+		/* check excludes outside projects */
+		exclude = outside
+				.isIgnored(pathName, baseName, dType, deepestDirectory);
+		if (exclude != null) {
+			return exclude;
+		}
+		
+		/* also check info/exclude file per repo */
+		exclude = infoExclude.isIgnored(baseName, baseName, dType,
+				deepestDirectory);
+		if (exclude != null) {
+			return exclude;
+		}
+		
+		/* also check core.excludesfile per repo */
+		exclude = coreExcludes.isIgnored(baseName, baseName, dType,
+				deepestDirectory);
+		if (exclude != null) {
+			return exclude;
+		}
+
+		return null;
 	}
 }
\ 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 414bd83..09766b6 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
@@ -63,6 +63,9 @@
 		@SuppressWarnings("synthetic-access")
 		public void resourceChanged(final IResourceChangeEvent event) {
 			switch (event.getType()) {
+			case IResourceChangeEvent.POST_CHANGE:
+				// GitIgnoreData.processChangesForIgnores(event);
+				break;
 			case IResourceChangeEvent.PRE_CLOSE:
 				uncache((IProject) event.getResource());
 				GitIgnoreData.uncacheProject(event.getResource());
diff --git a/org.spearce.jgit/META-INF/MANIFEST.MF b/org.spearce.jgit/META-INF/MANIFEST.MF
index 3344c3c..e5f6478 100644
--- a/org.spearce.jgit/META-INF/MANIFEST.MF
+++ b/org.spearce.jgit/META-INF/MANIFEST.MF
@@ -7,6 +7,7 @@ Bundle-Localization: plugin
 Bundle-Vendor: %provider_name
 Export-Package: org.spearce.jgit.dircache,
  org.spearce.jgit.errors;uses:="org.spearce.jgit.lib",
+ org.spearce.jgit.fnmatch,
  org.spearce.jgit.lib,
  org.spearce.jgit.revplot,
  org.spearce.jgit.revwalk,
-- 
1.6.0.6

^ permalink raw reply related	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-03-26 21:34 [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Ferry Huberts
  2009-03-26 21:34 ` [EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup Ferry Huberts
@ 2009-03-29  9:23 ` Robin Rosenberg
  2009-03-29 10:43   ` Ferry Huberts (Pelagic)
  2009-03-30  0:40 ` Jonathan Gossage
  2009-04-05 21:02 ` Shawn O. Pearce
  3 siblings, 1 reply; 16+ messages in thread
From: Robin Rosenberg @ 2009-03-29  9:23 UTC (permalink / raw)
  To: Ferry Huberts; +Cc: git, Shawn O. Pearce


A quick reply (I might come up with more later): Ignore support should be mostly
in jgit, with only extensions into egit.

-- robin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-03-29  9:23 ` [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Robin Rosenberg
@ 2009-03-29 10:43   ` Ferry Huberts (Pelagic)
  2009-03-30  4:27     ` Shawn O. Pearce
  0 siblings, 1 reply; 16+ messages in thread
From: Ferry Huberts (Pelagic) @ 2009-03-29 10:43 UTC (permalink / raw)
  To: Robin Rosenberg; +Cc: git, Shawn O. Pearce

Robin Rosenberg wrote:
> A quick reply (I might come up with more later): Ignore support should be mostly
> in jgit, with only extensions into egit.
> 
> -- robin
I discussed this with shawn and proposed to first implement it in egit
and when we have it right then move it into jgit. I think shawn agreed
with that.

Ferry

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-03-26 21:34 [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Ferry Huberts
  2009-03-26 21:34 ` [EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup Ferry Huberts
  2009-03-29  9:23 ` [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Robin Rosenberg
@ 2009-03-30  0:40 ` Jonathan Gossage
  2009-03-30  6:18   ` Robin Rosenberg
  2009-04-05 21:02 ` Shawn O. Pearce
  3 siblings, 1 reply; 16+ messages in thread
From: Jonathan Gossage @ 2009-03-30  0:40 UTC (permalink / raw)
  To: Ferry Huberts; +Cc: git

Ferry Huberts wrote:
> This is the first - early - code that adds ignore functionality to EGit.
> Currently it reads in all ignore patterns upon workspace startup into an
> ignore cache. From this cache the ignore state of a resource is evaluated
> in the same fashion as git does.
>
> The code does not yet react to changes in ignore files but I'm planning to add
> that soon and I can share a lot of code for that.
>
> I send this code to receive feedback and to give you insight into what I'm
> doing with it. I'm new both to EGit programming and Eclipse programming so
> there might be things that could be done more elegantly :-)
>
> A few notes:
> - The patches are rebased on the current master (e3440623)
> - The order of the patches must be re-arranged, but that is rather easy. The
>   correct order - once finished - would be:
>     Build up the ignore patterns cache upon workspace startup.
>     Use the ignore patterns cache to determine ignores
>     Enable the ignore handling of the plugin
>     Optimise ignore evaluation
>     Do not set .git as a Team ignore pattern
> - The core.excludesfile code is currently untested, the other code seems to be
>   in a good state.
> - There are a few FIXMEs in the code with questions and tasks. It's a work in
>   progress and these will disappear.
>
> Ferry Huberts (5):
>   Build up the ignore patterns cache upon workspace startup.
>   Enable the ignore handling of the plugin
>   Optimise ignore evaluation
>   Do not set .git as a Team ignore pattern
>   Use the ignore patterns cache to determine ignores
>
>  org.spearce.egit.core/META-INF/MANIFEST.MF         |    1 +
>  org.spearce.egit.core/plugin.xml                   |    6 -
>  .../src/org/spearce/egit/core/ignores/DType.java   |   44 ++
>  .../src/org/spearce/egit/core/ignores/Exclude.java |  243 +++++++++
>  .../spearce/egit/core/ignores/GitIgnoreData.java   |  180 +++++++
>  .../org/spearce/egit/core/ignores/IgnoreFile.java  |   82 +++
>  .../egit/core/ignores/IgnoreFileOutside.java       |  543 ++++++++++++++++++++
>  .../egit/core/ignores/IgnoreProjectCache.java      |  245 +++++++++
>  .../egit/core/ignores/IgnoreRepositoryCache.java   |  358 +++++++++++++
>  .../org/spearce/egit/core/op/TrackOperation.java   |    7 +-
>  .../spearce/egit/core/project/GitProjectData.java  |    8 +
>  .../decorators/DecoratableResourceAdapter.java     |   11 +-
>  org.spearce.jgit/META-INF/MANIFEST.MF              |    1 +
>  13 files changed, 1712 insertions(+), 17 deletions(-)
>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java
>  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
>
> --
> To unsubscribe from this list: send the line "unsubscribe git" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
>
>
>
>   

Eclipse supplies a repository-independent ignore file list as part of the repository-type independent Team support. A first step, which would provide useful functionality would be to populate your cache with this list and enable it's use in the Egit plugin. This would accomplish the goal of enabling EGit to use ignore lists in an immediately useful way with minimal effort. As a second stage you can add support for picking up Git specific files and updating them from Eclipse.

I think you will run into problems if you try to create a workspace wide
cache. It is quite possible that one workspace could have projects that
target different Git repositories. This means that your cache would need
to look at all projects in the workspace and potentially take into
account Eclipse working sets and other such complications. You also will
need to deal with projects being added and deleted from the Eclipse
workspace.

I think a better approach might be to go for lazy cache construction
where the cache is built only when actually needed by a user operation.
The cache would then be built only for a specific Git repository. JGit
should be responsible for assembling a merged list from the various Git
files. It should also be responsible for the actual updating of the
various Git ignore files. Since I believe that the Eclipse
repository-independent ignore file list should be the lowest priority in
the merged list, it will be necessary to pass the Eclipse list to JGit
as a parameter whenever a merged list is required.

In general, you should look to do Git specific things in JGit and do
Eclipse things in Eclipse. That way JGit continues to acquire the
functionality to support any IDE and EGit is kept as simple as possible.

HTH

Jonathan Gossage

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-03-29 10:43   ` Ferry Huberts (Pelagic)
@ 2009-03-30  4:27     ` Shawn O. Pearce
  0 siblings, 0 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2009-03-30  4:27 UTC (permalink / raw)
  To: Ferry Huberts (Pelagic); +Cc: Robin Rosenberg, git

"Ferry Huberts (Pelagic)" <ferry.huberts@pelagic.nl> wrote:
> Robin Rosenberg wrote:
> > A quick reply (I might come up with more later): Ignore support should be mostly
> > in jgit, with only extensions into egit.
> > 
> I discussed this with shawn and proposed to first implement it in egit
> and when we have it right then move it into jgit. I think shawn agreed
> with that.

I may have agreed with it.  My memory isn't *that* good.  :-)

In general principal I agree with Robin, Git specific handling
should be in JGit as much as possible so we can reuse the logic in
more applications than just EGit.

But it may have been easier to get a first working prototype by doing
the code in EGit, and later pulling some of it down into JGit as we
identity what isn't EGit specific.

The problem with that is the dual licenses; code in EGit can't
be pulled down to JGit without relicensing it under the BSD.
Only the original author of the code can do that.  So if you
contribute ignore support to EGit under the EPL which is better
placed in JGit, Robin or myself can't pull it down ourselves,
we'd have to rewrite it.

But even rewriting may be difficult, as the rewrite may be too close
to the original (same language, same surrounding code, likely going
to produce a similar result).

-- 
Shawn.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-03-30  0:40 ` Jonathan Gossage
@ 2009-03-30  6:18   ` Robin Rosenberg
  0 siblings, 0 replies; 16+ messages in thread
From: Robin Rosenberg @ 2009-03-30  6:18 UTC (permalink / raw)
  To: Jonathan Gossage; +Cc: Ferry Huberts, git

måndag 30 mars 2009 02:40:59 skrev Jonathan Gossage <jgossage@gmail.com>:
> Ferry Huberts wrote:
> Eclipse supplies a repository-independent ignore file list as part of the repository-type independent Team support. A first step, which would provide useful functionality would be to populate your cache with this list and enable it's use in the Egit plugin. This would accomplish the goal of enabling EGit to use ignore lists in an immediately useful way with minimal effort. As a second stage you can add support for picking up Git specific files and updating them from Eclipse.

The current EGit obeys the Eclipse ignore rules already, so no there is no need to hurry just for that feature.

-- robin

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-03-26 21:34 [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Ferry Huberts
                   ` (2 preceding siblings ...)
  2009-03-30  0:40 ` Jonathan Gossage
@ 2009-04-05 21:02 ` Shawn O. Pearce
  2009-04-06 16:46   ` Ferry Huberts (Pelagic)
  2009-04-06 16:51   ` Ferry Huberts (Pelagic)
  3 siblings, 2 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2009-04-05 21:02 UTC (permalink / raw)
  To: Ferry Huberts; +Cc: git, Robin Rosenberg

Ferry Huberts <ferry.huberts@pelagic.nl> wrote:
> This is the first - early - code that adds ignore functionality to EGit.
> Currently it reads in all ignore patterns upon workspace startup into an
> ignore cache. From this cache the ignore state of a resource is evaluated
> in the same fashion as git does.
> 
> The code does not yet react to changes in ignore files but I'm planning to add
> that soon and I can share a lot of code for that.
> 
> I send this code to receive feedback and to give you insight into what I'm
> doing with it. I'm new both to EGit programming and Eclipse programming so
> there might be things that could be done more elegantly :-)

Ok, I finally got a chance to review this series.


We really want as much of the Git specific logic as we can in JGit
under the BSD license.  This has already been raised elsewhere in
this thread.

JGit and EGit are holding the line on Java 5 support; that means
that String.isEmpty() must be spelled as String.length() == 0
(isEmpty was added in Java 6).

Style nit: Don't put /* Constructors */, /* Methods */ or
  / * Public Methods */ comments in code, e.g.
  IgnoreProjectCache l.52-54 or GitIgnoreData l.58-61.

Style nit: Don't assign fields to their default values.

  E.g. Exclude.java l.25,33,42,.. these are being set to the
  same value that the JRE sets the field to if the field is not
  explicitly initialized.  We find it much easier to read code when
  the defaults are assumed.

Style nit: Don't use "this." to refer to members.

  Your IDE should highlight field references differently than
  parameters, and a parameter should never shadow a field name,
  thus "this." is unnecessary and makes the code much more verbose
  to read.  E.g. see Exclude.java 's constructor on l.87-108; I can't
  see the forest (the code) due to all the trees (this.) appearing.

IgnoreFileOutside: Ugh, our own implementation of IFile ?

  I'm worried about the long-term stability of the IFile API.
  Is it really frozen enough that we can implement it ourselves?
  Of course, this may be moot if much of the code was moved back
  to JGit.

IgnoreRepositoryCache: Why not put this into RepositoryMapping?

  Instead of caching it inside a static HashMap of GitIgnoreData,
  wouldn't it be better to put it into RepositoryMapping?
  The TrackOperation for example already has the RepositoryMapping
  handle in scope, saving a few lookup operations, and avoiding
  needing to manage this new additional static HashMap against leaks.


I kind of wanted to tie exclude processing (and attribute processing)
into a TreeWalk, so that we can do an n-way merge against trees and
working directories by tossing all of their AbstractTreeIterators
into a single walk, possibly apply a path filter, and let the walk
handle the per-directory ignore rules as it goes.

Most of your code seems to be built around the Eclipse IResource
model, and the idea that it gets called for a single file path
at a time, which may make it less efficient when we put it into a
TreeWalk and apply the notion of entering and exiting a subdirectory.


OK, that's about all I have for now.  Its reasonable, but still an
early series.
 
-- 
Shawn.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-04-05 21:02 ` Shawn O. Pearce
@ 2009-04-06 16:46   ` Ferry Huberts (Pelagic)
  2009-04-06 16:51   ` Ferry Huberts (Pelagic)
  1 sibling, 0 replies; 16+ messages in thread
From: Ferry Huberts (Pelagic) @ 2009-04-06 16:46 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git, Robin Rosenberg

Shawn O. Pearce wrote:
> Ferry Huberts <ferry.huberts@pelagic.nl> wrote:
>> This is the first - early - code that adds ignore functionality to EGit.
>> Currently it reads in all ignore patterns upon workspace startup into an
>> ignore cache. From this cache the ignore state of a resource is evaluated
>> in the same fashion as git does.
>>
>> The code does not yet react to changes in ignore files but I'm planning to add
>> that soon and I can share a lot of code for that.
>>
>> I send this code to receive feedback and to give you insight into what I'm
>> doing with it. I'm new both to EGit programming and Eclipse programming so
>> there might be things that could be done more elegantly :-)
> 
> Ok, I finally got a chance to review this series.
> 
> 
> We really want as much of the Git specific logic as we can in JGit
> under the BSD license.  This has already been raised elsewhere in
> this thread.
> 
ack.

> JGit and EGit are holding the line on Java 5 support; that means
> that String.isEmpty() must be spelled as String.length() == 0
> (isEmpty was added in Java 6).
ok

> 
> Style nit: Don't put /* Constructors */, /* Methods */ or
>   / * Public Methods */ comments in code, e.g.
>   IgnoreProjectCache l.52-54 or GitIgnoreData l.58-61.
> 
ok

> Style nit: Don't assign fields to their default values.
> 
>   E.g. Exclude.java l.25,33,42,.. these are being set to the
>   same value that the JRE sets the field to if the field is not
>   explicitly initialized.  We find it much easier to read code when
>   the defaults are assumed.
> 
ok

> Style nit: Don't use "this." to refer to members.
> 
>   Your IDE should highlight field references differently than
>   parameters, and a parameter should never shadow a field name,
>   thus "this." is unnecessary and makes the code much more verbose
>   to read.  E.g. see Exclude.java 's constructor on l.87-108; I can't
>   see the forest (the code) due to all the trees (this.) appearing.
> 
ok

> IgnoreFileOutside: Ugh, our own implementation of IFile ?
> 
>   I'm worried about the long-term stability of the IFile API.
>   Is it really frozen enough that we can implement it ourselves?
>   Of course, this may be moot if much of the code was moved back
>   to JGit.
> 
when we convert the code to be in jgit we will not use this api.
this will disappear.

> IgnoreRepositoryCache: Why not put this into RepositoryMapping?
> 
>   Instead of caching it inside a static HashMap of GitIgnoreData,
>   wouldn't it be better to put it into RepositoryMapping?
>   The TrackOperation for example already has the RepositoryMapping
>   handle in scope, saving a few lookup operations, and avoiding
>   needing to manage this new additional static HashMap against leaks.
> 
ok

> 
> I kind of wanted to tie exclude processing (and attribute processing)
> into a TreeWalk, so that we can do an n-way merge against trees and
> working directories by tossing all of their AbstractTreeIterators
> into a single walk, possibly apply a path filter, and let the walk
> handle the per-directory ignore rules as it goes.
> 
ok. but I need much more input than that from you on how to go about
moving the code into jgit. If you have ideas on what the rough
architecture should be of the ignore processing in jgit then please let
me know, I don't know that code at all and also don't know the approach
you'd like to take there with its architecture.

I've been thinking on and off about how to free the ignore processing
from eclipse specific apis ever since the egit/jgit issue came up. I
think it's not that hard to do, depending on the approach you'd like to
take.

you talk about TreeWalk. but what is it's prupose, in what context is it
called? etc. You can probably much faster explain that to me than I can
find out from the code (the egit code is much easier to place in context
than the jgit code I think)

> Most of your code seems to be built around the Eclipse IResource
> model, and the idea that it gets called for a single file path
> at a time, which may make it less efficient when we put it into a
> TreeWalk and apply the notion of entering and exiting a subdirectory.
> 
the way it works now is more efficient than git itself (most of the
time), since we have a cache of the ignore patterns. I compared traces
of git itself and the plugin against eachother.

> 
> OK, that's about all I have for now.  Its reasonable, but still an
> early series.
>  
yep.
please let's discuss moving it into jgit asap. so that I can complete
the feature.

BTW
In the meantime I've also added ignore preferences to the plugin on
which the user can specify whether the Eclipse Team Ignored Resources
should be taken into account. It defaults to yes. When set to no the
ignore processing complies strict to git behaviour.

looking forward to your input!

Ferry

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-04-05 21:02 ` Shawn O. Pearce
  2009-04-06 16:46   ` Ferry Huberts (Pelagic)
@ 2009-04-06 16:51   ` Ferry Huberts (Pelagic)
  2009-04-06 17:03     ` Shawn O. Pearce
  2009-04-06 17:38     ` Sverre Rabbelier
  1 sibling, 2 replies; 16+ messages in thread
From: Ferry Huberts (Pelagic) @ 2009-04-06 16:51 UTC (permalink / raw)
  To: Shawn O. Pearce; +Cc: git, Robin Rosenberg

Shawn O. Pearce wrote:
> JGit and EGit are holding the line on Java 5 support; that means
> that String.isEmpty() must be spelled as String.length() == 0
> (isEmpty was added in Java 6).

just looked in the project settings for org.spearce.egit.core and it has
java 1.5 style specified _and_ eclipse does not give me a warning on the
*.isEmpty() calls. Am i missing something here?

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit
  2009-04-06 16:51   ` Ferry Huberts (Pelagic)
@ 2009-04-06 17:03     ` Shawn O. Pearce
  2009-04-06 17:38     ` Sverre Rabbelier
  1 sibling, 0 replies; 16+ messages in thread
From: Shawn O. Pearce @ 2009-04-06 17:03 UTC (permalink / raw)
  To: Ferry Huberts (Pelagic); +Cc: git, Robin Rosenberg

"Ferry Huberts (Pelagic)" <ferry.huberts@pelagic.nl> wrote:
> Shawn O. Pearce wrote:
> > JGit and EGit are holding the line on Java 5 support; that means
> > that String.isEmpty() must be spelled as String.length() == 0
> > (isEmpty was added in Java 6).
> 
> just looked in the project settings for org.spearce.egit.core and it has
> java 1.5 style specified _and_ eclipse does not give me a warning on the
> *.isEmpty() calls. Am i missing something here?

Your workspace default JRE must be set to a Java 6.  Switch it to
Java 5 in the workspace settings.

-- 
Shawn.

^ permalink raw reply	[flat|nested] 16+ messages in thread

* Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to  EGit
  2009-04-06 16:51   ` Ferry Huberts (Pelagic)
  2009-04-06 17:03     ` Shawn O. Pearce
@ 2009-04-06 17:38     ` Sverre Rabbelier
  1 sibling, 0 replies; 16+ messages in thread
From: Sverre Rabbelier @ 2009-04-06 17:38 UTC (permalink / raw)
  To: Ferry Huberts (Pelagic); +Cc: Shawn O. Pearce, git, Robin Rosenberg

Heya,

On Mon, Apr 6, 2009 at 18:51, Ferry Huberts (Pelagic)
<ferry.huberts@pelagic.nl> wrote:
> just looked in the project settings for org.spearce.egit.core and it has
> java 1.5 style specified _and_ eclipse does not give me a warning on the
> *.isEmpty() calls. Am i missing something here?

FYI, notice the #since annotation in the JavaDoc [0].

[0] http://java.sun.com/javase/6/docs/api/java/lang/String.html#isEmpty()

-- 
Cheers,

Sverre Rabbelier

^ permalink raw reply	[flat|nested] 16+ messages in thread

end of thread, other threads:[~2009-04-06 17:40 UTC | newest]

Thread overview: 16+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2009-03-26 21:34 [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Ferry Huberts
2009-03-26 21:34 ` [EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup Ferry Huberts
2009-03-26 21:34   ` [EGIT] [PATCH RFC v1 2/5] Enable the ignore handling of the plugin Ferry Huberts
2009-03-26 21:34     ` [EGIT] [PATCH RFC v1 3/5] Optimise ignore evaluation Ferry Huberts
2009-03-26 21:34       ` [EGIT] [PATCH RFC v1 4/5] Do not set .git as a Team ignore pattern Ferry Huberts
2009-03-26 21:34         ` [EGIT] [PATCH RFC v1 5/5] Use the ignore patterns cache to determine ignores Ferry Huberts
2009-03-29  9:23 ` [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit Robin Rosenberg
2009-03-29 10:43   ` Ferry Huberts (Pelagic)
2009-03-30  4:27     ` Shawn O. Pearce
2009-03-30  0:40 ` Jonathan Gossage
2009-03-30  6:18   ` Robin Rosenberg
2009-04-05 21:02 ` Shawn O. Pearce
2009-04-06 16:46   ` Ferry Huberts (Pelagic)
2009-04-06 16:51   ` Ferry Huberts (Pelagic)
2009-04-06 17:03     ` Shawn O. Pearce
2009-04-06 17:38     ` Sverre Rabbelier

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).