changeset 664:ae2d439fbed3

Utilize transaction when writing fncache. Better HgIOException
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Wed, 10 Jul 2013 19:33:51 +0200
parents 46b56864b483
children dde18bc7053b
files src/org/tmatesoft/hg/core/HgCloneCommand.java src/org/tmatesoft/hg/core/HgIOException.java src/org/tmatesoft/hg/core/HgPullCommand.java src/org/tmatesoft/hg/core/HgPushCommand.java src/org/tmatesoft/hg/internal/AddRevInspector.java src/org/tmatesoft/hg/internal/CommitFacility.java src/org/tmatesoft/hg/internal/FNCacheFile.java src/org/tmatesoft/hg/internal/RevlogStream.java src/org/tmatesoft/hg/internal/RevlogStreamWriter.java
diffstat 9 files changed, 65 insertions(+), 51 deletions(-) [+]
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/core/HgCloneCommand.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/core/HgCloneCommand.java	Wed Jul 10 19:33:51 2013 +0200
@@ -38,10 +38,10 @@
 import org.tmatesoft.hg.internal.RepoInitializer;
 import org.tmatesoft.hg.internal.RevlogCompressor;
 import org.tmatesoft.hg.internal.RevlogStreamWriter;
+import org.tmatesoft.hg.internal.Transaction;
 import org.tmatesoft.hg.repo.HgBundle;
 import org.tmatesoft.hg.repo.HgBundle.GroupElement;
 import org.tmatesoft.hg.repo.HgInvalidControlFileException;
-import org.tmatesoft.hg.repo.HgInvalidFileException;
 import org.tmatesoft.hg.repo.HgInvalidStateException;
 import org.tmatesoft.hg.repo.HgLookup;
 import org.tmatesoft.hg.repo.HgRemoteRepository;
@@ -126,8 +126,6 @@
 				completeChanges.inspectAll(mate);
 				mate.checkFailure();
 				mate.complete();
-			} catch (IOException ex) {
-				throw new HgInvalidFileException(getClass().getName(), ex);
 			} finally {
 				completeChanges.unlink();
 				progress.done();
@@ -189,8 +187,8 @@
 			fncacheFile = new FNCacheFile(Internals.getInstance(new HgLookup(ctx).detect(hgDir)));
 		}
 
