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 }