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