# HG changeset patch # User Artem Tikhomirov # Date 1373477631 -7200 # Node ID ae2d439fbed3bd76515b145f5518ee35a3749a59 # Parent 46b56864b483b5f64f7a20e4be3326193ea33b22 Utilize transaction when writing fncache. Better HgIOException diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/core/HgCloneCommand.java --- 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 { diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/core/HgIOException.java --- 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 null * @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 null + * @param troubleFile file we tried to deal with, or null if set later */ public HgIOException(String message, Throwable cause, File troubleFile) { super(message, cause); @@ -51,4 +51,12 @@ public File getFile() { return file; } + + /** + * @return this for convenience + */ + public HgIOException setFile(File f) { + file = f; + return this; + } } diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/core/HgPullCommand.java --- 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 ' 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; diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/core/HgPushCommand.java --- 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 ' counterpart, send local changes to a remote server * + * @since 1.2 * @author Artem Tikhomirov * @author TMate Software Ltd. */ diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/internal/AddRevInspector.java --- 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(); } diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/internal/CommitFacility.java --- 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 newlyAddedFiles = new LinkedHashMap(); + FNCacheFile.Mediator fncache = new FNCacheFile.Mediator(repo, transaction); ArrayList touchInDirstate = new ArrayList(); for (Pair e : files.values()) { HgDataFile df = e.first(); @@ -153,13 +153,15 @@ fp = new Pair(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)) { diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/internal/FNCacheFile.java --- 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); } } } diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/internal/RevlogStream.java --- 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 diff -r 46b56864b483 -r ae2d439fbed3 src/org/tmatesoft/hg/internal/RevlogStreamWriter.java --- 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; }