When checking out a branch we need to access the attributes stored in the tree to be checked out. E.g. directly after a clone we checkout the remote HEAD. In this case index and workingtree are still empty. So we have to search the tree to be checked out for attributes. Change-Id: I6d96f5d095ed2e3c259d4b12124e404f5215bd9ftags/v4.2.0.201601211800-r
@@ -49,6 +49,20 @@ | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/treewalk/WorkingTreeIterator.java" type="org.eclipse.jgit.treewalk.WorkingTreeIterator"> | |||
<filter comment="attributes weren't really usable in earlier versions" id="338792546"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/> | |||
<message_argument value="getGlobalAttributesNode()"/> | |||
</message_arguments> | |||
</filter> | |||
<filter comment="attributes weren't really usable in earlier versions" id="338792546"> | |||
<message_arguments> | |||
<message_argument value="org.eclipse.jgit.treewalk.WorkingTreeIterator"/> | |||
<message_argument value="getInfoAttributesNode()"/> | |||
</message_arguments> | |||
</filter> | |||
</resource> | |||
<resource path="src/org/eclipse/jgit/util/FileUtils.java" type="org.eclipse.jgit.util.FileUtils"> | |||
<filter id="338792546"> | |||
<message_arguments> |
@@ -103,9 +103,6 @@ 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> |
@@ -49,6 +49,7 @@ import java.io.IOException; | |||
import java.nio.ByteBuffer; | |||
import java.nio.CharBuffer; | |||
import org.eclipse.jgit.attributes.AttributesNode; | |||
import org.eclipse.jgit.dircache.DirCacheCheckout; | |||
import org.eclipse.jgit.errors.CorruptObjectException; | |||
import org.eclipse.jgit.errors.IncorrectObjectTypeException; | |||
@@ -92,6 +93,13 @@ public abstract class AbstractTreeIterator { | |||
/** The iterator this current entry is path equal to. */ | |||
AbstractTreeIterator matches; | |||
/** | |||
* Parsed rules of .gitattributes file if it exists. | |||
* | |||
* @since 4.2 | |||
*/ | |||
protected AttributesNode attributesNode; | |||
/** | |||
* Number of entries we moved forward to force a D/F conflict match. | |||
* |
@@ -45,8 +45,12 @@ | |||
package org.eclipse.jgit.treewalk; | |||
import java.io.IOException; | |||
import java.io.InputStream; | |||
import java.util.Arrays; | |||
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.errors.MissingObjectException; | |||
import org.eclipse.jgit.lib.AnyObjectId; | |||
@@ -54,10 +58,15 @@ import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.FileMode; | |||
import org.eclipse.jgit.lib.MutableObjectId; | |||
import org.eclipse.jgit.lib.ObjectId; | |||
import org.eclipse.jgit.lib.ObjectLoader; | |||
import org.eclipse.jgit.lib.ObjectReader; | |||
import org.eclipse.jgit.util.RawParseUtils; | |||
/** Parses raw Git trees from the canonical semi-text/semi-binary format. */ | |||
public class CanonicalTreeParser extends AbstractTreeIterator { | |||
private static final int ATTRIBUTESLENGTH = Constants.DOT_GIT_ATTRIBUTES | |||
.getBytes().length; | |||
private static final byte[] EMPTY = {}; | |||
private byte[] raw; | |||
@@ -364,5 +373,57 @@ public class CanonicalTreeParser extends AbstractTreeIterator { | |||
} | |||
pathLen = tmp; | |||
nextPtr = ptr + Constants.OBJECT_ID_LENGTH; | |||
// Check if this entry is a .gitattributes file | |||
if (RawParseUtils.match(path, pathOffset, | |||
Constants.DOT_GIT_ATTRIBUTES.getBytes()) == ATTRIBUTESLENGTH) | |||
attributesNode = new LazyLoadingAttributesNode( | |||
ObjectId.fromRaw(idBuffer(), idOffset())); | |||
} | |||
/** | |||
* Retrieve 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 4.2 | |||
*/ | |||
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 | |||
*/ | |||
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; | |||
} | |||
} | |||
} |
@@ -50,6 +50,7 @@ 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.AttributesNode; | |||
@@ -1131,8 +1132,10 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
WorkingTreeIterator workingTreeIterator = getTree(WorkingTreeIterator.class); | |||
DirCacheIterator dirCacheIterator = getTree(DirCacheIterator.class); | |||
CanonicalTreeParser other = getTree(CanonicalTreeParser.class); | |||
if (workingTreeIterator == null && dirCacheIterator == null) { | |||
if (workingTreeIterator == null && dirCacheIterator == null | |||
&& other == null) { | |||
// Can not retrieve the attributes without at least one of the above | |||
// iterators. | |||
return Collections.<String, Attribute> emptyMap(); | |||
@@ -1152,7 +1155,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
// Gets the attributes located on the current entry path | |||
getPerDirectoryEntryAttributes(path, isDir, operationType, | |||
workingTreeIterator, dirCacheIterator, | |||
workingTreeIterator, dirCacheIterator, other, | |||
attributes); | |||
// Gets the attributes located in the global attribute file | |||
@@ -1180,6 +1183,8 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
* a {@link WorkingTreeIterator} matching the current entry | |||
* @param dirCacheIterator | |||
* a {@link DirCacheIterator} matching the current entry | |||
* @param other | |||
* a {@link CanonicalTreeParser} matching the current entry | |||
* @param attributes | |||
* Non null map holding the existing attributes. This map will be | |||
* augmented with new entry. None entry will be overrided. | |||
@@ -1189,18 +1194,21 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
*/ | |||
private void getPerDirectoryEntryAttributes(String path, boolean isDir, | |||
OperationType opType, WorkingTreeIterator workingTreeIterator, | |||
DirCacheIterator dirCacheIterator, Map<String, Attribute> attributes) | |||
DirCacheIterator dirCacheIterator, CanonicalTreeParser other, | |||
Map<String, Attribute> attributes) | |||
throws IOException { | |||
// Prevents infinite recurrence | |||
if (workingTreeIterator != null || dirCacheIterator != null) { | |||
if (workingTreeIterator != null || dirCacheIterator != null | |||
|| other != null) { | |||
AttributesNode currentAttributesNode = getCurrentAttributesNode( | |||
opType, workingTreeIterator, dirCacheIterator); | |||
opType, workingTreeIterator, dirCacheIterator, other); | |||
if (currentAttributesNode != null) { | |||
currentAttributesNode.getAttributes(path, isDir, attributes); | |||
} | |||
getPerDirectoryEntryAttributes(path, isDir, opType, | |||
getParent(workingTreeIterator, WorkingTreeIterator.class), | |||
getParent(dirCacheIterator, DirCacheIterator.class), | |||
getParent(other, CanonicalTreeParser.class), | |||
attributes); | |||
} | |||
} | |||
@@ -1236,6 +1244,7 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
* @param opType | |||
* @param workingTreeIterator | |||
* @param dirCacheIterator | |||
* @param other | |||
* @return a {@link AttributesNode} of the current entry, | |||
* {@link NullPointerException} otherwise. | |||
* @throws IOException | |||
@@ -1243,8 +1252,10 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
* parsing one on the attributes file. | |||
*/ | |||
private AttributesNode getCurrentAttributesNode(OperationType opType, | |||
WorkingTreeIterator workingTreeIterator, | |||
DirCacheIterator dirCacheIterator) throws IOException { | |||
@Nullable WorkingTreeIterator workingTreeIterator, | |||
@Nullable DirCacheIterator dirCacheIterator, | |||
@Nullable CanonicalTreeParser other) | |||
throws IOException { | |||
AttributesNode attributesNode = null; | |||
switch (opType) { | |||
case CHECKIN_OP: | |||
@@ -1252,17 +1263,30 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
attributesNode = workingTreeIterator.getEntryAttributesNode(); | |||
} | |||
if (attributesNode == null && dirCacheIterator != null) { | |||
attributesNode = dirCacheIterator | |||
.getEntryAttributesNode(getObjectReader()); | |||
attributesNode = getAttributesNode(dirCacheIterator | |||
.getEntryAttributesNode(getObjectReader()), | |||
attributesNode); | |||
} | |||
if (attributesNode == null && other != null) { | |||
attributesNode = getAttributesNode( | |||
other.getEntryAttributesNode(getObjectReader()), | |||
attributesNode); | |||
} | |||
break; | |||
case CHECKOUT_OP: | |||
if (dirCacheIterator != null) { | |||
attributesNode = dirCacheIterator | |||
if (other != null) { | |||
attributesNode = other | |||
.getEntryAttributesNode(getObjectReader()); | |||
} | |||
if (dirCacheIterator != null) { | |||
attributesNode = getAttributesNode(dirCacheIterator | |||
.getEntryAttributesNode(getObjectReader()), | |||
attributesNode); | |||
} | |||
if (attributesNode == null && workingTreeIterator != null) { | |||
attributesNode = workingTreeIterator.getEntryAttributesNode(); | |||
attributesNode = getAttributesNode( | |||
workingTreeIterator.getEntryAttributesNode(), | |||
attributesNode); | |||
} | |||
break; | |||
default: | |||
@@ -1274,4 +1298,9 @@ public class TreeWalk implements AutoCloseable, AttributesProvider { | |||
return attributesNode; | |||
} | |||
private static AttributesNode getAttributesNode(AttributesNode value, | |||
AttributesNode defaultValue) { | |||
return (value == null) ? defaultValue : value; | |||
} | |||
} |
@@ -74,8 +74,6 @@ import org.eclipse.jgit.errors.NoWorkTreeException; | |||
import org.eclipse.jgit.ignore.FastIgnoreRule; | |||
import org.eclipse.jgit.ignore.IgnoreNode; | |||
import org.eclipse.jgit.internal.JGitText; | |||
import org.eclipse.jgit.internal.storage.file.GlobalAttributesNode; | |||
import org.eclipse.jgit.internal.storage.file.InfoAttributesNode; | |||
import org.eclipse.jgit.lib.Constants; | |||
import org.eclipse.jgit.lib.CoreConfig; | |||
import org.eclipse.jgit.lib.CoreConfig.CheckStat; | |||
@@ -136,9 +134,6 @@ 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; | |||
@@ -148,19 +143,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
/** The offset of the content id in {@link #idBuffer()} */ | |||
private int contentIdOffset; | |||
/** | |||
* Holds the {@link AttributesNode} that is stored in | |||
* $GIT_DIR/info/attributes file. | |||
*/ | |||
private AttributesNode infoAttributesNode; | |||
/** | |||
* Holds the {@link AttributesNode} that is stored in global attribute file. | |||
* | |||
* @see CoreConfig#getAttributesFile() | |||
*/ | |||
private AttributesNode globalAttributesNode; | |||
/** | |||
* Create a new iterator with no parent. | |||
* | |||
@@ -204,8 +186,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
protected WorkingTreeIterator(final WorkingTreeIterator p) { | |||
super(p); | |||
state = p.state; | |||
infoAttributesNode = p.infoAttributesNode; | |||
globalAttributesNode = p.globalAttributesNode; | |||
} | |||
/** | |||
@@ -225,10 +205,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
else | |||
entry = null; | |||
ignoreNode = new RootIgnoreNode(entry, repo); | |||
infoAttributesNode = new InfoAttributesNode(repo); | |||
globalAttributesNode = new GlobalAttributesNode(repo); | |||
} | |||
/** | |||
@@ -669,41 +645,6 @@ public abstract class WorkingTreeIterator extends AbstractTreeIterator { | |||
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 (infoAttributesNode instanceof InfoAttributesNode) | |||
infoAttributesNode = ((InfoAttributesNode) infoAttributesNode).load(); | |||
return infoAttributesNode; | |||
} | |||
/** | |||
* 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 (globalAttributesNode instanceof GlobalAttributesNode) | |||
globalAttributesNode = ((GlobalAttributesNode) globalAttributesNode) | |||
.load(); | |||
return globalAttributesNode; | |||
} | |||
private static final Comparator<Entry> ENTRY_CMP = new Comparator<Entry>() { | |||
public int compare(final Entry o1, final Entry o2) { | |||
final byte[] a = o1.encodedName; |