diff src/org/tmatesoft/hg/internal/CommitFacility.java @ 708:4ffc17c0b534

Merge: tests for resolver and complex scenario. Enable commit for merged revisions. Reuse file revisions if nothing changed
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 20 Aug 2013 18:41:34 +0200
parents b4242b7e7dfe
children
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/CommitFacility.java	Fri Aug 16 19:22:59 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/CommitFacility.java	Tue Aug 20 18:41:34 2013 +0200
@@ -19,14 +19,13 @@
 import static org.tmatesoft.hg.repo.HgRepository.DEFAULT_BRANCH_NAME;
 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
 import static org.tmatesoft.hg.repo.HgRepositoryFiles.*;
-import static org.tmatesoft.hg.repo.HgRepositoryFiles.Branch;
-import static org.tmatesoft.hg.repo.HgRepositoryFiles.UndoBranch;
 import static org.tmatesoft.hg.util.LogFacility.Severity.Error;
 
 import java.io.File;
 import java.io.FileOutputStream;
 import java.io.FileWriter;
 import java.io.IOException;
+import java.nio.ByteBuffer;
 import java.util.ArrayList;
 import java.util.HashMap;
 import java.util.LinkedHashMap;
@@ -39,11 +38,14 @@
 import org.tmatesoft.hg.core.HgIOException;
 import org.tmatesoft.hg.core.HgRepositoryLockException;
 import org.tmatesoft.hg.core.Nodeid;
+import org.tmatesoft.hg.internal.DataSerializer.ByteArraySerializer;
 import org.tmatesoft.hg.internal.DataSerializer.DataSource;
 import org.tmatesoft.hg.repo.HgChangelog;
 import org.tmatesoft.hg.repo.HgDataFile;
 import org.tmatesoft.hg.repo.HgPhase;
 import org.tmatesoft.hg.repo.HgRuntimeException;
+import org.tmatesoft.hg.util.ByteChannel;
+import org.tmatesoft.hg.util.CancelledException;
 import org.tmatesoft.hg.util.Pair;
 import org.tmatesoft.hg.util.Path;
 
@@ -149,19 +151,42 @@
 			DataSource bds = e.second();
 			Pair<Integer, Integer> fp = fileParents.get(df.getPath());
 			if (fp == null) {
-				// NEW FILE
-				fp = new Pair<Integer, Integer>(NO_REVISION, NO_REVISION);
+				// NEW FILE, either just added or resurrected from p2
+				Nodeid fileRevInP2;
+				if ((fileRevInP2 = c2Manifest.nodeid(df.getPath())) != null) {
+					fp = new Pair<Integer, Integer>(df.getRevisionIndex(fileRevInP2), NO_REVISION);
+				} else {
+					// brand new
+					fp = new Pair<Integer, Integer>(NO_REVISION, NO_REVISION);
+				}
 			}
-			RevlogStream contentStream = repo.getImplAccess().getStream(df);
+			// TODO if fp.first() != NO_REVISION and fp.second() != NO_REVISION check if one
+			// revision is ancestor of another and use the latest as p1, then
+			Nodeid fileRev = null;
 			final boolean isNewFile = !df.exists();
-			RevlogStreamWriter fileWriter = new RevlogStreamWriter(repo, contentStream, transaction);
-			Nodeid fileRev = fileWriter.addRevision(bds, clogRevisionIndex, fp.first(), fp.second()).second();
+			if (fp.first() != NO_REVISION && fp.second() == NO_REVISION && !isNewFile) {
+				// compare file contents to see if anything has changed, and reuse old revision, if unchanged.
+				// XXX ineffective, need better access to revision conten
+				ByteArraySerializer bas = new ByteArraySerializer();
+				bds.serialize(bas);
+				final byte[] newContent = bas.toByteArray();
+				// unless there's a way to reset DataSource, replace it with the content just read
+				bds = new DataSerializer.ByteArrayDataSource(newContent);
+				if (new ComparatorChannel(newContent).same(df, fp.first())) {
+					fileRev = df.getRevision(fp.first());
+				}
+			}
+			if (fileRev == null) {
+				RevlogStream contentStream = repo.getImplAccess().getStream(df);
+				RevlogStreamWriter fileWriter = new RevlogStreamWriter(repo, contentStream, transaction);
+				fileRev = fileWriter.addRevision(bds, clogRevisionIndex, fp.first(), fp.second()).second();
+				if (isNewFile) {
+					// registerNew shall go after fileWriter.addRevision as it needs to know if data is inlined or not
+					fncache.registerNew(df.getPath(), contentStream);
+				}
+			}
 			newManifestRevision.put(df.getPath(), fileRev);
 			touchInDirstate.add(df.getPath());
-			if (isNewFile) {
-				// registerNew shall go after fileWriter.addRevision as it needs to know if data is inlined or not
-				fncache.registerNew(df.getPath(), contentStream);
-			}
 		}
 		//
 		final EncodingHelper encHelper = repo.buildFileNameEncodingHelper();
@@ -251,6 +276,39 @@
 			new FileUtils(repo.getLog(), this).closeQuietly(w, lastMessage);
 		}
 	}
+	
+	private static class ComparatorChannel implements ByteChannel {
+		private int index;
+		private final byte[] content;
+
+		public ComparatorChannel(byte[] contentToCompare) {
+			content = contentToCompare;
+		}
+
+		public int write(ByteBuffer buffer) throws IOException, CancelledException {
+			int consumed = 0;
+			while (buffer.hasRemaining()) {
+				byte b = buffer.get();
+				consumed++;
+				if (content[index++] != b) {
+					throw new CancelledException();
+				}
+			}
+			return consumed;
+		}
+		
+		public boolean same(HgDataFile df, int fileRevIndex) {
+			index = 0;
+			try {
+				df.contentWithFilters(fileRevIndex, this);
+				return index == content.length;
+			} catch (CancelledException ex) {
+				// comparison failed, content differs, ok to go on
+			}
+			return false;
+		}
+	}
+
 /*
 	private Pair<Integer, Integer> getManifestParents() {
 		return new Pair<Integer, Integer>(extractManifestRevisionIndex(p1Commit), extractManifestRevisionIndex(p2Commit));