comparison src/org/tmatesoft/hg/repo/HgTags.java @ 234:b2cfbe46f9b6

HgTags got TagInfo to access tags. Tags are read from all branches/revisions now, not only working copy
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Tue, 07 Jun 2011 04:28:32 +0200
parents 54562de502f7
children 981f9f50bb6c
comparison
equal deleted inserted replaced
233:1d389c0cb0a5 234:b2cfbe46f9b6
18 18
19 import java.io.BufferedReader; 19 import java.io.BufferedReader;
20 import java.io.File; 20 import java.io.File;
21 import java.io.FileReader; 21 import java.io.FileReader;
22 import java.io.IOException; 22 import java.io.IOException;
23 import java.io.Reader;
23 import java.util.ArrayList; 24 import java.util.ArrayList;
25 import java.util.Collections;
24 import java.util.HashMap; 26 import java.util.HashMap;
25 import java.util.LinkedList; 27 import java.util.LinkedList;
26 import java.util.List; 28 import java.util.List;
27 import java.util.Map; 29 import java.util.Map;
28 import java.util.TreeMap; 30 import java.util.TreeMap;
37 */ 39 */
38 public class HgTags { 40 public class HgTags {
39 // global tags come from ".hgtags" 41 // global tags come from ".hgtags"
40 // local come from ".hg/localtags" 42 // local come from ".hg/localtags"
41 43
44 private final HgRepository repo;
45
42 private final Map<Nodeid, List<String>> globalToName; 46 private final Map<Nodeid, List<String>> globalToName;
43 private final Map<Nodeid, List<String>> localToName; 47 private final Map<Nodeid, List<String>> localToName;
44 private final Map<String, List<Nodeid>> globalFromName; 48 private final Map<String, List<Nodeid>> globalFromName;
45 private final Map<String, List<Nodeid>> localFromName; 49 private final Map<String, List<Nodeid>> localFromName;
46 50
47 51 private Map<String, TagInfo> tags;
48 /*package-local*/ HgTags() { 52
53 /*package-local*/ HgTags(HgRepository hgRepo) {
54 repo = hgRepo;
49 globalToName = new HashMap<Nodeid, List<String>>(); 55 globalToName = new HashMap<Nodeid, List<String>>();
50 localToName = new HashMap<Nodeid, List<String>>(); 56 localToName = new HashMap<Nodeid, List<String>>();
51 globalFromName = new TreeMap<String, List<Nodeid>>(); 57 globalFromName = new TreeMap<String, List<Nodeid>>();
52 localFromName = new TreeMap<String, List<Nodeid>>(); 58 localFromName = new TreeMap<String, List<Nodeid>>();
53 } 59 }
62 /*package-local*/ void readGlobal(File globalTags) throws IOException { 68 /*package-local*/ void readGlobal(File globalTags) throws IOException {
63 if (globalTags == null || globalTags.isDirectory()) { 69 if (globalTags == null || globalTags.isDirectory()) {
64 throw new IllegalArgumentException(String.valueOf(globalTags)); 70 throw new IllegalArgumentException(String.valueOf(globalTags));
65 } 71 }
66 read(globalTags, globalToName, globalFromName); 72 read(globalTags, globalToName, globalFromName);
73 }
74
75 /*package-local*/ void readGlobal(Reader globalTags) throws IOException {
76 BufferedReader r = null;
77 try {
78 r = new BufferedReader(globalTags);
79 read(r, globalToName, globalFromName);
80 } finally {
81 if (r != null) {
82 r.close();
83 }
84 }
67 } 85 }
68 86
69 private void read(File f, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException { 87 private void read(File f, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException {
70 if (!f.canRead()) { 88 if (!f.canRead()) {
71 return; 89 return;
99 Nodeid nid = Nodeid.fromAscii(nodeidBytes, 0, nodeidBytes.length); 117 Nodeid nid = Nodeid.fromAscii(nodeidBytes, 0, nodeidBytes.length);
100 String tagName = line.substring(spacePos+1); 118 String tagName = line.substring(spacePos+1);
101 List<Nodeid> nids = name2nid.get(tagName); 119 List<Nodeid> nids = name2nid.get(tagName);
102 if (nids == null) { 120 if (nids == null) {
103 nids = new LinkedList<Nodeid>(); 121 nids = new LinkedList<Nodeid>();
122 nids.add(nid);
104 // tagName is substring of full line, thus need a copy to let the line be GC'ed 123 // tagName is substring of full line, thus need a copy to let the line be GC'ed
105 // new String(tagName.toCharArray()) is more expressive, but results in 1 extra arraycopy 124 // new String(tagName.toCharArray()) is more expressive, but results in 1 extra arraycopy
106 tagName = new String(tagName); 125 tagName = new String(tagName);
107 name2nid.put(tagName, nids); 126 name2nid.put(tagName, nids);
127 } else if (!nid.equals(nids.get(0))) {
128 // Alternatively, !nids.contains(nid) might have come to mind.
129 // However, I guess that 'tag history' means we need to record each change of revision
130 // associated with the tag, i.e. imagine project evolution:
131 // tag1=r1, tag1=r2, tag1=r1. If we choose !contains, list top of tag1 would point to r2
132 // while we need it to point to r1.
133 // In fact, there are still possible odd patterns in name2nid list, e.g.
134 // when tag was removed and added back(initially rev1 tag1, on removal *added* nullrev tag1),
135 // then added back (rev2 tag1).
136 // name2nid would list (rev2 nullrev rev1) as many times, as there were revisions of the .hgtags file
137 // See cpython "v2.4.3c1" revision for example.
138 // It doesn't seem to hurt (unless there are clients that care about tag history and depend on
139 // unique revisions there), XXX but better to be fixed (not sure how, though)
140 ((LinkedList<Nodeid>) nids).addFirst(nid);
141 // XXX repo.getNodeidCache().nodeid(nid);
108 } 142 }
109 // XXX repo.getNodeidCache().nodeid(nid);
110 ((LinkedList<Nodeid>) nids).addFirst(nid);
111 List<String> revTags = nid2name.get(nid); 143 List<String> revTags = nid2name.get(nid);
112 if (revTags == null) { 144 if (revTags == null) {
113 revTags = new LinkedList<String>(); 145 revTags = new LinkedList<String>();
146 revTags.add(tagName);
114 nid2name.put(nid, revTags); 147 nid2name.put(nid, revTags);
148 } else if (!revTags.contains(tagName)) {
149 // !contains because we don't care about order of the tags per revision
150 revTags.add(tagName);
115 } 151 }
116 revTags.add(tagName); 152
117 } else { 153 } else {
118 System.out.println("Bad tags line:" + line); // FIXME see above 154 System.out.println("Bad tags line:" + line); // FIXME see above
119 } 155 }
120 } 156 }
121 } 157 }
145 if ((l = globalFromName.get(tagName)) != null) { 181 if ((l = globalFromName.get(tagName)) != null) {
146 rv.addAll(l); 182 rv.addAll(l);
147 } 183 }
148 return rv; 184 return rv;
149 } 185 }
186
187 public Map<String, TagInfo> getTags() {
188 if (tags == null) {
189 tags = new TreeMap<String, TagInfo>();
190 for (String t : globalFromName.keySet()) {
191 tags.put(t, new TagInfo(t));
192 }
193 for (String t : localFromName.keySet()) {
194 tags.put(t, new TagInfo(t));
195 }
196 tags = Collections.unmodifiableMap(tags);
197 }
198 return tags;
199 }
200
201
202 public final class TagInfo {
203 private final String name;
204 private String branch;
205
206 TagInfo(String tagName) {
207 this.name = tagName;
208 }
209 public String name() {
210 return name;
211 }
212
213 public boolean isLocal() {
214 return localFromName.containsKey(name);
215 }
216
217 public String branch() {
218 if (branch == null) {
219 int x = repo.getChangelog().getLocalRevision(revision());
220 branch = repo.getChangelog().range(x, x).get(0).branch();
221 }
222 return branch;
223 }
224 public Nodeid revision() {
225 if (isLocal()) {
226 return localFromName.get(name).get(0);
227 }
228 return globalFromName.get(name).get(0);
229 }
230 }
150 } 231 }