From 75697adc5a0024449351aacac89618c3b83add11 Mon Sep 17 00:00:00 2001 From: Ivan Motsch Date: Tue, 17 Nov 2015 13:32:20 +0100 Subject: [PATCH] Add the new class Attributes holding multiple Attribute(s) Attributes represents a semantic collector of Attribute(s) and replaces the anonymous Map. This class will be returned by TreeWalk.getAttributes(). It offers convenient access to the attributes wrapped in the Attributes object. Adds preparations for a future Attribute Macro Expansion Change-Id: I8348c8c457a2a7f1f0c48050e10399b0fa1cdbe1 Signed-off-by: Ivan Motsch --- .../AttributesNodeDirCacheIteratorTest.java | 10 +- .../jgit/attributes/AttributesNodeTest.java | 27 +-- ...AttributesNodeWorkingTreeIteratorTest.java | 10 +- .../attributes/TreeWalkAttributeTest.java | 5 +- org.eclipse.jgit/.settings/.api_filters | 8 + .../eclipse/jgit/attributes/Attribute.java | 16 +- .../eclipse/jgit/attributes/Attributes.java | 202 ++++++++++++++++++ .../jgit/attributes/AttributesNode.java | 12 +- .../jgit/attributes/AttributesProvider.java | 6 +- .../jgit/attributes/AttributesRule.java | 19 ++ .../storage/file/InfoAttributesNode.java | 3 +- .../src/org/eclipse/jgit/lib/Constants.java | 7 + .../org/eclipse/jgit/treewalk/TreeWalk.java | 41 ++-- 13 files changed, 306 insertions(+), 60 deletions(-) create mode 100644 org.eclipse.jgit/src/org/eclipse/jgit/attributes/Attributes.java diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java index 3e6ca62202..0e595e61f8 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeDirCacheIteratorTest.java @@ -52,9 +52,7 @@ import static org.junit.Assert.assertTrue; import java.io.IOException; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import org.eclipse.jgit.api.Git; import org.eclipse.jgit.attributes.Attribute.State; @@ -258,12 +256,14 @@ public class AttributesNodeDirCacheIteratorTest extends RepositoryTestCase { assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { - Map entryAttributes = new LinkedHashMap(); - attributesNode.getAttributes(pathName, false, entryAttributes); + Attributes entryAttributes = new Attributes(); + attributesNode.getAttributes(pathName, + false, entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { - assertThat(entryAttributes.values(), hasItem(attribute)); + assertThat(entryAttributes.getAll(), + hasItem(attribute)); } } else { assertTrue( diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java index d82baaa36e..d478a7cf08 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeTest.java @@ -49,10 +49,6 @@ import static org.junit.Assert.assertEquals; import java.io.ByteArrayInputStream; import java.io.IOException; import java.io.InputStream; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Set; import org.junit.After; import org.junit.Test; @@ -104,8 +100,8 @@ public class AttributesNodeTest { is = new ByteArrayInputStream(attributeFileContent.getBytes()); AttributesNode node = new AttributesNode(); node.parse(is); - assertAttribute("file.type1", node, Collections. emptySet()); - assertAttribute("file.type2", node, Collections. emptySet()); + assertAttribute("file.type1", node, new Attributes()); + assertAttribute("file.type2", node, new Attributes()); } @Test @@ -115,7 +111,7 @@ public class AttributesNodeTest { is = new ByteArrayInputStream(attributeFileContent.getBytes()); AttributesNode node = new AttributesNode(); node.parse(is); - assertAttribute("file.type1", node, Collections. emptySet()); + assertAttribute("file.type1", node, new Attributes()); assertAttribute("file.type2", node, asSet(A_UNSET_ATTR)); } @@ -127,8 +123,8 @@ public class AttributesNodeTest { is = new ByteArrayInputStream(attributeFileContent.getBytes()); AttributesNode node = new AttributesNode(); node.parse(is); - assertAttribute("file.type1", node, Collections. emptySet()); - assertAttribute("file.type2", node, Collections. emptySet()); + assertAttribute("file.type1", node, new Attributes()); + assertAttribute("file.type2", node, new Attributes()); assertAttribute("file.type3", node, asSet(new Attribute("attr", ""))); } @@ -166,17 +162,14 @@ public class AttributesNodeTest { } private void assertAttribute(String path, AttributesNode node, - Set attrs) { - HashMap attributes = new HashMap(); + Attributes attrs) { + Attributes attributes = new Attributes(); node.getAttributes(path, false, attributes); - assertEquals(attrs, new HashSet(attributes.values())); + assertEquals(attrs, attributes); } - static Set asSet(Attribute... attrs) { - Set result = new HashSet(); - for (Attribute attr : attrs) - result.add(attr); - return result; + static Attributes asSet(Attribute... attrs) { + return new Attributes(attrs); } } diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java index bcf17174b8..6ad19a2491 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/AttributesNodeWorkingTreeIteratorTest.java @@ -53,9 +53,7 @@ import static org.junit.Assert.assertTrue; import java.io.File; import java.io.IOException; import java.util.Collections; -import java.util.LinkedHashMap; import java.util.List; -import java.util.Map; import org.eclipse.jgit.attributes.Attribute.State; import org.eclipse.jgit.errors.CorruptObjectException; @@ -227,12 +225,14 @@ public class AttributesNodeWorkingTreeIteratorTest extends RepositoryTestCase { assertTrue(nodeAttrs == null || nodeAttrs.isEmpty()); else { - Map entryAttributes = new LinkedHashMap(); - attributesNode.getAttributes(pathName, false, entryAttributes); + Attributes entryAttributes = new Attributes(); + attributesNode.getAttributes(pathName, + false, entryAttributes); if (nodeAttrs != null && !nodeAttrs.isEmpty()) { for (Attribute attribute : nodeAttrs) { - assertThat(entryAttributes.values(), hasItem(attribute)); + assertThat(entryAttributes.getAll(), + hasItem(attribute)); } } else { assertTrue( diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java index c3d5e8752c..b044c01db6 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/attributes/TreeWalkAttributeTest.java @@ -802,8 +802,9 @@ public class TreeWalkAttributeTest extends RepositoryTestCase { assertEquals(type, walk.getFileMode(0)); assertEquals(checkinAttributes, - asSet(ci_walk.getAttributes().values())); - assertEquals(checkoutAttributes, asSet(walk.getAttributes().values())); + asSet(ci_walk.getAttributes().getAll())); + assertEquals(checkoutAttributes, + asSet(walk.getAttributes().getAll())); if (D.equals(type)) { walk.enterSubtree(); diff --git a/org.eclipse.jgit/.settings/.api_filters b/org.eclipse.jgit/.settings/.api_filters index b89aad4c59..b2a8f677f3 100644 --- a/org.eclipse.jgit/.settings/.api_filters +++ b/org.eclipse.jgit/.settings/.api_filters @@ -1,5 +1,13 @@ + + + + + + + + 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; *
  • Set - represented by {@link State#SET}
  • *
  • Unset - represented by {@link State#UNSET}
  • *
  • Set to a value - represented by {@link State#CUSTOM}
  • - *
  • Unspecified - null is used instead of an instance of this - * class
  • + *
  • 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
  • * *

    * @@ -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 + * + * 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 + *

    + * + * @since 4.2 + */ +public final class Attributes { + private final Map 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 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 null) + */ + 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 null) + */ + 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 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 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 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 @@ -272,6 +272,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 * 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 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 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. emptyMap(); + return new Attributes(); } String path = currentHead.getEntryPathString(); final boolean isDir = FileMode.TREE.equals(currentHead.mode); - Map attributes = new LinkedHashMap(); + 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 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 getParent(T current, + private static T getParent(T current, Class type) { if (current != null) { AbstractTreeIterator parent = current.parent; @@ -1224,7 +1229,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { return null; } - private T getTree(Class type) { + private T getTree(Class type) { for (int i = 0; i < trees.length; i++) { AbstractTreeIterator tree = trees[i]; if (type.isInstance(tree)) { -- 2.39.5