Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/DirstateBuilder.java @ 526:2f9ed6bcefa2
Initial support for Revert command with accompanying minor refactoring
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> | 
|---|---|
| date | Tue, 15 Jan 2013 17:07:19 +0100 | 
| parents | 0be5be8d57e9 | 
| children | 95bdcf75e71e | 
   comparison
  equal
  deleted
  inserted
  replaced
| 525:0be5be8d57e9 | 526:2f9ed6bcefa2 | 
|---|---|
| 14 * the terms of a license other than GNU General Public License | 14 * the terms of a license other than GNU General Public License | 
| 15 * contact TMate Software at support@hg4j.com | 15 * contact TMate Software at support@hg4j.com | 
| 16 */ | 16 */ | 
| 17 package org.tmatesoft.hg.internal; | 17 package org.tmatesoft.hg.internal; | 
| 18 | 18 | 
| 19 import java.io.File; | |
| 20 import java.io.FileOutputStream; | |
| 19 import java.io.IOException; | 21 import java.io.IOException; | 
| 20 import java.nio.ByteBuffer; | 22 import java.nio.ByteBuffer; | 
| 23 import java.nio.channels.FileChannel; | |
| 21 import java.nio.channels.WritableByteChannel; | 24 import java.nio.channels.WritableByteChannel; | 
| 22 import java.util.ArrayList; | 25 import java.util.Map; | 
| 23 import java.util.List; | 26 import java.util.TreeMap; | 
| 24 | 27 | 
| 28 import org.tmatesoft.hg.core.HgIOException; | |
| 25 import org.tmatesoft.hg.core.Nodeid; | 29 import org.tmatesoft.hg.core.Nodeid; | 
| 26 import org.tmatesoft.hg.repo.HgDirstate; | 30 import org.tmatesoft.hg.repo.HgDirstate; | 
| 31 import org.tmatesoft.hg.repo.HgDirstate.EntryKind; | |
| 32 import org.tmatesoft.hg.repo.HgDirstate.Record; | |
| 33 import org.tmatesoft.hg.repo.HgInvalidStateException; | |
| 27 import org.tmatesoft.hg.repo.HgManifest.Flags; | 34 import org.tmatesoft.hg.repo.HgManifest.Flags; | 
| 35 import org.tmatesoft.hg.repo.HgRepositoryFiles; | |
| 28 import org.tmatesoft.hg.util.Path; | 36 import org.tmatesoft.hg.util.Path; | 
| 29 | 37 | 
| 30 /** | 38 /** | 
| 31 * Facility to build a dirstate file as described in {@linkplain http://mercurial.selenic.com/wiki/DirState} | 39 * Facility to build a dirstate file as described in {@linkplain http://mercurial.selenic.com/wiki/DirState} | 
| 32 * | 40 * | 
| 34 * @see HgDirstate | 42 * @see HgDirstate | 
| 35 * @author Artem Tikhomirov | 43 * @author Artem Tikhomirov | 
| 36 * @author TMate Software Ltd. | 44 * @author TMate Software Ltd. | 
| 37 */ | 45 */ | 
| 38 public class DirstateBuilder { | 46 public class DirstateBuilder { | 
| 39 private List<HgDirstate.Record> normal = new ArrayList<HgDirstate.Record>(); | 47 private Map<Path, HgDirstate.Record> normal = new TreeMap<Path, HgDirstate.Record>(); | 
| 48 private Map<Path, HgDirstate.Record> added = new TreeMap<Path, HgDirstate.Record>(); | |
| 49 private Map<Path, HgDirstate.Record> removed = new TreeMap<Path, HgDirstate.Record>(); | |
| 50 private Map<Path, HgDirstate.Record> merged = new TreeMap<Path, HgDirstate.Record>(); | |
| 40 private Nodeid parent1, parent2; | 51 private Nodeid parent1, parent2; | 
| 52 private final Internals hgRepo; | |
| 41 private final EncodingHelper encodingHelper; | 53 private final EncodingHelper encodingHelper; | 
| 42 | 54 | 
| 43 public DirstateBuilder(EncodingHelper encHelper) { | 55 public DirstateBuilder(Internals internalRepo) { | 
| 44 encodingHelper = encHelper; | 56 hgRepo = internalRepo; | 
| 57 encodingHelper = internalRepo.buildFileNameEncodingHelper(); | |
| 45 } | 58 } | 
| 46 | 59 | 
| 47 public void parents(Nodeid p1, Nodeid p2) { | 60 public void parents(Nodeid p1, Nodeid p2) { | 
| 48 parent1 = p1 == null ? Nodeid.NULL : p1; | 61 parent1 = p1 == null ? Nodeid.NULL : p1; | 
| 49 parent2 = p2 == null ? Nodeid.NULL : p2; | 62 parent2 = p2 == null ? Nodeid.NULL : p2; | 
| 58 | 71 | 
| 59 // However, as long as we use this class to write clean copies of the files, we can put all the fields | 72 // However, as long as we use this class to write clean copies of the files, we can put all the fields | 
| 60 // right away. | 73 // right away. | 
| 61 int fmode = flags == Flags.RegularFile ? 0666 : 0777; // FIXME actual unix flags | 74 int fmode = flags == Flags.RegularFile ? 0666 : 0777; // FIXME actual unix flags | 
| 62 int mtime = (int) (System.currentTimeMillis() / 1000); | 75 int mtime = (int) (System.currentTimeMillis() / 1000); | 
| 63 normal.add(new HgDirstate.Record(fmode, bytesWritten, mtime,fname, null)); | 76 forget(fname); | 
| 64 | 77 normal.put(fname, new HgDirstate.Record(fmode, bytesWritten, mtime, fname, null)); | 
| 78 } | |
| 79 | |
| 80 public void recordUncertain(Path fname) { | |
| 81 // `hg revert` puts "n 0 -1 unset" for the reverted file, so shall we | |
| 82 forget(fname); | |
| 83 normal.put(fname, new HgDirstate.Record(0, -1, -1, fname, null)); | |
| 84 } | |
| 85 | |
| 86 private void forget(Path fname) { | |
| 87 normal.remove(fname); | |
| 88 added.remove(fname); | |
| 89 removed.remove(fname); | |
| 90 merged.remove(fname); | |
| 65 } | 91 } | 
| 66 | 92 | 
| 67 public void serialize(WritableByteChannel dest) throws IOException { | 93 public void serialize(WritableByteChannel dest) throws IOException { | 
| 68 assert parent1 != null : "Parent(s) of the working directory shall be set first"; | 94 assert parent1 != null : "Parent(s) of the working directory shall be set first"; | 
| 69 ByteBuffer bb = ByteBuffer.allocate(256); | 95 ByteBuffer bb = ByteBuffer.allocate(256); | 
| 75 if (written != bb.limit()) { | 101 if (written != bb.limit()) { | 
| 76 throw new IOException("Incomplete write"); | 102 throw new IOException("Incomplete write"); | 
| 77 } | 103 } | 
| 78 bb.clear(); | 104 bb.clear(); | 
| 79 // entries | 105 // entries | 
| 80 for (HgDirstate.Record r : normal) { | 106 @SuppressWarnings("unchecked") | 
| 81 // normal entry is 1+4+4+4+4+fname.length bytes | 107 Map<Path, HgDirstate.Record>[] all = new Map[] {normal, added, removed, merged}; | 
| 82 byte[] fname = encodingHelper.toDirstate(r.name().toString()); | 108 for (Map<Path, HgDirstate.Record> m : all) { | 
| 83 bb = ensureCapacity(bb, 17 + fname.length); | 109 for (HgDirstate.Record r : m.values()) { | 
| 84 bb.put((byte) 'n'); | 110 // regular entry is 1+4+4+4+4+fname.length bytes | 
| 85 bb.putInt(r.mode()); | 111 // it might get extended with copy origin, prepended with 0 byte | 
| 86 bb.putInt(r.size()); | 112 byte[] fname = encodingHelper.toDirstate(r.name()); | 
| 87 bb.putInt(r.modificationTime()); | 113 byte[] copyOrigin = r.copySource() == null ? null : encodingHelper.toDirstate(r.copySource()); | 
| 88 bb.putInt(fname.length); | 114 int length = fname.length + (copyOrigin == null ? 0 : (1 + copyOrigin.length)); | 
| 89 bb.put(fname); | 115 bb = ensureCapacity(bb, 17 + length); | 
| 90 bb.flip(); | 116 bb.put((byte) 'n'); | 
| 91 written = dest.write(bb); | 117 bb.putInt(r.mode()); | 
| 92 if (written != bb.limit()) { | 118 bb.putInt(r.size()); | 
| 93 throw new IOException("Incomplete write"); | 119 bb.putInt(r.modificationTime()); | 
| 120 bb.putInt(length); | |
| 121 bb.put(fname); | |
| 122 if (copyOrigin != null) { | |
| 123 bb.put((byte) 0); | |
| 124 bb.put(copyOrigin); | |
| 125 } | |
| 126 bb.flip(); | |
| 127 written = dest.write(bb); | |
| 128 if (written != bb.limit()) { | |
| 129 throw new IOException("Incomplete write"); | |
| 130 } | |
| 131 bb.clear(); | |
| 94 } | 132 } | 
| 95 bb.clear(); | |
| 96 } | 133 } | 
| 97 } | 134 } | 
| 135 | |
| 136 public void serialize() throws HgIOException { | |
| 137 File dirstateFile = hgRepo.getRepositoryFile(HgRepositoryFiles.Dirstate); | |
| 138 try { | |
| 139 FileChannel dirstate = new FileOutputStream(dirstateFile).getChannel(); | |
| 140 serialize(dirstate); | |
| 141 dirstate.close(); | |
| 142 } catch (IOException ex) { | |
| 143 throw new HgIOException("Can't write down new directory state", ex, dirstateFile); | |
| 144 } | |
| 145 } | |
| 146 | |
| 147 public void fillFrom(DirstateReader dirstate) { | |
| 148 // TODO preserve order, if reasonable and possible | |
| 149 dirstate.readInto(new HgDirstate.Inspector() { | |
| 150 | |
| 151 public boolean next(EntryKind kind, Record entry) { | |
| 152 switch (kind) { | |
| 153 case Normal: normal.put(entry.name(), entry); break; | |
| 154 case Added : added.put(entry.name(), entry); break; | |
| 155 case Removed : removed.put(entry.name(), entry); break; | |
| 156 case Merged : merged.put(entry.name(), entry); break; | |
| 157 default: throw new HgInvalidStateException(String.format("Unexpected entry in the dirstate: %s", kind)); | |
| 158 } | |
| 159 return true; | |
| 160 } | |
| 161 }); | |
| 162 parents(dirstate.parents().first(), dirstate.parents().second()); | |
| 163 } | |
| 164 | |
| 98 | 165 | 
| 99 private static ByteBuffer ensureCapacity(ByteBuffer buf, int cap) { | 166 private static ByteBuffer ensureCapacity(ByteBuffer buf, int cap) { | 
| 100 if (buf.capacity() >= cap) { | 167 if (buf.capacity() >= cap) { | 
| 101 return buf; | 168 return buf; | 
| 102 } | 169 } | 
