git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
From: Ferry Huberts <ferry.huberts@pelagic.nl>
To: git@vger.kernel.org
Cc: "Shawn O. Pearce" <spearce@spearce.org>,
	Robin Rosenberg <robin.rosenberg@dewire.com>,
	Ferry Huberts <ferry.huberts@pelagic.nl>
Subject: [EGIT] [PATCH RFC v1 5/5] Use the ignore patterns cache to determine ignores
Date: Thu, 26 Mar 2009 22:34:27 +0100	[thread overview]
Message-ID: <55baa61ac6caea86a364ddc0421d9422d96f2825.1238102327.git.ferry.huberts@pelagic.nl> (raw)
In-Reply-To: <6b4495184b92d42059a12af17c54f1951337338a.1238102327.git.ferry.huberts@pelagic.nl>
In-Reply-To: <cover.1238102327.git.ferry.huberts@pelagic.nl>

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

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

  reply	other threads:[~2009-03-26 21:36 UTC|newest]

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

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: http://vger.kernel.org/majordomo-info.html

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=55baa61ac6caea86a364ddc0421d9422d96f2825.1238102327.git.ferry.huberts@pelagic.nl \
    --to=ferry.huberts@pelagic.nl \
    --cc=git@vger.kernel.org \
    --cc=robin.rosenberg@dewire.com \
    --cc=spearce@spearce.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

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

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