Merge with -stable
authorMatt Mackall <mpm@selenic.com>
Wed, 14 Mar 2007 19:17:40 -0500
changeset 4214 5ccbc0be6cdb
parent 4209 dbc3846c09a1 (current diff)
parent 4213 3be4785f8994 (diff)
child 4222 2792dbd648c7
Merge with -stable
mercurial/commands.py
mercurial/localrepo.py
--- a/mercurial/commands.py	Wed Mar 14 01:26:09 2007 -0500
+++ b/mercurial/commands.py	Wed Mar 14 19:17:40 2007 -0500
@@ -2445,14 +2445,20 @@
                   "please use 'hg tag [-r REV] NAME' instead\n"))
         if opts['rev']:
             raise util.Abort(_("use only one form to specify the revision"))
+    if opts['rev'] and opts['remove']:
+        raise util.Abort(_("--rev and --remove are incompatible"))
     if opts['rev']:
         rev_ = opts['rev']
+    message = opts['message']
+    if opts['remove']:
+        rev_ = nullid
+        if not message:
+            message = _('Removed tag %s') % name
     if not rev_ and repo.dirstate.parents()[1] != nullid:
         raise util.Abort(_('uncommitted merge - please provide a '
                            'specific revision'))
     r = repo.changectx(rev_).node()
 
-    message = opts['message']
     if not message:
         message = _('Added tag %s for changeset %s') % (name, short(r))
 
@@ -2913,7 +2919,8 @@
           ('m', 'message', '', _('message for tag commit log entry')),
           ('d', 'date', '', _('record datecode as commit date')),
           ('u', 'user', '', _('record user as commiter')),
-          ('r', 'rev', '', _('revision to tag'))],
+          ('r', 'rev', '', _('revision to tag')),
+          ('', 'remove', None, _('remove a tag'))],
          _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
     "tags": (tags, [], _('hg tags')),
     "tip":
--- a/mercurial/localrepo.py	Wed Mar 14 01:26:09 2007 -0500
+++ b/mercurial/localrepo.py	Wed Mar 14 19:17:40 2007 -0500
@@ -278,55 +278,80 @@
 
     def tags(self):
         '''return a mapping of tag to node'''
-        if not self.tagscache:
-            self.tagscache = {}
+        if self.tagscache:
+            return self.tagscache
+
+        globaltags = {}
 
-            def parsetag(line, context):
-                if not line:
-                    return
+        def readtags(lines, fn):
+            filetags = {}
+            count = 0
+
+            def warn(msg):
+                self.ui.warn(_("%s, line %s: %s\n") % (fn, count, msg))
+
+            for l in lines:
+                count += 1
+                if not l:
+                    continue
                 s = l.split(" ", 1)
                 if len(s) != 2:
-                    self.ui.warn(_("%s: cannot parse entry\n") % context)
-                    return
+                    warn(_("cannot parse entry"))
+                    continue
                 node, key = s
                 key = util.tolocal(key.strip()) # stored in UTF-8
                 try:
                     bin_n = bin(node)
                 except TypeError:
-                    self.ui.warn(_("%s: node '%s' is not well formed\n") %
-                                 (context, node))
-                    return
+                    warn(_("node '%s' is not well formed") % node)
+                    continue
                 if bin_n not in self.changelog.nodemap:
-                    self.ui.warn(_("%s: tag '%s' refers to unknown node\n") %
-                                 (context, key))
-                    return
-                self.tagscache[key] = bin_n
+                    warn(_("tag '%s' refers to unknown node") % key)
+                    continue
+
+                h = {}
+                if key in filetags:
+                    n, h = filetags[key]
+                    h[n] = True
+                filetags[key] = (bin_n, h)
 
-            # read the tags file from each head, ending with the tip,
-            # and add each tag found to the map, with "newer" ones
-            # taking precedence
-            f = None
-            for rev, node, fnode in self._hgtagsnodes():
-                f = (f and f.filectx(fnode) or
-                     self.filectx('.hgtags', fileid=fnode))
-                count = 0
-                for l in f.data().splitlines():
-                    count += 1
-                    parsetag(l, _("%s, line %d") % (str(f), count))
+            for k,nh in filetags.items():
+                if k not in globaltags:
+                    globaltags[k] = nh
+                    continue
+                # we prefer the global tag if:
+                #  it supercedes us OR
+                #  mutual supercedes and it has a higher rank
+                # otherwise we win because we're tip-most
+                an, ah = nh
+                bn, bh = globaltags[k]
+                if bn != an and an in bh and \
+                   (bn not in ah or len(bh) > len(ah)):
+                    an = bn
+                ah.update(bh)
+                globaltags[k] = an, ah
 
-            try:
-                f = self.opener("localtags")
-                count = 0
-                for l in f:
-                    # localtags are stored in the local character set
-                    # while the internal tag table is stored in UTF-8
-                    l = util.fromlocal(l)
-                    count += 1
-                    parsetag(l, _("localtags, line %d") % count)
-            except IOError:
-                pass
+        # read the tags file from each head, ending with the tip
+        f = None
+        for rev, node, fnode in self._hgtagsnodes():
+            f = (f and f.filectx(fnode) or
+                 self.filectx('.hgtags', fileid=fnode))
+            readtags(f.data().splitlines(), f)
 
-            self.tagscache['tip'] = self.changelog.tip()
+        try:
+            data = util.fromlocal(self.opener("localtags").read())
+            # localtags are stored in the local character set
+            # while the internal tag table is stored in UTF-8
+            readtags(data.splitlines(), "localtags")
+        except IOError:
+            pass
+
+        self.tagscache = {}
+        for k,nh in globaltags.items():
+            n = nh[0]
+            if n != nullid:
+                self.tagscache[k] = n
+        self.tagscache['tip'] = self.changelog.tip()
 
         return self.tagscache
 
--- a/tests/test-tags	Wed Mar 14 01:26:09 2007 -0500
+++ b/tests/test-tags	Wed Mar 14 19:17:40 2007 -0500
@@ -63,7 +63,7 @@
 hg tags
 hg tip
 
-# tags from later heads override previous ones
+# test tag precedence rules
 cd ..
 hg init t2
 cd t2
@@ -79,3 +79,7 @@
 echo >> foo
 hg ci -m 'change foo 2' -d '1000000 0' # rev 4
 hg tags
+
+hg tag --remove -d '1000000 0' bar 
+hg tip
+hg tags
--- a/tests/test-tags.out	Wed Mar 14 01:26:09 2007 -0500
+++ b/tests/test-tags.out	Wed Mar 14 19:17:40 2007 -0500
@@ -41,4 +41,11 @@
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
 tip                                4:36195b728445
-bar                                0:b409d9da318e
+bar                                1:b204a97e6e8d
+changeset:   5:57e1983b4a60
+tag:         tip
+user:        test
+date:        Mon Jan 12 13:46:40 1970 +0000
+summary:     Removed tag bar
+
+tip                                5:57e1983b4a60