comparison src/org/tmatesoft/hg/repo/HgTags.java @ 610:5c68567b3645

Refresh tags, branches, bookmarks and ignore when their files (or csets in the repo) are changed
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 09 May 2013 21:06:48 +0200
parents d9c07e1432c4
children 6526d8adbc0f
comparison
equal deleted inserted replaced
609:e4a71afd3c71 610:5c68567b3645
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.repo; 17 package org.tmatesoft.hg.repo;
18 18
19 import static org.tmatesoft.hg.repo.HgRepositoryFiles.HgLocalTags;
20 import static org.tmatesoft.hg.repo.HgRepositoryFiles.HgTags;
21 import static org.tmatesoft.hg.util.LogFacility.Severity.*;
19 import static org.tmatesoft.hg.util.LogFacility.Severity.Error; 22 import static org.tmatesoft.hg.util.LogFacility.Severity.Error;
20 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn; 23 import static org.tmatesoft.hg.util.LogFacility.Severity.Warn;
21 24
22 import java.io.BufferedReader; 25 import java.io.BufferedReader;
23 import java.io.File; 26 import java.io.File;
24 import java.io.FileReader; 27 import java.io.FileReader;
25 import java.io.IOException; 28 import java.io.IOException;
26 import java.io.Reader; 29 import java.io.Reader;
30 import java.io.StringReader;
27 import java.util.ArrayList; 31 import java.util.ArrayList;
28 import java.util.Collections; 32 import java.util.Collections;
29 import java.util.HashMap; 33 import java.util.HashMap;
30 import java.util.LinkedList; 34 import java.util.LinkedList;
31 import java.util.List; 35 import java.util.List;
32 import java.util.Map; 36 import java.util.Map;
33 import java.util.TreeMap; 37 import java.util.TreeMap;
34 38
35 import org.tmatesoft.hg.core.HgBadNodeidFormatException; 39 import org.tmatesoft.hg.core.HgBadNodeidFormatException;
36 import org.tmatesoft.hg.core.Nodeid; 40 import org.tmatesoft.hg.core.Nodeid;
41 import org.tmatesoft.hg.internal.ByteArrayChannel;
42 import org.tmatesoft.hg.internal.ChangelogMonitor;
43 import org.tmatesoft.hg.internal.FileChangeMonitor;
44 import org.tmatesoft.hg.internal.Internals;
45 import org.tmatesoft.hg.util.CancelledException;
37 46
38 /** 47 /**
39 * @see http://mercurial.selenic.com/wiki/TagDesign 48 * @see http://mercurial.selenic.com/wiki/TagDesign
40 * 49 *
41 * @author Artem Tikhomirov 50 * @author Artem Tikhomirov
43 */ 52 */
44 public class HgTags { 53 public class HgTags {
45 // global tags come from ".hgtags" 54 // global tags come from ".hgtags"
46 // local come from ".hg/localtags" 55 // local come from ".hg/localtags"
47 56
48 private final HgRepository repo; 57 private final Internals repo;
49 58
50 private final Map<Nodeid, List<String>> globalToName; 59 private final Map<Nodeid, List<String>> globalToName;
51 private final Map<Nodeid, List<String>> localToName; 60 private final Map<Nodeid, List<String>> localToName;
52 private final Map<String, List<Nodeid>> globalFromName; 61 private final Map<String, List<Nodeid>> globalFromName;
53 private final Map<String, List<Nodeid>> localFromName; 62 private final Map<String, List<Nodeid>> localFromName;
54 63
64 private FileChangeMonitor globalTagsFileMonitor, localTagsFileMonitor;
65 private ChangelogMonitor repoChangeMonitor;
66
55 private Map<String, TagInfo> tags; 67 private Map<String, TagInfo> tags;
56 68
57 /*package-local*/ HgTags(HgRepository hgRepo) { 69 /*package-local*/ HgTags(Internals internalRepo) {
58 repo = hgRepo; 70 repo = internalRepo;
59 globalToName = new HashMap<Nodeid, List<String>>(); 71 globalToName = new HashMap<Nodeid, List<String>>();
60 localToName = new HashMap<Nodeid, List<String>>(); 72 localToName = new HashMap<Nodeid, List<String>>();
61 globalFromName = new TreeMap<String, List<Nodeid>>(); 73 globalFromName = new TreeMap<String, List<Nodeid>>();
62 localFromName = new TreeMap<String, List<Nodeid>>(); 74 localFromName = new TreeMap<String, List<Nodeid>>();
63 } 75 }
64 76
65 /*package-local*/ void readLocal(File localTags) throws IOException { 77 /*package-local*/ void read() throws HgInvalidControlFileException {
66 if (localTags == null || localTags.isDirectory()) { 78 readTagsFromHistory();
67 throw new IllegalArgumentException(String.valueOf(localTags)); 79 readGlobal();
68 } 80 readLocal();
69 read(localTags, localToName, localFromName); 81 }
70 } 82
71 83 private void readTagsFromHistory() throws HgInvalidControlFileException {
72 /*package-local*/ void readGlobal(File globalTags) throws IOException { 84 HgDataFile hgTags = repo.getRepo().getFileNode(HgTags.getPath());
73 if (globalTags == null || globalTags.isDirectory()) { 85 if (hgTags.exists()) {
74 throw new IllegalArgumentException(String.valueOf(globalTags)); 86 for (int i = 0; i <= hgTags.getLastRevision(); i++) { // TODO post-1.0 in fact, would be handy to have walk(start,end)
75 } 87 // method for data files as well, though it looks odd.
76 read(globalTags, globalToName, globalFromName); 88 try {
77 } 89 ByteArrayChannel sink = new ByteArrayChannel();
78 90 hgTags.content(i, sink);
79 /*package-local*/ void readGlobal(Reader globalTags) throws IOException { 91 final String content = new String(sink.toArray(), "UTF8");
92 readGlobal(new StringReader(content));
93 } catch (CancelledException ex) {
94 // IGNORE, can't happen, we did not configure cancellation
95 repo.getLog().dump(getClass(), Debug, ex, null);
96 } catch (IOException ex) {
97 // UnsupportedEncodingException can't happen (UTF8)
98 // only from readGlobal. Need to reconsider exceptions thrown from there:
99 // BufferedReader wraps String and unlikely to throw IOException, perhaps, log is enough?
100 repo.getLog().dump(getClass(), Error, ex, null);
101 // XXX need to decide what to do this. failure to read single revision shall not break complete cycle
102 }
103 }
104 }
105 if (repoChangeMonitor == null) {
106 repoChangeMonitor = new ChangelogMonitor(repo.getRepo());
107 }
108 repoChangeMonitor.touch();
109 }
110
111 private void readLocal() throws HgInvalidControlFileException {
112 File localTags = repo.getRepositoryFile(HgLocalTags);
113 if (localTags.canRead() && localTags.isFile()) {
114 read(localTags, localToName, localFromName);
115 }
116 if (localTagsFileMonitor == null) {
117 localTagsFileMonitor = new FileChangeMonitor(localTags);
118 }
119 localTagsFileMonitor.touch(this);
120 }
121
122 private void readGlobal() throws HgInvalidControlFileException {
123 File globalTags = repo.getRepositoryFile(HgTags); // XXX replace with HgDataFile.workingCopy
124 if (globalTags.canRead() && globalTags.isFile()) {
125 read(globalTags, globalToName, globalFromName);
126 }
127 if (globalTagsFileMonitor == null) {
128 globalTagsFileMonitor = new FileChangeMonitor(globalTags);
129 }
130 globalTagsFileMonitor.touch(this);
131 }
132
133 private void readGlobal(Reader globalTags) throws IOException {
80 BufferedReader r = null; 134 BufferedReader r = null;
81 try { 135 try {
82 r = new BufferedReader(globalTags); 136 r = new BufferedReader(globalTags);
83 read(r, globalToName, globalFromName); 137 read(r, globalToName, globalFromName);
84 } finally { 138 } finally {
86 r.close(); 140 r.close();
87 } 141 }
88 } 142 }
89 } 143 }
90 144
91 private void read(File f, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException { 145 private void read(File f, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws HgInvalidControlFileException {
92 if (!f.canRead()) { 146 if (!f.canRead()) {
93 return; 147 return;
94 } 148 }
95 BufferedReader r = null; 149 BufferedReader r = null;
96 try { 150 try {
97 r = new BufferedReader(new FileReader(f)); 151 r = new BufferedReader(new FileReader(f));
98 read(r, nid2name, name2nid); 152 read(r, nid2name, name2nid);
153 } catch (IOException ex) {
154 repo.getLog().dump(getClass(), Error, ex, null);
155 throw new HgInvalidControlFileException("Failed to read tags", ex, f);
99 } finally { 156 } finally {
100 if (r != null) { 157 if (r != null) {
101 r.close(); 158 try {
159 r.close();
160 } catch (IOException ex) {
161 // since it's read operation, do not treat close failure as error, but let user know, anyway
162 repo.getLog().dump(getClass(), Warn, ex, null);
163 }
102 } 164 }
103 } 165 }
104 } 166 }
105 167
106 private void read(BufferedReader reader, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException { 168 private void read(BufferedReader reader, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException {
110 if (line.length() == 0) { 172 if (line.length() == 0) {
111 continue; 173 continue;
112 } 174 }
113 final int spacePos = line.indexOf(' '); 175 final int spacePos = line.indexOf(' ');
114 if (line.length() < 40+2 /*nodeid, space and at least single-char tagname*/ || spacePos != 40) { 176 if (line.length() < 40+2 /*nodeid, space and at least single-char tagname*/ || spacePos != 40) {
115 repo.getSessionContext().getLog().dump(getClass(), Warn, "Bad tags line: %s", line); 177 repo.getLog().dump(getClass(), Warn, "Bad tags line: %s", line);
116 continue; 178 continue;
117 } 179 }
118 try { 180 try {
119 assert spacePos == 40; 181 assert spacePos == 40;
120 final byte[] nodeidBytes = line.substring(0, spacePos).getBytes(); 182 final byte[] nodeidBytes = line.substring(0, spacePos).getBytes();
152 } else if (!revTags.contains(tagName)) { 214 } else if (!revTags.contains(tagName)) {
153 // !contains because we don't care about order of the tags per revision 215 // !contains because we don't care about order of the tags per revision
154 revTags.add(tagName); 216 revTags.add(tagName);
155 } 217 }
156 } catch (HgBadNodeidFormatException ex) { 218 } catch (HgBadNodeidFormatException ex) {
157 repo.getSessionContext().getLog().dump(getClass(), Error, "Bad revision '%s' in line '%s':%s", line.substring(0, spacePos), line, ex.getMessage()); 219 repo.getLog().dump(getClass(), Error, "Bad revision '%s' in line '%s':%s", line.substring(0, spacePos), line, ex.getMessage());
158 } 220 }
159 } 221 }
160 } 222 }
161 223
162 public List<String> tags(Nodeid nid) { 224 public List<String> tags(Nodeid nid) {
215 } 277 }
216 } 278 }
217 return rv; 279 return rv;
218 } 280 }
219 281
282 // can be called only after instance has been initialized (#read() invoked)
283 /*package-local*/void reloadIfChanged() throws HgInvalidControlFileException {
284 assert repoChangeMonitor != null;
285 assert localTagsFileMonitor != null;
286 assert globalTagsFileMonitor != null;
287 if (repoChangeMonitor.isChanged() || globalTagsFileMonitor.changed(this)) {
288 globalFromName.clear();
289 globalToName.clear();
290 readTagsFromHistory();
291 readGlobal();
292 tags = null;
293 }
294 if (localTagsFileMonitor.changed(this)) {
295 readLocal();
296 tags = null;
297 }
298 }
220 299
221 public final class TagInfo { 300 public final class TagInfo {
222 private final String name; 301 private final String name;
223 private String branch; 302 private String branch;
224 303
233 return localFromName.containsKey(name); 312 return localFromName.containsKey(name);
234 } 313 }
235 314
236 public String branch() throws HgInvalidControlFileException { 315 public String branch() throws HgInvalidControlFileException {
237 if (branch == null) { 316 if (branch == null) {
238 int x = repo.getChangelog().getRevisionIndex(revision()); 317 int x = repo.getRepo().getChangelog().getRevisionIndex(revision());
239 branch = repo.getChangelog().range(x, x).get(0).branch(); 318 branch = repo.getRepo().getChangelog().range(x, x).get(0).branch();
240 } 319 }
241 return branch; 320 return branch;
242 } 321 }
243 public Nodeid revision() { 322 public Nodeid revision() {
244 if (isLocal()) { 323 if (isLocal()) {