Mercurial > hg4j
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()) { |