# HG changeset patch # User Artem Tikhomirov # Date 1292904666 -3600 # Node ID fc265ddeab262ff5c34b4cf4e2522d8d41f1f05b # Parent aa1912c70b36fa4b46f990f798415d532c736bb7 File content and non-effective, although working, patch application diff -r aa1912c70b36 -r fc265ddeab26 design.txt --- a/design.txt Mon Dec 20 04:20:52 2010 +0100 +++ b/design.txt Tue Dec 21 05:11:06 2010 +0100 @@ -25,3 +25,4 @@ input stream impl + lifecycle delta merge +Changeset to get index (local revision number) \ No newline at end of file diff -r aa1912c70b36 -r fc265ddeab26 src/com/tmate/hgkit/console/Cat.java --- a/src/com/tmate/hgkit/console/Cat.java Mon Dec 20 04:20:52 2010 +0100 +++ b/src/com/tmate/hgkit/console/Cat.java Tue Dec 21 05:11:06 2010 +0100 @@ -4,6 +4,7 @@ package com.tmate.hgkit.console; import com.tmate.hgkit.fs.RepositoryLookup; +import com.tmate.hgkit.ll.HgDataFile; import com.tmate.hgkit.ll.HgRepository; /** @@ -14,12 +15,26 @@ public static void main(String[] args) throws Exception { RepositoryLookup repoLookup = new RepositoryLookup(); - HgRepository hgRepo = repoLookup.detect(args); + RepositoryLookup.Options cmdLineOpts = RepositoryLookup.Options.parse(args); + HgRepository hgRepo = repoLookup.detect(cmdLineOpts); if (hgRepo.isInvalid()) { System.err.printf("Can't find repository in: %s\n", hgRepo.getLocation()); return; } - byte[] tipContent = hgRepo.getFileNode("hello.c").content(); - System.out.println(new String(tipContent)); + for (String fname : cmdLineOpts.files) { + System.out.println(fname); + HgDataFile fn = hgRepo.getFileNode(fname); + if (fn.exists()) { + int total = fn.getRevisionCount(); + System.out.printf("Total revisions: %d\n", total); + for (int i = 0; i < total; i++) { + byte[] content = fn.content(i); + System.out.println("==========>"); + System.out.println(new String(content)); + } + } else { + System.out.println(">>>Not found!"); + } + } } } diff -r aa1912c70b36 -r fc265ddeab26 src/com/tmate/hgkit/ll/Changeset.java --- a/src/com/tmate/hgkit/ll/Changeset.java Mon Dec 20 04:20:52 2010 +0100 +++ b/src/com/tmate/hgkit/ll/Changeset.java Tue Dec 21 05:11:06 2010 +0100 @@ -44,30 +44,35 @@ public static Changeset parse(byte[] data, int offset, int length) { Changeset rv = new Changeset(); + rv.init(data, offset, length); + return rv; + } + + /*package-local*/ void init(byte[] data, int offset, int length) { final int bufferEndIndex = offset + length; final byte lineBreak = (byte) '\n'; int breakIndex1 = indexOf(data, lineBreak, offset, bufferEndIndex); if (breakIndex1 == -1) { throw new IllegalArgumentException("Bad Changeset data"); } - rv.nodeid = Nodeid.fromAscii(data, 0, breakIndex1); + Nodeid _nodeid = Nodeid.fromAscii(data, 0, breakIndex1); int breakIndex2 = indexOf(data, lineBreak, breakIndex1+1, bufferEndIndex); if (breakIndex2 == -1) { throw new IllegalArgumentException("Bad Changeset data"); } - rv.user = new String(data, breakIndex1+1, breakIndex2 - breakIndex1 - 1); + String _user = new String(data, breakIndex1+1, breakIndex2 - breakIndex1 - 1); int breakIndex3 = indexOf(data, lineBreak, breakIndex2+1, bufferEndIndex); if (breakIndex3 == -1) { throw new IllegalArgumentException("Bad Changeset data"); } - rv.timezone = new String(data, breakIndex2+1, breakIndex3 - breakIndex2 - 1); + String _timezone = new String(data, breakIndex2+1, breakIndex3 - breakIndex2 - 1); // int lastStart = breakIndex3 + 1; int breakIndex4 = indexOf(data, lineBreak, lastStart, bufferEndIndex); - rv.files = new ArrayList(5); + ArrayList _files = new ArrayList(5); while (breakIndex4 != -1 && breakIndex4 + 1 < bufferEndIndex) { - rv.files.add(new String(data, lastStart, breakIndex4 - lastStart)); + _files.add(new String(data, lastStart, breakIndex4 - lastStart)); lastStart = breakIndex4 + 1; if (data[breakIndex4 + 1] == lineBreak) { // found \n\n @@ -79,13 +84,19 @@ if (breakIndex4 == -1 || breakIndex4 >= bufferEndIndex) { throw new IllegalArgumentException("Bad Changeset data"); } + String _comment; try { - rv.comment = new String(data, breakIndex4+2, bufferEndIndex - breakIndex4 - 2, "UTF-8"); + _comment = new String(data, breakIndex4+2, bufferEndIndex - breakIndex4 - 2, "UTF-8"); } catch (UnsupportedEncodingException ex) { - rv.comment = ""; + _comment = ""; throw new IllegalStateException("Could hardly happen"); } - return rv; + // change this instance at once, don't leave it partially changes in case of error + this.nodeid = _nodeid; + this.user = _user; + this.timezone = _timezone; + this.files = _files; + this.comment = _comment; } private static int indexOf(byte[] src, byte what, int startOffset, int endIndex) { diff -r aa1912c70b36 -r fc265ddeab26 src/com/tmate/hgkit/ll/HgDataFile.java --- a/src/com/tmate/hgkit/ll/HgDataFile.java Mon Dec 20 04:20:52 2010 +0100 +++ b/src/com/tmate/hgkit/ll/HgDataFile.java Tue Dec 21 05:11:06 2010 +0100 @@ -3,6 +3,8 @@ */ package com.tmate.hgkit.ll; +import static com.tmate.hgkit.ll.HgRepository.TIP; + /** * Extends Revlog/uses RevlogStream? * ? name:HgFileNode? @@ -31,14 +33,23 @@ return path; // hgRepo.backresolve(this) -> name? } - private static final int TIP = -2; + public int getRevisionCount() { + return content.revisionCount(); + } public byte[] content() { return content(TIP); } public byte[] content(int revision) { - throw HgRepository.notImplemented(); + final byte[][] dataPtr = new byte[1][]; + Revlog.Inspector insp = new Revlog.Inspector() { + public void next(int revisionNumber, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[] nodeid, byte[] data) { + dataPtr[0] = data; + } + }; + content.iterate(revision, revision, true, insp); + return dataPtr[0]; } public void history(Changeset.Inspector inspector) { diff -r aa1912c70b36 -r fc265ddeab26 src/com/tmate/hgkit/ll/HgRepository.java --- a/src/com/tmate/hgkit/ll/HgRepository.java Mon Dec 20 04:20:52 2010 +0100 +++ b/src/com/tmate/hgkit/ll/HgRepository.java Tue Dec 21 05:11:06 2010 +0100 @@ -9,6 +9,8 @@ */ public abstract class HgRepository { + public static final int TIP = -1; + // temp aux marker method public static IllegalStateException notImplemented() { return new IllegalStateException("Not implemented"); diff -r aa1912c70b36 -r fc265ddeab26 src/com/tmate/hgkit/ll/RevlogStream.java --- a/src/com/tmate/hgkit/ll/RevlogStream.java Mon Dec 20 04:20:52 2010 +0100 +++ b/src/com/tmate/hgkit/ll/RevlogStream.java Tue Dec 21 05:11:06 2010 +0100 @@ -3,6 +3,8 @@ */ package com.tmate.hgkit.ll; +import static com.tmate.hgkit.ll.HgRepository.TIP; + import java.io.BufferedInputStream; import java.io.DataInput; import java.io.DataInputStream; @@ -74,9 +76,12 @@ if (indexSize == 0) { return; } - if (end == -1 /*FIXME TIP*/) { + if (end == TIP) { end = indexSize - 1; } + if (start == TIP) { + start = indexSize - 1; + } if (start < 0 || start >= indexSize) { throw new IllegalArgumentException("Bad left range boundary " + start); } @@ -91,10 +96,17 @@ diData = getDataStream(); } try { - int skipped = diIndex.skipBytes(inline ? (int) index.get(start).offset : start * 64); byte[] lastData = null; - for (int i = start; i <= end; i++ ) { - IndexEntry ie = index.get(i); + int i; + boolean extraReadsToBaseRev = false; + if (needData && index.get(start).baseRevision < start) { + i = index.get(start).baseRevision; + extraReadsToBaseRev = true; + } else { + i = start; + } + diIndex.skipBytes(inline ? (int) index.get(i).offset : start * 64); + for (; i <= end; i++ ) { long l = diIndex.readLong(); long offset = l >>> 16; int flags = (int) (l & 0X0FFFF); @@ -114,7 +126,7 @@ if (inline) { diIndex.readFully(dataBuf); } else { - diData.skipBytes((int) ie.offset); // FIXME not skip but seek!!! (skip would work only for the first time) + diData.skipBytes((int) index.get(i).offset); // FIXME not skip but seek!!! (skip would work only for the first time) diData.readFully(dataBuf); } if (dataBuf[0] == 0x78 /* 'x' */) { @@ -145,23 +157,14 @@ int patchElementIndex = 0; do { final int x = patchElementIndex; // shorthand - int p1 = (data[x] << 24) | (data[x+1] << 16) | (data[x+2] << 8) | data[x+3]; - int p2 = (data[x+4] << 24) | (data[x+5] << 16) | (data[x+6] << 8) | data[x+7]; - int len = (data[x+8] << 24) | (data[x+9] << 16) | (data[x+10] << 8) | data[x+11]; + int p1 = ((data[x] & 0xFF)<< 24) | ((data[x+1] & 0xFF) << 16) | ((data[x+2] & 0xFF) << 8) | (data[x+3] & 0xFF); + int p2 = ((data[x+4] & 0xFF) << 24) | ((data[x+5] & 0xFF) << 16) | ((data[x+6] & 0xFF) << 8) | (data[x+7] & 0xFF); + int len = ((data[x+8] & 0xFF) << 24) | ((data[x+9] & 0xFF) << 16) | ((data[x+10] & 0xFF) << 8) | (data[x+11] & 0xFF); patchElementIndex += 12 + len; patches.add(new PatchRecord(p1, p2, len, data, x+12)); } while (patchElementIndex < data.length); // - byte[] baseRevContent; - if (baseRevision == i - 1) { - baseRevContent = lastData; - } else { - // FIXME implement delta collection from few revisions - // read baseRevision plus all deltas between this revision and base. Need to do this effectively. - throw HgRepository.notImplemented(); - } - - // FIXME need to collect all patches between baseRevision and current version + byte[] baseRevContent = lastData; data = apply(baseRevContent, patches); } } else { @@ -169,7 +172,9 @@ diIndex.skipBytes(compressedLen); } } - inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, buf, data); + if (!extraReadsToBaseRev || i >= start) { + inspector.next(i, actualLen, baseRevision, linkRevision, parent1Revision, parent2Revision, buf, data); + } lastData = data; } } catch (EOFException ex) { @@ -196,19 +201,20 @@ long offset = 0; // first offset is always 0, thus Hg uses it for other purposes while(true) { // EOFExcepiton should get us outta here. FIXME Our inputstream should has explicit no-more-data indicator int compressedLen = di.readInt(); - // 8+4 = 12 bytes total read -// int actualLen = di.readInt(); -// int baseRevision = di.readInt(); + // 8+4 = 12 bytes total read here + int actualLen = di.readInt(); + int baseRevision = di.readInt(); + // 12 + 8 = 20 bytes read here // int linkRevision = di.readInt(); // int parent1Revision = di.readInt(); // int parent2Revision = di.readInt(); // byte[] nodeid = new byte[32]; if (inline) { - res.add(new IndexEntry(offset + 64*res.size(), compressedLen)); - di.skipBytes(5*4 + 32 + compressedLen); // Check: 52 (skip) + 12 (read) = 64 (total RevlogNG record size) + res.add(new IndexEntry(offset + 64*res.size(), baseRevision)); + di.skipBytes(3*4 + 32 + compressedLen); // Check: 44 (skip) + 20 (read) = 64 (total RevlogNG record size) } else { - res.add(new IndexEntry(offset, compressedLen)); - di.skipBytes(5*4 + 32); + res.add(new IndexEntry(offset, baseRevision)); + di.skipBytes(3*4 + 32); } long l = di.readLong(); offset = l >>> 16; @@ -241,13 +247,15 @@ // perhaps, package-local or protected, if anyone else from low-level needs them + // XXX think over if we should keep offset in case of separate data file - we read the field anyway. Perhaps, distinct entry classes for Inline and non-inline indexes? private static class IndexEntry { public final long offset; // for separate .i and .d - copy of index record entry, for inline index - actual offset of the record in the .i file (record entry + revision * record size)) - public final int length; // data past fixed record (need to decide whether including header size or not), and whether length is of compressed data or not + //public final int length; // data past fixed record (need to decide whether including header size or not), and whether length is of compressed data or not + public final int baseRevision; - public IndexEntry(long o, int l) { + public IndexEntry(long o, int baseRev) { offset = o; - length = l; + baseRevision = baseRev; } }