-		public void complete() throws IOException {
-			fncacheFile.write();
+		public void complete() throws HgIOException {
+			fncacheFile.write(new Transaction.NoRollback());
 		}
 
 		public void changelogStart() throws HgInvalidControlFileException {
--- a/src/org/tmatesoft/hg/core/HgIOException.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/core/HgIOException.java	Wed Jul 10 19:33:51 2013 +0200
@@ -29,7 +29,7 @@
  */
 @SuppressWarnings("serial")
 public class HgIOException extends HgException {
-	private final File file;
+	private File file;
 
 	public HgIOException(String message, File troubleFile) {
 		this(message, null, troubleFile);
@@ -38,7 +38,7 @@
 	/**
 	 * @param message describes the issue, never <code>null</code>
 	 * @param cause root cause for the error, likely {@link IOException} or its subclass, but not necessarily, and may be omitted. 
-	 * @param troubleFile file we tried to deal with, never <code>null</code>
+	 * @param troubleFile file we tried to deal with, or <code>null</code> if set later
 	 */
 	public HgIOException(String message, Throwable cause, File troubleFile) {
 		super(message, cause);
@@ -51,4 +51,12 @@
 	public File getFile() {
 		return file;
 	}
+
+	/**
+	 * @return <code>this</code> for convenience
+	 */
+	public HgIOException setFile(File f) {
+		file = f;
+		return this;
+	}
 }
--- a/src/org/tmatesoft/hg/core/HgPullCommand.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/core/HgPullCommand.java	Wed Jul 10 19:33:51 2013 +0200
@@ -16,7 +16,6 @@
  */
 package org.tmatesoft.hg.core;
 
-import java.io.IOException;
 import java.util.Collection;
 import java.util.Collections;
 import java.util.List;
@@ -39,7 +38,9 @@
 import org.tmatesoft.hg.util.ProgressSupport;
 
 /**
+ * 'hg pull <remote>' counterpart, get remote changes to local repository
  * 
+ * @since 1.2
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
@@ -85,9 +86,9 @@
 			} catch (HgRuntimeException ex) {
 				tr.rollback();
 				throw ex;
-			} catch (IOException ex) {
+			} catch (HgIOException ex) {
 				tr.rollback();
-				throw new HgIOException(ex.getMessage(), ex, null); // FIXME throw HgIOException right away
+				throw ex;
 			} catch (RuntimeException ex) {
 				tr.rollback();
 				throw ex;
--- a/src/org/tmatesoft/hg/core/HgPushCommand.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/core/HgPushCommand.java	Wed Jul 10 19:33:51 2013 +0200
@@ -45,7 +45,9 @@
 import org.tmatesoft.hg.util.ProgressSupport;
 
 /**
+ * 'hg push <remote>' counterpart, send local changes to a remote server
  * 
+ * @since 1.2
  * @author Artem Tikhomirov
  * @author TMate Software Ltd.
  */
--- a/src/org/tmatesoft/hg/internal/AddRevInspector.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/AddRevInspector.java	Wed Jul 10 19:33:51 2013 +0200
@@ -16,7 +16,6 @@
  */
 package org.tmatesoft.hg.internal;
 
-import java.io.IOException;
 import java.util.HashMap;
 import java.util.Set;
 
@@ -50,7 +49,7 @@
 	public AddRevInspector(Internals implRepo, Transaction transaction) {
 		repo = implRepo;
 		tr = transaction;
-		fncache = new FNCacheFile.Mediator(implRepo);
+		fncache = new FNCacheFile.Mediator(implRepo, transaction);
 	}
 
 	public void changelogStart() throws HgRuntimeException {
@@ -114,7 +113,7 @@
 		return new RevisionSet(added);
 	}
 	
-	public void done() throws IOException {
+	public void done() throws HgIOException {
 		fncache.complete();
 	}
 
--- a/src/org/tmatesoft/hg/internal/CommitFacility.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/CommitFacility.java	Wed Jul 10 19:33:51 2013 +0200
@@ -142,7 +142,7 @@
 		saveCommitMessage(message);
 		//
 		// Register new/changed
-		LinkedHashMap<Path, RevlogStream> newlyAddedFiles = new LinkedHashMap<Path, RevlogStream>();
+		FNCacheFile.Mediator fncache = new FNCacheFile.Mediator(repo, transaction);
 		ArrayList<Path> touchInDirstate = new ArrayList<Path>();
 		for (Pair<HgDataFile, DataSource> e : files.values()) {
 			HgDataFile df = e.first();
@@ -153,13 +153,15 @@
 				fp = new Pair<Integer, Integer>(NO_REVISION, NO_REVISION);
 			}
 			RevlogStream contentStream = repo.getImplAccess().getStream(df);
-			if (!df.exists()) {
-				newlyAddedFiles.put(df.getPath(), contentStream);
-			}
+			final boolean isNewFile = !df.exists();
 			RevlogStreamWriter fileWriter = new RevlogStreamWriter(repo, contentStream, transaction);
 			Nodeid fileRev = fileWriter.addRevision(bds, clogRevisionIndex, fp.first(), fp.second()).second();
 			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);
+			}
 		}
 		//
 		// Manifest
@@ -178,22 +180,8 @@
 		changelogBuilder.manifest(manifestRev).comment(message);
 		RevlogStreamWriter changelogWriter = new RevlogStreamWriter(repo, repo.getImplAccess().getChangelogStream(), transaction);
 		Nodeid changesetRev = changelogWriter.addRevision(changelogBuilder, clogRevisionIndex, p1Commit, p2Commit).second();
-		// TODO move fncache update to an external facility, along with dirstate and bookmark update
-		if (!newlyAddedFiles.isEmpty() && repo.fncacheInUse()) {
-			FNCacheFile fncache = new FNCacheFile(repo);
-			for (Path p : newlyAddedFiles.keySet()) {
-				fncache.addIndex(p);
-				if (!newlyAddedFiles.get(p).isInlineData()) {
-					fncache.addData(p);
-				}
-			}
-			try {
-				fncache.write();
-			} catch (IOException ex) {
-				// see comment above for fnchache.read()
-				repo.getLog().dump(getClass(), Error, ex, "Failed to write fncache, error ignored");
-			}
-		}
+		// TODO move dirstate and bookmark update update to an external facility 
+		fncache.complete();
 		String oldBranchValue = DirstateReader.readBranch(repo);
 		String newBranchValue = branch == null ? DEFAULT_BRANCH_NAME : branch;
 		if (!oldBranchValue.equals(newBranchValue)) {
--- a/src/org/tmatesoft/hg/internal/FNCacheFile.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/FNCacheFile.java	Wed Jul 10 19:33:51 2013 +0200
@@ -28,6 +28,7 @@
 import java.util.ArrayList;
 import java.util.List;
 
+import org.tmatesoft.hg.core.HgIOException;
 import org.tmatesoft.hg.util.Path;
 
 /**
@@ -76,7 +77,7 @@
 	}
 	*/
 	
-	public void write() throws IOException { // FIXME transaction! and HgIOException
+	public void write(Transaction tr) throws HgIOException {
 		if (addedDotI.isEmpty() && addedDotD.isEmpty()) {
 			return;
 		}
@@ -97,14 +98,26 @@
 			cb.flip();
 			added.add(cb);
 		}
-		FileChannel fncacheFile = new FileOutputStream(f, true).getChannel();
-		ByteBuffer lf = ByteBuffer.wrap(new byte[] { 0x0A });
-		for (CharBuffer b : added) {
-			fncacheFile.write(filenameEncoding.encode(b));
-			fncacheFile.write(lf);
-			lf.rewind();
+		FileOutputStream fos = null;
+		f = tr.prepare(f);
+		try {
+			fos = new FileOutputStream(f, true);
+			FileChannel fncacheFile = fos.getChannel();
+			ByteBuffer lf = ByteBuffer.wrap(new byte[] { 0x0A });
+			for (CharBuffer b : added) {
+				fncacheFile.write(filenameEncoding.encode(b));
+				fncacheFile.write(lf);
+				lf.rewind();
+			}
+			fncacheFile.force(true);
+			tr.done(f);
+		} catch (IOException ex) {
+			tr.failure(f, ex);
+			throw new HgIOException("Failed to write fncache", ex, f);
+		} finally {
+			new FileUtils(repo.getLog(), this).closeQuietly(fos, f);
 		}
-		fncacheFile.close();
+		
 	}
 
 	public void addIndex(Path p) {
@@ -121,9 +134,11 @@
 	public static class Mediator {
 		private final Internals repo;
 		private FNCacheFile fncache;
+		private final Transaction tr;
 
-		public Mediator(Internals internalRepo) {
+		public Mediator(Internals internalRepo, Transaction transaction) {
 			repo = internalRepo;
+			tr = transaction;
 		}
 		
 		public void registerNew(Path f, RevlogStream rs) {
@@ -138,9 +153,9 @@
 			}
 		}
 		
-		public void complete() throws IOException {
+		public void complete() throws HgIOException {
 			if (fncache != null) {
-				fncache.write();
+				fncache.write(tr);
 			}
 		}
 	}
--- a/src/org/tmatesoft/hg/internal/RevlogStream.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/RevlogStream.java	Wed Jul 10 19:33:51 2013 +0200
@@ -136,6 +136,10 @@
 		return ex.setFile(indexFile);
 	}
 
+	public HgIOException initWithIndexFile(HgIOException ex) {
+		return ex.setFile(indexFile);
+	}
+
 	// initialize exception with the file where revlog data comes from
 	public HgInvalidControlFileException initWithDataFile(HgInvalidControlFileException ex) {
 		// exceptions are usually raised after read attepmt, hence inline shall be initialized
--- a/src/org/tmatesoft/hg/internal/RevlogStreamWriter.java	Wed Jul 10 16:41:49 2013 +0200
+++ b/src/org/tmatesoft/hg/internal/RevlogStreamWriter.java	Wed Jul 10 19:33:51 2013 +0200
@@ -20,7 +20,6 @@
 import static org.tmatesoft.hg.repo.HgRepository.BAD_REVISION;
 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION;
 
-import java.io.File;
 import java.io.IOException;
 import java.nio.ByteBuffer;
 
@@ -32,6 +31,7 @@
 import org.tmatesoft.hg.internal.DataSerializer.DataSource;
 import org.tmatesoft.hg.repo.HgBundle.GroupElement;
 import org.tmatesoft.hg.repo.HgInvalidControlFileException;
+import org.tmatesoft.hg.repo.HgInvalidDataFormatException;
 import org.tmatesoft.hg.repo.HgInvalidRevisionException;
 import org.tmatesoft.hg.repo.HgInvalidStateException;
 import org.tmatesoft.hg.repo.HgRepository;
@@ -126,9 +126,8 @@
 				revLen = complete.length;
 			} catch (IOException ex) {
 				// unlikely to happen, as ByteArrayDataSource throws IOException only in case of programming errors
-				// FIXME next approach to get indexFile is awful:
-				File indexFile = revlogStream.initWithIndexFile(new HgInvalidControlFileException("", ex, null)).getFile();
-				throw new HgIOException("Failed to reconstruct revision", ex, indexFile);
+				// hence, throwing rt exception here makes more sense here than HgIOException (even that latter is in throws)
+				throw new HgInvalidDataFormatException("Failed to reconstruct revision", ex);
 			}
 		}
 		doAdd(nodeRev, p1, p2, linkRev, baseRev, revLen, ds);
@@ -264,7 +263,7 @@
 		lastEntryIndex = revCount == 0 ? NO_REVISION : revCount - 1;
 	}
 	
-	private void populateLastEntryContent() throws HgRuntimeException {
+	private void populateLastEntryContent() throws HgIOException, HgRuntimeException {
 		if (lastFullContent != null && lastFullContent.first() == lastEntryIndex) {
 			// we have last entry cached
 			return;
@@ -401,12 +400,12 @@
 		public byte[] content;
 		private IOException failure;
 		
-		public ReadContentInspector read(RevlogStream rs, int revIndex) throws HgInvalidControlFileException {
+		public ReadContentInspector read(RevlogStream rs, int revIndex) throws HgIOException, HgRuntimeException {
 			assert revIndex >= 0;
 			rs.iterate(revIndex, revIndex, true, this);
 			if (failure != null) {
 				String m = String.format("Failed to get content of revision %d", revIndex);
-				throw rs.initWithDataFile(new HgInvalidControlFileException(m, failure, null));
+				throw rs.initWithIndexFile(new HgIOException(m, failure, null));
 			}
 			return this;
 		}