tag: allow multiple tags to be added or removed
- Example: "hg tag -r 42 build-25 beta-1" will add tags build-25 and beta-1
for rev 42.
- The deprecated and undocumented usage "hg tag arg1 arg2" used to emit a
warning, then add tag arg1 for rev arg2 (equivalent to "hg tag -r arg2 arg1").
It will now add tags arg1 and arg2 for the current revision.
- If one tag triggers an error, no tags are added/removed (all or nothing).
--- a/mercurial/commands.py Mon Mar 17 19:28:46 2008 +0200
+++ b/mercurial/commands.py Fri Mar 14 15:38:56 2008 -0700
@@ -2589,8 +2589,8 @@
if f in copy and (f in added or f in showcopy):
ui.write(' %s%s' % (repo.pathto(copy[f], cwd), end))
-def tag(ui, repo, name, rev_=None, **opts):
- """add a tag for the current or given revision
+def tag(ui, repo, name1, *names, **opts):
+ """add one or more tags for the current or given revision
Name a particular revision using <name>.
@@ -2609,47 +2609,49 @@
See 'hg help dates' for a list of formats valid for -d/--date.
"""
- if name in ['tip', '.', 'null']:
- raise util.Abort(_("the name '%s' is reserved") % name)
- if rev_ is not None:
- ui.warn(_("use of 'hg tag NAME [REV]' is deprecated, "
- "please use 'hg tag [-r REV] NAME' instead\n"))
- if opts['rev']:
- raise util.Abort(_("use only one form to specify the revision"))
+
+ rev_ = None
+ names = (name1,) + names
+ if len(names) != len(dict.fromkeys(names)):
+ raise util.Abort(_('tag names must be unique'))
+ for n in names:
+ if n in ['tip', '.', 'null']:
+ raise util.Abort(_('the name \'%s\' is reserved') % n)
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']:
- 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)
-
+ expectedtype = opts['local'] and 'local' or 'global'
+ for n in names:
+ if not repo.tagtype(n):
+ raise util.Abort(_('tag \'%s\' does not exist') % n)
+ if repo.tagtype(n) != expectedtype:
+ raise util.Abort(_('tag \'%s\' is not a %s tag') %
+ (n, expectedtype))
rev_ = nullid
if not message:
- message = _('Removed tag %s') % name
- elif name in repo.tags() and not opts['force']:
- raise util.Abort(_('a tag named %s already exists (use -f to force)')
- % name)
+ message = _('Removed tag %s') % ', '.join(names)
+ elif not opts['force']:
+ for n in names:
+ if n in repo.tags():
+ raise util.Abort(_('tag \'%s\' already exists '
+ '(use -f to force)') % n)
if not rev_ and repo.dirstate.parents()[1] != nullid:
raise util.Abort(_('uncommitted merge - please provide a '
'specific revision'))
r = repo.changectx(rev_).node()
if not message:
- message = _('Added tag %s for changeset %s') % (name, short(r))
+ message = (_('Added tag %s for changeset %s') %
+ (', '.join(names), short(r)))
date = opts.get('date')
if date:
date = util.parsedate(date)
- repo.tag(name, r, message, opts['local'], opts['user'], date)
+ repo.tag(names, r, message, opts['local'], opts['user'], date)
def tags(ui, repo):
"""list repository tags
@@ -3190,7 +3192,7 @@
# -l/--local is already there, commitopts cannot be used
('m', 'message', '', _('use <text> as commit message')),
] + commitopts2,
- _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME')),
+ _('hg tag [-l] [-m TEXT] [-d DATE] [-u USER] [-r REV] NAME...')),
"tags": (tags, [], _('hg tags')),
"tip":
(tip,
--- a/mercurial/localrepo.py Mon Mar 17 19:28:46 2008 +0200
+++ b/mercurial/localrepo.py Fri Mar 14 15:38:56 2008 -0700
@@ -124,21 +124,29 @@
tag_disallowed = ':\r\n'
- def _tag(self, name, node, message, local, user, date, parent=None,
+ def _tag(self, names, node, message, local, user, date, parent=None,
extra={}):
use_dirstate = parent is None
+ if isinstance(names, str):
+ allchars = names
+ names = (names,)
+ else:
+ allchars = ''.join(names)
for c in self.tag_disallowed:
- if c in name:
+ if c in allchars:
raise util.Abort(_('%r cannot be used in a tag name') % c)
- self.hook('pretag', throw=True, node=hex(node), tag=name, local=local)
+ for name in names:
+ self.hook('pretag', throw=True, node=hex(node), tag=name,
+ local=local)
- def writetag(fp, name, munge, prevtags):
+ def writetags(fp, names, munge, prevtags):
fp.seek(0, 2)
if prevtags and prevtags[-1] != '\n':
fp.write('\n')
- fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
+ for name in names:
+ fp.write('%s %s\n' % (hex(node), munge and munge(name) or name))
fp.close()
prevtags = ''
@@ -151,8 +159,9 @@
prevtags = fp.read()
# local tags are stored in the current charset
- writetag(fp, name, None, prevtags)
- self.hook('tag', node=hex(node), tag=name, local=local)
+ writetags(fp, names, None, prevtags)
+ for name in names:
+ self.hook('tag', node=hex(node), tag=name, local=local)
return
if use_dirstate:
@@ -172,7 +181,7 @@
fp.write(prevtags)
# committed tags are stored in UTF-8
- writetag(fp, name, util.fromlocal, prevtags)
+ writetags(fp, names, util.fromlocal, prevtags)
if use_dirstate and '.hgtags' not in self.dirstate:
self.add(['.hgtags'])
@@ -180,20 +189,24 @@
tagnode = self.commit(['.hgtags'], message, user, date, p1=parent,
extra=extra)
- self.hook('tag', node=hex(node), tag=name, local=local)
+ for name in names:
+ self.hook('tag', node=hex(node), tag=name, local=local)
return tagnode
- def tag(self, name, node, message, local, user, date):
- '''tag a revision with a symbolic name.
+ def tag(self, names, node, message, local, user, date):
+ '''tag a revision with one or more symbolic names.
- if local is True, the tag is stored in a per-repository file.
- otherwise, it is stored in the .hgtags file, and a new
+ names is a list of strings or, when adding a single tag, names may be a
+ string.
+
+ if local is True, the tags are stored in a per-repository file.
+ otherwise, they are stored in the .hgtags file, and a new
changeset is committed with the change.
keyword arguments:
- local: whether to store tag in non-version-controlled file
+ local: whether to store tags in non-version-controlled file
(default False)
message: commit message to use if committing
@@ -207,7 +220,7 @@
raise util.Abort(_('working copy of .hgtags is changed '
'(please commit .hgtags manually)'))
- self._tag(name, node, message, local, user, date)
+ self._tag(names, node, message, local, user, date)
def tags(self):
'''return a mapping of tag to node'''
--- a/tests/test-globalopts.out Mon Mar 17 19:28:46 2008 +0200
+++ b/tests/test-globalopts.out Fri Mar 14 15:38:56 2008 -0700
@@ -188,7 +188,7 @@
serve export the repository via HTTP
showconfig show combined config settings from all hgrc files
status show changed files in the working directory
- tag add a tag for the current or given revision
+ tag add one or more tags for the current or given revision
tags list repository tags
tip show the tip revision
unbundle apply one or more changegroup files
@@ -241,7 +241,7 @@
serve export the repository via HTTP
showconfig show combined config settings from all hgrc files
status show changed files in the working directory
- tag add a tag for the current or given revision
+ tag add one or more tags for the current or given revision
tags list repository tags
tip show the tip revision
unbundle apply one or more changegroup files
--- a/tests/test-help.out Mon Mar 17 19:28:46 2008 +0200
+++ b/tests/test-help.out Fri Mar 14 15:38:56 2008 -0700
@@ -80,7 +80,7 @@
serve export the repository via HTTP
showconfig show combined config settings from all hgrc files
status show changed files in the working directory
- tag add a tag for the current or given revision
+ tag add one or more tags for the current or given revision
tags list repository tags
tip show the tip revision
unbundle apply one or more changegroup files
@@ -129,7 +129,7 @@
serve export the repository via HTTP
showconfig show combined config settings from all hgrc files
status show changed files in the working directory
- tag add a tag for the current or given revision
+ tag add one or more tags for the current or given revision
tags list repository tags
tip show the tip revision
unbundle apply one or more changegroup files
--- a/tests/test-tag Mon Mar 17 19:28:46 2008 +0200
+++ b/tests/test-tag Fri Mar 14 15:38:56 2008 -0700
@@ -10,11 +10,21 @@
echo foo >> .hgtags
hg tag -d "1000000 0" "bleah2" || echo "failed"
-hg tag -d "1000000 0" -r 0 "bleah2" 1 || echo "failed"
hg revert .hgtags
+hg tag -d "1000000 0" -r 0 x y z y y z || echo "failed"
+hg tag -d "1000000 0" tap nada dot tip null . || echo "failed"
+hg tag -d "1000000 0" "bleah" || echo "failed"
+hg tag -d "1000000 0" "blecch" "bleah" || echo "failed"
+
+hg tag -d "1000000 0" --remove "blecch" || echo "failed"
+hg tag -d "1000000 0" --remove "bleah" "blecch" "blough" || echo "failed"
+
hg tag -d "1000000 0" -r 0 "bleah0"
-hg tag -l -d "1000000 0" "bleah1" 1
+hg tag -l -d "1000000 0" -r 1 "bleah1"
+hg tag -d "1000000 0" gack gawk gorp
+hg tag -d "1000000 0" -f gack
+hg tag -d "1000000 0" --remove gack gorp
cat .hgtags
cat .hg/localtags
--- a/tests/test-tag.out Mon Mar 17 19:28:46 2008 +0200
+++ b/tests/test-tag.out Fri Mar 14 15:38:56 2008 -0700
@@ -18,12 +18,26 @@
abort: working copy of .hgtags is changed (please commit .hgtags manually)
failed
-use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead
-abort: use only one form to specify the revision
+abort: tag names must be unique
+failed
+abort: the name 'tip' is reserved
+failed
+abort: tag 'bleah' already exists (use -f to force)
failed
-use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead
+abort: tag 'bleah' already exists (use -f to force)
+failed
+abort: tag 'blecch' does not exist
+failed
+abort: tag 'blecch' does not exist
+failed
0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
+868cc8fbb43b754ad09fa109885d243fc49adae7 gack
+868cc8fbb43b754ad09fa109885d243fc49adae7 gawk
+868cc8fbb43b754ad09fa109885d243fc49adae7 gorp
+3807bcf62c5614cb6c16436b514d7764ca5f1631 gack
+0000000000000000000000000000000000000000 gack
+0000000000000000000000000000000000000000 gorp
3ecf002a1c572a2f3bb4e665417e60fca65bbd42 bleah1
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar
--- a/tests/test-tags.out Mon Mar 17 19:28:46 2008 +0200
+++ b/tests/test-tags.out Fri Mar 14 15:38:56 2008 -0700
@@ -50,7 +50,7 @@
tip 5:57e1983b4a60
% remove nonexistent tag
-abort: tag foobar does not exist
+abort: tag 'foobar' does not exist
changeset: 5:57e1983b4a60
tag: tip
user: test
@@ -62,7 +62,7 @@
1 files updated, 0 files merged, 0 files removed, 0 files unresolved
tip 6:b5ff9d142648
bar 0:b409d9da318e
-abort: a tag named bar already exists (use -f to force)
+abort: tag 'bar' already exists (use -f to force)
tip 6:b5ff9d142648
bar 0:b409d9da318e
adding foo
@@ -72,8 +72,8 @@
tip 4:40af5d225513
bar 2:72b852876a42
adding foo
-abort: localtag tag is local
-abort: globaltag tag is global
+abort: tag 'localtag' is not a global tag
+abort: tag 'globaltag' is not a local tag
tip 1:a0b6fe111088
localtag 0:bbd179dfa0a7 local
globaltag 0:bbd179dfa0a7