[PATCH] tag: allow multiple tags to be added or removed
John Coomes
John.Coomes at sun.com
Sat Feb 23 01:22:44 CST 2008
Patch to allow multiple tags to be added/removed in a single invocation
of hg tag. Basic example of the new usage is
hg tag -r 42 build-25 beta-1
which adds tags 'build-25' and 'beta-1' for rev 42.
Worth noting:
- This changes the meaning of deprecated, but accepted, usage.
The undocumented form "hg tag arg1 arg2" used to emit a warning, then
add tag arg1 for rev arg2 (equivalent to "hg tag -r arg2 arg1"). That
form 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).
- When aborting, complain about all tag names with errors instead of just the
first one. This led to (mostly trivial) code to deal w/different
messages for single-tag vs. multiple-tag errors.
There is some email from December (had to put this on hold for a while):
http://www.selenic.com/pipermail/mercurial-devel/2007-December/003648.html
http://www.selenic.com/pipermail/mercurial-devel/2007-December/003655.html
http://www.selenic.com/pipermail/mercurial-devel/2007-December/003667.html
http://www.selenic.com/pipermail/mercurial-devel/2007-December/003820.html
http://www.selenic.com/pipermail/mercurial-devel/2007-December/003825.html
http://www.selenic.com/pipermail/mercurial-devel/2007-December/003828.html
-John
# HG changeset patch
# User John Coomes <john.coomes at sun.com>
# Date 1203750406 28800
# Node ID fa824ac29642ff702c4d920612ca5ea90135ab75
# Parent f857eac30cd54fc2249c8044388718687df7bed5
tag: allow multiple tags to be added or removed
- Example: "hg tag -r 42 build-25 beta-1" would add two tags for rev 42
- remove "hg tag NAME REV" deprecation warning; that syntax is now
interpreted as "hg tag NAME1 NAME2"
diff -r f857eac30cd5 -r fa824ac29642 mercurial/commands.py
--- a/mercurial/commands.py Thu Feb 21 20:56:06 2008 +0100
+++ b/mercurial/commands.py Fri Feb 22 23:06:46 2008 -0800
@@ -2532,13 +2532,13 @@
if copied:
ui.write(' %s%s' % (repo.pathto(copied, cwd), end))
-def tag(ui, repo, name, rev_=None, **opts):
- """add a tag for the current or given revision
+def tag(ui, repo, name1, *othernames, **opts):
+ """add one or more tags for the current or given revision
Name a particular revision using <name>.
Tags are used to name particular revisions of the repository and are
- very useful to compare different revision, to go back to significant
+ very useful to compare different revisions, to go back to significant
earlier versions or to mark branch points as releases, etc.
If no revision is given, the parent of the working directory is used,
@@ -2552,43 +2552,56 @@
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"))
+
+ def abort_nonempty(names, singular_msg, plural_msg):
+ '''if names is a non-empty list, abort with a message listing the
+ offending name or names'''
+ if not names:
+ return
+ if len(names) == 1:
+ raise util.Abort(singular_msg % ("'" + names[0] + "'"))
+ raise util.Abort(plural_msg % ("'" + "', '".join(names) + "'"))
+
+ rev_ = None
+ names = (name1,) + othernames
+
+ if othernames and len(names) != len({}.fromkeys(names)):
+ raise util.Abort(_('tag names must be unique'))
+ abort_nonempty([n for n in names if n in ['tip', '.', 'null']],
+ _('the name %s is reserved'),
+ _('the names %s are reserved'))
+
if opts['rev'] and opts['remove']:
raise util.Abort(_("--rev and --remove are incompatible"))
if opts['rev']:
rev_ = opts['rev']
message = opts['message']
+ tag_word = (len(names) > 1 and _('tags')) or _('tag')
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)
-
+ abort_nonempty([n for n in names if not repo.tagtype(n)],
+ _('tag %s does not exist'),
+ _('tags %s do not exist'))
+ expectedtype = opts['local'] and 'local' or 'global'
+ abort_nonempty([n for n in names if repo.tagtype(n) != expectedtype],
+ _('tag %s is not a ' + expectedtype + ' tag'),
+ _('tags %s are not ' + expectedtype + ' tags'))
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 %s %s') % (tag_word, ", ".join(names))
+ elif not opts['force']:
+ abort_nonempty([n for n in names if n in repo.tags()],
+ _('tag %s already exists (use -f to force)'),
+ _('tags %s already exist (use -f to force)'))
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 %s %s for changeset %s') %
+ (tag_word, ", ".join(names), short(r)))
- repo.tag(name, r, message, opts['local'], opts['user'], opts['date'])
+ repo.tag(names, r, message, opts['local'], opts['user'], opts['date'])
def tags(ui, repo):
"""list repository tags
@@ -3118,7 +3131,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,
diff -r f857eac30cd5 -r fa824ac29642 mercurial/localrepo.py
--- a/mercurial/localrepo.py Thu Feb 21 20:56:06 2008 +0100
+++ b/mercurial/localrepo.py Fri Feb 22 23:06:46 2008 -0800
@@ -124,21 +124,25 @@
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
+ allnames = "".join(names)
for c in self.tag_disallowed:
- if c in name:
+ if c in allnames:
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 +155,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 +177,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 +185,21 @@
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
+ 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
@@ -208,8 +214,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'''
diff -r f857eac30cd5 -r fa824ac29642 tests/test-globalopts.out
--- a/tests/test-globalopts.out Thu Feb 21 20:56:06 2008 +0100
+++ b/tests/test-globalopts.out Fri Feb 22 23:06:46 2008 -0800
@@ -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
diff -r f857eac30cd5 -r fa824ac29642 tests/test-help.out
--- a/tests/test-help.out Thu Feb 21 20:56:06 2008 +0100
+++ b/tests/test-help.out Fri Feb 22 23:06:46 2008 -0800
@@ -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
diff -r f857eac30cd5 -r fa824ac29642 tests/test-tag
--- a/tests/test-tag Thu Feb 21 20:56:06 2008 +0100
+++ b/tests/test-tag Fri Feb 22 23:06:46 2008 -0800
@@ -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" tip tap null nada . dot || echo "failed"
+hg tag -d "1000000 0" "bleah" || echo "failed"
+hg tag -d "1000000 0" "bleah" "blecch" || 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
diff -r f857eac30cd5 -r fa824ac29642 tests/test-tag.out
--- a/tests/test-tag.out Thu Feb 21 20:56:06 2008 +0100
+++ b/tests/test-tag.out Fri Feb 22 23:06:46 2008 -0800
@@ -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
-use of 'hg tag NAME [REV]' is deprecated, please use 'hg tag [-r REV] NAME' instead
+abort: the names 'tip', 'null', '.' are reserved
+failed
+abort: tag 'bleah' already exists (use -f to force)
+failed
+abort: tag 'bleah' already exists (use -f to force)
+failed
+abort: tag 'blecch' does not exist
+failed
+abort: tags 'blecch', 'blough' do not exist
+failed
0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah
0acdaf8983679e0aac16e811534eb49d7ee1f2b4 bleah0
+868cc8fbb43b754ad09fa109885d243fc49adae7 gack
+868cc8fbb43b754ad09fa109885d243fc49adae7 gawk
+868cc8fbb43b754ad09fa109885d243fc49adae7 gorp
+8990e39091eb986fa5930705ffb2bf68ddbe8133 gack
+0000000000000000000000000000000000000000 gack
+0000000000000000000000000000000000000000 gorp
3ecf002a1c572a2f3bb4e665417e60fca65bbd42 bleah1
0 files updated, 0 files merged, 1 files removed, 0 files unresolved
0acdaf8983679e0aac16e811534eb49d7ee1f2b4 foobar
diff -r f857eac30cd5 -r fa824ac29642 tests/test-tags.out
--- a/tests/test-tags.out Thu Feb 21 20:56:06 2008 +0100
+++ b/tests/test-tags.out Fri Feb 22 23:06:46 2008 -0800
@@ -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
More information about the Mercurial-devel
mailing list