Partial support for index file format "3".
Extended flags are processed and available via DirCacheEntry's new isSkipWorkTree() and isIntentToAdd() methods. "resolve-undo" information is completely ignored since its an optional extension. Change-Id: Ie6e9c6784c9f265ca3c013c6dc0e6bd29d3b7233
This commit is contained in:
parent
b3aa5802b9
commit
253b36d27a
Binary file not shown.
|
@ -44,10 +44,12 @@
|
||||||
package org.eclipse.jgit.dircache;
|
package org.eclipse.jgit.dircache;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileInputStream;
|
import java.io.FileInputStream;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.Arrays;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
@ -59,6 +61,7 @@
|
||||||
import org.eclipse.jgit.lib.Repository;
|
import org.eclipse.jgit.lib.Repository;
|
||||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||||
import org.eclipse.jgit.util.FS;
|
import org.eclipse.jgit.util.FS;
|
||||||
|
import org.eclipse.jgit.util.IO;
|
||||||
import org.eclipse.jgit.util.JGitTestUtil;
|
import org.eclipse.jgit.util.JGitTestUtil;
|
||||||
|
|
||||||
public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase {
|
public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase {
|
||||||
|
@ -179,6 +182,35 @@ public void testReadIndex_DirCacheTree() throws Exception {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testReadWriteV3() throws Exception {
|
||||||
|
final File file = pathOf("gitgit.index.v3.skipWorkTree");
|
||||||
|
final DirCache dc = new DirCache(file, FS.DETECTED);
|
||||||
|
dc.read();
|
||||||
|
|
||||||
|
assertEquals(7, dc.getEntryCount());
|
||||||
|
assertV3TreeEntry(0, "dir1/file1.txt", false, false, dc);
|
||||||
|
assertV3TreeEntry(1, "dir2/file2.txt", true, false, dc);
|
||||||
|
assertV3TreeEntry(2, "dir3/file3.txt", false, false, dc);
|
||||||
|
assertV3TreeEntry(3, "dir3/file3a.txt", true, false, dc);
|
||||||
|
assertV3TreeEntry(4, "dir4/file4.txt", true, false, dc);
|
||||||
|
assertV3TreeEntry(5, "dir4/file4a.txt", false, false, dc);
|
||||||
|
assertV3TreeEntry(6, "file.txt", true, false, dc);
|
||||||
|
|
||||||
|
final ByteArrayOutputStream bos = new ByteArrayOutputStream();
|
||||||
|
dc.writeTo(bos);
|
||||||
|
final byte[] indexBytes = bos.toByteArray();
|
||||||
|
final byte[] expectedBytes = IO.readFully(file);
|
||||||
|
assertTrue(Arrays.equals(expectedBytes, indexBytes));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void assertV3TreeEntry(int indexPosition, String path,
|
||||||
|
boolean skipWorkTree, boolean intentToAdd, DirCache dc) {
|
||||||
|
final DirCacheEntry entry = dc.getEntry(indexPosition);
|
||||||
|
assertEquals(path, entry.getPathString());
|
||||||
|
assertEquals(skipWorkTree, entry.isSkipWorkTree());
|
||||||
|
assertEquals(intentToAdd, entry.isIntentToAdd());
|
||||||
|
}
|
||||||
|
|
||||||
private File pathOf(final String name) {
|
private File pathOf(final String name) {
|
||||||
return JGitTestUtil.getTestResourceFile(name);
|
return JGitTestUtil.getTestResourceFile(name);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,6 +2,7 @@ DIRCChecksumMismatch=DIRC checksum mismatch
|
||||||
DIRCExtensionIsTooLargeAt=DIRC extension {0} is too large at {1} bytes.
|
DIRCExtensionIsTooLargeAt=DIRC extension {0} is too large at {1} bytes.
|
||||||
DIRCExtensionNotSupportedByThisVersion=DIRC extension {0} not supported by this version.
|
DIRCExtensionNotSupportedByThisVersion=DIRC extension {0} not supported by this version.
|
||||||
DIRCHasTooManyEntries=DIRC has too many entries.
|
DIRCHasTooManyEntries=DIRC has too many entries.
|
||||||
|
DIRCUnrecognizedExtendedFlags=Unrecognized extended flags: {0}
|
||||||
JRELacksMD5Implementation=JRE lacks MD5 implementation
|
JRELacksMD5Implementation=JRE lacks MD5 implementation
|
||||||
URINotSupported=URI not supported: {0}
|
URINotSupported=URI not supported: {0}
|
||||||
URLNotFound={0} not found
|
URLNotFound={0} not found
|
||||||
|
|
|
@ -62,6 +62,7 @@ public static JGitText get() {
|
||||||
/***/ public String DIRCExtensionIsTooLargeAt;
|
/***/ public String DIRCExtensionIsTooLargeAt;
|
||||||
/***/ public String DIRCExtensionNotSupportedByThisVersion;
|
/***/ public String DIRCExtensionNotSupportedByThisVersion;
|
||||||
/***/ public String DIRCHasTooManyEntries;
|
/***/ public String DIRCHasTooManyEntries;
|
||||||
|
/***/ public String DIRCUnrecognizedExtendedFlags;
|
||||||
/***/ public String JRELacksMD5Implementation;
|
/***/ public String JRELacksMD5Implementation;
|
||||||
/***/ public String URINotSupported;
|
/***/ public String URINotSupported;
|
||||||
/***/ public String URLNotFound;
|
/***/ public String URLNotFound;
|
||||||
|
|
|
@ -91,8 +91,6 @@ public class DirCache {
|
||||||
|
|
||||||
private static final int EXT_TREE = 0x54524545 /* 'TREE' */;
|
private static final int EXT_TREE = 0x54524545 /* 'TREE' */;
|
||||||
|
|
||||||
private static final int INFO_LEN = DirCacheEntry.INFO_LEN;
|
|
||||||
|
|
||||||
private static final DirCacheEntry[] NO_ENTRIES = {};
|
private static final DirCacheEntry[] NO_ENTRIES = {};
|
||||||
|
|
||||||
static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
|
static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
|
||||||
|
@ -322,7 +320,7 @@ public void clear() {
|
||||||
tree = null;
|
tree = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void readFrom(final FileInputStream inStream) throws IOException,
|
private void readFrom(final InputStream inStream) throws IOException,
|
||||||
CorruptObjectException {
|
CorruptObjectException {
|
||||||
final BufferedInputStream in = new BufferedInputStream(inStream);
|
final BufferedInputStream in = new BufferedInputStream(inStream);
|
||||||
final MessageDigest md = Constants.newMessageDigest();
|
final MessageDigest md = Constants.newMessageDigest();
|
||||||
|
@ -335,7 +333,10 @@ private void readFrom(final FileInputStream inStream) throws IOException,
|
||||||
if (!is_DIRC(hdr))
|
if (!is_DIRC(hdr))
|
||||||
throw new CorruptObjectException(JGitText.get().notADIRCFile);
|
throw new CorruptObjectException(JGitText.get().notADIRCFile);
|
||||||
final int ver = NB.decodeInt32(hdr, 4);
|
final int ver = NB.decodeInt32(hdr, 4);
|
||||||
if (ver != 2)
|
boolean extended = false;
|
||||||
|
if (ver == 3)
|
||||||
|
extended = true;
|
||||||
|
else if (ver != 2)
|
||||||
throw new CorruptObjectException(MessageFormat.format(JGitText.get().unknownDIRCVersion, ver));
|
throw new CorruptObjectException(MessageFormat.format(JGitText.get().unknownDIRCVersion, ver));
|
||||||
entryCnt = NB.decodeInt32(hdr, 8);
|
entryCnt = NB.decodeInt32(hdr, 8);
|
||||||
if (entryCnt < 0)
|
if (entryCnt < 0)
|
||||||
|
@ -343,10 +344,13 @@ private void readFrom(final FileInputStream inStream) throws IOException,
|
||||||
|
|
||||||
// Load the individual file entries.
|
// Load the individual file entries.
|
||||||
//
|
//
|
||||||
final byte[] infos = new byte[INFO_LEN * entryCnt];
|
final int infoLength = DirCacheEntry.getMaximumInfoLength(extended);
|
||||||
|
final byte[] infos = new byte[infoLength * entryCnt];
|
||||||
sortedEntries = new DirCacheEntry[entryCnt];
|
sortedEntries = new DirCacheEntry[entryCnt];
|
||||||
|
|
||||||
|
final MutableInteger infoAt = new MutableInteger();
|
||||||
for (int i = 0; i < entryCnt; i++)
|
for (int i = 0; i < entryCnt; i++)
|
||||||
sortedEntries[i] = new DirCacheEntry(infos, i * INFO_LEN, in, md);
|
sortedEntries[i] = new DirCacheEntry(infos, infoAt, in, md);
|
||||||
lastModified = liveFile.lastModified();
|
lastModified = liveFile.lastModified();
|
||||||
|
|
||||||
// After the file entries are index extensions, and then a footer.
|
// After the file entries are index extensions, and then a footer.
|
||||||
|
@ -484,15 +488,19 @@ public void write() throws IOException {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void writeTo(final OutputStream os) throws IOException {
|
void writeTo(final OutputStream os) throws IOException {
|
||||||
final MessageDigest foot = Constants.newMessageDigest();
|
final MessageDigest foot = Constants.newMessageDigest();
|
||||||
final DigestOutputStream dos = new DigestOutputStream(os, foot);
|
final DigestOutputStream dos = new DigestOutputStream(os, foot);
|
||||||
|
|
||||||
|
boolean extended = false;
|
||||||
|
for (int i = 0; i < entryCnt; i++)
|
||||||
|
extended |= sortedEntries[i].isExtended();
|
||||||
|
|
||||||
// Write the header.
|
// Write the header.
|
||||||
//
|
//
|
||||||
final byte[] tmp = new byte[128];
|
final byte[] tmp = new byte[128];
|
||||||
System.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length);
|
System.arraycopy(SIG_DIRC, 0, tmp, 0, SIG_DIRC.length);
|
||||||
NB.encodeInt32(tmp, 4, /* version */2);
|
NB.encodeInt32(tmp, 4, extended ? 3 : 2);
|
||||||
NB.encodeInt32(tmp, 8, entryCnt);
|
NB.encodeInt32(tmp, 8, entryCnt);
|
||||||
dos.write(tmp, 0, 12);
|
dos.write(tmp, 0, 12);
|
||||||
|
|
||||||
|
|
|
@ -62,6 +62,7 @@
|
||||||
import org.eclipse.jgit.lib.FileMode;
|
import org.eclipse.jgit.lib.FileMode;
|
||||||
import org.eclipse.jgit.lib.ObjectId;
|
import org.eclipse.jgit.lib.ObjectId;
|
||||||
import org.eclipse.jgit.util.IO;
|
import org.eclipse.jgit.util.IO;
|
||||||
|
import org.eclipse.jgit.util.MutableInteger;
|
||||||
import org.eclipse.jgit.util.NB;
|
import org.eclipse.jgit.util.NB;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -108,12 +109,19 @@ public class DirCacheEntry {
|
||||||
private static final int P_OBJECTID = 40;
|
private static final int P_OBJECTID = 40;
|
||||||
|
|
||||||
private static final int P_FLAGS = 60;
|
private static final int P_FLAGS = 60;
|
||||||
|
private static final int P_FLAGS2 = 62;
|
||||||
|
|
||||||
/** Mask applied to data in {@link #P_FLAGS} to get the name length. */
|
/** Mask applied to data in {@link #P_FLAGS} to get the name length. */
|
||||||
private static final int NAME_MASK = 0xfff;
|
private static final int NAME_MASK = 0xfff;
|
||||||
|
|
||||||
static final int INFO_LEN = 62;
|
private static final int INTENT_TO_ADD = 0x20000000;
|
||||||
|
private static final int SKIP_WORKTREE = 0x40000000;
|
||||||
|
private static final int EXTENDED_FLAGS = (INTENT_TO_ADD | SKIP_WORKTREE);
|
||||||
|
|
||||||
|
private static final int INFO_LEN = 62;
|
||||||
|
private static final int INFO_LEN_EXTENDED = 64;
|
||||||
|
|
||||||
|
private static final int EXTENDED = 0x40;
|
||||||
private static final int ASSUME_VALID = 0x80;
|
private static final int ASSUME_VALID = 0x80;
|
||||||
|
|
||||||
/** In-core flag signaling that the entry should be considered as modified. */
|
/** In-core flag signaling that the entry should be considered as modified. */
|
||||||
|
@ -131,13 +139,26 @@ public class DirCacheEntry {
|
||||||
/** Flags which are never stored to disk. */
|
/** Flags which are never stored to disk. */
|
||||||
private byte inCoreFlags;
|
private byte inCoreFlags;
|
||||||
|
|
||||||
DirCacheEntry(final byte[] sharedInfo, final int infoAt,
|
DirCacheEntry(final byte[] sharedInfo, final MutableInteger infoAt,
|
||||||
final InputStream in, final MessageDigest md) throws IOException {
|
final InputStream in, final MessageDigest md) throws IOException {
|
||||||
info = sharedInfo;
|
info = sharedInfo;
|
||||||
infoOffset = infoAt;
|
infoOffset = infoAt.value;
|
||||||
|
|
||||||
IO.readFully(in, info, infoOffset, INFO_LEN);
|
IO.readFully(in, info, infoOffset, INFO_LEN);
|
||||||
md.update(info, infoOffset, INFO_LEN);
|
|
||||||
|
final int len;
|
||||||
|
if (isExtended()) {
|
||||||
|
len = INFO_LEN_EXTENDED;
|
||||||
|
IO.readFully(in, info, infoOffset + INFO_LEN, INFO_LEN_EXTENDED - INFO_LEN);
|
||||||
|
|
||||||
|
if ((getExtendedFlags() & ~EXTENDED_FLAGS) != 0)
|
||||||
|
throw new IOException(MessageFormat.format(JGitText.get()
|
||||||
|
.DIRCUnrecognizedExtendedFlags, String.valueOf(getExtendedFlags())));
|
||||||
|
} else
|
||||||
|
len = INFO_LEN;
|
||||||
|
|
||||||
|
infoAt.value += len;
|
||||||
|
md.update(info, infoOffset, len);
|
||||||
|
|
||||||
int pathLen = NB.decodeUInt16(info, infoOffset + P_FLAGS) & NAME_MASK;
|
int pathLen = NB.decodeUInt16(info, infoOffset + P_FLAGS) & NAME_MASK;
|
||||||
int skipped = 0;
|
int skipped = 0;
|
||||||
|
@ -170,7 +191,7 @@ public class DirCacheEntry {
|
||||||
// Index records are padded out to the next 8 byte alignment
|
// Index records are padded out to the next 8 byte alignment
|
||||||
// for historical reasons related to how C Git read the files.
|
// for historical reasons related to how C Git read the files.
|
||||||
//
|
//
|
||||||
final int actLen = INFO_LEN + pathLen;
|
final int actLen = len + pathLen;
|
||||||
final int expLen = (actLen + 8) & ~7;
|
final int expLen = (actLen + 8) & ~7;
|
||||||
final int padLen = expLen - actLen - skipped;
|
final int padLen = expLen - actLen - skipped;
|
||||||
if (padLen > 0) {
|
if (padLen > 0) {
|
||||||
|
@ -258,14 +279,15 @@ public DirCacheEntry(final byte[] newPath, final int stage) {
|
||||||
}
|
}
|
||||||
|
|
||||||
void write(final OutputStream os) throws IOException {
|
void write(final OutputStream os) throws IOException {
|
||||||
|
final int len = isExtended() ? INFO_LEN_EXTENDED : INFO_LEN;
|
||||||
final int pathLen = path.length;
|
final int pathLen = path.length;
|
||||||
os.write(info, infoOffset, INFO_LEN);
|
os.write(info, infoOffset, len);
|
||||||
os.write(path, 0, pathLen);
|
os.write(path, 0, pathLen);
|
||||||
|
|
||||||
// Index records are padded out to the next 8 byte alignment
|
// Index records are padded out to the next 8 byte alignment
|
||||||
// for historical reasons related to how C Git read the files.
|
// for historical reasons related to how C Git read the files.
|
||||||
//
|
//
|
||||||
final int actLen = INFO_LEN + pathLen;
|
final int actLen = len + pathLen;
|
||||||
final int expLen = (actLen + 8) & ~7;
|
final int expLen = (actLen + 8) & ~7;
|
||||||
if (actLen != expLen)
|
if (actLen != expLen)
|
||||||
os.write(nullpad, 0, expLen - actLen);
|
os.write(nullpad, 0, expLen - actLen);
|
||||||
|
@ -400,6 +422,24 @@ public int getStage() {
|
||||||
return (info[infoOffset + P_FLAGS] >>> 4) & 0x3;
|
return (info[infoOffset + P_FLAGS] >>> 4) & 0x3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this entry should be skipped from the working tree.
|
||||||
|
*
|
||||||
|
* @return true if this entry should be skipepd.
|
||||||
|
*/
|
||||||
|
public boolean isSkipWorkTree() {
|
||||||
|
return (getExtendedFlags() & SKIP_WORKTREE) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether this entry is intent to be added to the Index.
|
||||||
|
*
|
||||||
|
* @return true if this entry is intent to add.
|
||||||
|
*/
|
||||||
|
public boolean isIntentToAdd() {
|
||||||
|
return (getExtendedFlags() & INTENT_TO_ADD) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Obtain the raw {@link FileMode} bits for this entry.
|
* Obtain the raw {@link FileMode} bits for this entry.
|
||||||
*
|
*
|
||||||
|
@ -575,6 +615,13 @@ public void copyMetaData(final DirCacheEntry src) {
|
||||||
| NB.decodeUInt16(info, infoOffset + P_FLAGS) & ~NAME_MASK);
|
| NB.decodeUInt16(info, infoOffset + P_FLAGS) & ~NAME_MASK);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return true if the entry contains extended flags.
|
||||||
|
*/
|
||||||
|
boolean isExtended() {
|
||||||
|
return (info[infoOffset + P_FLAGS] & EXTENDED) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
private long decodeTS(final int pIdx) {
|
private long decodeTS(final int pIdx) {
|
||||||
final int base = infoOffset + pIdx;
|
final int base = infoOffset + pIdx;
|
||||||
final int sec = NB.decodeInt32(info, base);
|
final int sec = NB.decodeInt32(info, base);
|
||||||
|
@ -588,6 +635,13 @@ private void encodeTS(final int pIdx, final long when) {
|
||||||
NB.encodeInt32(info, base + 4, ((int) (when % 1000)) * 1000000);
|
NB.encodeInt32(info, base + 4, ((int) (when % 1000)) * 1000000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private int getExtendedFlags() {
|
||||||
|
if (isExtended())
|
||||||
|
return NB.decodeUInt16(info, infoOffset + P_FLAGS2) << 16;
|
||||||
|
else
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
private static String toString(final byte[] path) {
|
private static String toString(final byte[] path) {
|
||||||
return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();
|
return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();
|
||||||
}
|
}
|
||||||
|
@ -615,4 +669,8 @@ static boolean isValidPath(final byte[] path) {
|
||||||
}
|
}
|
||||||
return componentHasChars;
|
return componentHasChars;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int getMaximumInfoLength(boolean extended) {
|
||||||
|
return extended ? INFO_LEN_EXTENDED : INFO_LEN;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue