diff src/org/tmatesoft/hg/internal/RevlogStream.java @ 539:9edfd5a223b8

Commit: handle empty repository case
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 13 Feb 2013 18:44:58 +0100
parents dd4f6311af52
children 6ca3d0c5b4bc
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/RevlogStream.java	Tue Feb 05 22:30:21 2013 +0100
+++ b/src/org/tmatesoft/hg/internal/RevlogStream.java	Wed Feb 13 18:44:58 2013 +0100
@@ -224,6 +224,10 @@
 		return BAD_REVISION;
 	}
 	
+	/**
+	 * @return value suitable for the corresponding field in the new revision's header, not physical offset in the file 
+	 * (which is different in case of inline revlogs)
+	 */
 	public long newEntryOffset() {
 		if (revisionCount() == 0) {
 			return 0;
@@ -324,6 +328,31 @@
 		}
 	}
 
+	void revisionAdded(int revisionIndex, Nodeid revision, int baseRevisionIndex, long revisionOffset) throws HgInvalidControlFileException {
+		if (!outlineCached()) {
+			return;
+		}
+		if (baseRevisions.length != revisionIndex) {
+			throw new HgInvalidControlFileException(String.format("New entry's index shall be %d, not %d", baseRevisions.length, revisionIndex), null, indexFile);
+		}
+		if (baseRevisionIndex < 0 || baseRevisionIndex > baseRevisions.length) {
+			// baseRevisionIndex MAY be == to baseRevisions.length, it's when new revision is based on itself
+			throw new HgInvalidControlFileException(String.format("Base revision index %d doesn't fit [0..%d] range", baseRevisionIndex, baseRevisions.length), null, indexFile);
+		}
+		assert revision != null;
+		assert !revision.isNull();
+		int[] baseRevisionsCopy = new int[baseRevisions.length + 1];
+		System.arraycopy(baseRevisions, 0, baseRevisionsCopy, 0, baseRevisions.length);
+		baseRevisionsCopy[baseRevisions.length] = baseRevisionIndex;
+		baseRevisions = baseRevisionsCopy;
+		if (inline && indexRecordOffset != null) {
+			assert indexRecordOffset.length == revisionIndex;
+			int[] indexRecordOffsetCopy = new int[indexRecordOffset.length + 1];
+			indexRecordOffsetCopy[indexRecordOffset.length] = offsetFieldToInlineFileOffset(revisionOffset, revisionIndex);
+			indexRecordOffset = indexRecordOffsetCopy;
+		}
+	}
+	
 	private int getBaseRevision(int revision) {
 		return baseRevisions[revision];
 	}
@@ -347,9 +376,25 @@
 		}
 		return revisionIndex;
 	}
+	
+	private boolean outlineCached() {
+		return baseRevisions != null && baseRevisions.length > 0;
+	}
+	
+	// translate 6-byte offset field value to pysical file offset for inline revlogs
+	// DOESN'T MAKE SENSE if revlog with data is separate
+	private static int offsetFieldToInlineFileOffset(long offset, int recordIndex) throws HgInvalidStateException {
+		int o = Internals.ltoi(offset);
+		if (o != offset) {
+			// just in case, can't happen, ever, unless HG (or some other bad tool) produces index file 
+			// with inlined data of size greater than 2 Gb.
+			throw new HgInvalidStateException("Data too big, offset didn't fit to sizeof(int)");
+		}
+		return o + REVLOGV1_RECORD_SIZE * recordIndex;
+	}
 
 	private void initOutline() throws HgInvalidControlFileException {
-		if (baseRevisions != null && baseRevisions.length > 0) {
+		if (outlineCached()) {
 			return;
 		}
 		DataAccess da = getIndexStream();
@@ -357,6 +402,8 @@
 			if (da.isEmpty()) {
 				// do not fail with exception if stream is empty, it's likely intentional
 				baseRevisions = new int[0];
+				// empty revlog, likely to be populated, indicate we start with a single file
+				inline = true;
 				return;
 			}
 			int versionField = da.readInt();
@@ -385,13 +432,8 @@
 //				byte[] nodeid = new byte[32];
 				resBases.add(baseRevision);
 				if (inline) {
-					int o = Internals.ltoi(offset);
-					if (o != offset) {
-						// just in case, can't happen, ever, unless HG (or some other bad tool) produces index file 
-						// with inlined data of size greater than 2 Gb.
-						throw new HgInvalidStateException("Data too big, offset didn't fit to sizeof(int)");
-					}
-					resOffsets.add(o + REVLOGV1_RECORD_SIZE * resOffsets.size());
+					int o = offsetFieldToInlineFileOffset(offset, resOffsets.size());
+					resOffsets.add(o);
 					da.skip(3*4 + 32 + compressedLen); // Check: 44 (skip) + 20 (read) = 64 (total RevlogNG record size)
 				} else {
 					da.skip(3*4 + 32);
@@ -611,4 +653,5 @@
 		// implementers shall not invoke DataAccess.done(), it's accomplished by #iterate at appropraite moment
 		void next(int revisionIndex, int actualLen, int baseRevision, int linkRevision, int parent1Revision, int parent2Revision, byte[/*20*/] nodeid, DataAccess data);
 	}
+
 }