Browse Source

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
tags/v0.9.1
Marc Strapetz 13 years ago
parent
commit
253b36d27a

BIN
org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/gitgit.index.v3.skipWorkTree View File


+ 32
- 0
org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java View File

@@ -44,10 +44,12 @@
package org.eclipse.jgit.dircache;

import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
@@ -59,6 +61,7 @@ import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.treewalk.TreeWalk;
import org.eclipse.jgit.util.FS;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.JGitTestUtil;

public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase {
@@ -179,6 +182,35 @@ public class DirCacheCGitCompatabilityTest extends LocalDiskRepositoryTestCase {
}
}

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) {
return JGitTestUtil.getTestResourceFile(name);
}

+ 1
- 0
org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties View File

@@ -2,6 +2,7 @@ DIRCChecksumMismatch=DIRC checksum mismatch
DIRCExtensionIsTooLargeAt=DIRC extension {0} is too large at {1} bytes.
DIRCExtensionNotSupportedByThisVersion=DIRC extension {0} not supported by this version.
DIRCHasTooManyEntries=DIRC has too many entries.
DIRCUnrecognizedExtendedFlags=Unrecognized extended flags: {0}
JRELacksMD5Implementation=JRE lacks MD5 implementation
URINotSupported=URI not supported: {0}
URLNotFound={0} not found

+ 1
- 0
org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java View File

@@ -62,6 +62,7 @@ public class JGitText extends TranslationBundle {
/***/ public String DIRCExtensionIsTooLargeAt;
/***/ public String DIRCExtensionNotSupportedByThisVersion;
/***/ public String DIRCHasTooManyEntries;
/***/ public String DIRCUnrecognizedExtendedFlags;
/***/ public String JRELacksMD5Implementation;
/***/ public String URINotSupported;
/***/ public String URLNotFound;

+ 16
- 8
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java View File

@@ -91,8 +91,6 @@ public class DirCache {

private static final int EXT_TREE = 0x54524545 /* 'TREE' */;

private static final int INFO_LEN = DirCacheEntry.INFO_LEN;

private static final DirCacheEntry[] NO_ENTRIES = {};

static final Comparator<DirCacheEntry> ENT_CMP = new Comparator<DirCacheEntry>() {
@@ -322,7 +320,7 @@ public class DirCache {
tree = null;
}

private void readFrom(final FileInputStream inStream) throws IOException,
private void readFrom(final InputStream inStream) throws IOException,
CorruptObjectException {
final BufferedInputStream in = new BufferedInputStream(inStream);
final MessageDigest md = Constants.newMessageDigest();
@@ -335,7 +333,10 @@ public class DirCache {
if (!is_DIRC(hdr))
throw new CorruptObjectException(JGitText.get().notADIRCFile);
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));
entryCnt = NB.decodeInt32(hdr, 8);
if (entryCnt < 0)
@@ -343,10 +344,13 @@ public class DirCache {

// 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];

final MutableInteger infoAt = new MutableInteger();
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();

// After the file entries are index extensions, and then a footer.
@@ -484,15 +488,19 @@ public class DirCache {
}
}

private void writeTo(final OutputStream os) throws IOException {
void writeTo(final OutputStream os) throws IOException {
final MessageDigest foot = Constants.newMessageDigest();
final DigestOutputStream dos = new DigestOutputStream(os, foot);

boolean extended = false;
for (int i = 0; i < entryCnt; i++)
extended |= sortedEntries[i].isExtended();

// Write the header.
//
final byte[] tmp = new byte[128];
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);
dos.write(tmp, 0, 12);


+ 65
- 7
org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java View File

@@ -62,6 +62,7 @@ import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.FileMode;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.util.IO;
import org.eclipse.jgit.util.MutableInteger;
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_FLAGS = 60;
private static final int P_FLAGS2 = 62;

/** Mask applied to data in {@link #P_FLAGS} to get the name length. */
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;

/** 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. */
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 {
info = sharedInfo;
infoOffset = infoAt;
infoOffset = infoAt.value;

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 skipped = 0;
@@ -170,7 +191,7 @@ public class DirCacheEntry {
// Index records are padded out to the next 8 byte alignment
// 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 padLen = expLen - actLen - skipped;
if (padLen > 0) {
@@ -258,14 +279,15 @@ public class DirCacheEntry {
}

void write(final OutputStream os) throws IOException {
final int len = isExtended() ? INFO_LEN_EXTENDED : INFO_LEN;
final int pathLen = path.length;
os.write(info, infoOffset, INFO_LEN);
os.write(info, infoOffset, len);
os.write(path, 0, pathLen);

// Index records are padded out to the next 8 byte alignment
// 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;
if (actLen != expLen)
os.write(nullpad, 0, expLen - actLen);
@@ -400,6 +422,24 @@ public class DirCacheEntry {
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.
*
@@ -575,6 +615,13 @@ public class DirCacheEntry {
| 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) {
final int base = infoOffset + pIdx;
final int sec = NB.decodeInt32(info, base);
@@ -588,6 +635,13 @@ public class DirCacheEntry {
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) {
return Constants.CHARSET.decode(ByteBuffer.wrap(path)).toString();
}
@@ -615,4 +669,8 @@ public class DirCacheEntry {
}
return componentHasChars;
}

static int getMaximumInfoLength(boolean extended) {
return extended ? INFO_LEN_EXTENDED : INFO_LEN;
}
}

Loading…
Cancel
Save