From 253b36d27a3d249ec8290b99bf38275d003766e1 Mon Sep 17 00:00:00 2001 From: Marc Strapetz Date: Tue, 31 Aug 2010 13:26:29 +0200 Subject: [PATCH] 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 --- .../resources/gitgit.index.v3.skipWorkTree | Bin 0 -> 741 bytes .../DirCacheCGitCompatabilityTest.java | 32 ++++++++ .../org/eclipse/jgit/JGitText.properties | 1 + .../src/org/eclipse/jgit/JGitText.java | 1 + .../org/eclipse/jgit/dircache/DirCache.java | 24 ++++-- .../eclipse/jgit/dircache/DirCacheEntry.java | 72 ++++++++++++++++-- 6 files changed, 115 insertions(+), 15 deletions(-) create mode 100644 org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/gitgit.index.v3.skipWorkTree diff --git a/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/gitgit.index.v3.skipWorkTree b/org.eclipse.jgit.test/tst-rsrc/org/eclipse/jgit/test/resources/gitgit.index.v3.skipWorkTree new file mode 100644 index 0000000000000000000000000000000000000000..4cbd703808bc509b413cba980e8375700b0533e0 GIT binary patch literal 741 zcmZ?q402{*U|U^DQxPW4-T{;otD__H*A@zktU90D|7r+W-In literal 0 HcmV?d00001 diff --git a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java index f37d04004..f5cdfae29 100644 --- a/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java +++ b/org.eclipse.jgit.test/tst/org/eclipse/jgit/dircache/DirCacheCGitCompatabilityTest.java @@ -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.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 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) { return JGitTestUtil.getTestResourceFile(name); } diff --git a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties index 14c0ba0fc..5bebd18b3 100644 --- a/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties +++ b/org.eclipse.jgit/resources/org/eclipse/jgit/JGitText.properties @@ -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 diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java index ea40fe884..927fa7c52 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/JGitText.java @@ -62,6 +62,7 @@ public static JGitText get() { /***/ public String DIRCExtensionIsTooLargeAt; /***/ public String DIRCExtensionNotSupportedByThisVersion; /***/ public String DIRCHasTooManyEntries; + /***/ public String DIRCUnrecognizedExtendedFlags; /***/ public String JRELacksMD5Implementation; /***/ public String URINotSupported; /***/ public String URLNotFound; diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java index a117c8df2..143447c8d 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCache.java @@ -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 ENT_CMP = new Comparator() { @@ -322,7 +320,7 @@ public void clear() { 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 @@ private void readFrom(final FileInputStream inStream) throws IOException, 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 @@ private void readFrom(final FileInputStream inStream) throws IOException, // 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 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 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); diff --git a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java index 308d4d168..defab97a0 100644 --- a/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java +++ b/org.eclipse.jgit/src/org/eclipse/jgit/dircache/DirCacheEntry.java @@ -62,6 +62,7 @@ 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 DirCacheEntry(final byte[] newPath, final int stage) { } 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 int getStage() { 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 void copyMetaData(final DirCacheEntry src) { | 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 @@ private void encodeTS(final int pIdx, final long when) { 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 @@ static boolean isValidPath(final byte[] path) { } return componentHasChars; } + + static int getMaximumInfoLength(boolean extended) { + return extended ? INFO_LEN_EXTENDED : INFO_LEN; + } }