Mercurial > jhg
diff src/org/tmatesoft/hg/internal/MergeStateBuilder.java @ 707:42b88709e41d
Merge: support 'unresolved' resolution with MergeStateBuilder
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 16 Aug 2013 19:22:59 +0200 |
parents | b4242b7e7dfe |
children |
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/internal/MergeStateBuilder.java Fri Aug 16 14:54:09 2013 +0200 +++ b/src/org/tmatesoft/hg/internal/MergeStateBuilder.java Fri Aug 16 19:22:59 2013 +0200 @@ -16,9 +16,24 @@ */ package org.tmatesoft.hg.internal; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.OutputStream; +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; + +import org.tmatesoft.hg.core.HgFileRevision; import org.tmatesoft.hg.core.HgIOException; +import org.tmatesoft.hg.core.Nodeid; +import org.tmatesoft.hg.repo.HgManifest; import org.tmatesoft.hg.repo.HgMergeState; +import org.tmatesoft.hg.repo.HgRepositoryFiles; +import org.tmatesoft.hg.util.ByteChannel; +import org.tmatesoft.hg.util.CancelledException; import org.tmatesoft.hg.util.Path; +import org.tmatesoft.hg.util.LogFacility.Severity; /** * Constructs merge/state file @@ -28,21 +43,139 @@ * @author TMate Software Ltd. */ public class MergeStateBuilder { - + private final Internals repo; + private final List<Record> unresolved = new ArrayList<Record>(); + private Nodeid stateParent = Nodeid.NULL; public MergeStateBuilder(Internals implRepo) { repo = implRepo; } + public void prepare(Nodeid nodeid) { + assert nodeid != null; + unresolved.clear(); + stateParent = nodeid; + abandon(); + } + public void resolved() { throw Internals.notImplemented(); } - public void unresolved(Path file) { - throw Internals.notImplemented(); + public void unresolved(Path file, HgFileRevision first, HgFileRevision second, HgFileRevision base, HgManifest.Flags flags) throws HgIOException { + Record r = new Record(file, first.getPath(), second.getPath(), base.getPath(), base.getRevision(), flags); + final File d = mergeStateDir(); + d.mkdirs(); + File f = new File(d, r.hash()); + try { + FileOutputStream fos = new FileOutputStream(f); + first.putContentTo(new OutputStreamSink(fos)); + fos.flush(); + fos.close(); + unresolved.add(r); + } catch (IOException ex) { + throw new HgIOException(String.format("Failed to write content of unresolved file %s to merge state at %s", file, f), f); + } catch (CancelledException ex) { + repo.getLog().dump(getClass(), Severity.Error, ex, "Our impl doesn't throw cancellation"); + } } - public void serialize(Transaction tr) throws HgIOException { + // merge/state serialization is not a part of a transaction + public void serialize() throws HgIOException { + if (unresolved.isEmpty()) { + return; + } + File mergeStateFile = repo.getRepositoryFile(HgRepositoryFiles.MergeState); + try { + final byte NL = '\n'; + FileOutputStream fos = new FileOutputStream(mergeStateFile); + fos.write(stateParent.toString().getBytes()); + fos.write(NL); + for(Record r : unresolved) { + fos.write(r.key.toString().getBytes()); + fos.write(0); + fos.write('u'); + fos.write(0); + fos.write(r.hash().toString().getBytes()); + fos.write(0); + fos.write(r.fnameA.toString().getBytes()); + fos.write(0); + fos.write(r.fnameAncestor.toString().getBytes()); + fos.write(0); + fos.write(r.ancestorRev.toString().getBytes()); + fos.write(0); + fos.write(r.fnameB.toString().getBytes()); + fos.write(0); + fos.write(r.flags.mercurialString().getBytes()); + fos.write(NL); + } + fos.flush(); + fos.close(); + } catch (IOException ex) { + throw new HgIOException("Failed to serialize merge state", mergeStateFile); + } + } + + public void abandon() { + File mergeStateDir = mergeStateDir(); + try { + FileUtils.rmdir(mergeStateDir); + } catch (IOException ex) { + // ignore almost silently + repo.getLog().dump(getClass(), Severity.Warn, ex, String.format("Failed to delete merge state in %s", mergeStateDir)); + } + } + + private File mergeStateDir() { + return repo.getRepositoryFile(HgRepositoryFiles.MergeState).getParentFile(); + } + + private static class Record { + public final Path key; + public final Path fnameA, fnameB, fnameAncestor; + public final Nodeid ancestorRev; + public final HgManifest.Flags flags; + private String hash; + + public Record(Path fname, Path a, Path b, Path ancestor, Nodeid rev, HgManifest.Flags f) { + key = fname; + fnameA = a; + fnameB = b; + fnameAncestor = ancestor; + ancestorRev = rev; + flags = f; + } + + public String hash() { + if (hash == null) { + hash = new DigestHelper().sha1(key).asHexString(); + } + return hash; + } + } + + private static class OutputStreamSink implements ByteChannel { + private final OutputStream out; + + public OutputStreamSink(OutputStream outputStream) { + out = outputStream; + } + + public int write(ByteBuffer buffer) throws IOException { + final int toWrite = buffer.remaining(); + if (toWrite <= 0) { + return 0; + } + if (buffer.hasArray()) { + out.write(buffer.array(), buffer.arrayOffset(), toWrite); + } else { + while (buffer.hasRemaining()) { + out.write(buffer.get()); + } + } + buffer.position(buffer.limit()); + return toWrite; + } } }