# HG changeset patch # User Matt Mackall # Date 1174723047 18000 # Node ID cb6107f78b929aba1ca8866833a4ba85f5681ea5 # Parent 73c918c713003ff50b148e0ca381b22a3503e318# Parent 81402b2b294de1d7419cbe6662bd8080278545de Merge with crew diff --git a/mercurial/appendfile.py b/mercurial/appendfile.py deleted file mode 100644 --- a/mercurial/appendfile.py +++ /dev/null @@ -1,161 +0,0 @@ -# appendfile.py - special classes to make repo updates atomic -# -# Copyright 2006 Vadim Gelfer -# -# This software may be used and distributed according to the terms -# of the GNU General Public License, incorporated herein by reference. - -import cStringIO, changelog, errno, manifest, os, tempfile, util - -# writes to metadata files are ordered. reads: changelog, manifest, -# normal files. writes: normal files, manifest, changelog. - -# manifest contains pointers to offsets in normal files. changelog -# contains pointers to offsets in manifest. if reader reads old -# changelog while manifest or normal files are written, it has no -# pointers into new parts of those files that are maybe not consistent -# yet, so will not read them. - -# localrepo.addchangegroup thinks it writes changelog first, then -# manifest, then normal files (this is order they are available, and -# needed for computing linkrev fields), but uses appendfile to hide -# updates from readers. data not written to manifest or changelog -# until all normal files updated. write manifest first, then -# changelog. - -# with this write ordering, readers cannot see inconsistent view of -# repo during update. - -class appendfile(object): - '''implement enough of file protocol to append to revlog file. - appended data is written to temp file. reads and seeks span real - file and temp file. readers cannot see appended data until - writedata called.''' - - def __init__(self, fp, tmpname): - if tmpname: - self.tmpname = tmpname - self.tmpfp = util.posixfile(self.tmpname, 'ab+') - else: - fd, self.tmpname = tempfile.mkstemp(prefix="hg-appendfile-") - os.close(fd) - self.tmpfp = util.posixfile(self.tmpname, 'ab+') - self.realfp = fp - self.offset = fp.tell() - # real file is not written by anyone else. cache its size so - # seek and read can be fast. - self.realsize = util.fstat(fp).st_size - self.name = fp.name - - def end(self): - self.tmpfp.flush() # make sure the stat is correct - return self.realsize + util.fstat(self.tmpfp).st_size - - def tell(self): - return self.offset - - def flush(self): - self.tmpfp.flush() - - def close(self): - self.realfp.close() - self.tmpfp.close() - - def seek(self, offset, whence=0): - '''virtual file offset spans real file and temp file.''' - if whence == 0: - self.offset = offset - elif whence == 1: - self.offset += offset - elif whence == 2: - self.offset = self.end() + offset - - if self.offset < self.realsize: - self.realfp.seek(self.offset) - else: - self.tmpfp.seek(self.offset - self.realsize) - - def read(self, count=-1): - '''only trick here is reads that span real file and temp file.''' - fp = cStringIO.StringIO() - old_offset = self.offset - if self.offset < self.realsize: - s = self.realfp.read(count) - fp.write(s) - self.offset += len(s) - if count > 0: - count -= len(s) - if count != 0: - if old_offset != self.offset: - self.tmpfp.seek(self.offset - self.realsize) - s = self.tmpfp.read(count) - fp.write(s) - self.offset += len(s) - return fp.getvalue() - - def write(self, s): - '''append to temp file.''' - self.tmpfp.seek(0, 2) - self.tmpfp.write(s) - # all writes are appends, so offset must go to end of file. - self.offset = self.realsize + self.tmpfp.tell() - -class appendopener(object): - '''special opener for files that only read or append.''' - - def __init__(self, opener): - self.realopener = opener - # key: file name, value: appendfile name - self.tmpnames = {} - - def __call__(self, name, mode='r'): - '''open file.''' - - assert mode in 'ra+' - try: - realfp = self.realopener(name, 'r') - except IOError, err: - if err.errno != errno.ENOENT: raise - realfp = self.realopener(name, 'w+') - tmpname = self.tmpnames.get(name) - fp = appendfile(realfp, tmpname) - if tmpname is None: - self.tmpnames[name] = fp.tmpname - return fp - - def writedata(self): - '''copy data from temp files to real files.''' - # write .d file before .i file. - tmpnames = self.tmpnames.items() - tmpnames.sort() - for name, tmpname in tmpnames: - ifp = open(tmpname, 'rb') - ofp = self.realopener(name, 'a') - for chunk in util.filechunkiter(ifp): - ofp.write(chunk) - ifp.close() - os.unlink(tmpname) - del self.tmpnames[name] - ofp.close() - - def cleanup(self): - '''delete temp files (this discards unwritten data!)''' - for tmpname in self.tmpnames.values(): - os.unlink(tmpname) - -# files for changelog and manifest are in different appendopeners, so -# not mixed up together. - -class appendchangelog(changelog.changelog, appendopener): - def __init__(self, opener, version): - appendopener.__init__(self, opener) - changelog.changelog.__init__(self, self, version) - def checkinlinesize(self, fp, tr): - return - -class appendmanifest(manifest.manifest, appendopener): - def __init__(self, opener, version): - appendopener.__init__(self, opener) - manifest.manifest.__init__(self, self, version) - def checkinlinesize(self, fp, tr): - return diff --git a/mercurial/bundlerepo.py b/mercurial/bundlerepo.py --- a/mercurial/bundlerepo.py +++ b/mercurial/bundlerepo.py @@ -17,7 +17,7 @@ import changegroup, util, os, struct, bz import localrepo, changelog, manifest, filelog, revlog class bundlerevlog(revlog.revlog): - def __init__(self, opener, indexfile, datafile, bundlefile, + def __init__(self, opener, indexfile, bundlefile, linkmapper=None): # How it works: # to retrieve a revision, we need to know the offset of @@ -28,7 +28,7 @@ class bundlerevlog(revlog.revlog): # len(index[r]). If the tuple is bigger than 7, it is a bundle # (it is bigger since we store the node to which the delta is) # - revlog.revlog.__init__(self, opener, indexfile, datafile) + revlog.revlog.__init__(self, opener, indexfile) self.bundlefile = bundlefile self.basemap = {} def chunkpositer(): @@ -140,20 +140,19 @@ class bundlerevlog(revlog.revlog): class bundlechangelog(bundlerevlog, changelog.changelog): def __init__(self, opener, bundlefile): changelog.changelog.__init__(self, opener) - bundlerevlog.__init__(self, opener, self.indexfile, self.datafile, - bundlefile) + bundlerevlog.__init__(self, opener, self.indexfile, bundlefile) class bundlemanifest(bundlerevlog, manifest.manifest): def __init__(self, opener, bundlefile, linkmapper): manifest.manifest.__init__(self, opener) - bundlerevlog.__init__(self, opener, self.indexfile, self.datafile, - bundlefile, linkmapper) + bundlerevlog.__init__(self, opener, self.indexfile, bundlefile, + linkmapper) class bundlefilelog(bundlerevlog, filelog.filelog): def __init__(self, opener, path, bundlefile, linkmapper): filelog.filelog.__init__(self, opener, path) - bundlerevlog.__init__(self, opener, self.indexfile, self.datafile, - bundlefile, linkmapper) + bundlerevlog.__init__(self, opener, self.indexfile, bundlefile, + linkmapper) class bundlerepository(localrepo.localrepository): def __init__(self, ui, path, bundlename): diff --git a/mercurial/changelog.py b/mercurial/changelog.py --- a/mercurial/changelog.py +++ b/mercurial/changelog.py @@ -26,10 +26,100 @@ def _string_escape(text): def _string_unescape(text): return text.decode('string_escape') +class appender: + '''the changelog index must be update last on disk, so we use this class + to delay writes to it''' + def __init__(self, fp, buf): + self.data = buf + self.fp = fp + self.offset = fp.tell() + self.size = util.fstat(fp).st_size + + def end(self): + return self.size + len("".join(self.data)) + def tell(self): + return self.offset + def flush(self): + pass + def close(self): + close(self.fp) + + def seek(self, offset, whence=0): + '''virtual file offset spans real file and data''' + if whence == 0: + self.offset = offset + elif whence == 1: + self.offset += offset + elif whence == 2: + self.offset = self.end() + offset + if self.offset < self.size: + self.fp.seek(self.offset) + + def read(self, count=-1): + '''only trick here is reads that span real file and data''' + ret = "" + old_offset = self.offset + if self.offset < self.size: + s = self.fp.read(count) + ret = s + self.offset += len(s) + if count > 0: + count -= len(s) + if count != 0: + doff = self.offset - self.size + self.data.insert(0, "".join(self.data)) + del self.data[1:] + s = self.data[0][doff:doff+count] + self.offset += len(s) + ret += s + return ret + + def write(self, s): + self.data.append(s) + self.offset += len(s) + class changelog(revlog): - def __init__(self, opener, defversion=REVLOGV0): - revlog.__init__(self, opener, "00changelog.i", "00changelog.d", - defversion) + def __init__(self, opener): + revlog.__init__(self, opener, "00changelog.i") + + def delayupdate(self): + "delay visibility of index updates to other readers" + self._realopener = self.opener + self.opener = self._delayopener + self._delaycount = self.count() + self._delaybuf = [] + self._delayname = None + + def finalize(self, tr): + "finalize index updates" + self.opener = self._realopener + # move redirected index data back into place + if self._delayname: + util.rename(self._delayname + ".a", self._delayname) + elif self._delaybuf: + fp = self.opener(self.indexfile, 'a') + fp.write("".join(self._delaybuf)) + fp.close() + del self._delaybuf + # split when we're done + self.checkinlinesize(tr) + + def _delayopener(self, name, mode='r'): + fp = self._realopener(name, mode) + # only divert the index + if not name == self.indexfile: + return fp + # if we're doing an initial clone, divert to another file + if self._delaycount == 0: + self._delayname = fp.name + return self._realopener(name + ".a", mode) + # otherwise, divert to memory + return appender(fp, self._delaybuf) + + def checkinlinesize(self, tr, fp=None): + if self.opener == self._delayopener: + return + return revlog.checkinlinesize(self, tr, fp) def decode_extra(self, text): extra = {} diff --git a/mercurial/commands.py b/mercurial/commands.py --- a/mercurial/commands.py +++ b/mercurial/commands.py @@ -666,7 +666,7 @@ def copy(ui, repo, *pats, **opts): def debugancestor(ui, index, rev1, rev2): """find the ancestor revision of two revisions in a given index""" - r = revlog.revlog(util.opener(os.getcwd(), audit=False), index, "", 0) + r = revlog.revlog(util.opener(os.getcwd(), audit=False), index) a = r.ancestor(r.lookup(rev1), r.lookup(rev2)) ui.write("%d:%s\n" % (r.rev(a), hex(a))) @@ -794,9 +794,8 @@ def debugstate(ui, repo): ui.write(_("copy: %s -> %s\n") % (repo.dirstate.copied(f), f)) def debugdata(ui, file_, rev): - """dump the contents of an data file revision""" - r = revlog.revlog(util.opener(os.getcwd(), audit=False), - file_[:-2] + ".i", file_, 0) + """dump the contents of a data file revision""" + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_[:-2] + ".i") try: ui.write(r.revision(r.lookup(rev))) except KeyError: @@ -816,7 +815,7 @@ def debugdate(ui, date, range=None, **op def debugindex(ui, file_): """dump the contents of an index file""" - r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0) + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) ui.write(" rev offset length base linkrev" + " nodeid p1 p2\n") for i in xrange(r.count()): @@ -828,7 +827,7 @@ def debugindex(ui, file_): def debugindexdot(ui, file_): """dump an index DAG as a .dot file""" - r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_, "", 0) + r = revlog.revlog(util.opener(os.getcwd(), audit=False), file_) ui.write("digraph G {\n") for i in xrange(r.count()): node = r.node(i) @@ -2501,6 +2500,9 @@ def tag(ui, repo, name, rev_=None, **opt 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) if not rev_ and repo.dirstate.parents()[1] != nullid: raise util.Abort(_('uncommitted merge - please provide a ' 'specific revision')) @@ -2964,7 +2966,8 @@ table = { _('hg status [OPTION]... [FILE]...')), "tag": (tag, - [('l', 'local', None, _('make the tag local')), + [('f', 'force', None, _('replace existing tag')), + ('l', 'local', None, _('make the tag local')), ('m', 'message', '', _('message for tag commit log entry')), ('d', 'date', '', _('record datecode as commit date')), ('u', 'user', '', _('record user as commiter')), diff --git a/mercurial/filelog.py b/mercurial/filelog.py --- a/mercurial/filelog.py +++ b/mercurial/filelog.py @@ -9,11 +9,9 @@ from revlog import * import os class filelog(revlog): - def __init__(self, opener, path, defversion=REVLOG_DEFAULT_VERSION): + def __init__(self, opener, path): revlog.__init__(self, opener, - "/".join(("data", self.encodedir(path + ".i"))), - "/".join(("data", self.encodedir(path + ".d"))), - defversion) + "/".join(("data", self.encodedir(path + ".i")))) # This avoids a collision between a file named foo and a dir named # foo.i or foo.d diff --git a/mercurial/hgweb/hgweb_mod.py b/mercurial/hgweb/hgweb_mod.py --- a/mercurial/hgweb/hgweb_mod.py +++ b/mercurial/hgweb/hgweb_mod.py @@ -1018,7 +1018,7 @@ class hgweb(object): def do_capabilities(self, req): caps = ['lookup', 'changegroupsubset'] if self.configbool('server', 'uncompressed'): - caps.append('stream=%d' % self.repo.revlogversion) + caps.append('stream=%d' % self.repo.changelog.version) # XXX: make configurable and/or share code with do_unbundle: unbundleversions = ['HG10GZ', 'HG10BZ', 'HG10UN'] if unbundleversions: diff --git a/mercurial/localrepo.py b/mercurial/localrepo.py --- a/mercurial/localrepo.py +++ b/mercurial/localrepo.py @@ -7,7 +7,7 @@ from node import * from i18n import _ -import repo, appendfile, changegroup +import repo, changegroup import changelog, dirstate, filelog, manifest, context import re, lock, transaction, tempfile, stat, mdiff, errno, ui import os, revlog, time, util @@ -88,34 +88,14 @@ class localrepository(repo.repository): except IOError: pass - v = self.ui.configrevlog() - self.revlogversion = int(v.get('format', revlog.REVLOG_DEFAULT_FORMAT)) - self.revlogv1 = self.revlogversion != revlog.REVLOGV0 - fl = v.get('flags', None) - flags = 0 - if fl != None: - for x in fl.split(): - flags |= revlog.flagstr(x) - elif self.revlogv1: - flags = revlog.REVLOG_DEFAULT_FLAGS - - v = self.revlogversion | flags - self.manifest = manifest.manifest(self.sopener, v) - self.changelog = changelog.changelog(self.sopener, v) + self.changelog = changelog.changelog(self.sopener) + self.sopener.defversion = self.changelog.version + self.manifest = manifest.manifest(self.sopener) fallback = self.ui.config('ui', 'fallbackencoding') if fallback: util._fallbackencoding = fallback - # the changelog might not have the inline index flag - # on. If the format of the changelog is the same as found in - # .hgrc, apply any flags found in the .hgrc as well. - # Otherwise, just version from the changelog - v = self.changelog.version - if v == self.revlogversion: - v |= flags - self.revlogversion = v - self.tagscache = None self.branchcache = None self.nodetagscache = None @@ -304,10 +284,10 @@ class localrepository(repo.repository): warn(_("tag '%s' refers to unknown node") % key) continue - h = {} + h = [] if key in filetags: n, h = filetags[key] - h[n] = True + h.append(n) filetags[key] = (bin_n, h) for k,nh in filetags.items(): @@ -323,7 +303,7 @@ class localrepository(repo.repository): if bn != an and an in bh and \ (bn not in ah or len(bh) > len(ah)): an = bn - ah.update(bh) + ah.append([n for n in bh if n not in ah]) globaltags[k] = an, ah # read the tags file from each head, ending with the tip @@ -488,7 +468,7 @@ class localrepository(repo.repository): def file(self, f): if f[0] == '/': f = f[1:] - return filelog.filelog(self.sopener, f, self.revlogversion) + return filelog.filelog(self.sopener, f) def changectx(self, changeid=None): return context.changectx(self, changeid) @@ -1801,55 +1781,45 @@ class localrepository(repo.repository): # write changelog data to temp files so concurrent readers will not see # inconsistent view - cl = None - try: - cl = appendfile.appendchangelog(self.sopener, - self.changelog.version) + cl = self.changelog + cl.delayupdate() + oldheads = len(cl.heads()) - oldheads = len(cl.heads()) + # pull off the changeset group + self.ui.status(_("adding changesets\n")) + cor = cl.count() - 1 + chunkiter = changegroup.chunkiter(source) + if cl.addgroup(chunkiter, csmap, tr, 1) is None: + raise util.Abort(_("received changelog group is empty")) + cnr = cl.count() - 1 + changesets = cnr - cor - # pull off the changeset group - self.ui.status(_("adding changesets\n")) - cor = cl.count() - 1 - chunkiter = changegroup.chunkiter(source) - if cl.addgroup(chunkiter, csmap, tr, 1) is None: - raise util.Abort(_("received changelog group is empty")) - cnr = cl.count() - 1 - changesets = cnr - cor + # pull off the manifest group + self.ui.status(_("adding manifests\n")) + chunkiter = changegroup.chunkiter(source) + # no need to check for empty manifest group here: + # if the result of the merge of 1 and 2 is the same in 3 and 4, + # no new manifest will be created and the manifest group will + # be empty during the pull + self.manifest.addgroup(chunkiter, revmap, tr) - # pull off the manifest group - self.ui.status(_("adding manifests\n")) + # process the files + self.ui.status(_("adding file changes\n")) + while 1: + f = changegroup.getchunk(source) + if not f: + break + self.ui.debug(_("adding %s revisions\n") % f) + fl = self.file(f) + o = fl.count() chunkiter = changegroup.chunkiter(source) - # no need to check for empty manifest group here: - # if the result of the merge of 1 and 2 is the same in 3 and 4, - # no new manifest will be created and the manifest group will - # be empty during the pull - self.manifest.addgroup(chunkiter, revmap, tr) - - # process the files - self.ui.status(_("adding file changes\n")) - while 1: - f = changegroup.getchunk(source) - if not f: - break - self.ui.debug(_("adding %s revisions\n") % f) - fl = self.file(f) - o = fl.count() - chunkiter = changegroup.chunkiter(source) - if fl.addgroup(chunkiter, revmap, tr) is None: - raise util.Abort(_("received file revlog group is empty")) - revisions += fl.count() - o - files += 1 - - cl.writedata() - finally: - if cl: - cl.cleanup() + if fl.addgroup(chunkiter, revmap, tr) is None: + raise util.Abort(_("received file revlog group is empty")) + revisions += fl.count() - o + files += 1 # make changelog see real files again - self.changelog = changelog.changelog(self.sopener, - self.changelog.version) - self.changelog.checkinlinesize(tr) + cl.finalize(tr) newheads = len(self.changelog.heads()) heads = "" diff --git a/mercurial/manifest.py b/mercurial/manifest.py --- a/mercurial/manifest.py +++ b/mercurial/manifest.py @@ -35,11 +35,10 @@ class manifestdict(dict): return manifestdict(dict.copy(self), dict.copy(self._flags)) class manifest(revlog): - def __init__(self, opener, defversion=REVLOGV0): + def __init__(self, opener): self.mapcache = None self.listcache = None - revlog.__init__(self, opener, "00manifest.i", "00manifest.d", - defversion) + revlog.__init__(self, opener, "00manifest.i") def parselines(self, lines): for l in lines.splitlines(1): diff --git a/mercurial/revlog.py b/mercurial/revlog.py --- a/mercurial/revlog.py +++ b/mercurial/revlog.py @@ -311,8 +311,7 @@ class revlog(object): remove data, and can use some simple techniques to avoid the need for locking while reading. """ - def __init__(self, opener, indexfile, datafile, - defversion=REVLOG_DEFAULT_VERSION): + def __init__(self, opener, indexfile): """ create a revlog object @@ -320,13 +319,15 @@ class revlog(object): and can be used to implement COW semantics or the like. """ self.indexfile = indexfile - self.datafile = datafile + self.datafile = indexfile[:-2] + ".d" self.opener = opener self.indexstat = None self.cache = None self.chunkcache = None - self.defversion = defversion + self.defversion=REVLOG_DEFAULT_VERSION + if hasattr(opener, "defversion"): + self.defversion = opener.defversion self.load() def load(self): diff --git a/mercurial/sshserver.py b/mercurial/sshserver.py --- a/mercurial/sshserver.py +++ b/mercurial/sshserver.py @@ -73,7 +73,7 @@ class sshserver(object): caps = ['unbundle', 'lookup', 'changegroupsubset'] if self.ui.configbool('server', 'uncompressed'): - caps.append('stream=%d' % self.repo.revlogversion) + caps.append('stream=%d' % self.repo.changelog.version) self.respond("capabilities: %s\n" % (' '.join(caps),)) def do_lock(self): diff --git a/mercurial/statichttprepo.py b/mercurial/statichttprepo.py --- a/mercurial/statichttprepo.py +++ b/mercurial/statichttprepo.py @@ -32,7 +32,6 @@ class statichttprepository(localrepo.loc def __init__(self, ui, path): self._url = path self.ui = ui - self.revlogversion = 0 self.path = (path + "/.hg") self.opener = opener(self.path) diff --git a/mercurial/ui.py b/mercurial/ui.py --- a/mercurial/ui.py +++ b/mercurial/ui.py @@ -325,12 +325,6 @@ class ui(object): result.append(os.path.expanduser(value)) return result - def configrevlog(self): - result = {} - for key, value in self.configitems("revlog"): - result[key.lower()] = value - return result - def username(self): """Return default username to be used in commits. diff --git a/mercurial/verify.py b/mercurial/verify.py --- a/mercurial/verify.py +++ b/mercurial/verify.py @@ -39,8 +39,8 @@ def verify(repo): elif revlogv1: warn(_("warning: `%s' uses revlog format 0") % name) - revlogv1 = repo.revlogversion != revlog.REVLOGV0 - if repo.ui.verbose or revlogv1 != repo.revlogv1: + revlogv1 = repo.changelog.version != revlog.REVLOGV0 + if repo.ui.verbose or not revlogv1: repo.ui.status(_("repository uses revlog format %d\n") % (revlogv1 and 1 or 0)) diff --git a/tests/test-hup.out b/tests/test-hup.out --- a/tests/test-hup.out +++ b/tests/test-hup.out @@ -4,4 +4,4 @@ adding changesets killed! transaction abort! rollback completed -.hg/00changelog.i .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i +.hg/00changelog.i .hg/journal.dirstate .hg/requires .hg/store .hg/store/00changelog.i .hg/store/00changelog.i.a diff --git a/tests/test-tags b/tests/test-tags --- a/tests/test-tags +++ b/tests/test-tags @@ -74,12 +74,34 @@ hg tag -d '1000000 0' bar # echo >> foo hg ci -m 'change foo 1' -d '1000000 0' # rev 2 hg up -C 1 -hg tag -r 1 -d '1000000 0' bar # rev 3 +hg tag -r 1 -d '1000000 0' -f bar # rev 3 hg up -C 1 echo >> foo hg ci -m 'change foo 2' -d '1000000 0' # rev 4 hg tags +# test tag removal hg tag --remove -d '1000000 0' bar hg tip hg tags + +# test tag rank +cd .. +hg init t3 +cd t3 +echo foo > foo +hg add foo +hg ci -m 'add foo' -d '1000000 0' # rev 0 +hg tag -d '1000000 0' -f bar # rev 1 bar -> 0 +hg tag -d '1000000 0' -f bar # rev 2 bar -> 1 +hg tag -d '1000000 0' -fr 0 bar # rev 3 bar -> 0 +hg tag -d '1000000 0' -fr 1 bar # rev 3 bar -> 1 +hg tag -d '1000000 0' -fr 0 bar # rev 4 bar -> 0 +hg tags +hg co 3 +echo barbar > foo +hg ci -m 'change foo' -d '1000000 0' # rev 0 +hg tags + +hg tag -d '1000000 0' -r 3 bar # should complain +hg tags \ No newline at end of file diff --git a/tests/test-tags.out b/tests/test-tags.out --- a/tests/test-tags.out +++ b/tests/test-tags.out @@ -49,3 +49,11 @@ date: Mon Jan 12 13:46:40 1970 +0 summary: Removed tag bar tip 5:57e1983b4a60 +tip 5:d8bb4d1eff25 +bar 0:b409d9da318e +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) +tip 6:b5ff9d142648 +bar 0:b409d9da318e