diff 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
line wrap: on
line diff
--- a/src/org/tmatesoft/hg/repo/HgTags.java	Fri Jun 03 04:50:09 2011 +0200
+++ b/src/org/tmatesoft/hg/repo/HgTags.java	Tue Jun 07 04:28:32 2011 +0200
@@ -20,7 +20,9 @@
 import java.io.File;
 import java.io.FileReader;
 import java.io.IOException;
+import java.io.Reader;
 import java.util.ArrayList;
+import java.util.Collections;
 import java.util.HashMap;
 import java.util.LinkedList;
 import java.util.List;
@@ -39,13 +41,17 @@
 	// global tags come from ".hgtags"
 	// local come from ".hg/localtags"
 
+	private final HgRepository repo;
+
 	private final Map<Nodeid, List<String>> globalToName;
 	private final Map<Nodeid, List<String>> localToName;
 	private final Map<String, List<Nodeid>> globalFromName;
 	private final Map<String, List<Nodeid>> localFromName;
 	
+	private Map<String, TagInfo> tags;
 	
-	/*package-local*/ HgTags() {
+	/*package-local*/ HgTags(HgRepository hgRepo) {
+		repo = hgRepo;
 		globalToName =  new HashMap<Nodeid, List<String>>();
 		localToName  =  new HashMap<Nodeid, List<String>>();
 		globalFromName = new TreeMap<String, List<Nodeid>>();
@@ -65,6 +71,18 @@
 		}
 		read(globalTags, globalToName, globalFromName);
 	}
+
+	/*package-local*/ void readGlobal(Reader globalTags) throws IOException {
+		BufferedReader r = null;
+		try {
+			r = new BufferedReader(globalTags);
+			read(r, globalToName, globalFromName);
+		} finally {
+			if (r != null) {
+				r.close();
+			}
+		}
+	}
 	
 	private void read(File f, Map<Nodeid,List<String>> nid2name, Map<String, List<Nodeid>> name2nid) throws IOException {
 		if (!f.canRead()) {
@@ -101,19 +119,37 @@
 				List<Nodeid> nids = name2nid.get(tagName);
 				if (nids == null) {
 					nids = new LinkedList<Nodeid>();
+					nids.add(nid);
 					// tagName is substring of full line, thus need a copy to let the line be GC'ed
 					// new String(tagName.toCharArray()) is more expressive, but results in 1 extra arraycopy
 					tagName = new String(tagName);
 					name2nid.put(tagName, nids);
+				} else if (!nid.equals(nids.get(0))) {
+					// Alternatively, !nids.contains(nid) might have come to mind.
+					// However, I guess that 'tag history' means we need to record each change of revision
+					// associated with the tag, i.e. imagine project evolution:
+					// tag1=r1, tag1=r2, tag1=r1. If we choose !contains, list top of tag1 would point to r2
+					// while we need it to point to r1.
+					// In fact, there are still possible odd patterns in name2nid list, e.g.
+					// when tag was removed and added back(initially rev1 tag1, on removal *added* nullrev tag1), 
+					// then added back (rev2 tag1).
+					// name2nid would list (rev2 nullrev rev1) as many times, as there were revisions of the .hgtags file
+					// See cpython "v2.4.3c1" revision for example.
+					// It doesn't seem to hurt (unless there are clients that care about tag history and depend on
+					// unique revisions there), XXX but better to be fixed (not sure how, though) 
+					((LinkedList<Nodeid>) nids).addFirst(nid);
+					// XXX repo.getNodeidCache().nodeid(nid);
 				}
-				// XXX repo.getNodeidCache().nodeid(nid);
-				((LinkedList<Nodeid>) nids).addFirst(nid);
 				List<String> revTags = nid2name.get(nid);
 				if (revTags == null) {
 					revTags = new LinkedList<String>();
+					revTags.add(tagName);
 					nid2name.put(nid, revTags);
+				} else if (!revTags.contains(tagName)) {
+					// !contains because we don't care about order of the tags per revision
+					revTags.add(tagName);
 				}
-				revTags.add(tagName);
+				
 			} else {
 				System.out.println("Bad tags line:" + line); // FIXME see above
 			}
@@ -147,4 +183,49 @@
 		}
 		return rv;
 	}
+	
+	public Map<String, TagInfo> getTags() {
+		if (tags == null) {
+			tags = new TreeMap<String, TagInfo>();
+			for (String t : globalFromName.keySet()) {
+				tags.put(t, new TagInfo(t));
+			}
+			for (String t : localFromName.keySet()) {
+				tags.put(t, new TagInfo(t));
+			}
+			tags = Collections.unmodifiableMap(tags);
+		}
+		return tags;
+	}
+
+	
+	public final class TagInfo {
+		private final String name;
+		private String branch;
+
+		TagInfo(String tagName) {
+			this.name = tagName;
+		}
+		public String name() {
+			return name;
+		}
+
+		public boolean isLocal() {
+			return localFromName.containsKey(name);
+		}
+
+		public String branch() {
+			if (branch == null) {
+				int x = repo.getChangelog().getLocalRevision(revision());
+				branch = repo.getChangelog().range(x, x).get(0).branch();
+			}
+			return branch;
+		}
+		public Nodeid revision() {
+			if (isLocal()) {
+				return localFromName.get(name).get(0);
+			}
+			return globalFromName.get(name).get(0);
+		}
+	}
 }