aboutsummaryrefslogtreecommitdiffstats
path: root/org.eclipse.jgit/src/org/eclipse/jgit
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.jgit/src/org/eclipse/jgit')
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java184
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java161
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java203
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/attributes/package-info.java4
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java64
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java16
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java14
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java7
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java13
-rw-r--r--org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java160
11 files changed, 818 insertions, 15 deletions
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
new file mode 100644
index 0000000000..d3ce685187
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java
@@ -0,0 +1,184 @@
+/*
+ * Copyright (C) 2010, Marc Strapetz <marc.strapetz@syntevo.com>
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+/**
+ * Represents an attribute.
+ * <p>
+ * According to the man page, an attribute can have the following states:
+ * <ul>
+ * <li>Set - represented by {@link State#SET}</li>
+ * <li>Unset - represented by {@link State#UNSET}</li>
+ * <li>Set to a value - represented by {@link State#CUSTOM}</li>
+ * <li>Unspecified - <code>null</code> is used instead of an instance of this
+ * class</li>
+ * </ul>
+ * </p>
+ *
+ * @since 3.7
+ */
+public final class Attribute {
+
+ /**
+ * The attribute value state
+ */
+ public static enum State {
+ /** the attribute is set */
+ SET,
+
+ /** the attribute is unset */
+ UNSET,
+
+ /** the attribute is set to a custom value */
+ CUSTOM
+ }
+
+ private final String key;
+ private final State state;
+ private final String value;
+
+ /**
+ * Creates a new instance
+ *
+ * @param key
+ * the attribute key. Should not be <code>null</code>.
+ * @param state
+ * the attribute state. It should be either {@link State#SET} or
+ * {@link State#UNSET}. In order to create a custom value
+ * attribute prefer the use of {@link #Attribute(String, String)}
+ * constructor.
+ */
+ public Attribute(String key, State state) {
+ this(key, state, null);
+ }
+
+ private Attribute(String key, State state, String value) {
+ if (key == null)
+ throw new NullPointerException(
+ "The key of an attribute should not be null"); //$NON-NLS-1$
+ if (state == null)
+ throw new NullPointerException(
+ "The state of an attribute should not be null"); //$NON-NLS-1$
+
+ this.key = key;
+ this.state = state;
+ this.value = value;
+ }
+
+ /**
+ * Creates a new instance.
+ *
+ * @param key
+ * the attribute key. Should not be <code>null</code>.
+ * @param value
+ * the custom attribute value
+ */
+ public Attribute(String key, String value) {
+ this(key, State.CUSTOM, value);
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (!(obj instanceof Attribute))
+ return false;
+ Attribute other = (Attribute) obj;
+ if (!key.equals(other.key))
+ return false;
+ if (state != other.state)
+ return false;
+ if (value == null) {
+ if (other.value != null)
+ return false;
+ } else if (!value.equals(other.value))
+ return false;
+ return true;
+ }
+
+ /**
+ * @return the attribute key (never returns <code>null</code>)
+ */
+ public String getKey() {
+ return key;
+ }
+
+ /**
+ * Returns the state.
+ *
+ * @return the state (never returns <code>null</code>)
+ */
+ public State getState() {
+ return state;
+ }
+
+ /**
+ * @return the attribute value (may be <code>null</code>)
+ */
+ public String getValue() {
+ return value;
+ }
+
+ @Override
+ public int hashCode() {
+ final int prime = 31;
+ int result = 1;
+ result = prime * result + key.hashCode();
+ result = prime * result + state.hashCode();
+ result = prime * result + ((value == null) ? 0 : value.hashCode());
+ return result;
+ }
+
+ @Override
+ public String toString() {
+ switch (state) {
+ case SET:
+ return key;
+ case UNSET:
+ return "-" + key; //$NON-NLS-1$
+ case CUSTOM:
+ default:
+ return key + "=" + value; //$NON-NLS-1$
+ }
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
new file mode 100644
index 0000000000..70f56ff964
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2010, Red Hat Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.ListIterator;
+import java.util.Map;
+
+import org.eclipse.jgit.lib.Constants;
+
+/**
+ * Represents a bundle of attributes inherited from a base directory.
+ *
+ * This class is not thread safe, it maintains state about the last match.
+ *
+ * @since 3.7
+ */
+public class AttributesNode {
+ /** The rules that have been parsed into this node. */
+ private final List<AttributesRule> rules;
+
+ /** Create an empty ignore node with no rules. */
+ public AttributesNode() {
+ rules = new ArrayList<AttributesRule>();
+ }
+
+ /**
+ * Create an ignore node with given rules.
+ *
+ * @param rules
+ * list of rules.
+ **/
+ public AttributesNode(List<AttributesRule> rules) {
+ this.rules = rules;
+ }
+
+ /**
+ * Parse files according to gitattribute standards.
+ *
+ * @param in
+ * input stream holding the standard ignore format. The caller is
+ * responsible for closing the stream.
+ * @throws IOException
+ * Error thrown when reading an ignore file.
+ */
+ public void parse(InputStream in) throws IOException {
+ BufferedReader br = asReader(in);
+ String txt;
+ while ((txt = br.readLine()) != null) {
+ txt = txt.trim();
+ if (txt.length() > 0 && !txt.startsWith("#") /* Comments *///$NON-NLS-1$
+ && !txt.startsWith("!") /* Negative pattern forbidden for attributes */) { //$NON-NLS-1$
+ int patternEndSpace = txt.indexOf(' ');
+ int patternEndTab = txt.indexOf('\t');
+
+ final int patternEnd;
+ if (patternEndSpace == -1)
+ patternEnd = patternEndTab;
+ else if (patternEndTab == -1)
+ patternEnd = patternEndSpace;
+ else
+ patternEnd = Math.min(patternEndSpace, patternEndTab);
+
+ if (patternEnd > -1)
+ rules.add(new AttributesRule(txt.substring(0, patternEnd),
+ txt.substring(patternEnd + 1).trim()));
+ }
+ }
+ }
+
+ private static BufferedReader asReader(InputStream in) {
+ return new BufferedReader(new InputStreamReader(in, Constants.CHARSET));
+ }
+
+ /** @return list of all ignore rules held by this node. */
+ public List<AttributesRule> getRules() {
+ return Collections.unmodifiableList(rules);
+ }
+
+ /**
+ * Returns the matching attributes for an entry path.
+ *
+ * @param entryPath
+ * the path to test. The path must be relative to this attribute
+ * node's own repository path, and in repository path format
+ * (uses '/' and not '\').
+ * @param isDirectory
+ * true if the target item is a directory.
+ * @param attributes
+ * Map that will hold the attributes matching this entry path. If
+ * it is not empty, this method will NOT override any
+ * existing entry.
+ */
+ public void getAttributes(String entryPath, boolean isDirectory,
+ Map<String, Attribute> attributes) {
+ // Parse rules in the reverse order that they were read since the last
+ // entry should be used
+ ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules
+ .size());
+ while (ruleIterator.hasPrevious()) {
+ AttributesRule rule = ruleIterator.previous();
+ if (rule.isMatch(entryPath, isDirectory)) {
+ ListIterator<Attribute> attributeIte = rule.getAttributes()
+ .listIterator(rule.getAttributes().size());
+ // Parses the attributes in the reverse order that they were
+ // read since the last entry should be used
+ while (attributeIte.hasPrevious()) {
+ Attribute attr = attributeIte.previous();
+ if (!attributes.containsKey(attr.getKey()))
+ attributes.put(attr.getKey(), attr);
+ }
+ }
+ }
+ }
+}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
new file mode 100644
index 0000000000..bcac14b5ff
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java
@@ -0,0 +1,203 @@
+/*
+ * Copyright (C) 2010, Red Hat Inc.
+ * and other copyright owners as documented in the project's IP log.
+ *
+ * This program and the accompanying materials are made available
+ * under the terms of the Eclipse Distribution License v1.0 which
+ * accompanies this distribution, is reproduced below, and is
+ * available at http://www.eclipse.org/org/documents/edl-v10.php
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or
+ * without modification, are permitted provided that the following
+ * conditions are met:
+ *
+ * - Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ *
+ * - Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ *
+ * - Neither the name of the Eclipse Foundation, Inc. nor the
+ * names of its contributors may be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND
+ * CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
+ * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+package org.eclipse.jgit.attributes;
+
+import static org.eclipse.jgit.ignore.internal.IMatcher.NO_MATCH;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import org.eclipse.jgit.attributes.Attribute.State;
+import org.eclipse.jgit.errors.InvalidPatternException;
+import org.eclipse.jgit.ignore.FastIgnoreRule;
+import org.eclipse.jgit.ignore.internal.IMatcher;
+import org.eclipse.jgit.ignore.internal.PathMatcher;
+
+/**
+ * A single attributes rule corresponding to one line in a .gitattributes file.
+ *
+ * Inspiration from: {@link FastIgnoreRule}
+ *
+ * @since 3.7
+ */
+public class AttributesRule {
+
+ /**
+ * regular expression for splitting attributes - space, tab and \r (the C
+ * implementation oddly enough allows \r between attributes)
+ * */
+ private static final String ATTRIBUTES_SPLIT_REGEX = "[ \t\r]"; //$NON-NLS-1$
+
+ private static List<Attribute> parseAttributes(String attributesLine) {
+ // the C implementation oddly enough allows \r between attributes too.
+ ArrayList<Attribute> result = new ArrayList<Attribute>();
+ for (String attribute : attributesLine.split(ATTRIBUTES_SPLIT_REGEX)) {
+ attribute = attribute.trim();
+ if (attribute.length() == 0)
+ continue;
+
+ if (attribute.startsWith("-")) {//$NON-NLS-1$
+ if (attribute.length() > 1)
+ result.add(new Attribute(attribute.substring(1),
+ State.UNSET));
+ continue;
+ }
+
+ final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$
+ if (equalsIndex == -1)
+ result.add(new Attribute(attribute, State.SET));
+ else {
+ String attributeKey = attribute.substring(0, equalsIndex);
+ if (attributeKey.length() > 0) {
+ String attributeValue = attribute
+ .substring(equalsIndex + 1);
+ result.add(new Attribute(attributeKey, attributeValue));
+ }
+ }
+ }
+ return result;
+ }
+
+ private final String pattern;
+ private final List<Attribute> attributes;
+
+ private boolean nameOnly;
+ private boolean dirOnly;
+
+ private IMatcher matcher;
+
+ /**
+ * Create a new attribute rule with the given pattern. Assumes that the
+ * pattern is already trimmed.
+ *
+ * @param pattern
+ * Base pattern for the attributes rule. This pattern will be
+ * parsed to generate rule parameters. It can not be
+ * <code>null</code>.
+ * @param attributes
+ * the rule attributes. This string will be parsed to read the
+ * attributes.
+ */
+ public AttributesRule(String pattern, String attributes) {
+ this.attributes = parseAttributes(attributes);
+ nameOnly = false;
+ dirOnly = false;
+
+ if (pattern.endsWith("/")) { //$NON-NLS-1$
+ pattern = pattern.substring(0, pattern.length() - 1);
+ dirOnly = true;
+ }
+
+ boolean hasSlash = pattern.contains("/"); //$NON-NLS-1$
+
+ if (!hasSlash)
+ nameOnly = true;
+ else if (!pattern.startsWith("/")) { //$NON-NLS-1$
+ // Contains "/" but does not start with one
+ // Adding / to the start should not interfere with matching
+ pattern = "/" + pattern; //$NON-NLS-1$
+ }
+
+ try {
+ matcher = PathMatcher.createPathMatcher(pattern,
+ Character.valueOf(FastIgnoreRule.PATH_SEPARATOR), dirOnly);
+ } catch (InvalidPatternException e) {
+ matcher = NO_MATCH;
+ }
+
+ this.pattern = pattern;
+ }
+
+ /**
+ * @return True if the pattern should match directories only
+ */
+ public boolean dirOnly() {
+ return dirOnly;
+ }
+
+ /**
+ * Returns the attributes.
+ *
+ * @return an unmodifiable list of attributes (never returns
+ * <code>null</code>)
+ */
+ public List<Attribute> getAttributes() {
+ return Collections.unmodifiableList(attributes);
+ }
+
+ /**
+ * @return <code>true</code> if the pattern is just a file name and not a
+ * path
+ */
+ public boolean isNameOnly() {
+ return nameOnly;
+ }
+
+ /**
+ * @return The blob pattern to be used as a matcher (never returns
+ * <code>null</code>)
+ */
+ public String getPattern() {
+ return pattern;
+ }
+
+ /**
+ * Returns <code>true</code> if a match was made.
+ *
+ * @param relativeTarget
+ * Name pattern of the file, relative to the base directory of
+ * this rule
+ * @param isDirectory
+ * Whether the target file is a directory or not
+ * @return True if a match was made.
+ */
+ public boolean isMatch(String relativeTarget, boolean isDirectory) {
+ if (relativeTarget == null)
+ return false;
+ if (relativeTarget.length() == 0)
+ return false;
+ boolean match = matcher.matches(relativeTarget, isDirectory);
+ return match;
+ }
+} \ No newline at end of file
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/package-info.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/package-info.java
new file mode 100644
index 0000000000..5d133d828a
--- /dev/null
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/package-info.java
@@ -0,0 +1,4 @@
+/**
+ * Support for reading .gitattributes.
+ */
+package org.eclipse.jgit.attributes;
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
index 706e057480..354a07439a 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheIterator.java
@@ -45,13 +45,20 @@
package org.eclipse.jgit.dircache;
import java.io.IOException;
+import java.io.InputStream;
+import java.util.Collections;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
+import org.eclipse.jgit.lib.ObjectId;
+import org.eclipse.jgit.lib.ObjectLoader;
import org.eclipse.jgit.lib.ObjectReader;
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
+import org.eclipse.jgit.util.RawParseUtils;
/**
* Iterate a {@link DirCache} as part of a <code>TreeWalk</code>.
@@ -65,6 +72,10 @@ import org.eclipse.jgit.treewalk.EmptyTreeIterator;
* @see org.eclipse.jgit.treewalk.TreeWalk
*/
public class DirCacheIterator extends AbstractTreeIterator {
+ /** Byte array holding ".gitattributes" string */
+ private static final byte[] DOT_GIT_ATTRIBUTES_BYTES = Constants.DOT_GIT_ATTRIBUTES
+ .getBytes();
+
/** The cache this iterator was created to walk. */
protected final DirCache cache;
@@ -92,6 +103,9 @@ public class DirCacheIterator extends AbstractTreeIterator {
/** The subtree containing {@link #currentEntry} if this is first entry. */
protected DirCacheTree currentSubtree;
+ /** Holds an {@link AttributesNode} for the current entry */
+ private AttributesNode attributesNode;
+
/**
* Create a new iterator for an already loaded DirCache instance.
* <p>
@@ -254,6 +268,10 @@ public class DirCacheIterator extends AbstractTreeIterator {
path = cep;
pathLen = cep.length;
currentSubtree = null;
+ // Checks if this entry is a .gitattributes file
+ if (RawParseUtils.match(path, pathOffset, DOT_GIT_ATTRIBUTES_BYTES) == path.length)
+ attributesNode = new LazyLoadingAttributesNode(
+ currentEntry.getObjectId());
}
/**
@@ -265,4 +283,50 @@ public class DirCacheIterator extends AbstractTreeIterator {
public DirCacheEntry getDirCacheEntry() {
return currentSubtree == null ? currentEntry : null;
}
+
+ /**
+ * Retrieves the {@link AttributesNode} for the current entry.
+ *
+ * @param reader
+ * {@link ObjectReader} used to parse the .gitattributes entry.
+ * @return {@link AttributesNode} for the current entry.
+ * @throws IOException
+ * @since 3.7
+ */
+ public AttributesNode getEntryAttributesNode(ObjectReader reader)
+ throws IOException {
+ if (attributesNode instanceof LazyLoadingAttributesNode)
+ attributesNode = ((LazyLoadingAttributesNode) attributesNode)
+ .load(reader);
+ return attributesNode;
+ }
+
+ /**
+ * {@link AttributesNode} implementation that provides lazy loading
+ * facilities.
+ */
+ private static class LazyLoadingAttributesNode extends AttributesNode {
+ final ObjectId objectId;
+
+ LazyLoadingAttributesNode(ObjectId objectId) {
+ super(Collections.<AttributesRule> emptyList());
+ this.objectId = objectId;
+
+ }
+
+ AttributesNode load(ObjectReader reader) throws IOException {
+ AttributesNode r = new AttributesNode();
+ ObjectLoader loader = reader.open(objectId);
+ if (loader != null) {
+ InputStream in = loader.openStream();
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ }
+ return r.getRules().isEmpty() ? null : r;
+ }
+ }
+
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
index 02863bd16a..2303ffd6d6 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/FastIgnoreRule.java
@@ -43,7 +43,7 @@
package org.eclipse.jgit.ignore;
import static org.eclipse.jgit.ignore.internal.Strings.stripTrailing;
-
+import static org.eclipse.jgit.ignore.internal.IMatcher.NO_MATCH;
import org.eclipse.jgit.errors.InvalidPatternException;
import org.eclipse.jgit.ignore.internal.IMatcher;
import org.eclipse.jgit.ignore.internal.PathMatcher;
@@ -63,8 +63,6 @@ public class FastIgnoreRule {
*/
public static final char PATH_SEPARATOR = '/';
- private static final NoResultMatcher NO_MATCH = new NoResultMatcher();
-
private final IMatcher matcher;
private final boolean inverse;
@@ -214,16 +212,4 @@ public class FastIgnoreRule {
return false;
return matcher.equals(other.matcher);
}
-
- static final class NoResultMatcher implements IMatcher {
-
- public boolean matches(String path, boolean assumeDirectory) {
- return false;
- }
-
- public boolean matches(String segment, int startIncl, int endExcl,
- boolean assumeDirectory) {
- return false;
- }
- }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
index 10b5e49e1f..8bb4dfb564 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/ignore/internal/IMatcher.java
@@ -50,6 +50,20 @@ package org.eclipse.jgit.ignore.internal;
public interface IMatcher {
/**
+ * Matcher that does not match any pattern.
+ */
+ public static final IMatcher NO_MATCH = new IMatcher() {
+ public boolean matches(String path, boolean assumeDirectory) {
+ return false;
+ }
+
+ public boolean matches(String segment, int startIncl, int endExcl,
+ boolean assumeDirectory) {
+ return false;
+ }
+ };
+
+ /**
* Matches entire given string
*
* @param path
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
index ccbfed720a..8a2080bac8 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/ConfigConstants.java
@@ -113,6 +113,13 @@ public class ConfigConstants {
/** The "excludesfile" key */
public static final String CONFIG_KEY_EXCLUDESFILE = "excludesfile";
+ /**
+ * The "attributesfile" key
+ *
+ * @since 3.7
+ */
+ public static final String CONFIG_KEY_ATTRIBUTESFILE = "attributesfile";
+
/** The "filemode" key */
public static final String CONFIG_KEY_FILEMODE = "filemode";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
index f149749843..705d54cfa3 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java
@@ -356,6 +356,13 @@ public final class Constants {
/** A bare repository typically ends with this string */
public static final String DOT_GIT_EXT = ".git";
+ /**
+ * Name of the attributes file
+ *
+ * @since 3.7
+ */
+ public static final String DOT_GIT_ATTRIBUTES = ".gitattributes";
+
/** Name of the ignore file */
public static final String DOT_GIT_IGNORE = ".gitignore";
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
index 8f31d96de6..5a7634a6f1 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/CoreConfig.java
@@ -1,4 +1,5 @@
/*
+ * Copyright (C) 2013, Gunnar Wagenknecht
* Copyright (C) 2010, Chris Aniszczyk <caniszczyk@gmail.com>
* Copyright (C) 2009, Christian Halstrick <christian.halstrick@sap.com>
* Copyright (C) 2009, Google Inc.
@@ -101,6 +102,8 @@ public class CoreConfig {
private final String excludesfile;
+ private final String attributesfile;
+
/**
* Options for symlink handling
*
@@ -136,6 +139,8 @@ public class CoreConfig {
ConfigConstants.CONFIG_KEY_LOGALLREFUPDATES, true);
excludesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION, null,
ConfigConstants.CONFIG_KEY_EXCLUDESFILE);
+ attributesfile = rc.getString(ConfigConstants.CONFIG_CORE_SECTION,
+ null, ConfigConstants.CONFIG_KEY_ATTRIBUTESFILE);
}
/**
@@ -165,4 +170,12 @@ public class CoreConfig {
public String getExcludesFile() {
return excludesfile;
}
+
+ /**
+ * @return path of attributesfile
+ * @since 3.7
+ */
+ public String getAttributesFile() {
+ return attributesfile;
+ }
}
diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
index 6311da6b68..3838149a4f 100644
--- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
+++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java
@@ -63,6 +63,8 @@ import java.util.Collections;
import java.util.Comparator;
import org.eclipse.jgit.api.errors.JGitInternalException;
+import org.eclipse.jgit.attributes.AttributesNode;
+import org.eclipse.jgit.attributes.AttributesRule;
import org.eclipse.jgit.diff.RawText;
import org.eclipse.jgit.dircache.DirCache;
import org.eclipse.jgit.dircache.DirCacheEntry;
@@ -133,6 +135,9 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
/** If there is a .gitignore file present, the parsed rules from it. */
private IgnoreNode ignoreNode;
+ /** If there is a .gitattributes file present, the parsed rules from it. */
+ private AttributesNode attributesNode;
+
/** Repository that is the root level being iterated over */
protected Repository repository;
@@ -143,6 +148,19 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
private int contentIdOffset;
/**
+ * Holds the {@link AttributesNode} that is stored in
+ * $GIT_DIR/info/attributes file.
+ */
+ private AttributesNode infoAttributeNode;
+
+ /**
+ * Holds the {@link AttributesNode} that is stored in global attribute file.
+ *
+ * @see CoreConfig#getAttributesFile()
+ */
+ private AttributesNode globalAttributeNode;
+
+ /**
* Create a new iterator with no parent.
*
* @param options
@@ -185,6 +203,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
protected WorkingTreeIterator(final WorkingTreeIterator p) {
super(p);
state = p.state;
+ infoAttributeNode = p.infoAttributeNode;
+ globalAttributeNode = p.globalAttributeNode;
}
/**
@@ -204,6 +224,10 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
else
entry = null;
ignoreNode = new RootIgnoreNode(entry, repo);
+
+ infoAttributeNode = new InfoAttributesNode(repo);
+
+ globalAttributeNode = new GlobalAttributesNode(repo);
}
/**
@@ -626,6 +650,56 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
return ignoreNode;
}
+ /**
+ * Retrieves the {@link AttributesNode} for the current entry.
+ *
+ * @return {@link AttributesNode} for the current entry.
+ * @throws IOException
+ * if an error is raised while parsing the .gitattributes file
+ * @since 3.7
+ */
+ public AttributesNode getEntryAttributesNode() throws IOException {
+ if (attributesNode instanceof PerDirectoryAttributesNode)
+ attributesNode = ((PerDirectoryAttributesNode) attributesNode)
+ .load();
+ return attributesNode;
+ }
+
+ /**
+ * Retrieves the {@link AttributesNode} that holds the information located
+ * in $GIT_DIR/info/attributes file.
+ *
+ * @return the {@link AttributesNode} that holds the information located in
+ * $GIT_DIR/info/attributes file.
+ * @throws IOException
+ * if an error is raised while parsing the attributes file
+ * @since 3.7
+ */
+ public AttributesNode getInfoAttributesNode() throws IOException {
+ if (infoAttributeNode instanceof InfoAttributesNode)
+ infoAttributeNode = ((InfoAttributesNode) infoAttributeNode).load();
+ return infoAttributeNode;
+ }
+
+ /**
+ * Retrieves the {@link AttributesNode} that holds the information located
+ * in system-wide file.
+ *
+ * @return the {@link AttributesNode} that holds the information located in
+ * system-wide file.
+ * @throws IOException
+ * IOException if an error is raised while parsing the
+ * attributes file
+ * @see CoreConfig#getAttributesFile()
+ * @since 3.7
+ */
+ public AttributesNode getGlobalAttributesNode() throws IOException {
+ if (globalAttributeNode instanceof GlobalAttributesNode)
+ globalAttributeNode = ((GlobalAttributesNode) globalAttributeNode)
+ .load();
+ return globalAttributeNode;
+ }
+
private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() {
public int compare(final Entry o1, final Entry o2) {
final byte[] a = o1.encodedName;
@@ -679,6 +753,8 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
continue;
if (Constants.DOT_GIT_IGNORE.equals(name))
ignoreNode = new PerDirectoryIgnoreNode(e);
+ if (Constants.DOT_GIT_ATTRIBUTES.equals(name))
+ attributesNode = new PerDirectoryAttributesNode(e);
if (i != o)
entries[o] = e;
e.encodeName(nameEncoder);
@@ -1223,6 +1299,90 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator {
}
}
+ /** Magic type indicating we know rules exist, but they aren't loaded. */
+ private static class PerDirectoryAttributesNode extends AttributesNode {
+ final Entry entry;
+
+ PerDirectoryAttributesNode(Entry entry) {
+ super(Collections.<AttributesRule> emptyList());
+ this.entry = entry;
+ }
+
+ AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
+ InputStream in = entry.openInputStream();
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ return r.getRules().isEmpty() ? null : r;
+ }
+ }
+
+ /**
+ * Attributes node loaded from global system-wide file.
+ */
+ private static class GlobalAttributesNode extends AttributesNode {
+ final Repository repository;
+
+ GlobalAttributesNode(Repository repository) {
+ this.repository = repository;
+ }
+
+ AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
+
+ FS fs = repository.getFS();
+ String path = repository.getConfig().get(CoreConfig.KEY)
+ .getAttributesFile();
+ if (path != null) {
+ File attributesFile;
+ if (path.startsWith("~/")) //$NON-NLS-1$
+ attributesFile = fs.resolve(fs.userHome(),
+ path.substring(2));
+ else
+ attributesFile = fs.resolve(null, path);
+ loadRulesFromFile(r, attributesFile);
+ }
+ return r.getRules().isEmpty() ? null : r;
+ }
+ }
+
+ /** Magic type indicating there may be rules for the top level. */
+ private static class InfoAttributesNode extends AttributesNode {
+ final Repository repository;
+
+ InfoAttributesNode(Repository repository) {
+ this.repository = repository;
+ }
+
+ AttributesNode load() throws IOException {
+ AttributesNode r = new AttributesNode();
+
+ FS fs = repository.getFS();
+
+ File attributes = fs.resolve(repository.getDirectory(),
+ "info/attributes"); //$NON-NLS-1$
+ loadRulesFromFile(r, attributes);
+
+ return r.getRules().isEmpty() ? null : r;
+ }
+
+ }
+
+ private static void loadRulesFromFile(AttributesNode r, File attrs)
+ throws FileNotFoundException, IOException {
+ if (attrs.exists()) {
+ FileInputStream in = new FileInputStream(attrs);
+ try {
+ r.parse(in);
+ } finally {
+ in.close();
+ }
+ }
+ }
+
private static final class IteratorState {
/** Options used to process the working tree. */
final WorkingTreeOptions options;