{"thread":{"id":"18579","subject":"[EGIT] [PATCH RFC v1 4/5] Do not set .git as a Team ignore pattern","startedAt":"2009-03-26T21:34:22Z","lastAt":"2009-04-06T17:38:12Z","messageCount":16,"participants":["Ferry Huberts","Robin Rosenberg","Ferry Huberts (Pelagic)","Jonathan Gossage","Shawn O. Pearce","Sverre Rabbelier"],"isPatch":true,"patchVersion":1,"patchTotal":5},"messages":[{"id":"109577","messageId":"cover.1238102327.git.ferry.huberts@pelagic.nl","threadId":"18579","inReplyTo":null,"subject":"[EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Ferry Huberts","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-03-26T21:34:22Z","receivedAt":"2009-03-26T21:34:22Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"This is the first - early - code that adds ignore functionality to EGit.\nCurrently it reads in all ignore patterns upon workspace startup into an\nignore cache. From this cache the ignore state of a resource is evaluated\nin the same fashion as git does.\n\nThe code does not yet react to changes in ignore files but I'm planning to add\nthat soon and I can share a lot of code for that.\n\nI send this code to receive feedback and to give you insight into what I'm\ndoing with it. I'm new both to EGit programming and Eclipse programming so\nthere might be things that could be done more elegantly :-)\n\nA few notes:\n- The patches are rebased on the current master (e3440623)\n- The order of the patches must be re-arranged, but that is rather easy. The\n  correct order - once finished - would be:\n    Build up the ignore patterns cache upon workspace startup.\n    Use the ignore patterns cache to determine ignores\n    Enable the ignore handling of the plugin\n    Optimise ignore evaluation\n    Do not set .git as a Team ignore pattern\n- The core.excludesfile code is currently untested, the other code seems to be\n  in a good state.\n- There are a few FIXMEs in the code with questions and tasks. It's a work in\n  progress and these will disappear.\n\nFerry Huberts (5):\n  Build up the ignore patterns cache upon workspace startup.\n  Enable the ignore handling of the plugin\n  Optimise ignore evaluation\n  Do not set .git as a Team ignore pattern\n  Use the ignore patterns cache to determine ignores\n\n org.spearce.egit.core/META-INF/MANIFEST.MF         |    1 +\n org.spearce.egit.core/plugin.xml                   |    6 -\n .../src/org/spearce/egit/core/ignores/DType.java   |   44 ++\n .../src/org/spearce/egit/core/ignores/Exclude.java |  243 +++++++++\n .../spearce/egit/core/ignores/GitIgnoreData.java   |  180 +++++++\n .../org/spearce/egit/core/ignores/IgnoreFile.java  |   82 +++\n .../egit/core/ignores/IgnoreFileOutside.java       |  543 ++++++++++++++++++++\n .../egit/core/ignores/IgnoreProjectCache.java      |  245 +++++++++\n .../egit/core/ignores/IgnoreRepositoryCache.java   |  358 +++++++++++++\n .../org/spearce/egit/core/op/TrackOperation.java   |    7 +-\n .../spearce/egit/core/project/GitProjectData.java  |    8 +\n .../decorators/DecoratableResourceAdapter.java     |   11 +-\n org.spearce.jgit/META-INF/MANIFEST.MF              |    1 +\n 13 files changed, 1712 insertions(+), 17 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java\n"},{"id":"109580","messageId":"213427d4111185ae3a094a3cd578be8e6c3d5ffb.1238102327.git.ferry.huberts@pelagic.nl","threadId":"18579","inReplyTo":"cover.1238102327.git.ferry.huberts@pelagic.nl","subject":"[EGIT] [PATCH RFC v1 1/5] Build up the ignore patterns cache upon workspace startup.","fromName":"Ferry Huberts","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-03-26T21:34:23Z","receivedAt":"2009-03-26T21:34:23Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"Read in all relevant ignore files: all .gitignore files in the projects,\nall .gitignore files between the checkout directory and the project\ndirectories, the info/exclude ignore files of the repositories, the\nrepository core.excludesfile ignore files and the global\ncore.excludesfile ignore file.\n\nSigned-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>\n---\n .../src/org/spearce/egit/core/ignores/Exclude.java |  158 ++++++\n .../spearce/egit/core/ignores/GitIgnoreData.java   |  139 +++++\n .../org/spearce/egit/core/ignores/IgnoreFile.java  |   82 +++\n .../egit/core/ignores/IgnoreFileOutside.java       |  543 ++++++++++++++++++++\n .../egit/core/ignores/IgnoreProjectCache.java      |  201 ++++++++\n .../egit/core/ignores/IgnoreRepositoryCache.java   |  308 +++++++++++\n .../spearce/egit/core/project/GitProjectData.java  |    5 +\n 7 files changed, 1436 insertions(+), 0 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java\n\ndiff --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\nnew file mode 100644\nindex 0000000..c4c48e9\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java\n@@ -0,0 +1,158 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.ignores;\n+\n+import java.util.regex.Pattern;\n+\n+/**\n+ * This class describes an ignore pattern in the same way as git does, with some\n+ * extra information to support Eclipse specific functionality.\n+ * \n+ * The git definition can be found in the source file dir.h, within the\n+ * exclude_list structure definition. The code can be found in the source file\n+ * dir.c:excluded_1\n+ */\n+class Exclude {\n+\t/** the pattern to match */\n+\tprivate String pattern = null;\n+\n+\t/**\n+\t * the directory in which the pattern is anchored, relative to the checkout\n+\t * directory and with a trailing slash (except when in the checkout\n+\t * directory, in which case it will be an empty string). Slashes are in Unix\n+\t * format: forward slashes\n+\t */\n+\tprivate String base = null;\n+\n+\t/**\n+\t * true when the resource must be excluded when matched, false in case of a\n+\t * negative pattern: when it must be included\n+\t */\n+\tprivate boolean to_exclude = true;\n+\n+\t/** true when the resource must be a directory */\n+\tprivate boolean mustBeDir = false;\n+\n+\t/** true when the pattern does not contain directories */\n+\tprivate boolean noDir = false;\n+\n+\t/** true when the resource must end with pattern.substring(1) */\n+\tprivate boolean endsWith = false;\n+\n+\t/** true when the pattern has no wildcards */\n+\tprivate boolean noWildcard = false;\n+\n+\t/*\n+\t * Extra Information\n+\t */\n+\n+\t/**\n+\t * the full path name of the ignore file. Stored so that a user can ask\n+\t * 'which pattern in which ignore file makes this resource be ignored?'\n+\t */\n+\tprivate String ignoreFileAbsolutePath = null;\n+\n+\t/**\n+\t * the line number of the pattern in the ignore file. Stored for the same\n+\t * reason as the ignoreFileFullPath field\n+\t */\n+\tprivate int lineNumber = 0;\n+\n+\t/**\n+\t * Constructor. See the git source file dir.c, method add_exclude\n+\t * \n+\t * @param pattern\n+\t *            the pattern to match\n+\t * @param base\n+\t *            the directory in which the pattern is anchored, relative to\n+\t *            the checkout directory and with a trailing slash (except when\n+\t *            in the checkout directory, in which case it will be an empty\n+\t *            string). Slashes are in Unix format: forward slashes\n+\t * @param ignoreFileAbsolutePath\n+\t *            the full path name of the ignore file. Stored so that a user\n+\t *            can ask 'which pattern in which ignore file makes this\n+\t *            resource be ignored?'\n+\t * @param lineNumber\n+\t *            the line number of the pattern in the ignore file. Stored for\n+\t *            the same reason as the ignoreFileFullPath field\n+\t */\n+\tExclude(final String pattern, final String base,\n+\t\t\tfinal String ignoreFileAbsolutePath, final int lineNumber) {\n+\t\tthis.pattern = pattern;\n+\t\tthis.base = base;\n+\n+\t\tthis.to_exclude = !this.pattern.startsWith(\"!\");\n+\t\tif (!this.to_exclude) {\n+\t\t\tthis.pattern = this.pattern.substring(1);\n+\t\t}\n+\n+\t\tthis.mustBeDir = this.pattern.endsWith(\"/\");\n+\t\tif (this.mustBeDir) {\n+\t\t\tthis.pattern = this.pattern.substring(0, this.pattern.length() - 1);\n+\t\t}\n+\t\tthis.noDir = !this.pattern.contains(\"/\");\n+\t\tthis.noWildcard = no_wildcard(this.pattern);\n+\t\tthis.endsWith = ((this.pattern.charAt(0) == '*') && no_wildcard(this.pattern\n+\t\t\t\t.substring(1)));\n+\n+\t\tthis.ignoreFileAbsolutePath = ignoreFileAbsolutePath;\n+\t\tthis.lineNumber = lineNumber;\n+\t}\n+\n+\t/*\n+\t * Private Methods\n+\t */\n+\n+\tprivate static Pattern wildcardPattern = Pattern\n+\t\t\t.compile(\"^.*[\\\\*\\\\?\\\\[\\\\{].*$\");\n+\n+\t/* dir.c::no_wildcard */\n+\tprivate boolean no_wildcard(final String string) {\n+\t\treturn !wildcardPattern.matcher(string).matches();\n+\t}\n+\n+\t/*\n+\t * Getters / Setters\n+\t */\n+\n+\tpublic String getIgnoreFileAbsolutePath() {\n+\t\treturn ignoreFileAbsolutePath;\n+\t}\n+\n+\tpublic int getLineNumber() {\n+\t\treturn lineNumber;\n+\t}\n+\n+\t/**\n+\t * @return the base\n+\t */\n+\tpublic String getBase() {\n+\t\treturn base;\n+\t}\n+\n+\t/**\n+\t * @return the noDir\n+\t */\n+\tpublic boolean isNoDir() {\n+\t\treturn noDir;\n+\t}\n+\n+\t/**\n+\t * @return the endsWith\n+\t */\n+\tpublic boolean isEndsWith() {\n+\t\treturn endsWith;\n+\t}\n+\n+\t/**\n+\t * @return the noWildcard\n+\t */\n+\tpublic boolean isNoWildcard() {\n+\t\treturn noWildcard;\n+\t}\n+}\n\\ No newline at end of file\ndiff --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\nnew file mode 100644\nindex 0000000..401a378\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java\n@@ -0,0 +1,139 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.ignores;\n+\n+import java.util.HashMap;\n+\n+import org.eclipse.core.resources.IProject;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.resources.IWorkspace;\n+import org.eclipse.core.resources.ResourcesPlugin;\n+import org.eclipse.core.runtime.CoreException;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.jgit.lib.Repository;\n+\n+/**\n+ * This class provides management of ignore data. It deals with .gitignore\n+ * files, the .git/info/exclude file, and core.excludefile settings. It also\n+ * deals with with changes to those files.\n+ * \n+ * The git code for ignores can be found in its files dir.{h,c}.\n+ * \n+ * See the file Documentation/gitignore.txt in the git repository for a\n+ * description of how ignores work.\n+ */\n+public class GitIgnoreData {\n+\n+\t/*\n+\t * Ignore Data Cache\n+\t */\n+\n+\tprivate static HashMap<Repository, IgnoreRepositoryCache> repositories = new HashMap<Repository, IgnoreRepositoryCache>();\n+\n+\t/**\n+\t * Retrieve a repository mapping from the repositories cache. When the\n+\t * repository is not yet in the cache then create a new mapping for it and\n+\t * store it in the cache first.\n+\t * \n+\t * @param repository\n+\t *            the repository to retrieve from the repositories cache\n+\t * @return the repository mapping in the cache\n+\t */\n+\tprivate static IgnoreRepositoryCache getRepositoryFromCache(\n+\t\t\tfinal Repository repository) {\n+\t\tIgnoreRepositoryCache cache = repositories.get(repository);\n+\t\tif (cache == null) {\n+\t\t\tcache = new IgnoreRepositoryCache(repository);\n+\t\t\trepositories.put(repository, cache);\n+\t\t}\n+\t\treturn cache;\n+\t}\n+\n+\t/*\n+\t * Public Methods\n+\t */\n+\n+\t/**\n+\t * This method must be invoked upon shutdown of the plugin. It empties the\n+\t * ignore cache.\n+\t */\n+\tpublic synchronized static void clear() {\n+\t\tfor (final IgnoreRepositoryCache projectCache : repositories.values()) {\n+\t\t\tprojectCache.clear();\n+\t\t}\n+\t\trepositories.clear();\n+\t}\n+\n+\t/**\n+\t * @param project\n+\t *            the project to remove the ignores for\n+\t */\n+\tpublic synchronized static void uncacheProject(final IResource project) {\n+\t\tif ((project == null) || (!(project instanceof IProject))) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal RepositoryMapping mapping = RepositoryMapping.getMapping(project);\n+\t\tif (mapping == null) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal Repository repository = mapping.getRepository();\n+\t\tfinal IgnoreRepositoryCache ignoreData = repositories.get(repository);\n+\t\tif (ignoreData == null) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tignoreData.uncacheProject((IProject) project);\n+\t}\n+\n+\t/**\n+\t * This method must be invoked upon startup. It goes through all files in\n+\t * the workspace and picks up all .gitignore files, parses them so that the\n+\t * plugin knows what to ignore. It also goes up the directory tree from the\n+\t * project root directories to look for .gitignore files.\n+\t */\n+\tpublic synchronized static void importWorkspaceIgnores() {\n+\t\tfinal IWorkspace workSpace = ResourcesPlugin.getWorkspace();\n+\t\tfinal IProject[] projects = workSpace.getRoot().getProjects();\n+\t\tfor (final IProject project : projects) {\n+\t\t\tfinal RepositoryMapping mapping = RepositoryMapping\n+\t\t\t\t\t.getMapping(project);\n+\t\t\tif (mapping != null) {\n+\t\t\t\ttry {\n+\t\t\t\t\tfinal IResource[] projectChildren = project.members();\n+\t\t\t\t\tfinal IgnoreRepositoryCache ignoreRepositoryCache = getRepositoryFromCache(mapping\n+\t\t\t\t\t\t\t.getRepository());\n+\t\t\t\t\tignoreRepositoryCache.importProjectIgnores(project,\n+\t\t\t\t\t\t\tprojectChildren);\n+\t\t\t\t\tignoreRepositoryCache.importProjectIgnoresOutside(project);\n+\t\t\t\t} catch (final CoreException e) {\n+\t\t\t\t\t/* swallow */\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\n+\t\tfor (final IgnoreRepositoryCache repositoryCache : repositories\n+\t\t\t\t.values()) {\n+\t\t\trepositoryCache.importRepositoryInfoExclude();\n+\t\t\trepositoryCache.importRepositoryCoreExclude();\n+\t\t}\n+\n+\t\t/*\n+\t\t * FIXME: also do the file `git config --global --get\n+\t\t * core.excludesfile`, is this already covered? RepositoryConfig\n+\t\t * globalConfig = new RepositoryConfig(null, new File(FS.userHome(),\n+\t\t * \".gitconfig\"));\n+\t\t */\n+\n+\t\t/*\n+\t\t * System.out.println(\"== GitIgnoreData.repositories ==\\n\" +\n+\t\t * repositories.toString());\n+\t\t */\n+\t}\n+}\ndiff --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\nnew file mode 100644\nindex 0000000..78dd71d\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java\n@@ -0,0 +1,82 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.ignores;\n+\n+import java.io.BufferedReader;\n+import java.io.IOException;\n+import java.io.InputStreamReader;\n+import java.util.LinkedList;\n+\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.runtime.CoreException;\n+\n+/**\n+ * This class implements ignore file helpers.\n+ */\n+class IgnoreFile {\n+\t/**\n+\t * This method parses an ignore file.\n+\t * \n+\t * @param ignoreFileBaseDir\n+\t *            the directory of the ignore file, relative to the checkout\n+\t *            directory and with a trailing slash. Slashes are in Unix\n+\t *            format: forward slashes\n+\t * @param ignoreFile\n+\t *            the .gitignore file\n+\t * @return returns a set of Excludes that reflects the ignore patterns.\n+\t */\n+\tstatic LinkedList<Exclude> parseIgnoreFile(final String ignoreFileBaseDir,\n+\t\t\tfinal IFile ignoreFile) {\n+\t\tfinal LinkedList<Exclude> excludes = new LinkedList<Exclude>();\n+\n+\t\t/* make sure that the resource is synchronized */\n+\t\ttry {\n+\t\t\tif (!ignoreFile.isSynchronized(IResource.DEPTH_ZERO)) {\n+\t\t\t\tignoreFile.refreshLocal(IResource.DEPTH_ZERO, null);\n+\t\t\t}\n+\t\t} catch (final Exception e) {\n+\t\t\treturn excludes;\n+\t\t}\n+\n+\t\tString base = ignoreFileBaseDir;\n+\t\tif (base.equals(\"/\")) {\n+\t\t\tbase = \"\";\n+\t\t}\n+\t\tfinal String ignoreFileName = ignoreFile.getLocation().toOSString();\n+\t\tBufferedReader txtIn = null;\n+\t\tint lineNumber = 0;\n+\t\ttry {\n+\t\t\ttxtIn = new BufferedReader(new InputStreamReader(ignoreFile\n+\t\t\t\t\t.getContents()));\n+\t\t\tString line;\n+\t\t\twhile ((line = txtIn.readLine()) != null) {\n+\t\t\t\tlineNumber++;\n+\t\t\t\tline = line.trim();\n+\t\t\t\tif (!line.startsWith(\"#\") && (line.length() > 0)) {\n+\t\t\t\t\texcludes.add(new Exclude(line, base, ignoreFileName,\n+\t\t\t\t\t\t\tlineNumber));\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} catch (final CoreException e) {\n+\t\t\t/* swallow */\n+\t\t} catch (final IOException e) {\n+\t\t\t/* swallow */\n+\t\t} finally {\n+\t\t\ttry {\n+\t\t\t\tif (txtIn != null) {\n+\t\t\t\t\ttxtIn.close();\n+\t\t\t\t\ttxtIn = null;\n+\t\t\t\t}\n+\t\t\t} catch (final IOException e1) {\n+\t\t\t\t/* swallow */\n+\t\t\t}\n+\t\t}\n+\t\treturn excludes;\n+\t}\n+}\ndiff --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\nnew file mode 100644\nindex 0000000..8ad5a48\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java\n@@ -0,0 +1,543 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.ignores;\n+\n+import java.io.File;\n+import java.io.FileInputStream;\n+import java.io.FileNotFoundException;\n+import java.io.InputStream;\n+import java.io.Reader;\n+import java.net.URI;\n+import java.util.Map;\n+\n+import org.eclipse.core.resources.IContainer;\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IFileState;\n+import org.eclipse.core.resources.IMarker;\n+import org.eclipse.core.resources.IProject;\n+import org.eclipse.core.resources.IProjectDescription;\n+import org.eclipse.core.resources.IResourceProxy;\n+import org.eclipse.core.resources.IResourceProxyVisitor;\n+import org.eclipse.core.resources.IResourceVisitor;\n+import org.eclipse.core.resources.IWorkspace;\n+import org.eclipse.core.resources.ResourceAttributes;\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IPath;\n+import org.eclipse.core.runtime.IProgressMonitor;\n+import org.eclipse.core.runtime.IStatus;\n+import org.eclipse.core.runtime.Path;\n+import org.eclipse.core.runtime.QualifiedName;\n+import org.eclipse.core.runtime.Status;\n+import org.eclipse.core.runtime.content.IContentDescription;\n+import org.eclipse.core.runtime.jobs.ISchedulingRule;\n+import org.spearce.jgit.lib.Repository;\n+\n+/**\n+ * This class is only used to be able to store the .gitignore files in the\n+ * ignore cache that are outside the projects (up in the checkout directory tree\n+ * from the project root directory)\n+ */\n+class IgnoreFileOutside implements IFile {\n+\tprivate String relativeDir = null;\n+\n+\tprivate String relativePath = null;\n+\n+\tprivate String resourceBaseName = null;\n+\n+\tprivate String absoluteDir = null;\n+\n+\tprivate String fullPath = null;\n+\n+\tprivate File fullPathFile = null;\n+\n+\tprivate long lastModificationTime = 0L;\n+\n+\t/**\n+\t * Constructor\n+\t * \n+\t * @param repository\n+\t *            the repository. when null then the relativeDir parameter is\n+\t *            taken to be an absolute path\n+\t * @param directory\n+\t *            the directory in which the pattern is anchored, relative to\n+\t *            the checkout directory and with a trailing slash (except when\n+\t *            in the checkout directory, in which case it will be an empty\n+\t *            string). Slashes are in Unix format: forward slashes. When\n+\t *            repository is null then this is taken to be an absolute\n+\t *            directory with slashes in platform format.\n+\t * @param resourceBasename\n+\t *            the name of the ignore file.\n+\t */\n+\tIgnoreFileOutside(final Repository repository, final String directory,\n+\t\t\tfinal String resourceBasename) {\n+\t\tif ((directory == null) || (resourceBasename == null)) {\n+\t\t\tthrow new IllegalArgumentException(\"Can not handle NULL values: \"\n+\t\t\t\t\t+ directory + \", \" + resourceBasename);\n+\t\t}\n+\n+\t\tthis.relativeDir = directory.replaceAll(\"/\", File.separator);\n+\t\tthis.resourceBaseName = resourceBasename\n+\t\t\t\t.replaceAll(\"/\", File.separator);\n+\t\tthis.relativePath = this.relativeDir + this.resourceBaseName;\n+\n+\t\tString repoRoot = \"\";\n+\t\tif (repository != null) {\n+\t\t\trepoRoot = repository.getWorkDir().getAbsolutePath()\n+\t\t\t\t\t+ File.separator;\n+\t\t}\n+\t\tthis.absoluteDir = repoRoot + this.relativeDir;\n+\t\tthis.fullPath = this.absoluteDir + this.resourceBaseName;\n+\t\tthis.fullPathFile = new File(this.fullPath);\n+\t}\n+\n+\t/* used interface methods */\n+\n+\tpublic boolean exists() {\n+\t\treturn this.fullPathFile.exists();\n+\t}\n+\n+\tpublic InputStream getContents() throws CoreException {\n+\t\ttry {\n+\t\t\treturn new FileInputStream(fullPath);\n+\t\t} catch (final FileNotFoundException e) {\n+\t\t\t/* FIXME: use actual plugin id */\n+\t\t\tthrow new CoreException(new Status(IStatus.WARNING, \"git plugin\", e\n+\t\t\t\t\t.getLocalizedMessage()));\n+\t\t}\n+\t}\n+\n+\tpublic String getName() {\n+\t\treturn resourceBaseName;\n+\t}\n+\n+\tpublic IPath getProjectRelativePath() {\n+\t\treturn new Path(relativePath);\n+\t}\n+\n+\tpublic boolean isSynchronized(final int depth) {\n+\t\treturn (lastModificationTime == this.fullPathFile.lastModified());\n+\t}\n+\n+\tpublic void refreshLocal(final int depth, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\tlastModificationTime = this.fullPathFile.lastModified();\n+\t\treturn;\n+\t}\n+\n+\tpublic IPath getLocation() {\n+\t\treturn new Path(fullPath);\n+\t}\n+\n+\t/*\n+\t * Overridden Methods\n+\t */\n+\n+\t@Override\n+\tpublic boolean equals(final Object obj) {\n+\t\tif (!(obj instanceof IgnoreFileOutside)) {\n+\t\t\tthrow new IllegalArgumentException(\"Wrong type\");\n+\t\t}\n+\t\treturn ((IgnoreFileOutside) obj).fullPath.equals(this.fullPath);\n+\t}\n+\n+\t@Override\n+\tpublic int hashCode() {\n+\t\treturn fullPath.hashCode();\n+\t}\n+\n+\t@Override\n+\tpublic String toString() {\n+\t\treturn fullPath;\n+\t}\n+\n+\t/*\n+\t * Unused Interface Methods\n+\t */\n+\n+\tpublic void appendContents(final InputStream source, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void appendContents(final InputStream source, final boolean force,\n+\t\t\tfinal boolean keepHistory, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void create(final InputStream source, final boolean force,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void create(final InputStream source, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void createLink(final IPath localLocation, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void createLink(final URI location, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void delete(final boolean force, final boolean keepHistory,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic String getCharset() throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic String getCharset(final boolean checkImplicit) throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic String getCharsetFor(final Reader reader) throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic IContentDescription getContentDescription() throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic InputStream getContents(final boolean force) throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic int getEncoding() throws CoreException {\n+\t\treturn 0;\n+\t}\n+\n+\tpublic IPath getFullPath() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic IFileState[] getHistory(final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic boolean isReadOnly() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic void move(final IPath destination, final boolean force,\n+\t\t\tfinal boolean keepHistory, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setCharset(final String newCharset) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setCharset(final String newCharset,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setContents(final InputStream source, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setContents(final IFileState source, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setContents(final InputStream source, final boolean force,\n+\t\t\tfinal boolean keepHistory, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setContents(final IFileState source, final boolean force,\n+\t\t\tfinal boolean keepHistory, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void accept(final IResourceVisitor visitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void accept(final IResourceProxyVisitor visitor,\n+\t\t\tfinal int memberFlags) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void accept(final IResourceVisitor visitor, final int depth,\n+\t\t\tfinal boolean includePhantoms) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void accept(final IResourceVisitor visitor, final int depth,\n+\t\t\tfinal int memberFlags) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void clearHistory(final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void copy(final IPath destination, final boolean force,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void copy(final IPath destination, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void copy(final IProjectDescription description,\n+\t\t\tfinal boolean force, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void copy(final IProjectDescription description,\n+\t\t\tfinal int updateFlags, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic IMarker createMarker(final String type) throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic IResourceProxy createProxy() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic void delete(final boolean force, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void delete(final int updateFlags, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void deleteMarkers(final String type, final boolean includeSubtypes,\n+\t\t\tfinal int depth) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic IMarker findMarker(final long id) throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic IMarker[] findMarkers(final String type,\n+\t\t\tfinal boolean includeSubtypes, final int depth)\n+\t\t\tthrows CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic int findMaxProblemSeverity(final String type,\n+\t\t\tfinal boolean includeSubtypes, final int depth)\n+\t\t\tthrows CoreException {\n+\t\treturn 0;\n+\t}\n+\n+\tpublic String getFileExtension() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic long getLocalTimeStamp() {\n+\t\treturn 0;\n+\t}\n+\n+\tpublic URI getLocationURI() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic IMarker getMarker(final long id) {\n+\t\treturn null;\n+\t}\n+\n+\tpublic long getModificationStamp() {\n+\t\treturn 0;\n+\t}\n+\n+\tpublic IContainer getParent() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic Map getPersistentProperties() throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic String getPersistentProperty(final QualifiedName key)\n+\t\t\tthrows CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic IProject getProject() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic IPath getRawLocation() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic URI getRawLocationURI() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic ResourceAttributes getResourceAttributes() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic Map getSessionProperties() throws CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic Object getSessionProperty(final QualifiedName key)\n+\t\t\tthrows CoreException {\n+\t\treturn null;\n+\t}\n+\n+\tpublic int getType() {\n+\t\treturn 0;\n+\t}\n+\n+\tpublic IWorkspace getWorkspace() {\n+\t\treturn null;\n+\t}\n+\n+\tpublic boolean isAccessible() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isDerived() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isDerived(final int options) {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isHidden() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isLinked() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isLinked(final int options) {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isLocal(final int depth) {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isPhantom() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isTeamPrivateMember() {\n+\t\treturn false;\n+\t}\n+\n+\tpublic void move(final IPath destination, final boolean force,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void move(final IPath destination, final int updateFlags,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void move(final IProjectDescription description,\n+\t\t\tfinal int updateFlags, final IProgressMonitor monitor)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void move(final IProjectDescription description,\n+\t\t\tfinal boolean force, final boolean keepHistory,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void revertModificationStamp(final long value) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setDerived(final boolean isDerived) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setHidden(final boolean isHidden) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setLocal(final boolean flag, final int depth,\n+\t\t\tfinal IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic long setLocalTimeStamp(final long value) throws CoreException {\n+\t\treturn 0;\n+\t}\n+\n+\tpublic void setPersistentProperty(final QualifiedName key,\n+\t\t\tfinal String value) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setReadOnly(final boolean readOnly) {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setResourceAttributes(final ResourceAttributes attributes)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setSessionProperty(final QualifiedName key, final Object value)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void setTeamPrivateMember(final boolean isTeamPrivate)\n+\t\t\tthrows CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic void touch(final IProgressMonitor monitor) throws CoreException {\n+\t\t/** not used */\n+\t}\n+\n+\tpublic Object getAdapter(final Class adapter) {\n+\t\treturn null;\n+\t}\n+\n+\tpublic boolean contains(final ISchedulingRule rule) {\n+\t\treturn false;\n+\t}\n+\n+\tpublic boolean isConflicting(final ISchedulingRule rule) {\n+\t\treturn false;\n+\t}\n+\n+}\n\\ No newline at end of file\ndiff --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\nnew file mode 100644\nindex 0000000..fe2f529\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java\n@@ -0,0 +1,201 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.ignores;\n+\n+import java.util.HashMap;\n+import java.util.LinkedList;\n+\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IFolder;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.resources.IResourceDelta;\n+import org.eclipse.core.runtime.CoreException;\n+\n+/**\n+ * This class implements a cache of ignore patterns for an Eclipse project: it\n+ * holds a list of ignore patterns, stored in 'ignoreFiles' against the ignore\n+ * file handle. We also keep an ignoreFilesIndex of the ignore file handle\n+ * stored against the directory names (relative to the 'checkoutDir') of the\n+ * ignore ignoreFiles in 'ignoreFilesIndex'. We use this to access the cache and\n+ * retrieve the list of ignore patterns. This is because when trying to\n+ * determine whether a resource is ignored we must first try the ignore file in\n+ * the directory of the resource, and if it doesn't match, then in the directory\n+ * up from that, and so on, al the way up to the checkout directory.\n+ */\n+class IgnoreProjectCache {\n+\t/**\n+\t * the directory of the project, relative to the checkout, with a trailing\n+\t * slash, except when in the checkout directory in which case it will be\n+\t * empty\n+\t */\n+\tprivate String projectDirInCheckout = null;\n+\n+\t/**\n+\t * Map used to find .gitignore ignore files in ignoreFiles. key=directory\n+\t * path of the .gitignore file, relative to the checkout directory,\n+\t * value=IFile to use to ignoreFilesIndex ignoreFiles\n+\t */\n+\tprivate final HashMap<String, IFile> ignoreFilesIndex = new HashMap<String, IFile>();\n+\n+\t/**\n+\t * Map with .gitignore ignore files and their exclude patterns.\n+\t * key=.gitignore file handle, value=list with its ignore patterns\n+\t */\n+\tprivate final HashMap<IFile, LinkedList<Exclude>> ignoreFiles = new HashMap<IFile, LinkedList<Exclude>>();\n+\n+\t/*\n+\t * Constructors\n+\t */\n+\n+\t/**\n+\t * Constructor\n+\t * \n+\t * @param projectDirInCheckout\n+\t *            the directory of the project, relative to the checkout\n+\t */\n+\tIgnoreProjectCache(final String projectDirInCheckout) {\n+\t\tif (projectDirInCheckout == null) {\n+\t\t\tthrow new ExceptionInInitializerError(\n+\t\t\t\t\t\"NULL is not a valid project directory\");\n+\t\t}\n+\n+\t\tthis.projectDirInCheckout = projectDirInCheckout;\n+\t\tif (!this.projectDirInCheckout.isEmpty()\n+\t\t\t\t&& !this.projectDirInCheckout.endsWith(\"/\")) {\n+\t\t\tthis.projectDirInCheckout = this.projectDirInCheckout.concat(\"/\");\n+\t\t}\n+\t}\n+\n+\t/*\n+\t * Methods\n+\t */\n+\n+\tsynchronized void clear() {\n+\t\tignoreFilesIndex.clear();\n+\n+\t\tfor (final LinkedList<Exclude> excludeList : ignoreFiles.values()) {\n+\t\t\texcludeList.clear();\n+\t\t}\n+\t\tignoreFiles.clear();\n+\t}\n+\n+\tsynchronized void importProjectIgnores(final IResource[] projectChildren) {\n+\t\tif (projectChildren == null) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfor (final IResource projectChild : projectChildren) {\n+\t\t\tif (projectChild != null) {\n+\t\t\t\tif (projectChild instanceof IFile) {\n+\t\t\t\t\tif (projectChild.getName().equals(\".gitignore\")) {\n+\t\t\t\t\t\tString projectRelativeDir = projectChild\n+\t\t\t\t\t\t\t\t.getProjectRelativePath().removeLastSegments(1)\n+\t\t\t\t\t\t\t\t.toString();\n+\t\t\t\t\t\tif (!projectRelativeDir.isEmpty()) {\n+\t\t\t\t\t\t\tprojectRelativeDir = projectRelativeDir + \"/\";\n+\t\t\t\t\t\t}\n+\t\t\t\t\t\tfinal String ignoreFileBaseDir = projectDirInCheckout\n+\t\t\t\t\t\t\t\t+ projectRelativeDir;\n+\t\t\t\t\t\tprocessIgnoreFile((IFile) projectChild,\n+\t\t\t\t\t\t\t\tignoreFileBaseDir, IResourceDelta.ADDED,\n+\t\t\t\t\t\t\t\tIResourceDelta.CONTENT);\n+\t\t\t\t\t}\n+\t\t\t\t} else if (projectChild instanceof IFolder) {\n+\t\t\t\t\ttry {\n+\t\t\t\t\t\timportProjectIgnores(((IFolder) projectChild).members());\n+\t\t\t\t\t} catch (final CoreException e) {\n+\t\t\t\t\t\t/* swallow */\n+\t\t\t\t\t}\n+\t\t\t\t} else {\n+\t\t\t\t\t/* FIXME: signal an error */\n+\t\t\t\t\tSystem.out.println(\"Unhandled resource type in\"\n+\t\t\t\t\t\t\t+ \" processResourceForIgnoreChild: \"\n+\t\t\t\t\t\t\t+ projectChild.getClass().getName());\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t}\n+\n+\t/**\n+\t * This method parses a .gitignore file and stores the patterns for use by\n+\t * the plugin. It is shared between startup of the workspace and changes to\n+\t * the workspace.\n+\t * \n+\t * @param ignoreFile\n+\t *            the .gitignore file\n+\t * @param ignoreFileBaseDir\n+\t *            the directory of the ignore file, relative to the checkout\n+\t *            directory and with a trailing slash (except when in the\n+\t *            checkout directory, in which case it will be an empty string).\n+\t *            Slashes are in Unix format: forward slashes\n+\t * @param changeKind\n+\t *            the kind of the change\n+\t * @param changeFlags\n+\t *            further information on the change\n+\t */\n+\tsynchronized void processIgnoreFile(final IFile ignoreFile,\n+\t\t\tfinal String ignoreFileBaseDir, final int changeKind,\n+\t\t\tfinal int changeFlags) {\n+\t\tif ((ignoreFile == null) || (changeKind == IResourceDelta.NO_CHANGE)) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tif (((changeKind & IResourceDelta.ADDED) == IResourceDelta.ADDED)\n+\t\t\t\t|| ((changeKind & IResourceDelta.ADDED_PHANTOM) == IResourceDelta.ADDED_PHANTOM)\n+\t\t\t\t|| (((changeKind & IResourceDelta.CHANGED) == IResourceDelta.CHANGED) && ((changeFlags & IResourceDelta.CONTENT) == IResourceDelta.CONTENT))) {\n+\t\t\tignoreFiles.put(ignoreFile, IgnoreFile.parseIgnoreFile(\n+\t\t\t\t\tignoreFileBaseDir, ignoreFile));\n+\t\t\tignoreFilesIndex.put(ignoreFileBaseDir, ignoreFile);\n+\t\t} else if (((changeKind & IResourceDelta.REMOVED) == IResourceDelta.REMOVED)\n+\t\t\t\t|| ((changeKind & IResourceDelta.REMOVED_PHANTOM) == IResourceDelta.REMOVED_PHANTOM)) {\n+\t\t\tignoreFiles.remove(ignoreFile);\n+\t\t\tignoreFilesIndex.remove(ignoreFileBaseDir);\n+\t\t} else {\n+\t\t\tSystem.out.println(\"Unhandled change combination kind/flags: \"\n+\t\t\t\t\t+ changeKind + \"/\" + changeFlags);\n+\t\t}\n+\n+\t\t// int changeKind = projectChild.getKind();\n+\t\t// int changeFlags = projectChild.getFlags();\n+\t\t// switch (changeKind) {\n+\t\t// case IResourceDelta.ADDED:\n+\t\t// case IResourceDelta.ADDED_PHANTOM:\n+\t\t// if ((changeFlags & IResourceDelta.MOVED_FROM) ==\n+\t\t// IResourceDelta.MOVED_FROM) {\n+\t\t// /*\n+\t\t// * The resource has moved: getMovedToPath will\n+\t\t// * return the path of where it was moved to.\n+\t\t// */\n+\t\t// break;\n+\t\t// }\n+\t\t//\n+\t\t// /* simply parse the content and process it */\n+\t\t// break;\n+\t\t//\n+\t\t// case IResourceDelta.REMOVED:\n+\t\t// case IResourceDelta.REMOVED_PHANTOM:\n+\t\t// /* remove the patterns from the file */\n+\t\t// break;\n+\t\t//\n+\t\t// case IResourceDelta.CHANGED:\n+\t\t// /* this one is more involved, also have to deal with moved\n+\t\t// ignoreFiles */\n+\t\t// if ((changeFlags & IResourceDelta.REPLACED) ==\n+\t\t// IResourceDelta.REPLACED) {\n+\t\t// /*\n+\t\t// * The resource has moved: getMovedToPath will\n+\t\t// * return the path of where it was moved to.\n+\t\t// */\n+\t\t// }\n+\t\t// break;\n+\t\t//\n+\t\t// default:\n+\t\t// break;\n+\t\t// }\n+\t}\n+}\n\\ No newline at end of file\ndiff --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\nnew file mode 100644\nindex 0000000..4b6bf61\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java\n@@ -0,0 +1,308 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.ignores;\n+\n+import java.io.File;\n+import java.util.HashMap;\n+\n+import org.eclipse.core.resources.IProject;\n+import org.eclipse.core.resources.IResource;\n+import org.eclipse.core.resources.IResourceDelta;\n+import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IPath;\n+import org.eclipse.core.runtime.Path;\n+import org.spearce.egit.core.project.RepositoryMapping;\n+import org.spearce.jgit.lib.Repository;\n+import org.spearce.jgit.lib.RepositoryConfig;\n+\n+/**\n+ * This class manages the ignore data for a single repository (and its\n+ * checkout). It implements the model that a single repository can contain\n+ * multiple projects.\n+ * \n+ * It contains a cache that holds ignore data on a per-project basis and it also\n+ * contains a cache that holds ignore data that does not belong to any project\n+ * but still belongs to the repository. The latter cache we need because Eclipse\n+ * will not send change events for those files; we have to re-read these files\n+ * every time there is a change.\n+ */\n+class IgnoreRepositoryCache {\n+\t/** the repository */\n+\tprivate Repository repository = null;\n+\n+\t/** the checkout directory for the repository */\n+\tprivate String checkoutDir = null;\n+\n+\t/** the cache that holds ignore data on a per-project basis */\n+\tprivate final HashMap<IProject, IgnoreProjectCache> projects = new HashMap<IProject, IgnoreProjectCache>();\n+\n+\t/** cache that holds ignore data that does not belong to any project */\n+\tprivate final IgnoreProjectCache outside = new IgnoreProjectCache(\"\");\n+\n+\t/** cache that holds ignore data of the .git/info/exclude files */\n+\tprivate final IgnoreProjectCache infoExclude = new IgnoreProjectCache(\"\");\n+\n+\t/** cache that holds ignore data of the core exclude setting */\n+\tprivate final IgnoreProjectCache coreExcludes = new IgnoreProjectCache(\"\");\n+\n+\t/** the .git/info/exclude file */\n+\tprivate IgnoreFileOutside infoExcludesFile = null;\n+\n+\t/** the core.excludes file setting from the config */\n+\tprivate IgnoreFileOutside coreExcludesFile = null;\n+\n+\t/** the core.excludesfile setting */\n+\tprivate String coreExcludesSetting = null;\n+\n+\t/**\n+\t * Retrieve a project mapping from the projects cache. When the project is\n+\t * not yet in the cache then create a new mapping for it and store it in the\n+\t * cache first.\n+\t * \n+\t * @param project\n+\t *            the project to retrieve from the projects cache\n+\t * @return the project mapping in the cache\n+\t */\n+\tprivate IgnoreProjectCache getProjectFromCache(final IProject project) {\n+\t\tIgnoreProjectCache cache = projects.get(project);\n+\t\tif (cache == null) {\n+\t\t\tcache = new IgnoreProjectCache(RepositoryMapping\n+\t\t\t\t\t.getMapping(project).getRepoRelativePath(project));\n+\t\t\tprojects.put(project, cache);\n+\t\t}\n+\t\treturn cache;\n+\t}\n+\n+\t/*\n+\t * Constructors\n+\t */\n+\n+\t/**\n+\t * Constructor\n+\t * \n+\t * @param repository\n+\t *            the repository\n+\t */\n+\tIgnoreRepositoryCache(final Repository repository) {\n+\t\tif (repository == null) {\n+\t\t\tthrow new ExceptionInInitializerError(\n+\t\t\t\t\t\"NULL is not a valid repository\");\n+\t\t}\n+\t\tthis.repository = repository;\n+\t\tthis.checkoutDir = repository.getWorkDir().getAbsolutePath();\n+\t}\n+\n+\t/*\n+\t * Methods\n+\t */\n+\n+\tsynchronized void clear() {\n+\t\tfor (final IgnoreProjectCache projectCache : projects.values()) {\n+\t\t\tprojectCache.clear();\n+\t\t}\n+\t\tprojects.clear();\n+\t\toutside.clear();\n+\t\tinfoExclude.clear();\n+\t\tcoreExcludes.clear();\n+\t}\n+\n+\t/**\n+\t * @param project\n+\t *            the project for which to remove the ignore data\n+\t */\n+\tsynchronized void uncacheProject(final IProject project) {\n+\t\tif (project == null) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal IgnoreProjectCache cache = projects.get(project);\n+\t\tif (cache == null) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\t/*\n+\t\t * FIXME: remove everything that is not 'outside' to remaining projects\n+\t\t */\n+\n+\t\tcache.clear();\n+\n+\t\tprojects.remove(project);\n+\t}\n+\n+\t/**\n+\t * Process all project children: look for .gitignore files and read in the\n+\t * ignore patterns.\n+\t * \n+\t * @param project\n+\t *            the project\n+\t * @param projectChildren\n+\t *            the project children\n+\t */\n+\tsynchronized void importProjectIgnores(final IProject project,\n+\t\t\tfinal IResource[] projectChildren) {\n+\t\tif ((project == null) || (projectChildren == null)) {\n+\t\t\treturn;\n+\t\t}\n+\n+\t\tfinal IgnoreProjectCache projectCache = getProjectFromCache(project);\n+\t\tprojectCache.importProjectIgnores(projectChildren);\n+\t}\n+\n+\t/*\n+\t * walk directory tree up looking for .gitignore files until in the checkout\n+\t * directory\n+\t */\n+\tsynchronized boolean importProjectIgnoresOutside(final IProject project) {\n+\t\tboolean changes = false;\n+\n+\t\tString projectDirectory = RepositoryMapping.getMapping(project)\n+\t\t\t\t.getRepoRelativePath(project);\n+\t\twhile (!projectDirectory.isEmpty()) {\n+\t\t\tfinal int pos = projectDirectory.lastIndexOf('/');\n+\t\t\tif (pos < 0) {\n+\t\t\t\tprojectDirectory = \"\";\n+\t\t\t} else {\n+\t\t\t\tprojectDirectory = projectDirectory.substring(0, pos) + \"/\";\n+\t\t\t}\n+\n+\t\t\tfinal IgnoreFileOutside ignoreFile = new IgnoreFileOutside(\n+\t\t\t\t\trepository, projectDirectory, \".gitignore\");\n+\t\t\tif (!ignoreFile.isSynchronized(0)) {\n+\t\t\t\tchanges = true;\n+\t\t\t\tString projectRelativeDir = ignoreFile.getProjectRelativePath()\n+\t\t\t\t\t\t.removeLastSegments(1).toString();\n+\t\t\t\tif (!projectRelativeDir.isEmpty()) {\n+\t\t\t\t\tprojectRelativeDir = projectRelativeDir + \"/\";\n+\t\t\t\t}\n+\t\t\t\tfinal String ignoreFileBaseDir = projectDirectory\n+\t\t\t\t\t\t+ projectRelativeDir;\n+\t\t\t\toutside.processIgnoreFile(ignoreFile, ignoreFileBaseDir,\n+\t\t\t\t\t\tIResourceDelta.ADDED, IResourceDelta.CONTENT);\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn changes;\n+\t}\n+\n+\tsynchronized boolean importRepositoryInfoExclude() {\n+\t\treadRepositoryInfoExcludesFile();\n+\n+\t\tif ((infoExcludesFile != null) && !infoExcludesFile.isSynchronized(0)) {\n+\t\t\ttry {\n+\t\t\t\tinfoExcludesFile.refreshLocal(IResource.DEPTH_ZERO, null);\n+\t\t\t} catch (final CoreException e) {\n+\t\t\t\t/* swallow */\n+\t\t\t}\n+\t\t\tinfoExclude.clear();\n+\t\t\tinfoExclude.processIgnoreFile(infoExcludesFile, \"\",\n+\t\t\t\t\tIResourceDelta.ADDED, IResourceDelta.CONTENT);\n+\t\t\treturn true;\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\tsynchronized boolean importRepositoryCoreExclude() {\n+\t\treadRepositoryCoreExcludesSetting();\n+\t\tif ((coreExcludesFile != null) && !coreExcludesFile.isSynchronized(0)) {\n+\t\t\ttry {\n+\t\t\t\tcoreExcludesFile.refreshLocal(IResource.DEPTH_ZERO, null);\n+\t\t\t} catch (final CoreException e) {\n+\t\t\t\t/* swallow */\n+\t\t\t}\n+\t\t\tcoreExcludes.clear();\n+\t\t\tcoreExcludes.processIgnoreFile(coreExcludesFile, \"\",\n+\t\t\t\t\tIResourceDelta.ADDED, IResourceDelta.CONTENT);\n+\t\t\treturn true;\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\t/*\n+\t * Private Methods\n+\t */\n+\n+\tprivate boolean readRepositoryInfoExcludesFile() {\n+\t\tboolean changes = false;\n+\n+\t\tif (infoExcludesFile == null) {\n+\t\t\tinfoExcludesFile = new IgnoreFileOutside(this.repository, \"\",\n+\t\t\t\t\t\".git/info/exclude\");\n+\t\t\tchanges = true;\n+\t\t\tif (!infoExcludesFile.exists()) {\n+\t\t\t\tinfoExcludesFile = null;\n+\t\t\t\tchanges = false;\n+\t\t\t}\n+\t\t} else {\n+\t\t\tif (!infoExcludesFile.exists()) {\n+\t\t\t\tinfoExcludesFile = null;\n+\t\t\t\tchanges = true;\n+\t\t\t}\n+\t\t}\n+\n+\t\treturn changes;\n+\t}\n+\n+\tprivate boolean readRepositoryCoreExcludesSetting() {\n+\t\tfinal RepositoryConfig config = repository.getConfig();\n+\t\tif (config == null) {\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\tboolean changes = false;\n+\t\tString newCoreExcludesSetting = config.getString(\"core\", null,\n+\t\t\t\t\"excludesfile\");\n+\t\tif (newCoreExcludesSetting != null) {\n+\t\t\t/* FIXME check this! both for per-repo and global */\n+\t\t\tif (!newCoreExcludesSetting.equals(coreExcludesSetting)) {\n+\t\t\t\tchanges = true;\n+\n+\t\t\t\tfinal IPath newCoreExcludesSettingPath = new Path(\n+\t\t\t\t\t\tnewCoreExcludesSetting);\n+\t\t\t\tnewCoreExcludesSetting = newCoreExcludesSettingPath\n+\t\t\t\t\t\t.toOSString();\n+\t\t\t\tif (!newCoreExcludesSettingPath.isAbsolute()) {\n+\t\t\t\t\tnewCoreExcludesSetting = repository.getWorkDir()\n+\t\t\t\t\t\t\t.getAbsolutePath().toString()\n+\t\t\t\t\t\t\t+ File.separator + newCoreExcludesSetting;\n+\t\t\t\t}\n+\n+\t\t\t\tif (coreExcludesFile != null) {\n+\t\t\t\t\tcoreExcludesFile = null;\n+\t\t\t\t}\n+\n+\t\t\t\tfinal int pos = newCoreExcludesSetting\n+\t\t\t\t\t\t.lastIndexOf(File.separatorChar);\n+\t\t\t\tfinal String directory = newCoreExcludesSetting.substring(0,\n+\t\t\t\t\t\tpos);\n+\t\t\t\tfinal String resourceBasename = newCoreExcludesSetting\n+\t\t\t\t\t\t.substring(pos + 1);\n+\n+\t\t\t\tcoreExcludes.clear();\n+\t\t\t\tcoreExcludesSetting = newCoreExcludesSetting;\n+\t\t\t\tcoreExcludesFile = new IgnoreFileOutside(null, directory,\n+\t\t\t\t\t\tresourceBasename);\n+\t\t\t}\n+\t\t} else {\n+\t\t\tchanges = (coreExcludesSetting != null);\n+\t\t\tcoreExcludesSetting = null;\n+\t\t\tcoreExcludesFile = null;\n+\t\t}\n+\t\treturn changes;\n+\t}\n+\t\n+\t/*\n+\t * Getters / Setters\n+\t */\n+\n+\t/**\n+\t * @return the checkoutDir\n+\t */\n+\tpublic String getCheckoutDir() {\n+\t\treturn checkoutDir;\n+\t}\n+}\n\\ No newline at end of file\ndiff --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\nindex 31d5483..414bd83 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java\n@@ -40,6 +40,7 @@\n import org.spearce.egit.core.CoreText;\n import org.spearce.egit.core.GitCorePreferences;\n import org.spearce.egit.core.GitProvider;\n+import org.spearce.egit.core.ignores.GitIgnoreData;\n import org.spearce.jgit.lib.Repository;\n import org.spearce.jgit.lib.WindowCache;\n import org.spearce.jgit.lib.WindowCacheConfig;\n@@ -64,9 +65,11 @@ public void resourceChanged(final IResourceChangeEvent event) {\n \t\t\tswitch (event.getType()) {\n \t\t\tcase IResourceChangeEvent.PRE_CLOSE:\n \t\t\t\tuncache((IProject) event.getResource());\n+\t\t\t\tGitIgnoreData.uncacheProject(event.getResource());\n \t\t\t\tbreak;\n \t\t\tcase IResourceChangeEvent.PRE_DELETE:\n \t\t\t\tdelete((IProject) event.getResource());\n+\t\t\t\tGitIgnoreData.uncacheProject(event.getResource());\n \t\t\t\tbreak;\n \t\t\tdefault:\n \t\t\t\tbreak;\n@@ -84,6 +87,7 @@ public void resourceChanged(final IResourceChangeEvent event) {\n \t */\n \tpublic static void attachToWorkspace(final boolean includeChange) {\n \t\ttrace(\"attachToWorkspace - addResourceChangeListener\");\n+\t\tGitIgnoreData.importWorkspaceIgnores();\n \t\tResourcesPlugin.getWorkspace().addResourceChangeListener(\n \t\t\t\trcl,\n \t\t\t\t(includeChange ? IResourceChangeEvent.POST_CHANGE : 0)\n@@ -97,6 +101,7 @@ public static void attachToWorkspace(final boolean includeChange) {\n \tpublic static void detachFromWorkspace() {\n \t\ttrace(\"detachFromWorkspace - removeResourceChangeListener\");\n \t\tResourcesPlugin.getWorkspace().removeResourceChangeListener(rcl);\n+\t\tGitIgnoreData.clear();\n \t}\n \n \t/**\n-- \n1.6.0.6\n"},{"id":"109578","messageId":"77afc036be3e7704333130c1cd2ab28466a91e67.1238102327.git.ferry.huberts@pelagic.nl","threadId":"18579","inReplyTo":"213427d4111185ae3a094a3cd578be8e6c3d5ffb.1238102327.git.ferry.huberts@pelagic.nl","subject":"[EGIT] [PATCH RFC v1 2/5] Enable the ignore handling of the plugin","fromName":"Ferry Huberts","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-03-26T21:34:24Z","receivedAt":"2009-03-26T21:34:24Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>\n---\n org.spearce.egit.core/META-INF/MANIFEST.MF         |    1 +\n .../org/spearce/egit/core/op/TrackOperation.java   |    6 +++---\n .../decorators/DecoratableResourceAdapter.java     |    4 ++--\n 3 files changed, 6 insertions(+), 5 deletions(-)\n\ndiff --git a/org.spearce.egit.core/META-INF/MANIFEST.MF b/org.spearce.egit.core/META-INF/MANIFEST.MF\nindex 20df15f..546ea72 100644\n--- a/org.spearce.egit.core/META-INF/MANIFEST.MF\n+++ b/org.spearce.egit.core/META-INF/MANIFEST.MF\n@@ -13,6 +13,7 @@ Require-Bundle: org.eclipse.core.runtime,\n  org.eclipse.core.filesystem,\n  org.eclipse.ui\n Export-Package: org.spearce.egit.core,\n+ org.spearce.egit.core.ignores;x-friends:=\"org.spearce.egit.ui\",\n  org.spearce.egit.core.internal.storage;x-friends:=\"org.spearce.egit.ui\",\n  org.spearce.egit.core.internal.util;x-friends:=\"org.spearce.egit.ui\",\n  org.spearce.egit.core.op,\ndiff --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\nindex 29b4344..4a4b93c 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java\n@@ -24,9 +24,9 @@\n import org.eclipse.core.runtime.IAdaptable;\n import org.eclipse.core.runtime.IProgressMonitor;\n import org.eclipse.core.runtime.NullProgressMonitor;\n-import org.eclipse.team.core.Team;\n import org.spearce.egit.core.Activator;\n import org.spearce.egit.core.CoreText;\n+import org.spearce.egit.core.ignores.GitIgnoreData;\n import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.jgit.lib.GitIndex;\n import org.spearce.jgit.lib.GitIndex.Entry;\n@@ -96,12 +96,12 @@ public boolean visit(IResource resource) throws CoreException {\n \t\t\t\t\t\t\t\t\t// by explicitly selecting and invoking track on it.\n \t\t\t\t\t\t\t\t\tif (resource.getType() == IResource.FILE) {\n \t\t\t\t\t\t\t\t\t\tEntry entry = index.getEntry(repoPath);\n-\t\t\t\t\t\t\t\t\t\tif (!Team.isIgnoredHint(resource) || entry != null && entry.isAssumedValid()) {\n+\t\t\t\t\t\t\t\t\t\tif (!GitIgnoreData.isIgnored(resource) || ((entry != null) && entry.isAssumedValid())) {\n \t\t\t\t\t\t\t\t\t\t\tentry = index.add(rm.getWorkDir(), new File(rm.getWorkDir(), repoPath));\n \t\t\t\t\t\t\t\t\t\t\tentry.setAssumeValid(false);\n \t\t\t\t\t\t\t\t\t\t}\n \t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\tif (Team.isIgnoredHint(resource))\n+\t\t\t\t\t\t\t\t\tif (GitIgnoreData.isIgnored(resource))\n \t\t\t\t\t\t\t\t\t\treturn false;\n \n \t\t\t\t\t\t\t\t} catch (IOException e) {\ndiff --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\nindex 5c68d5b..4c740d5 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n@@ -22,10 +22,10 @@\n import org.eclipse.core.resources.IResource;\n import org.eclipse.core.resources.IWorkspaceRoot;\n import org.eclipse.jface.preference.IPreferenceStore;\n-import org.eclipse.team.core.Team;\n import org.spearce.egit.core.AdaptableFileTreeIterator;\n import org.spearce.egit.core.ContainerTreeIterator;\n import org.spearce.egit.core.ContainerTreeIterator.ResourceEntry;\n+import org.spearce.egit.core.ignores.GitIgnoreData;\n import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.egit.ui.Activator;\n import org.spearce.egit.ui.UIPreferences;\n@@ -363,7 +363,7 @@ private static boolean timestampMatches(DirCacheEntry indexEntry,\n \n \tprivate static boolean isIgnored(IResource resource) {\n \t\t// TODO: Also read ignores from .git/info/excludes et al.\n-\t\treturn Team.isIgnoredHint(resource);\n+\t\treturn GitIgnoreData.isIgnored(resource);\n \t}\n \n \tpublic String getName() {\n-- \n1.6.0.6\n"},{"id":"109576","messageId":"e9605b0b1bda26f568009a42b31220d60692fce6.1238102327.git.ferry.huberts@pelagic.nl","threadId":"18579","inReplyTo":"77afc036be3e7704333130c1cd2ab28466a91e67.1238102327.git.ferry.huberts@pelagic.nl","subject":"[EGIT] [PATCH RFC v1 3/5] Optimise ignore evaluation","fromName":"Ferry Huberts","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-03-26T21:34:25Z","receivedAt":"2009-03-26T21:34:25Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"Do not call GitIgnoreData.isIgnored(resource) multiple times when\nnot needed.\n\nSigned-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>\n---\n .../org/spearce/egit/core/op/TrackOperation.java   |    5 +++--\n .../decorators/DecoratableResourceAdapter.java     |    9 ++-------\n 2 files changed, 5 insertions(+), 9 deletions(-)\n\ndiff --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\nindex 4a4b93c..a64c1dd 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/op/TrackOperation.java\n@@ -94,14 +94,15 @@ public boolean visit(IResource resource) throws CoreException {\n \t\t\t\t\t\t\t\t\t// first. If a resource within a ignored folder is marked\n \t\t\t\t\t\t\t\t\t// we ignore it here, i.e. there is no way to unmark it expect\n \t\t\t\t\t\t\t\t\t// by explicitly selecting and invoking track on it.\n+\t\t\t\t\t\t\t\t\tboolean ignored = GitIgnoreData.isIgnored(resource);\n \t\t\t\t\t\t\t\t\tif (resource.getType() == IResource.FILE) {\n \t\t\t\t\t\t\t\t\t\tEntry entry = index.getEntry(repoPath);\n-\t\t\t\t\t\t\t\t\t\tif (!GitIgnoreData.isIgnored(resource) || ((entry != null) && entry.isAssumedValid())) {\n+\t\t\t\t\t\t\t\t\t\tif (!ignored || ((entry != null) && entry.isAssumedValid())) {\n \t\t\t\t\t\t\t\t\t\t\tentry = index.add(rm.getWorkDir(), new File(rm.getWorkDir(), repoPath));\n \t\t\t\t\t\t\t\t\t\t\tentry.setAssumeValid(false);\n \t\t\t\t\t\t\t\t\t\t}\n \t\t\t\t\t\t\t\t\t}\n-\t\t\t\t\t\t\t\t\tif (GitIgnoreData.isIgnored(resource))\n+\t\t\t\t\t\t\t\t\tif (ignored)\n \t\t\t\t\t\t\t\t\t\treturn false;\n \n \t\t\t\t\t\t\t\t} catch (IOException e) {\ndiff --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\nindex 4c740d5..7b48fd8 100644\n--- a/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n+++ b/org.spearce.egit.ui/src/org/spearce/egit/ui/internal/decorators/DecoratableResourceAdapter.java\n@@ -118,7 +118,7 @@ private void extractResourceProperties(TreeWalk treeWalk) {\n \t\tif (resourceEntry == null)\n \t\t\treturn;\n \n-\t\tif (isIgnored(resourceEntry.getResource())) {\n+\t\tif (GitIgnoreData.isIgnored(resourceEntry.getResource())) {\n \t\t\tignored = true;\n \t\t\treturn;\n \t\t}\n@@ -261,7 +261,7 @@ public boolean shouldBeRecursive() {\n \n \tprivate void extractContainerProperties(TreeWalk treeWalk) throws IOException {\n \n-\t\tif (isIgnored(resource)) {\n+\t\tif (GitIgnoreData.isIgnored(resource)) {\n \t\t\tignored = true;\n \t\t\treturn;\n \t\t}\n@@ -361,11 +361,6 @@ private static boolean timestampMatches(DirCacheEntry indexEntry,\n \t\t}\n \t}\n \n-\tprivate static boolean isIgnored(IResource resource) {\n-\t\t// TODO: Also read ignores from .git/info/excludes et al.\n-\t\treturn GitIgnoreData.isIgnored(resource);\n-\t}\n-\n \tpublic String getName() {\n \t\treturn resource.getName();\n \t}\n-- \n1.6.0.6\n"},{"id":"109575","messageId":"6b4495184b92d42059a12af17c54f1951337338a.1238102327.git.ferry.huberts@pelagic.nl","threadId":"18579","inReplyTo":"e9605b0b1bda26f568009a42b31220d60692fce6.1238102327.git.ferry.huberts@pelagic.nl","subject":"[EGIT] [PATCH RFC v1 4/5] Do not set .git as a Team ignore pattern","fromName":"Ferry Huberts","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-03-26T21:34:26Z","receivedAt":"2009-03-26T21:34:26Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"The .git ignore pattern is only valid in the context that it matches\na .git directory that is actually a repository.\n\nSigned-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>\n---\n org.spearce.egit.core/plugin.xml |    6 ------\n 1 files changed, 0 insertions(+), 6 deletions(-)\n\ndiff --git a/org.spearce.egit.core/plugin.xml b/org.spearce.egit.core/plugin.xml\nindex 77ecebf..ff24ac7 100644\n--- a/org.spearce.egit.core/plugin.xml\n+++ b/org.spearce.egit.core/plugin.xml\n@@ -11,10 +11,4 @@\n \t\tid=\"org.spearce.egit.core.GitProvider\">\n \t</repository>\n   </extension>\n-  <extension\n-        point=\"org.eclipse.team.core.ignore\">\n-     <ignore\n-           enabled=\"true\"\n-           pattern=\".git\"/>\n-  </extension>   \n </plugin>\n-- \n1.6.0.6\n"},{"id":"109579","messageId":"55baa61ac6caea86a364ddc0421d9422d96f2825.1238102327.git.ferry.huberts@pelagic.nl","threadId":"18579","inReplyTo":"6b4495184b92d42059a12af17c54f1951337338a.1238102327.git.ferry.huberts@pelagic.nl","subject":"[EGIT] [PATCH RFC v1 5/5] Use the ignore patterns cache to determine ignores","fromName":"Ferry Huberts","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-03-26T21:34:27Z","receivedAt":"2009-03-26T21:34:27Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"Signed-off-by: Ferry Huberts <ferry.huberts@pelagic.nl>\n---\n .../src/org/spearce/egit/core/ignores/DType.java   |   44 ++++++\n .../src/org/spearce/egit/core/ignores/Exclude.java |  143 ++++++++++++++++----\n .../spearce/egit/core/ignores/GitIgnoreData.java   |   43 ++++++-\n .../egit/core/ignores/IgnoreProjectCache.java      |   44 ++++++\n .../egit/core/ignores/IgnoreRepositoryCache.java   |   68 ++++++++--\n .../spearce/egit/core/project/GitProjectData.java  |    3 +\n org.spearce.jgit/META-INF/MANIFEST.MF              |    1 +\n 7 files changed, 307 insertions(+), 39 deletions(-)\n create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java\n\ndiff --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\nnew file mode 100644\nindex 0000000..5661dca\n--- /dev/null\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java\n@@ -0,0 +1,44 @@\n+/*******************************************************************************\n+ * Copyright (C) 2009, Ferry Huberts <ferry.huberts@pelagic.nl>\n+ *\n+ * All rights reserved. This program and the accompanying materials\n+ * are made available under the terms of the Eclipse Public License v1.0\n+ * See LICENSE for the full license text, also available.\n+ *******************************************************************************/\n+package org.spearce.egit.core.ignores;\n+\n+import org.eclipse.core.resources.IFile;\n+import org.eclipse.core.resources.IFolder;\n+import org.eclipse.core.resources.IProject;\n+import org.eclipse.core.resources.IResource;\n+\n+/**\n+ * This class describes a file type in the same way as git does.\n+ * \n+ * The git definition can be found in the source file cache.h. The code can be\n+ * found in the source file dir.c:get_dtype\n+ */\n+enum DType {\n+\tDT_UNKNOWN, DT_DIR, DT_REG, DT_LINK;\n+\n+\t/*\n+\t * Interface Methods\n+\t */\n+\n+\tstatic DType get(final IResource resource) {\n+\t\tif (resource == null) {\n+\t\t\treturn DT_UNKNOWN;\n+\t\t}\n+\t\tif (resource instanceof IFile) {\n+\t\t\treturn DT_REG;\n+\t\t}\n+\t\tif ((resource instanceof IFolder) || (resource instanceof IProject)) {\n+\t\t\treturn DT_DIR;\n+\t\t}\n+\t\tif (resource.isLinked()) {\n+\t\t\treturn DT_LINK;\n+\t\t}\n+\n+\t\treturn DT_UNKNOWN;\n+\t}\n+}\n\\ No newline at end of file\ndiff --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\nindex c4c48e9..eaddd17 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java\n@@ -9,6 +9,9 @@\n \n import java.util.regex.Pattern;\n \n+import org.spearce.jgit.errors.InvalidPatternException;\n+import org.spearce.jgit.fnmatch.FileNameMatcher;\n+\n /**\n  * This class describes an ignore pattern in the same way as git does, with some\n  * extra information to support Eclipse specific functionality.\n@@ -105,6 +108,110 @@ Exclude(final String pattern, final String base,\n \t}\n \n \t/*\n+\t * Interface Methods\n+\t */\n+\n+\t/**\n+\t * Tries to match a given resource to the Exclude\n+\t * \n+\t * @param pathName\n+\t *            the full path of the resource, relative to the checkout\n+\t *            directory\n+\t * @param baseName\n+\t *            the baseName of the resource\n+\t * @param resourceType\n+\t *            the type of the resource\n+\t * @return true when the resource matches this Exclude, false otherwise\n+\t * \n+\t */\n+\tboolean isMatch(final String pathName, final String baseName,\n+\t\t\tfinal DType resourceType) {\n+\t\t/* this is needed to make the exact same match as git does */\n+\t\tString xbase = pathName;\n+\t\tfinal int pos = xbase.lastIndexOf('/');\n+\t\tif (pos < 0) {\n+\t\t\txbase = \"\";\n+\t\t} else {\n+\t\t\txbase = xbase.substring(0, pos + 1);\n+\t\t}\n+\n+\t\tif (mustBeDir && (resourceType != DType.DT_DIR)) {\n+\t\t\treturn false;\n+\t\t}\n+\n+\t\tif (noDir) {\n+\t\t\t/* pattern does not contain directories. dir.c: match basename */\n+\t\t\tif (noWildcard) {\n+\t\t\t\t/* pattern does not contain directories and has no wildcards */\n+\t\t\t\tif (baseName.equals(pattern)) {\n+\t\t\t\t\treturn to_exclude;\n+\t\t\t\t}\n+\t\t\t} else if (endsWith) {\n+\t\t\t\t/*\n+\t\t\t\t * pattern does not contain directories and resource must end\n+\t\t\t\t * with pattern.substring(1)\n+\t\t\t\t */\n+\t\t\t\tif (baseName.endsWith(pattern.substring(1))) {\n+\t\t\t\t\treturn to_exclude;\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\t/*\n+\t\t\t\t * pattern does not contain directories, has wildcards, and does\n+\t\t\t\t * not end with pattern.substring(1)\n+\t\t\t\t */\n+\t\t\t\ttry {\n+\t\t\t\t\tfinal FileNameMatcher matcher = new FileNameMatcher(\n+\t\t\t\t\t\t\tpattern, null);\n+\t\t\t\t\tmatcher.append(baseName);\n+\t\t\t\t\tif (matcher.isMatch()) {\n+\t\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\t}\n+\t\t\t\t} catch (final InvalidPatternException e) {\n+\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t} else {\n+\t\t\t/*\n+\t\t\t * pattern contains directories. dir.c: match with FNM_PATHNAME:\n+\t\t\t * exclude (e.g. 'this.pattern') has base (baselen long) implicitly\n+\t\t\t * in front of it.\n+\t\t\t */\n+\t\t\tfinal int baselen = base.length();\n+\t\t\tString matchPattern = this.pattern;\n+\t\t\tif (matchPattern.startsWith(\"/\")) {\n+\t\t\t\tmatchPattern = matchPattern.substring(1);\n+\t\t\t}\n+\n+\t\t\tif ((pathName.length() < baselen)\n+\t\t\t\t\t|| ((baselen > 0) && (pathName.charAt(baselen - 1) != '/'))\n+\t\t\t\t\t|| !pathName.substring(0, baselen).equals(xbase)) {\n+\t\t\t\treturn false;\n+\t\t\t}\n+\n+\t\t\tfinal String remainingResourceName = pathName.substring(baselen);\n+\t\t\tif (noWildcard) {\n+\t\t\t\t/* pattern contains directories and has no wildcards */\n+\t\t\t\tif (remainingResourceName.equals(matchPattern)) {\n+\t\t\t\t\treturn to_exclude;\n+\t\t\t\t}\n+\t\t\t} else {\n+\t\t\t\t/* pattern contains directories and has wildcards */\n+\t\t\t\ttry {\n+\t\t\t\t\tfinal FileNameMatcher matcher = new FileNameMatcher(\n+\t\t\t\t\t\t\tmatchPattern, Character.valueOf('/'));\n+\t\t\t\t\tmatcher.append(remainingResourceName);\n+\t\t\t\t\tif (matcher.isMatch()) {\n+\t\t\t\t\t\treturn to_exclude;\n+\t\t\t\t\t}\n+\t\t\t\t} catch (final InvalidPatternException e) {\n+\t\t\t\t\treturn false;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t}\n+\t\treturn false;\n+\t}\n+\n+\t/*\n \t * Private Methods\n \t */\n \n@@ -119,40 +226,18 @@ private boolean no_wildcard(final String string) {\n \t/*\n \t * Getters / Setters\n \t */\n-\n-\tpublic String getIgnoreFileAbsolutePath() {\n-\t\treturn ignoreFileAbsolutePath;\n-\t}\n-\n-\tpublic int getLineNumber() {\n-\t\treturn lineNumber;\n-\t}\n-\n-\t/**\n-\t * @return the base\n-\t */\n-\tpublic String getBase() {\n-\t\treturn base;\n-\t}\n-\n-\t/**\n-\t * @return the noDir\n-\t */\n-\tpublic boolean isNoDir() {\n-\t\treturn noDir;\n-\t}\n-\n+\t\n \t/**\n-\t * @return the endsWith\n+\t * @return the ignoreFileAbsolutePath\n \t */\n-\tpublic boolean isEndsWith() {\n-\t\treturn endsWith;\n+\tpublic String getIgnoreFileAbsolutePath() {\n+\t\treturn ignoreFileAbsolutePath;\n \t}\n \n \t/**\n-\t * @return the noWildcard\n+\t * @return the lineNumber\n \t */\n-\tpublic boolean isNoWildcard() {\n-\t\treturn noWildcard;\n+\tpublic int getLineNumber() {\n+\t\treturn lineNumber;\n \t}\n }\n\\ No newline at end of file\ndiff --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\nindex 401a378..48cf454 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java\n@@ -14,6 +14,7 @@\n import org.eclipse.core.resources.IWorkspace;\n import org.eclipse.core.resources.ResourcesPlugin;\n import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.team.core.Team;\n import org.spearce.egit.core.project.RepositoryMapping;\n import org.spearce.jgit.lib.Repository;\n \n@@ -128,7 +129,7 @@ public synchronized static void importWorkspaceIgnores() {\n \t\t * FIXME: also do the file `git config --global --get\n \t\t * core.excludesfile`, is this already covered? RepositoryConfig\n \t\t * globalConfig = new RepositoryConfig(null, new File(FS.userHome(),\n-\t\t * \".gitconfig\"));\n+\t\t * \".gitconfig\")); RepositoryConfig.openUserConfig\n \t\t */\n \n \t\t/*\n@@ -136,4 +137,44 @@ public synchronized static void importWorkspaceIgnores() {\n \t\t * repositories.toString());\n \t\t */\n \t}\n+\n+\t/**\n+\t * @param resource\n+\t *            the resource to check\n+\t * @return null when not matched, the matching Exclude otherwise the\n+\t *         resource\n+\t */\n+\tsynchronized static Exclude isResourceExcluded(final IResource resource) {\n+\t\tif (resource == null) {\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\tfinal RepositoryMapping mapping = RepositoryMapping\n+\t\t\t\t.getMapping(resource);\n+\t\tif (mapping == null) {\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\tfinal IgnoreRepositoryCache cache = repositories.get(mapping\n+\t\t\t\t.getRepository());\n+\t\tif (cache == null) {\n+\t\t\treturn null;\n+\t\t}\n+\n+\t\t/* FIXME: also check global core.excludesfile, is this already covered? */\n+\n+\t\treturn cache.isIgnored(resource, mapping);\n+\t}\n+\n+\t/**\n+\t * @param resource\n+\t * @return true when the resource is ignored\n+\t */\n+\tpublic synchronized static boolean isIgnored(final IResource resource) {\n+\t\tif (isResourceExcluded(resource) != null) {\n+\t\t\treturn true;\n+\t\t}\n+\n+\t\treturn Team.isIgnoredHint(resource);\n+\t}\n }\ndiff --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\nindex fe2f529..7f35240 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java\n@@ -15,6 +15,7 @@\n import org.eclipse.core.resources.IResource;\n import org.eclipse.core.resources.IResourceDelta;\n import org.eclipse.core.runtime.CoreException;\n+import org.eclipse.core.runtime.IPath;\n \n /**\n  * This class implements a cache of ignore patterns for an Eclipse project: it\n@@ -198,4 +199,47 @@ synchronized void processIgnoreFile(final IFile ignoreFile,\n \t\t// break;\n \t\t// }\n \t}\n+\n+\tsynchronized Exclude isIgnored(final String pathName,\n+\t\t\tfinal String baseName, final DType dType,\n+\t\t\tfinal IPath deepestDirectory) {\n+\t\tIPath searchDir = deepestDirectory;\n+\t\tString lookupKey = (searchDir.isEmpty() ? searchDir.toString()\n+\t\t\t\t: searchDir.toString() + \"/\");\n+\t\tfinal boolean result = false;\n+\t\tsearcher: while (!result) {\n+\t\t\t/* look for the first ignore file up in the tree */\n+\t\t\twhile (!ignoreFilesIndex.containsKey(lookupKey)) {\n+\t\t\t\tsearchDir = searchDir.removeLastSegments(1);\n+\t\t\t\tif (searchDir.isEmpty()) {\n+\t\t\t\t\tbreak searcher;\n+\t\t\t\t}\n+\t\t\t\tlookupKey = (searchDir.isEmpty() ? searchDir.toString()\n+\t\t\t\t\t\t: searchDir.toString() + \"/\");\n+\t\t\t}\n+\t\t\tfinal IFile ignoreFile = ignoreFilesIndex.get(lookupKey);\n+\n+\t\t\t/* when found then try to match the resource to those patterns */\n+\t\t\tif (ignoreFile != null) {\n+\t\t\t\tfinal LinkedList<Exclude> excludeList = ignoreFiles\n+\t\t\t\t\t\t.get(ignoreFile);\n+\t\t\t\tfor (int i = excludeList.size() - 1; i >= 0; i--) {\n+\t\t\t\t\tfinal Exclude x = excludeList.get(i);\n+\t\t\t\t\tif (x.isMatch(pathName, baseName, dType)) {\n+\t\t\t\t\t\treturn x;\n+\t\t\t\t\t}\n+\t\t\t\t}\n+\t\t\t}\n+\n+\t\t\tif (searchDir.isEmpty()) {\n+\t\t\t\tbreak searcher;\n+\t\t\t}\n+\t\t\tsearchDir = searchDir.removeLastSegments(1);\n+\t\t\tlookupKey = (searchDir.isEmpty() ? searchDir.toString() : searchDir\n+\t\t\t\t\t.toString()\n+\t\t\t\t\t+ \"/\");\n+\t\t}\n+\n+\t\treturn null;\n+\t}\n }\n\\ No newline at end of file\ndiff --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\nindex 4b6bf61..9bfb197 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java\n@@ -35,7 +35,7 @@\n \t/** the repository */\n \tprivate Repository repository = null;\n \n-\t/** the checkout directory for the repository */\n+\t/** the checkout directory for the repository, full path, platform specific */\n \tprivate String checkoutDir = null;\n \n \t/** the cache that holds ignore data on a per-project basis */\n@@ -59,6 +59,9 @@\n \t/** the core.excludesfile setting */\n \tprivate String coreExcludesSetting = null;\n \n+\t/** the exclude for the repository itself */\n+\tprivate Exclude repositoryExclude = null;\n+\n \t/**\n \t * Retrieve a project mapping from the projects cache. When the project is\n \t * not yet in the cache then create a new mapping for it and store it in the\n@@ -95,6 +98,8 @@ IgnoreRepositoryCache(final Repository repository) {\n \t\t}\n \t\tthis.repository = repository;\n \t\tthis.checkoutDir = repository.getWorkDir().getAbsolutePath();\n+\t\tthis.repositoryExclude = new Exclude(\"/.git/\", \"\", checkoutDir\n+\t\t\t\t+ \"/.git/config\", 1);\n \t}\n \n \t/*\n@@ -295,14 +300,59 @@ private boolean readRepositoryCoreExcludesSetting() {\n \t\treturn changes;\n \t}\n \t\n-\t/*\n-\t * Getters / Setters\n-\t */\n+\tsynchronized Exclude isIgnored(final IResource resource,\n+\t\t\tfinal RepositoryMapping mapping) {\n+\t\tfinal String pathName = mapping.getRepoRelativePath(resource);\n+\t\tIPath deepestDirectory = new Path(pathName).removeLastSegments(1);\n+\t\tfinal String baseName = resource.getName();\n+\t\tfinal DType dType = DType.get(resource);\n+\n+\t\t/* the repository directory is always ignored */\n+\t\tif (this.repositoryExclude.isMatch(pathName, resource.getName(), DType\n+\t\t\t\t.get(resource))) {\n+\t\t\treturn this.repositoryExclude;\n+\t\t}\n \n-\t/**\n-\t * @return the checkoutDir\n-\t */\n-\tpublic String getCheckoutDir() {\n-\t\treturn checkoutDir;\n+\t\tExclude exclude = null;\n+\n+\t\t/* check project excludes */\n+\t\tfinal IProject project = resource.getProject();\n+\t\tif (project != null) {\n+\t\t\tfinal IgnoreProjectCache cache = projects.get(project);\n+\t\t\tif (cache != null) {\n+\t\t\t\texclude = cache.isIgnored(pathName, baseName, dType,\n+\t\t\t\t\t\tdeepestDirectory);\n+\t\t\t\tif (exclude != null) {\n+\t\t\t\t\treturn exclude;\n+\t\t\t\t}\n+\t\t\t}\n+\t\t\tdeepestDirectory = new Path(mapping.getRepoRelativePath(project))\n+\t\t\t\t\t.removeLastSegments(1);\n+\t\t} else {\n+\t\t\tdeepestDirectory = new Path(\"\");\n+\t\t}\n+\t\t\n+\t\t/* check excludes outside projects */\n+\t\texclude = outside\n+\t\t\t\t.isIgnored(pathName, baseName, dType, deepestDirectory);\n+\t\tif (exclude != null) {\n+\t\t\treturn exclude;\n+\t\t}\n+\t\t\n+\t\t/* also check info/exclude file per repo */\n+\t\texclude = infoExclude.isIgnored(baseName, baseName, dType,\n+\t\t\t\tdeepestDirectory);\n+\t\tif (exclude != null) {\n+\t\t\treturn exclude;\n+\t\t}\n+\t\t\n+\t\t/* also check core.excludesfile per repo */\n+\t\texclude = coreExcludes.isIgnored(baseName, baseName, dType,\n+\t\t\t\tdeepestDirectory);\n+\t\tif (exclude != null) {\n+\t\t\treturn exclude;\n+\t\t}\n+\n+\t\treturn null;\n \t}\n }\n\\ No newline at end of file\ndiff --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\nindex 414bd83..09766b6 100644\n--- a/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java\n+++ b/org.spearce.egit.core/src/org/spearce/egit/core/project/GitProjectData.java\n@@ -63,6 +63,9 @@\n \t\t@SuppressWarnings(\"synthetic-access\")\n \t\tpublic void resourceChanged(final IResourceChangeEvent event) {\n \t\t\tswitch (event.getType()) {\n+\t\t\tcase IResourceChangeEvent.POST_CHANGE:\n+\t\t\t\t// GitIgnoreData.processChangesForIgnores(event);\n+\t\t\t\tbreak;\n \t\t\tcase IResourceChangeEvent.PRE_CLOSE:\n \t\t\t\tuncache((IProject) event.getResource());\n \t\t\t\tGitIgnoreData.uncacheProject(event.getResource());\ndiff --git a/org.spearce.jgit/META-INF/MANIFEST.MF b/org.spearce.jgit/META-INF/MANIFEST.MF\nindex 3344c3c..e5f6478 100644\n--- a/org.spearce.jgit/META-INF/MANIFEST.MF\n+++ b/org.spearce.jgit/META-INF/MANIFEST.MF\n@@ -7,6 +7,7 @@ Bundle-Localization: plugin\n Bundle-Vendor: %provider_name\n Export-Package: org.spearce.jgit.dircache,\n  org.spearce.jgit.errors;uses:=\"org.spearce.jgit.lib\",\n+ org.spearce.jgit.fnmatch,\n  org.spearce.jgit.lib,\n  org.spearce.jgit.revplot,\n  org.spearce.jgit.revwalk,\n-- \n1.6.0.6\n"},{"id":"109752","messageId":"200903291123.24433.robin.rosenberg@dewire.com","threadId":"18579","inReplyTo":"cover.1238102327.git.ferry.huberts@pelagic.nl","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg@dewire.com","sentAt":"2009-03-29T09:23:23Z","receivedAt":"2009-03-29T09:23:23Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"\nA quick reply (I might come up with more later): Ignore support should be mostly\nin jgit, with only extensions into egit.\n\n-- robin\n"},{"id":"109764","messageId":"49CF50CC.10400@pelagic.nl","threadId":"18579","inReplyTo":"200903291123.24433.robin.rosenberg@dewire.com","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Ferry Huberts (Pelagic)","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-03-29T10:43:24Z","receivedAt":"2009-03-29T10:43:24Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"Robin Rosenberg wrote:\n> A quick reply (I might come up with more later): Ignore support should be mostly\n> in jgit, with only extensions into egit.\n> \n> -- robin\nI discussed this with shawn and proposed to first implement it in egit\nand when we have it right then move it into jgit. I think shawn agreed\nwith that.\n\nFerry\n"},{"id":"109800","messageId":"49D0151B.6070408@magma.ca","threadId":"18579","inReplyTo":"cover.1238102327.git.ferry.huberts@pelagic.nl","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Jonathan Gossage","fromEmail":"jgossage@gmail.com","sentAt":"2009-03-30T00:40:59Z","receivedAt":"2009-03-30T00:40:59Z","isPatch":true,"sender":{"key":"jgossage@gmail.com","avatar":null},"body":"Ferry Huberts wrote:\n> This is the first - early - code that adds ignore functionality to EGit.\n> Currently it reads in all ignore patterns upon workspace startup into an\n> ignore cache. From this cache the ignore state of a resource is evaluated\n> in the same fashion as git does.\n>\n> The code does not yet react to changes in ignore files but I'm planning to add\n> that soon and I can share a lot of code for that.\n>\n> I send this code to receive feedback and to give you insight into what I'm\n> doing with it. I'm new both to EGit programming and Eclipse programming so\n> there might be things that could be done more elegantly :-)\n>\n> A few notes:\n> - The patches are rebased on the current master (e3440623)\n> - The order of the patches must be re-arranged, but that is rather easy. The\n>   correct order - once finished - would be:\n>     Build up the ignore patterns cache upon workspace startup.\n>     Use the ignore patterns cache to determine ignores\n>     Enable the ignore handling of the plugin\n>     Optimise ignore evaluation\n>     Do not set .git as a Team ignore pattern\n> - The core.excludesfile code is currently untested, the other code seems to be\n>   in a good state.\n> - There are a few FIXMEs in the code with questions and tasks. It's a work in\n>   progress and these will disappear.\n>\n> Ferry Huberts (5):\n>   Build up the ignore patterns cache upon workspace startup.\n>   Enable the ignore handling of the plugin\n>   Optimise ignore evaluation\n>   Do not set .git as a Team ignore pattern\n>   Use the ignore patterns cache to determine ignores\n>\n>  org.spearce.egit.core/META-INF/MANIFEST.MF         |    1 +\n>  org.spearce.egit.core/plugin.xml                   |    6 -\n>  .../src/org/spearce/egit/core/ignores/DType.java   |   44 ++\n>  .../src/org/spearce/egit/core/ignores/Exclude.java |  243 +++++++++\n>  .../spearce/egit/core/ignores/GitIgnoreData.java   |  180 +++++++\n>  .../org/spearce/egit/core/ignores/IgnoreFile.java  |   82 +++\n>  .../egit/core/ignores/IgnoreFileOutside.java       |  543 ++++++++++++++++++++\n>  .../egit/core/ignores/IgnoreProjectCache.java      |  245 +++++++++\n>  .../egit/core/ignores/IgnoreRepositoryCache.java   |  358 +++++++++++++\n>  .../org/spearce/egit/core/op/TrackOperation.java   |    7 +-\n>  .../spearce/egit/core/project/GitProjectData.java  |    8 +\n>  .../decorators/DecoratableResourceAdapter.java     |   11 +-\n>  org.spearce.jgit/META-INF/MANIFEST.MF              |    1 +\n>  13 files changed, 1712 insertions(+), 17 deletions(-)\n>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/DType.java\n>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/Exclude.java\n>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/GitIgnoreData.java\n>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFile.java\n>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreFileOutside.java\n>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreProjectCache.java\n>  create mode 100644 org.spearce.egit.core/src/org/spearce/egit/core/ignores/IgnoreRepositoryCache.java\n>\n> --\n> To unsubscribe from this list: send the line \"unsubscribe git\" in\n> the body of a message to majordomo@vger.kernel.org\n> More majordomo info at  http://vger.kernel.org/majordomo-info.html\n>\n>\n>\n>   \n\nEclipse 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.\n\nI think you will run into problems if you try to create a workspace wide\ncache. It is quite possible that one workspace could have projects that\ntarget different Git repositories. This means that your cache would need\nto look at all projects in the workspace and potentially take into\naccount Eclipse working sets and other such complications. You also will\nneed to deal with projects being added and deleted from the Eclipse\nworkspace.\n\nI think a better approach might be to go for lazy cache construction\nwhere the cache is built only when actually needed by a user operation.\nThe cache would then be built only for a specific Git repository. JGit\nshould be responsible for assembling a merged list from the various Git\nfiles. It should also be responsible for the actual updating of the\nvarious Git ignore files. Since I believe that the Eclipse\nrepository-independent ignore file list should be the lowest priority in\nthe merged list, it will be necessary to pass the Eclipse list to JGit\nas a parameter whenever a merged list is required.\n\nIn general, you should look to do Git specific things in JGit and do\nEclipse things in Eclipse. That way JGit continues to acquire the\nfunctionality to support any IDE and EGit is kept as simple as possible.\n\nHTH\n\nJonathan Gossage\n"},{"id":"109811","messageId":"20090330042732.GS23521@spearce.org","threadId":"18579","inReplyTo":"49CF50CC.10400@pelagic.nl","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-03-30T04:27:32Z","receivedAt":"2009-03-30T04:27:32Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"\"Ferry Huberts (Pelagic)\" <ferry.huberts@pelagic.nl> wrote:\n> Robin Rosenberg wrote:\n> > A quick reply (I might come up with more later): Ignore support should be mostly\n> > in jgit, with only extensions into egit.\n> > \n> I discussed this with shawn and proposed to first implement it in egit\n> and when we have it right then move it into jgit. I think shawn agreed\n> with that.\n\nI may have agreed with it.  My memory isn't *that* good.  :-)\n\nIn general principal I agree with Robin, Git specific handling\nshould be in JGit as much as possible so we can reuse the logic in\nmore applications than just EGit.\n\nBut it may have been easier to get a first working prototype by doing\nthe code in EGit, and later pulling some of it down into JGit as we\nidentity what isn't EGit specific.\n\nThe problem with that is the dual licenses; code in EGit can't\nbe pulled down to JGit without relicensing it under the BSD.\nOnly the original author of the code can do that.  So if you\ncontribute ignore support to EGit under the EPL which is better\nplaced in JGit, Robin or myself can't pull it down ourselves,\nwe'd have to rewrite it.\n\nBut even rewriting may be difficult, as the rewrite may be too close\nto the original (same language, same surrounding code, likely going\nto produce a similar result).\n\n-- \nShawn.\n"},{"id":"109834","messageId":"200903300818.26983.robin.rosenberg.lists@dewire.com","threadId":"18579","inReplyTo":"49D0151B.6070408@magma.ca","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Robin Rosenberg","fromEmail":"robin.rosenberg.lists@dewire.com","sentAt":"2009-03-30T06:18:26Z","receivedAt":"2009-03-30T06:18:26Z","isPatch":true,"sender":{"key":"robin.rosenberg@dewire.com","avatar":"https://avatars.githubusercontent.com/u/46357?v=4"},"body":"måndag 30 mars 2009 02:40:59 skrev Jonathan Gossage <jgossage@gmail.com>:\n> Ferry Huberts wrote:\n> 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.\n\nThe current EGit obeys the Eclipse ignore rules already, so no there is no need to hurry just for that feature.\n\n-- robin\n"},{"id":"110457","messageId":"20090405210248.GA23604@spearce.org","threadId":"18579","inReplyTo":"cover.1238102327.git.ferry.huberts@pelagic.nl","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-04-05T21:02:48Z","receivedAt":"2009-04-05T21:02:48Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"Ferry Huberts <ferry.huberts@pelagic.nl> wrote:\n> This is the first - early - code that adds ignore functionality to EGit.\n> Currently it reads in all ignore patterns upon workspace startup into an\n> ignore cache. From this cache the ignore state of a resource is evaluated\n> in the same fashion as git does.\n> \n> The code does not yet react to changes in ignore files but I'm planning to add\n> that soon and I can share a lot of code for that.\n> \n> I send this code to receive feedback and to give you insight into what I'm\n> doing with it. I'm new both to EGit programming and Eclipse programming so\n> there might be things that could be done more elegantly :-)\n\nOk, I finally got a chance to review this series.\n\n\nWe really want as much of the Git specific logic as we can in JGit\nunder the BSD license.  This has already been raised elsewhere in\nthis thread.\n\nJGit and EGit are holding the line on Java 5 support; that means\nthat String.isEmpty() must be spelled as String.length() == 0\n(isEmpty was added in Java 6).\n\nStyle nit: Don't put /* Constructors */, /* Methods */ or\n  / * Public Methods */ comments in code, e.g.\n  IgnoreProjectCache l.52-54 or GitIgnoreData l.58-61.\n\nStyle nit: Don't assign fields to their default values.\n\n  E.g. Exclude.java l.25,33,42,.. these are being set to the\n  same value that the JRE sets the field to if the field is not\n  explicitly initialized.  We find it much easier to read code when\n  the defaults are assumed.\n\nStyle nit: Don't use \"this.\" to refer to members.\n\n  Your IDE should highlight field references differently than\n  parameters, and a parameter should never shadow a field name,\n  thus \"this.\" is unnecessary and makes the code much more verbose\n  to read.  E.g. see Exclude.java 's constructor on l.87-108; I can't\n  see the forest (the code) due to all the trees (this.) appearing.\n\nIgnoreFileOutside: Ugh, our own implementation of IFile ?\n\n  I'm worried about the long-term stability of the IFile API.\n  Is it really frozen enough that we can implement it ourselves?\n  Of course, this may be moot if much of the code was moved back\n  to JGit.\n\nIgnoreRepositoryCache: Why not put this into RepositoryMapping?\n\n  Instead of caching it inside a static HashMap of GitIgnoreData,\n  wouldn't it be better to put it into RepositoryMapping?\n  The TrackOperation for example already has the RepositoryMapping\n  handle in scope, saving a few lookup operations, and avoiding\n  needing to manage this new additional static HashMap against leaks.\n\n\nI kind of wanted to tie exclude processing (and attribute processing)\ninto a TreeWalk, so that we can do an n-way merge against trees and\nworking directories by tossing all of their AbstractTreeIterators\ninto a single walk, possibly apply a path filter, and let the walk\nhandle the per-directory ignore rules as it goes.\n\nMost of your code seems to be built around the Eclipse IResource\nmodel, and the idea that it gets called for a single file path\nat a time, which may make it less efficient when we put it into a\nTreeWalk and apply the notion of entering and exiting a subdirectory.\n\n\nOK, that's about all I have for now.  Its reasonable, but still an\nearly series.\n \n-- \nShawn.\n"},{"id":"110601","messageId":"49DA31DC.3070903@pelagic.nl","threadId":"18579","inReplyTo":"20090405210248.GA23604@spearce.org","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Ferry Huberts (Pelagic)","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-04-06T16:46:20Z","receivedAt":"2009-04-06T16:46:20Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> Ferry Huberts <ferry.huberts@pelagic.nl> wrote:\n>> This is the first - early - code that adds ignore functionality to EGit.\n>> Currently it reads in all ignore patterns upon workspace startup into an\n>> ignore cache. From this cache the ignore state of a resource is evaluated\n>> in the same fashion as git does.\n>>\n>> The code does not yet react to changes in ignore files but I'm planning to add\n>> that soon and I can share a lot of code for that.\n>>\n>> I send this code to receive feedback and to give you insight into what I'm\n>> doing with it. I'm new both to EGit programming and Eclipse programming so\n>> there might be things that could be done more elegantly :-)\n> \n> Ok, I finally got a chance to review this series.\n> \n> \n> We really want as much of the Git specific logic as we can in JGit\n> under the BSD license.  This has already been raised elsewhere in\n> this thread.\n> \nack.\n\n> JGit and EGit are holding the line on Java 5 support; that means\n> that String.isEmpty() must be spelled as String.length() == 0\n> (isEmpty was added in Java 6).\nok\n\n> \n> Style nit: Don't put /* Constructors */, /* Methods */ or\n>   / * Public Methods */ comments in code, e.g.\n>   IgnoreProjectCache l.52-54 or GitIgnoreData l.58-61.\n> \nok\n\n> Style nit: Don't assign fields to their default values.\n> \n>   E.g. Exclude.java l.25,33,42,.. these are being set to the\n>   same value that the JRE sets the field to if the field is not\n>   explicitly initialized.  We find it much easier to read code when\n>   the defaults are assumed.\n> \nok\n\n> Style nit: Don't use \"this.\" to refer to members.\n> \n>   Your IDE should highlight field references differently than\n>   parameters, and a parameter should never shadow a field name,\n>   thus \"this.\" is unnecessary and makes the code much more verbose\n>   to read.  E.g. see Exclude.java 's constructor on l.87-108; I can't\n>   see the forest (the code) due to all the trees (this.) appearing.\n> \nok\n\n> IgnoreFileOutside: Ugh, our own implementation of IFile ?\n> \n>   I'm worried about the long-term stability of the IFile API.\n>   Is it really frozen enough that we can implement it ourselves?\n>   Of course, this may be moot if much of the code was moved back\n>   to JGit.\n> \nwhen we convert the code to be in jgit we will not use this api.\nthis will disappear.\n\n> IgnoreRepositoryCache: Why not put this into RepositoryMapping?\n> \n>   Instead of caching it inside a static HashMap of GitIgnoreData,\n>   wouldn't it be better to put it into RepositoryMapping?\n>   The TrackOperation for example already has the RepositoryMapping\n>   handle in scope, saving a few lookup operations, and avoiding\n>   needing to manage this new additional static HashMap against leaks.\n> \nok\n\n> \n> I kind of wanted to tie exclude processing (and attribute processing)\n> into a TreeWalk, so that we can do an n-way merge against trees and\n> working directories by tossing all of their AbstractTreeIterators\n> into a single walk, possibly apply a path filter, and let the walk\n> handle the per-directory ignore rules as it goes.\n> \nok. but I need much more input than that from you on how to go about\nmoving the code into jgit. If you have ideas on what the rough\narchitecture should be of the ignore processing in jgit then please let\nme know, I don't know that code at all and also don't know the approach\nyou'd like to take there with its architecture.\n\nI've been thinking on and off about how to free the ignore processing\nfrom eclipse specific apis ever since the egit/jgit issue came up. I\nthink it's not that hard to do, depending on the approach you'd like to\ntake.\n\nyou talk about TreeWalk. but what is it's prupose, in what context is it\ncalled? etc. You can probably much faster explain that to me than I can\nfind out from the code (the egit code is much easier to place in context\nthan the jgit code I think)\n\n> Most of your code seems to be built around the Eclipse IResource\n> model, and the idea that it gets called for a single file path\n> at a time, which may make it less efficient when we put it into a\n> TreeWalk and apply the notion of entering and exiting a subdirectory.\n> \nthe way it works now is more efficient than git itself (most of the\ntime), since we have a cache of the ignore patterns. I compared traces\nof git itself and the plugin against eachother.\n\n> \n> OK, that's about all I have for now.  Its reasonable, but still an\n> early series.\n>  \nyep.\nplease let's discuss moving it into jgit asap. so that I can complete\nthe feature.\n\nBTW\nIn the meantime I've also added ignore preferences to the plugin on\nwhich the user can specify whether the Eclipse Team Ignored Resources\nshould be taken into account. It defaults to yes. When set to no the\nignore processing complies strict to git behaviour.\n\nlooking forward to your input!\n\nFerry\n"},{"id":"110603","messageId":"49DA3305.7090601@pelagic.nl","threadId":"18579","inReplyTo":"20090405210248.GA23604@spearce.org","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Ferry Huberts (Pelagic)","fromEmail":"ferry.huberts@pelagic.nl","sentAt":"2009-04-06T16:51:17Z","receivedAt":"2009-04-06T16:51:17Z","isPatch":true,"sender":{"key":"ferry.huberts@pelagic.nl","avatar":"https://gravatar.com/avatar/9f63c0289ad23cbdef0f7609a0af85ff0f4b3babfd066de9ff58f62d48cfd6f2?d=mp&s=160"},"body":"Shawn O. Pearce wrote:\n> JGit and EGit are holding the line on Java 5 support; that means\n> that String.isEmpty() must be spelled as String.length() == 0\n> (isEmpty was added in Java 6).\n\njust looked in the project settings for org.spearce.egit.core and it has\njava 1.5 style specified _and_ eclipse does not give me a warning on the\n*.isEmpty() calls. Am i missing something here?\n"},{"id":"110605","messageId":"20090406170326.GI23604@spearce.org","threadId":"18579","inReplyTo":"49DA3305.7090601@pelagic.nl","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Shawn O. Pearce","fromEmail":"spearce@spearce.org","sentAt":"2009-04-06T17:03:26Z","receivedAt":"2009-04-06T17:03:26Z","isPatch":true,"sender":{"key":"spearce@spearce.org","avatar":"https://avatars.githubusercontent.com/u/34844?v=4"},"body":"\"Ferry Huberts (Pelagic)\" <ferry.huberts@pelagic.nl> wrote:\n> Shawn O. Pearce wrote:\n> > JGit and EGit are holding the line on Java 5 support; that means\n> > that String.isEmpty() must be spelled as String.length() == 0\n> > (isEmpty was added in Java 6).\n> \n> just looked in the project settings for org.spearce.egit.core and it has\n> java 1.5 style specified _and_ eclipse does not give me a warning on the\n> *.isEmpty() calls. Am i missing something here?\n\nYour workspace default JRE must be set to a Java 6.  Switch it to\nJava 5 in the workspace settings.\n\n-- \nShawn.\n"},{"id":"110606","messageId":"fabb9a1e0904061038x657177beyf6686eb6683a971@mail.gmail.com","threadId":"18579","inReplyTo":"49DA3305.7090601@pelagic.nl","subject":"Re: [EGIT] [PATCH RFC v1 0/5] Add (static) ignore functionality to EGit","fromName":"Sverre Rabbelier","fromEmail":"srabbelier@gmail.com","sentAt":"2009-04-06T17:38:12Z","receivedAt":"2009-04-06T17:38:12Z","isPatch":true,"sender":{"key":"srabbelier@gmail.com","avatar":"https://avatars.githubusercontent.com/u/3098?v=4"},"body":"Heya,\n\nOn Mon, Apr 6, 2009 at 18:51, Ferry Huberts (Pelagic)\n<ferry.huberts@pelagic.nl> wrote:\n> just looked in the project settings for org.spearce.egit.core and it has\n> java 1.5 style specified _and_ eclipse does not give me a warning on the\n> *.isEmpty() calls. Am i missing something here?\n\nFYI, notice the #since annotation in the JavaDoc [0].\n\n[0] http://java.sun.com/javase/6/docs/api/java/lang/String.html#isEmpty()\n\n-- \nCheers,\n\nSverre Rabbelier\n"}]}