diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -2428,8 +2428,15 @@ def tag(ui, repo, name, rev_=None, **opt rev_ = opts['rev'] message = opts['message'] if opts['remove']: - if not name in repo.tags(): + tagtype = repo.tagtype(name) + + if not tagtype: raise util.Abort(_('tag %s does not exist') % name) + if opts['local'] and tagtype == 'global': + raise util.Abort(_('%s tag is global') % name) + if not opts['local'] and tagtype == 'local': + raise util.Abort(_('%s tag is local') % name) + rev_ = nullid if not message: message = _('Removed tag %s') % name diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -79,6 +79,7 @@ class localrepository(repo.repository): pass self.tagscache = None + self._tagstypecache = None self.branchcache = None self.nodetagscache = None self.filterpats = {} @@ -198,8 +199,9 @@ class localrepository(repo.repository): return self.tagscache globaltags = {} + tagtypes = {} - def readtags(lines, fn): + def readtags(lines, fn, tagtype): filetags = {} count = 0 @@ -234,7 +236,9 @@ class localrepository(repo.repository): for k, nh in filetags.items(): if k not in globaltags: globaltags[k] = nh + tagtypes[k] = tagtype continue + # we prefer the global tag if: # it supercedes us OR # mutual supercedes and it has a higher rank @@ -246,31 +250,47 @@ class localrepository(repo.repository): an = bn ah.extend([n for n in bh if n not in ah]) globaltags[k] = an, ah + tagtypes[k] = tagtype # 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) + readtags(f.data().splitlines(), f, "global") 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") + readtags(data.splitlines(), "localtags", "local") except IOError: pass self.tagscache = {} + self._tagstypecache = {} for k,nh in globaltags.items(): n = nh[0] if n != nullid: self.tagscache[k] = n + self._tagstypecache[k] = tagtypes[k] self.tagscache['tip'] = self.changelog.tip() return self.tagscache + def tagtype(self, tagname): + ''' + return the type of the given tag. result can be: + + 'local' : a local tag + 'global' : a global tag + None : tag does not exist + ''' + + self.tags() + + return self._tagstypecache.get(tagname) + def _hgtagsnodes(self): heads = self.heads() heads.reverse() @@ -553,6 +573,7 @@ class localrepository(repo.repository): if hasattr(self, a): self.__delattr__(a) self.tagscache = None + self._tagstypecache = None self.nodetagscache = None def _lock(self, lockname, wait, releasefn, acquirefn, desc): diff --git a/tests/test-tags b/tests/test-tags --- a/tests/test-tags +++ b/tests/test-tags @@ -126,3 +126,20 @@ hg up -qC 0 hg tag -m 'retag rev 0' -fr 0 bar # rev 4 bar -> 0, but bar stays at 2 echo % bar should still point to rev 2 hg tags + + +# test that removing global/local tags does not get confused when trying +# to remove a tag of type X which actually only exists as a type Y +cd .. +hg init t5 +cd t5 +echo foo > foo +hg add +hg ci -m 'add foo' # rev 0 + +hg tag -r 0 -l localtag +hg tag --remove localtag + +hg tag -r 0 globaltag +hg tag --remove -l globaltag +exit 0 diff --git a/tests/test-tags.out b/tests/test-tags.out --- a/tests/test-tags.out +++ b/tests/test-tags.out @@ -71,3 +71,6 @@ bar 2:72b % bar should still point to rev 2 tip 4:40af5d225513 bar 2:72b852876a42 +adding foo +abort: localtag tag is local +abort: globaltag tag is global