Merge changes If1490ca2,Ife950253
* changes: DirCacheBuilder: Speed up reading from trees Delay locating .gitattributes until requested
This commit is contained in:
commit
8078021e8f
|
@ -43,6 +43,8 @@
|
|||
|
||||
package org.eclipse.jgit.treewalk;
|
||||
|
||||
import static org.eclipse.jgit.lib.FileMode.REGULAR_FILE;
|
||||
import static org.eclipse.jgit.lib.FileMode.SYMLINK;
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertFalse;
|
||||
import static org.junit.Assert.assertSame;
|
||||
|
@ -50,9 +52,11 @@
|
|||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
|
||||
import org.eclipse.jgit.errors.CorruptObjectException;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.FileMode;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.TreeFormatter;
|
||||
import org.eclipse.jgit.util.RawParseUtils;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
|
@ -369,4 +373,41 @@ public void testFreakingHugePathName() throws Exception {
|
|||
assertEquals(name, RawParseUtils.decode(Constants.CHARSET, ctp.path,
|
||||
ctp.pathOffset, ctp.pathLen));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAttributesWhenFirst() throws CorruptObjectException {
|
||||
TreeFormatter tree = new TreeFormatter();
|
||||
tree.append(".gitattributes", REGULAR_FILE, hash_a);
|
||||
ctp.reset(tree.toByteArray());
|
||||
|
||||
assertTrue(ctp.findFile(".gitattributes"));
|
||||
assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
|
||||
assertEquals(".gitattributes", ctp.getEntryPathString());
|
||||
assertEquals(hash_a, ctp.getEntryObjectId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAttributesWhenSecond() throws CorruptObjectException {
|
||||
TreeFormatter tree = new TreeFormatter();
|
||||
tree.append(".config", SYMLINK, hash_a);
|
||||
tree.append(".gitattributes", REGULAR_FILE, hash_foo);
|
||||
ctp.reset(tree.toByteArray());
|
||||
|
||||
assertTrue(ctp.findFile(".gitattributes"));
|
||||
assertEquals(REGULAR_FILE.getBits(), ctp.getEntryRawMode());
|
||||
assertEquals(".gitattributes", ctp.getEntryPathString());
|
||||
assertEquals(hash_foo, ctp.getEntryObjectId());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testFindAttributesWhenMissing() throws CorruptObjectException {
|
||||
TreeFormatter tree = new TreeFormatter();
|
||||
tree.append("src", REGULAR_FILE, hash_a);
|
||||
tree.append("zoo", REGULAR_FILE, hash_foo);
|
||||
ctp.reset(tree.toByteArray());
|
||||
|
||||
assertFalse(ctp.findFile(".gitattributes"));
|
||||
assertEquals(11, ctp.idOffset()); // Did not walk the entire tree.
|
||||
assertEquals("src", ctp.getEntryPathString());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -44,6 +44,9 @@
|
|||
|
||||
package org.eclipse.jgit.dircache;
|
||||
|
||||
import static org.eclipse.jgit.lib.FileMode.TYPE_MASK;
|
||||
import static org.eclipse.jgit.lib.FileMode.TYPE_TREE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.text.MessageFormat;
|
||||
import java.util.Arrays;
|
||||
|
@ -51,9 +54,7 @@
|
|||
import org.eclipse.jgit.internal.JGitText;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectReader;
|
||||
import org.eclipse.jgit.treewalk.AbstractTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.CanonicalTreeParser;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
|
||||
/**
|
||||
* Updates a {@link DirCache} by adding individual {@link DirCacheEntry}s.
|
||||
|
@ -163,27 +164,56 @@ public void keep(final int pos, int cnt) {
|
|||
* @throws IOException
|
||||
* a tree cannot be read to iterate through its entries.
|
||||
*/
|
||||
public void addTree(final byte[] pathPrefix, final int stage,
|
||||
final ObjectReader reader, final AnyObjectId tree) throws IOException {
|
||||
final TreeWalk tw = new TreeWalk(reader);
|
||||
tw.addTree(new CanonicalTreeParser(pathPrefix, reader, tree
|
||||
.toObjectId()));
|
||||
tw.setRecursive(true);
|
||||
if (tw.next()) {
|
||||
final DirCacheEntry newEntry = toEntry(stage, tw);
|
||||
beforeAdd(newEntry);
|
||||
fastAdd(newEntry);
|
||||
while (tw.next())
|
||||
fastAdd(toEntry(stage, tw));
|
||||
public void addTree(byte[] pathPrefix, int stage, ObjectReader reader,
|
||||
AnyObjectId tree) throws IOException {
|
||||
CanonicalTreeParser p = createTreeParser(pathPrefix, reader, tree);
|
||||
while (!p.eof()) {
|
||||
if (isTree(p)) {
|
||||
p = enterTree(p, reader);
|
||||
continue;
|
||||
}
|
||||
|
||||
DirCacheEntry first = toEntry(stage, p);
|
||||
beforeAdd(first);
|
||||
fastAdd(first);
|
||||
p = p.next();
|
||||
break;
|
||||
}
|
||||
|
||||
// Rest of tree entries are correctly sorted; use fastAdd().
|
||||
while (!p.eof()) {
|
||||
if (isTree(p)) {
|
||||
p = enterTree(p, reader);
|
||||
} else {
|
||||
fastAdd(toEntry(stage, p));
|
||||
p = p.next();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private DirCacheEntry toEntry(final int stage, final TreeWalk tw) {
|
||||
final DirCacheEntry e = new DirCacheEntry(tw.getRawPath(), stage);
|
||||
final AbstractTreeIterator i;
|
||||
private static CanonicalTreeParser createTreeParser(byte[] pathPrefix,
|
||||
ObjectReader reader, AnyObjectId tree) throws IOException {
|
||||
return new CanonicalTreeParser(pathPrefix, reader, tree);
|
||||
}
|
||||
|
||||
i = tw.getTree(0, AbstractTreeIterator.class);
|
||||
e.setFileMode(tw.getFileMode(0));
|
||||
private static boolean isTree(CanonicalTreeParser p) {
|
||||
return (p.getEntryRawMode() & TYPE_MASK) == TYPE_TREE;
|
||||
}
|
||||
|
||||
private static CanonicalTreeParser enterTree(CanonicalTreeParser p,
|
||||
ObjectReader reader) throws IOException {
|
||||
p = p.createSubtreeIterator(reader);
|
||||
return p.eof() ? p.next() : p;
|
||||
}
|
||||
|
||||
private static DirCacheEntry toEntry(int stage, CanonicalTreeParser i) {
|
||||
byte[] buf = i.getEntryPathBuffer();
|
||||
int len = i.getEntryPathLength();
|
||||
byte[] path = new byte[len];
|
||||
System.arraycopy(buf, 0, path, 0, len);
|
||||
|
||||
DirCacheEntry e = new DirCacheEntry(path, stage);
|
||||
e.setFileMode(i.getEntryRawMode());
|
||||
e.setObjectIdFromRaw(i.idBuffer(), i.idOffset());
|
||||
return e;
|
||||
}
|
||||
|
|
|
@ -505,6 +505,10 @@ public void setFileMode(final FileMode mode) {
|
|||
NB.encodeInt32(info, infoOffset + P_MODE, mode.getBits());
|
||||
}
|
||||
|
||||
void setFileMode(int mode) {
|
||||
NB.encodeInt32(info, infoOffset + P_MODE, mode);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the cached creation time of this file, in milliseconds.
|
||||
*
|
||||
|
|
|
@ -327,6 +327,42 @@ int pathCompare(final AbstractTreeIterator p, final int pMode) {
|
|||
return pathCompare(p.path, cPos, p.pathLen, pMode, cPos);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the iterator on a file, if present.
|
||||
*
|
||||
* @param name
|
||||
* file name to find (will not find a directory).
|
||||
* @return true if the file exists in this tree; false otherwise.
|
||||
* @throws CorruptObjectException
|
||||
* tree is invalid.
|
||||
* @since 4.2
|
||||
*/
|
||||
public boolean findFile(String name) throws CorruptObjectException {
|
||||
return findFile(Constants.encode(name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Seek the iterator on a file, if present.
|
||||
*
|
||||
* @param name
|
||||
* file name to find (will not find a directory).
|
||||
* @return true if the file exists in this tree; false otherwise.
|
||||
* @throws CorruptObjectException
|
||||
* tree is invalid.
|
||||
* @since 4.2
|
||||
*/
|
||||
public boolean findFile(byte[] name) throws CorruptObjectException {
|
||||
for (; !eof(); next(1)) {
|
||||
int cmp = pathCompare(name, 0, name.length, 0, pathOffset);
|
||||
if (cmp == 0) {
|
||||
return true;
|
||||
} else if (cmp > 0) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare the path of this current entry to a raw buffer.
|
||||
*
|
||||
|
|
|
@ -44,6 +44,13 @@
|
|||
|
||||
package org.eclipse.jgit.treewalk;
|
||||
|
||||
import static org.eclipse.jgit.lib.Constants.DOT_GIT_ATTRIBUTES;
|
||||
import static org.eclipse.jgit.lib.Constants.OBJECT_ID_LENGTH;
|
||||
import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
|
||||
import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
|
||||
import static org.eclipse.jgit.lib.Constants.TYPE_TREE;
|
||||
import static org.eclipse.jgit.lib.Constants.encode;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Arrays;
|
||||
|
@ -54,20 +61,15 @@
|
|||
import org.eclipse.jgit.errors.IncorrectObjectTypeException;
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
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 byte[] EMPTY = {};
|
||||
|
||||
private static final byte[] ATTRS = Constants
|
||||
.encode(Constants.DOT_GIT_ATTRIBUTES);
|
||||
private static final byte[] ATTRS = encode(DOT_GIT_ATTRIBUTES);
|
||||
|
||||
private byte[] raw;
|
||||
|
||||
|
@ -133,6 +135,7 @@ public CanonicalTreeParser getParent() {
|
|||
* the raw tree content.
|
||||
*/
|
||||
public void reset(final byte[] treeData) {
|
||||
attributesNode = null;
|
||||
raw = treeData;
|
||||
prevPtr = -1;
|
||||
currPtr = 0;
|
||||
|
@ -208,7 +211,7 @@ public CanonicalTreeParser next() {
|
|||
*/
|
||||
public void reset(final ObjectReader reader, final AnyObjectId id)
|
||||
throws IncorrectObjectTypeException, IOException {
|
||||
reset(reader.open(id, Constants.OBJ_TREE).getCachedBytes());
|
||||
reset(reader.open(id, OBJ_TREE).getCachedBytes());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -218,7 +221,7 @@ public CanonicalTreeParser createSubtreeIterator(final ObjectReader reader,
|
|||
idBuffer.fromRaw(idBuffer(), idOffset());
|
||||
if (!FileMode.TREE.equals(mode)) {
|
||||
final ObjectId me = idBuffer.toObjectId();
|
||||
throw new IncorrectObjectTypeException(me, Constants.TYPE_TREE);
|
||||
throw new IncorrectObjectTypeException(me, TYPE_TREE);
|
||||
}
|
||||
return createSubtreeIterator0(reader, idBuffer);
|
||||
}
|
||||
|
@ -263,7 +266,7 @@ public byte[] idBuffer() {
|
|||
|
||||
@Override
|
||||
public int idOffset() {
|
||||
return nextPtr - Constants.OBJECT_ID_LENGTH;
|
||||
return nextPtr - OBJECT_ID_LENGTH;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -301,7 +304,7 @@ public void next(int delta) {
|
|||
prevPtr = ptr;
|
||||
while (raw[ptr] != 0)
|
||||
ptr++;
|
||||
ptr += Constants.OBJECT_ID_LENGTH + 1;
|
||||
ptr += OBJECT_ID_LENGTH + 1;
|
||||
}
|
||||
if (delta != 0)
|
||||
throw new ArrayIndexOutOfBoundsException(delta);
|
||||
|
@ -337,7 +340,7 @@ public void back(int delta) {
|
|||
trace[delta] = ptr;
|
||||
while (raw[ptr] != 0)
|
||||
ptr++;
|
||||
ptr += Constants.OBJECT_ID_LENGTH + 1;
|
||||
ptr += OBJECT_ID_LENGTH + 1;
|
||||
}
|
||||
if (trace[1] == -1)
|
||||
throw new ArrayIndexOutOfBoundsException(delta);
|
||||
|
@ -372,12 +375,7 @@ private void parseEntry() {
|
|||
}
|
||||
}
|
||||
pathLen = tmp;
|
||||
nextPtr = ptr + Constants.OBJECT_ID_LENGTH;
|
||||
|
||||
// Check if this entry is a .gitattributes file
|
||||
if (path[pathOffset] == '.'
|
||||
&& RawParseUtils.match(path, pathOffset, ATTRS) > 0)
|
||||
attributesNode = new LazyLoadingAttributesNode(idOffset());
|
||||
nextPtr = ptr + OBJECT_ID_LENGTH;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -391,36 +389,32 @@ private void parseEntry() {
|
|||
*/
|
||||
public AttributesNode getEntryAttributesNode(ObjectReader reader)
|
||||
throws IOException {
|
||||
if (attributesNode instanceof LazyLoadingAttributesNode)
|
||||
attributesNode = ((LazyLoadingAttributesNode) attributesNode)
|
||||
.load(reader);
|
||||
return attributesNode;
|
||||
if (attributesNode == null) {
|
||||
attributesNode = findAttributes(reader);
|
||||
}
|
||||
return attributesNode.getRules().isEmpty() ? null : attributesNode;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@link AttributesNode} implementation that provides lazy loading
|
||||
*/
|
||||
private class LazyLoadingAttributesNode extends AttributesNode {
|
||||
private final int idOffset;
|
||||
|
||||
LazyLoadingAttributesNode(int idOffset) {
|
||||
super(Collections.<AttributesRule> emptyList());
|
||||
this.idOffset = idOffset;
|
||||
private AttributesNode findAttributes(ObjectReader reader)
|
||||
throws IOException {
|
||||
CanonicalTreeParser itr = new CanonicalTreeParser();
|
||||
itr.reset(raw);
|
||||
if (itr.findFile(ATTRS)) {
|
||||
return loadAttributes(reader, itr.getEntryObjectId());
|
||||
}
|
||||
return noAttributes();
|
||||
}
|
||||
|
||||
AttributesNode load(ObjectReader reader) throws IOException {
|
||||
AttributesNode r = new AttributesNode();
|
||||
ObjectId id = ObjectId.fromRaw(raw, idOffset);
|
||||
ObjectLoader loader = reader.open(id);
|
||||
if (loader != null) {
|
||||
InputStream in = loader.openStream();
|
||||
try {
|
||||
r.parse(in);
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
return r.getRules().isEmpty() ? null : r;
|
||||
private static AttributesNode loadAttributes(ObjectReader reader,
|
||||
AnyObjectId id) throws IOException {
|
||||
AttributesNode r = new AttributesNode();
|
||||
try (InputStream in = reader.open(id, OBJ_BLOB).openStream()) {
|
||||
r.parse(in);
|
||||
}
|
||||
return r.getRules().isEmpty() ? noAttributes() : r;
|
||||
}
|
||||
|
||||
private static AttributesNode noAttributes() {
|
||||
return new AttributesNode(Collections.<AttributesRule> emptyList());
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue