diff options
Diffstat (limited to 'org.eclipse.jgit/src')
8 files changed, 275 insertions, 31 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 index d3ce685187..905ad76929 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attribute.java @@ -50,8 +50,10 @@ package org.eclipse.jgit.attributes; * <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> + * <li>Unspecified - used to revert an attribute . This is crucial in order to + * mark an attribute as unspecified in the attributes map and thus preventing + * following (with lower priority) nodes from setting the attribute to a value + * at all</li> * </ul> * </p> * @@ -61,6 +63,7 @@ public final class Attribute { /** * The attribute value state + * see also https://www.kernel.org/pub/software/scm/git/docs/gitattributes.html */ public static enum State { /** the attribute is set */ @@ -69,6 +72,13 @@ public final class Attribute { /** the attribute is unset */ UNSET, + /** + * the attribute appears as if it would not be defined at all + * + * @since 4.2 + */ + UNSPECIFIED, + /** the attribute is set to a custom value */ CUSTOM } @@ -176,6 +186,8 @@ public final class Attribute { return key; case UNSET: return "-" + key; //$NON-NLS-1$ + case UNSPECIFIED: + return "!" + key; //$NON-NLS-1$ case CUSTOM: default: return key + "=" + value; //$NON-NLS-1$ diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java new file mode 100644 index 0000000000..0810e31682 --- /dev/null +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java @@ -0,0 +1,202 @@ +/* + * Copyright (C) 2015, Ivan Motsch <ivan.motsch@bsiag.com> + * + * 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.util.ArrayList; +import java.util.Collection; +import java.util.LinkedHashMap; +import java.util.Map; + +import org.eclipse.jgit.attributes.Attribute.State; + +/** + * Represents a set of attributes for a path + * <p> + * + * @since 4.2 + */ +public final class Attributes { + private final Map<String, Attribute> map = new LinkedHashMap<>(); + + /** + * Creates a new instance + * + * @param attributes + */ + public Attributes(Attribute... attributes) { + if (attributes != null) { + for (Attribute a : attributes) { + put(a); + } + } + } + + /** + * @return true if the set does not contain any attributes + */ + public boolean isEmpty() { + return map.isEmpty(); + } + + /** + * @param key + * @return the attribute or null + */ + public Attribute get(String key) { + return map.get(key); + } + + /** + * @return all attributes + */ + public Collection<Attribute> getAll() { + return new ArrayList<>(map.values()); + } + + /** + * @param a + */ + public void put(Attribute a) { + map.put(a.getKey(), a); + } + + /** + * @param key + */ + public void remove(String key) { + map.remove(key); + } + + /** + * @param key + * @return true if the {@link Attributes} contains this key + */ + public boolean containsKey(String key) { + return map.containsKey(key); + } + + /** + * Returns the state. + * + * @param key + * + * @return the state (never returns <code>null</code>) + */ + public Attribute.State getState(String key) { + Attribute a = map.get(key); + return a != null ? a.getState() : Attribute.State.UNSPECIFIED; + } + + /** + * @param key + * @return true if the key is {@link State#SET}, false in all other cases + */ + public boolean isSet(String key) { + return (getState(key) == State.SET); + } + + /** + * @param key + * @return true if the key is {@link State#UNSET}, false in all other cases + */ + public boolean isUnset(String key) { + return (getState(key) == State.UNSET); + } + + /** + * @param key + * @return true if the key is {@link State#UNSPECIFIED}, false in all other + * cases + */ + public boolean isUnspecified(String key) { + return (getState(key) == State.UNSPECIFIED); + } + + /** + * @param key + * @return true if the key is {@link State#CUSTOM}, false in all other cases + * see {@link #getValue(String)} for the value of the key + */ + public boolean isCustom(String key) { + return (getState(key) == State.CUSTOM); + } + + /** + * @param key + * @return the attribute value (may be <code>null</code>) + */ + public String getValue(String key) { + Attribute a = map.get(key); + return a != null ? a.getValue() : null; + } + + @Override + public String toString() { + StringBuilder buf = new StringBuilder(); + buf.append(getClass().getSimpleName()); + buf.append("["); //$NON-NLS-1$ + buf.append(" "); //$NON-NLS-1$ + for (Attribute a : map.values()) { + buf.append(a.toString()); + buf.append(" "); //$NON-NLS-1$ + } + buf.append("]"); //$NON-NLS-1$ + return buf.toString(); + } + + @Override + public int hashCode() { + return map.hashCode(); + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!(obj instanceof Attributes)) + return false; + Attributes other = (Attributes) obj; + return this.map.equals(other.map); + } + +} diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java index 70f56ff964..5c0aba2e0e 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesNode.java @@ -50,7 +50,6 @@ 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; @@ -134,11 +133,12 @@ public class AttributesNode { * 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. + * it is not empty, this method will NOT override any existing + * entry. + * @since 4.2 */ - public void getAttributes(String entryPath, boolean isDirectory, - Map<String, Attribute> attributes) { + public void getAttributes(String entryPath, + boolean isDirectory, Attributes attributes) { // Parse rules in the reverse order that they were read since the last // entry should be used ListIterator<AttributesRule> ruleIterator = rules.listIterator(rules @@ -153,7 +153,7 @@ public class AttributesNode { while (attributeIte.hasPrevious()) { Attribute attr = attributeIte.previous(); if (!attributes.containsKey(attr.getKey())) - attributes.put(attr.getKey(), attr); + attributes.put(attr); } } } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java index 8f23a83f78..1037f697d4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesProvider.java @@ -42,8 +42,6 @@ */ package org.eclipse.jgit.attributes; -import java.util.Map; - /** * Interface for classes which provide git attributes * @@ -51,7 +49,7 @@ import java.util.Map; */ public interface AttributesProvider { /** - * @return the currently active attributes by attribute key + * @return the currently active attributes */ - public Map<String, Attribute> getAttributes(); + public Attributes getAttributes(); } diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java index bcac14b5ff..35d18c4b2a 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/attributes/AttributesRule.java @@ -84,6 +84,13 @@ public class AttributesRule { continue; } + if (attribute.startsWith("!")) {//$NON-NLS-1$ + if (attribute.length() > 1) + result.add(new Attribute(attribute.substring(1), + State.UNSPECIFIED)); + continue; + } + final int equalsIndex = attribute.indexOf("="); //$NON-NLS-1$ if (equalsIndex == -1) result.add(new Attribute(attribute, State.SET)); @@ -200,4 +207,16 @@ public class AttributesRule { boolean match = matcher.matches(relativeTarget, isDirectory); return match; } + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append(pattern); + for (Attribute a : attributes) { + sb.append(" "); //$NON-NLS-1$ + sb.append(a); + } + return sb.toString(); + + } }
\ No newline at end of file diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java index eb53434b74..bda5cbeba4 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/internal/storage/file/InfoAttributesNode.java @@ -47,6 +47,7 @@ import java.io.File; import java.io.IOException; import org.eclipse.jgit.attributes.AttributesNode; +import org.eclipse.jgit.lib.Constants; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.util.FS; @@ -71,7 +72,7 @@ public class InfoAttributesNode extends AttributesNode { FS fs = repository.getFS(); File attributes = fs.resolve(repository.getDirectory(), - "info/attributes"); //$NON-NLS-1$ + Constants.INFO_ATTRIBUTES); FileRepository.AttributesNodeProviderImpl.loadRulesFromFile(r, attributes); return r.getRules().isEmpty() ? null : r; 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 535a6ee175..613df37a75 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/lib/Constants.java @@ -273,6 +273,13 @@ public final class Constants { public static final String INFO_EXCLUDE = "info/exclude"; /** + * Attributes-override-file + * + * @since 4.2 + */ + public static final String INFO_ATTRIBUTES = "info/attributes"; + + /** * The system property that contains the system user name * * @since 3.6 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java index 826ce0973d..8a59a700e3 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/treewalk/TreeWalk.java @@ -45,17 +45,16 @@ package org.eclipse.jgit.treewalk; import java.io.IOException; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; import java.util.Set; import org.eclipse.jgit.annotations.Nullable; import org.eclipse.jgit.api.errors.JGitInternalException; import org.eclipse.jgit.attributes.Attribute; +import org.eclipse.jgit.attributes.Attributes; import org.eclipse.jgit.attributes.AttributesNode; import org.eclipse.jgit.attributes.AttributesNodeProvider; import org.eclipse.jgit.attributes.AttributesProvider; +import org.eclipse.jgit.attributes.Attribute.State; import org.eclipse.jgit.dircache.DirCacheIterator; import org.eclipse.jgit.errors.CorruptObjectException; import org.eclipse.jgit.errors.IncorrectObjectTypeException; @@ -258,7 +257,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { AbstractTreeIterator currentHead; /** Cached attribute for the current entry */ - private Map<String, Attribute> attrs = null; + private Attributes attrs = null; /** * Create a new tree walker for a given repository. @@ -1119,7 +1118,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { * @return a {@link Set} of {@link Attribute}s that match the current entry. * @since 4.2 */ - public Map<String, Attribute> getAttributes() { + public Attributes getAttributes() { if (attrs != null) return attrs; @@ -1138,35 +1137,42 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { && other == null) { // Can not retrieve the attributes without at least one of the above // iterators. - return Collections.<String, Attribute> emptyMap(); + return new Attributes(); } String path = currentHead.getEntryPathString(); final boolean isDir = FileMode.TREE.equals(currentHead.mode); - Map<String, Attribute> attributes = new LinkedHashMap<String, Attribute>(); + Attributes attributes = new Attributes(); try { - // Gets the info attributes + // Gets the global attributes node + AttributesNode globalNodeAttr = attributesNodeProvider + .getGlobalAttributesNode(); + // Gets the info attributes node AttributesNode infoNodeAttr = attributesNodeProvider .getInfoAttributesNode(); + + // Gets the info attributes if (infoNodeAttr != null) { infoNodeAttr.getAttributes(path, isDir, attributes); } - // Gets the attributes located on the current entry path getPerDirectoryEntryAttributes(path, isDir, operationType, - workingTreeIterator, dirCacheIterator, other, - attributes); + workingTreeIterator, dirCacheIterator, other, attributes); // Gets the attributes located in the global attribute file - AttributesNode globalNodeAttr = attributesNodeProvider - .getGlobalAttributesNode(); if (globalNodeAttr != null) { globalNodeAttr.getAttributes(path, isDir, attributes); } } catch (IOException e) { throw new JGitInternalException("Error while parsing attributes", e); //$NON-NLS-1$ } + // now after all attributes are collected - in the correct hierarchy + // order - remove all unspecified entries (the ! marker) + for (Attribute a : attributes.getAll()) { + if (a.getState() == State.UNSPECIFIED) + attributes.remove(a.getKey()); + } return attributes; } @@ -1195,7 +1201,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { private void getPerDirectoryEntryAttributes(String path, boolean isDir, OperationType opType, WorkingTreeIterator workingTreeIterator, DirCacheIterator dirCacheIterator, CanonicalTreeParser other, - Map<String, Attribute> attributes) + Attributes attributes) throws IOException { // Prevents infinite recurrence if (workingTreeIterator != null || dirCacheIterator != null @@ -1208,12 +1214,11 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { getPerDirectoryEntryAttributes(path, isDir, opType, getParent(workingTreeIterator, WorkingTreeIterator.class), getParent(dirCacheIterator, DirCacheIterator.class), - getParent(other, CanonicalTreeParser.class), - attributes); + getParent(other, CanonicalTreeParser.class), attributes); } } - private <T extends AbstractTreeIterator> T getParent(T current, + private static <T extends AbstractTreeIterator> T getParent(T current, Class<T> type) { if (current != null) { AbstractTreeIterator parent = current.parent; @@ -1224,7 +1229,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { return null; } - private <T> T getTree(Class<T> type) { + private <T extends AbstractTreeIterator> T getTree(Class<T> type) { for (int i = 0; i < trees.length; i++) { AbstractTreeIterator tree = trees[i]; if (type.isInstance(tree)) { |