Mercurial > jhg
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 } |
