convert: remove tags from convert

Summary:
A future diff will remove the tags feature. Convert uses tags heavily
and it breaks a number of tests, so let's remove tags from the convert extension
before we remove tags entirely.

Reviewed By: quark-zju

Differential Revision: D19043330

fbshipit-source-id: 628d27fea1601931da45d1280eff981c5d12f93c
This commit is contained in:
Durham Goode 2019-12-17 15:01:37 -08:00 committed by Facebook Github Bot
parent 245f179697
commit ff41e331e0
16 changed files with 32 additions and 522 deletions

View File

@ -211,25 +211,19 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
Subversion Source
#################
Subversion source detects classical trunk/branches/tags layouts.
By default, the supplied ``svn://repo/path/`` source URL is
converted as a single branch. If ``svn://repo/path/trunk`` exists
it replaces the default branch. If ``svn://repo/path/branches``
exists, its subdirectories are listed as possible branches. If
``svn://repo/path/tags`` exists, it is looked for tags referencing
converted branches. Default ``trunk``, ``branches`` and ``tags``
values can be overridden with following options. Set them to paths
relative to the source URL, or leave them blank to disable auto
detection.
Subversion source detects classical trunk/branches layouts. By default, the
supplied ``svn://repo/path/`` source URL is converted as a single branch. If
``svn://repo/path/trunk`` exists it replaces the default branch. If
``svn://repo/path/branches`` exists, its subdirectories are listed as
possible branches. Default ``trunk`` and ``branches`` values can be
overridden with following options. Set them to paths relative to the source
URL, or leave them blank to disable auto detection.
The following options can be set with ``--config``:
:convert.svn.branches: specify the directory containing branches.
The default is ``branches``.
:convert.svn.tags: specify the directory containing tags. The
default is ``tags``.
:convert.svn.trunk: specify the name of the trunk branch. The
default is ``trunk``.
@ -346,22 +340,11 @@ def convert(ui, src, dest=None, revmapfile=None, **opts):
:convert.hg.clonebranches: dispatch source branches in separate
clones. The default is False.
:convert.hg.tagsbranch: branch name for tag revisions, defaults to
``default``.
:convert.hg.usebranchnames: preserve branch names. The default is
True.
:convert.hg.sourcename: records the given string as a 'convert_source' extra
value on each commit made in the target repository. The default is None.
All Destinations
################
All destination types accept the following options:
:convert.skiptags: does not convert tags from the source repo to the target
repo. The default is False.
"""
return convcmd.convert(ui, src, dest, revmapfile, **opts)

View File

@ -184,16 +184,6 @@ class bzr_source(common.converter_source):
rev=version,
)
def gettags(self):
bytetags = {}
for branch in self._bzrbranches():
if not branch.supports_tags():
return {}
tagdict = branch.tags.get_tag_dict()
for name, rev in tagdict.iteritems():
bytetags[self.recode(name)] = rev
return bytetags
def getchangedfiles(self, rev, i):
self._modecache = {}
curtree = self.sourcerepo.revision_tree(rev)

View File

@ -168,13 +168,6 @@ class converter_source(object):
"""
return None
def gettags(self):
"""Return the tags as a dictionary of name: revision
Tag names must be UTF-8 strings.
"""
raise NotImplementedError
def recode(self, s, encoding=None):
if not encoding:
encoding = self.encoding or "utf-8"
@ -284,15 +277,6 @@ class converter_sink(object):
"""
raise NotImplementedError
def puttags(self, tags):
"""Put tags into sink.
tags: {tagname: sink_rev_id, ...} where tagname is an UTF-8 string.
Return a pair (tag_revision, tag_parent_revision), or (None, None)
if nothing was changed.
"""
raise NotImplementedError
def setbranch(self, branch, pbranches):
"""Set the current branch name. Called before the first putcommit
on the branch.

View File

@ -524,25 +524,6 @@ class converter(object):
prog.value = i
self.copy(c)
if not self.ui.configbool("convert", "skiptags"):
tags = self.source.gettags()
ctags = {}
for k in tags:
v = tags[k]
if self.map.get(v, SKIPREV) != SKIPREV:
ctags[k] = self.map[v]
if c and ctags:
nrev, tagsparent = self.dest.puttags(ctags)
if nrev and tagsparent:
# write another hash correspondence to override the
# previous one so we don't end up with extra tag heads
tagsparents = [
e for e in self.map.iteritems() if e[1] == tagsparent
]
if tagsparents:
self.map[tagsparents[0][0]] = nrev
bookmarks = self.source.getbookmarks()
cbookmarks = {}
for k in bookmarks:

View File

@ -85,7 +85,6 @@ class darcs_source(common.converter_source, common.commandline):
self.lastrev = None
self.changes = {}
self.parents = {}
self.tags = {}
# Check darcs repository format
format = self.format()
@ -105,16 +104,10 @@ class darcs_source(common.converter_source, common.commandline):
self.checkexit(status)
tree = self.xml("changes", xml_output=True, summary=True, repodir=self.path)
tagname = None
child = None
for elt in tree.findall("patch"):
node = elt.get("hash")
name = elt.findtext("name", "")
if name.startswith("TAG "):
tagname = name[4:].strip()
elif tagname is not None:
self.tags[tagname] = node
tagname = None
self.changes[node] = elt
self.parents[child] = [node]
child = node
@ -246,6 +239,3 @@ class darcs_source(common.converter_source, common.commandline):
raise
mode = (mode & 0o111) and "x" or ""
return data, mode
def gettags(self):
return self.tags

View File

@ -467,9 +467,6 @@ class filemap_source(common.converter_source):
realname, realrev = rev
return self.base.getfile(realname, realrev)
def gettags(self):
return self.base.gettags()
def hasnativeorder(self):
return self.base.hasnativeorder()

View File

@ -388,37 +388,6 @@ class convert_git(common.converter_source, common.commandline):
raise error.Abort(_("cannot retrieve number of commits in %s") % self.path)
return len(output)
def gettags(self):
tags = {}
alltags = {}
output, status = self.gitrunlines("ls-remote", "--tags", self.path)
if status:
raise error.Abort(_("cannot read tags from %s") % self.path)
prefix = "refs/tags/"
# Build complete list of tags, both annotated and bare ones
for line in output:
line = line.strip()
if line.startswith("error:") or line.startswith("fatal:"):
raise error.Abort(_("cannot read tags from %s") % self.path)
node, tag = line.split(None, 1)
if not tag.startswith(prefix):
continue
alltags[tag[len(prefix) :]] = node
# Filter out tag objects for annotated tag refs
for tag in alltags:
if tag.endswith("^{}"):
tags[tag[:-3]] = alltags[tag]
else:
if tag + "^{}" in alltags:
continue
else:
tags[tag] = alltags[tag]
return tags
def getchangedfiles(self, version, i):
changes = []
if i is None:

View File

@ -63,7 +63,6 @@ class gnuarch_source(common.converter_source, common.commandline):
self.lastrev = None
self.changes = {}
self.parents = {}
self.tags = {}
self.catlogparser = email.Parser.Parser()
self.encoding = encoding.encoding
self.archives = []
@ -196,9 +195,6 @@ class gnuarch_source(common.converter_source, common.commandline):
rev=rev,
)
def gettags(self):
return self.tags
def _execute(self, cmd, *args, **kwargs):
cmdline = [self.execmd, cmd]
cmdline += args
@ -299,7 +295,7 @@ class gnuarch_source(common.converter_source, common.commandline):
)
self.changes[rev].summary = self.recode(self.changes[rev].summary)
# Commit revision origin when dealing with a branch or tag
# Commit revision origin when dealing with a branch
if "Continuation-of" in catlog:
self.changes[rev].continuationof = self.recode(
catlog["Continuation-of"]

View File

@ -59,7 +59,6 @@ class mercurial_sink(common.converter_sink):
common.converter_sink.__init__(self, ui, repotype, path)
self.branchnames = ui.configbool("convert", "hg.usebranchnames")
self.clonebranches = ui.configbool("convert", "hg.clonebranches")
self.tagsbranch = ui.config("convert", "hg.tagsbranch")
self.lastbranch = None
if os.path.isdir(path) and len(os.listdir(path)) > 0:
try:
@ -143,21 +142,6 @@ class mercurial_sink(common.converter_sink):
exchange.pull(self.repo, prepo, [prepo.lookup(h) for h in heads])
self.before()
def _rewritetags(self, source, revmap, data):
fp = stringio()
for line in data.splitlines():
s = line.split(" ", 1)
if len(s) != 2:
continue
revid = revmap.get(source.lookuprev(s[0]))
if not revid:
if s[0] == nodemod.nullhex:
revid = s[0]
else:
continue
fp.write("%s %s\n" % (revid, s[1]))
return fp.getvalue()
def _calculatemergedfiles(self, source, p1ctx, p2ctx):
"""Calculates the files from p2 that we need to pull in when merging p1
and p2, given that the merge is coming from the given source.
@ -222,8 +206,6 @@ class mercurial_sink(common.converter_sink):
data, mode = source.getfile(f, v)
if data is None:
return None
if f == ".hgtags":
data = self._rewritetags(source, revmap, data)
return context.memfilectx(
self.repo, memctx, f, data, "l" in mode, "x" in mode, copies.get(f)
)
@ -349,66 +331,6 @@ class mercurial_sink(common.converter_sink):
return parent
return p2
def puttags(self, tags):
try:
parentctx = self.repo[self.tagsbranch]
tagparent = parentctx.node()
except error.RepoError:
parentctx = None
tagparent = nodemod.nullid
oldlines = set()
for branch, heads in self.repo.branchmap().iteritems():
for h in heads:
if ".hgtags" in self.repo[h]:
oldlines.update(
set(self.repo[h][".hgtags"].data().splitlines(True))
)
oldlines = sorted(list(oldlines))
newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
if newlines == oldlines:
return None, None
# if the old and new tags match, then there is nothing to update
oldtags = set()
newtags = set()
for line in oldlines:
s = line.strip().split(" ", 1)
if len(s) != 2:
continue
oldtags.add(s[1])
for line in newlines:
s = line.strip().split(" ", 1)
if len(s) != 2:
continue
if s[1] not in oldtags:
newtags.add(s[1].strip())
if not newtags:
return None, None
data = "".join(newlines)
def getfilectx(repo, memctx, f):
return context.memfilectx(repo, memctx, f, data, False, False, None)
self.ui.status(_("updating tags\n"))
date = "%s 0" % int(time.mktime(time.gmtime()))
extra = {"branch": self.tagsbranch}
ctx = context.memctx(
self.repo,
(tagparent, None),
"update tags",
[".hgtags"],
getfilectx,
"convert-repo",
date,
extra,
)
node = self.repo.commitctx(ctx)
return nodemod.hex(node), nodemod.hex(tagparent)
def setfilemapmode(self, active):
self.filemapmode = active
@ -604,13 +526,6 @@ class mercurial_source(common.converter_source):
phase=ctx.phase(),
)
def gettags(self):
# This will get written to .hgtags, filter non global tags out.
tags = [t for t in self.repo.tagslist() if self.repo.tagtype(t[0]) == "global"]
return dict(
[(name, nodemod.hex(node)) for name, node in tags if self.keep(node)]
)
def getchangedfiles(self, rev, i):
ctx = self._changectx(rev)
parents = self._parents(ctx)

View File

@ -375,8 +375,5 @@ class p4_source(common.converter_source):
return self._construct_commit(d, parents=None)
raise error.Abort(_("cannot find %s in the revmap or parsed changesets") % rev)
def gettags(self):
return {}
def getchangedfiles(self, rev, i):
return sorted([x[0] for x in self.files[rev]])

View File

@ -1012,12 +1012,6 @@ class repo_source(common.converter_source):
rawcount = functools.reduce(sumfn, output, 0)
return 3 * rawcount # 1 for each of rooted, dirred and unified
def gettags(self):
"""See common.converter_source.gettags"""
# TODO: Convert to manifest tags only?
# tagoutput = self.repo.forallbyproject("git tag")
return []
def getchangedfiles(self, rev, i):
"""See common.converter_source.getchangedfiles"""
if rev is None:

View File

@ -496,7 +496,6 @@ class svn_source(converter_source):
rev = optrev(self.last_changed)
oldmodule = ""
trunk = getcfgpath("trunk", rev)
self.tags = getcfgpath("tags", rev)
branches = getcfgpath("branches", rev)
# If the project has a trunk or branches, we will extract heads
@ -510,8 +509,6 @@ class svn_source(converter_source):
# First head in the list is the module's head
self.heads = [self.head]
if self.tags is not None:
self.tags = "%s/%s" % (oldmodule, (self.tags or "tags"))
# Check if branches bring a few more heads to the list
if branches:
@ -622,111 +619,6 @@ class svn_source(converter_source):
def numcommits(self):
return int(self.head.rsplit("@", 1)[1]) - self.startrev
def gettags(self):
tags = {}
if self.tags is None:
return tags
# svn tags are just a convention, project branches left in a
# 'tags' directory. There is no other relationship than
# ancestry, which is expensive to discover and makes them hard
# to update incrementally. Worse, past revisions may be
# referenced by tags far away in the future, requiring a deep
# history traversal on every calculation. Current code
# performs a single backward traversal, tracking moves within
# the tags directory (tag renaming) and recording a new tag
# everytime a project is copied from outside the tags
# directory. It also lists deleted tags, this behaviour may
# change in the future.
pendings = []
tagspath = self.tags
start = svn.ra.get_latest_revnum(self.ra)
stream = self._getlog([self.tags], start, self.startrev)
try:
for entry in stream:
origpaths, revnum, author, date, message = entry
if not origpaths:
origpaths = []
copies = [
(e.copyfrom_path, e.copyfrom_rev, p)
for p, e in origpaths.iteritems()
if e.copyfrom_path
]
# Apply moves/copies from more specific to general
copies.sort(reverse=True)
srctagspath = tagspath
if copies and copies[-1][2] == tagspath:
# Track tags directory moves
srctagspath = copies.pop()[0]
for source, sourcerev, dest in copies:
if not dest.startswith(tagspath + "/"):
continue
for tag in pendings:
if tag[0].startswith(dest):
tagpath = source + tag[0][len(dest) :]
tag[:2] = [tagpath, sourcerev]
break
else:
pendings.append([source, sourcerev, dest])
# Filter out tags with children coming from different
# parts of the repository like:
# /tags/tag.1 (from /trunk:10)
# /tags/tag.1/foo (from /branches/foo:12)
# Here/tags/tag.1 discarded as well as its children.
# It happens with tools like cvs2svn. Such tags cannot
# be represented in mercurial.
addeds = dict(
(p, e.copyfrom_path)
for p, e in origpaths.iteritems()
if e.action == "A" and e.copyfrom_path
)
badroots = set()
for destroot in addeds:
for source, sourcerev, dest in pendings:
if not dest.startswith(destroot + "/") or source.startswith(
addeds[destroot] + "/"
):
continue
badroots.add(destroot)
break
for badroot in badroots:
pendings = [
p
for p in pendings
if p[2] != badroot and not p[2].startswith(badroot + "/")
]
# Tell tag renamings from tag creations
renamings = []
for source, sourcerev, dest in pendings:
tagname = dest.split("/")[-1]
if source.startswith(srctagspath):
renamings.append([source, sourcerev, tagname])
continue
if tagname in tags:
# Keep the latest tag value
continue
# From revision may be fake, get one with changes
try:
tagid = self.latest(source, sourcerev)
if tagid and tagname not in tags:
tags[tagname] = tagid
except SvnPathNotFound:
# It happens when we are following directories
# we assumed were copied with their parents
# but were really created in the tag
# directory.
pass
pendings = renamings
tagspath = srctagspath
finally:
stream.close()
return tags
def converted(self, rev, destrev):
if not self.wc:
return
@ -1470,10 +1362,6 @@ class svn_sink(converter_sink, commandline):
finally:
os.unlink(messagefile)
def puttags(self, tags):
self.ui.warn(_("writing Subversion tags is not yet implemented\n"))
return None, None
def hascommitfrommap(self, rev):
# We trust that revisions referenced in a map still is present
# TODO: implement something better if necessary and feasible

View File

@ -19,17 +19,8 @@
$ mkdir foo
$ echo file > foo/file
$ hg ci -qAm 'add foo/file'
$ hg tag some-tag
$ hg tag -l local-tag
$ hg log
changeset: 3:593cbf6fb2b4
tag: local-tag
user: test
date: Thu Jan 01 00:00:00 1970 +0000
summary: Added tag some-tag for changeset ad681a868e44
changeset: 2:ad681a868e44
tag: some-tag
user: test
date: Thu Jan 01 00:00:00 1970 +0000
summary: add foo/file
@ -51,14 +42,11 @@
scanning source...
sorting...
converting...
3 add foo and bar
2 remove foo
1 add foo/file
0 Added tag some-tag for changeset ad681a868e44
2 add foo and bar
1 remove foo
0 add foo/file
$ cd new
$ hg log -G --template '{rev} {node|short} ({phase}) "{desc}"\n'
o 3 593cbf6fb2b4 (public) "Added tag some-tag for changeset ad681a868e44"
|
o 2 ad681a868e44 (public) "add foo/file"
|
o 1 cbba8ecc03b7 (public) "remove foo"
@ -77,7 +65,7 @@ dirstate should be empty:
$ hg debugstate
$ hg parents -q
$ hg up -C
3 files updated, 0 files merged, 0 files removed, 0 files unresolved
2 files updated, 0 files merged, 0 files removed, 0 files unresolved
$ hg copy bar baz
put something in the dirstate:
@ -118,27 +106,6 @@ no copies
baz not renamed
$ cd ..
test tag rewriting
$ cat > filemap <<EOF
> exclude foo
> EOF
$ hg convert --filemap filemap orig new-filemap 2>&1 | grep -v 'subversion python bindings could not be loaded'
initializing destination new-filemap repository
scanning source...
sorting...
converting...
4 add foo and bar
3 remove foo
2 add foo/file
1 Added tag some-tag for changeset ad681a868e44
0 add baz
$ cd new-filemap
$ hg tags
some-tag 0:ba8636729451
$ cd ..
Test cases for hg-hg roundtrip
Helper

View File

@ -42,12 +42,6 @@ Test that template can print all file copies (issue4362)
(branch merge, don't forget to commit)
$ hg ci -m 'merge remote copy' -d '4 0'
Make and delete some tags
$ hg tag that
$ hg tag --remove that
$ hg tag this
#if execbit
$ chmod +x baz
#else
@ -60,14 +54,11 @@ Make and delete some tags
scanning source...
sorting...
converting...
8 add foo bar
7 change foo
6 make bar and baz copies of foo
5 merge local copy
4 merge remote copy
3 Added tag that for changeset 88586c4e9f02
2 Removed tag that
1 Added tag this for changeset c56a7f387039
5 add foo bar
4 change foo
3 make bar and baz copies of foo
2 merge local copy
1 merge remote copy
0 mark baz executable
updating bookmarks
$ cd new
@ -79,12 +70,12 @@ Make and delete some tags
#if execbit
$ hg bookmarks
premerge1 3:973ef48a98a4
premerge2 8:91d107c423ba
premerge2 5:13d9b87cf8f8
#else
Different hash because no x bit
$ hg bookmarks
premerge1 3:973ef48a98a4
premerge2 8:3537b15eaaca
premerge2 5:13d9b87cf8f8
#endif
Test that redoing a convert results in an identical graph
@ -94,24 +85,15 @@ Test that redoing a convert results in an identical graph
scanning source...
sorting...
converting...
8 add foo bar
7 change foo
6 make bar and baz copies of foo
5 merge local copy
4 merge remote copy
3 Added tag that for changeset 88586c4e9f02
2 Removed tag that
1 Added tag this for changeset c56a7f387039
5 add foo bar
4 change foo
3 make bar and baz copies of foo
2 merge local copy
1 merge remote copy
0 mark baz executable
updating bookmarks
$ hg -R new log -G -T '{rev} {desc}'
o 8 mark baz executable
|
o 7 Added tag this for changeset c56a7f387039
|
o 6 Removed tag that
|
o 5 Added tag that for changeset 88586c4e9f02
o 5 mark baz executable
|
o 4 merge remote copy
|\

View File

@ -1,107 +0,0 @@
#chg-compatible
$ . helpers-usechg.sh
#require git
$ echo "[core]" >> $HOME/.gitconfig
$ echo "autocrlf = false" >> $HOME/.gitconfig
$ echo "[core]" >> $HOME/.gitconfig
$ echo "autocrlf = false" >> $HOME/.gitconfig
$ cat <<EOF >> $HGRCPATH
> [extensions]
> convert =
> [convert]
> hg.usebranchnames = False
> hg.tagsbranch = tags-update
> EOF
$ GIT_AUTHOR_NAME='test'; export GIT_AUTHOR_NAME
$ GIT_AUTHOR_EMAIL='test@example.org'; export GIT_AUTHOR_EMAIL
$ GIT_AUTHOR_DATE="2007-01-01 00:00:00 +0000"; export GIT_AUTHOR_DATE
$ GIT_COMMITTER_NAME="$GIT_AUTHOR_NAME"; export GIT_COMMITTER_NAME
$ GIT_COMMITTER_EMAIL="$GIT_AUTHOR_EMAIL"; export GIT_COMMITTER_EMAIL
$ GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"; export GIT_COMMITTER_DATE
$ count=10
$ action()
> {
> GIT_AUTHOR_DATE="2007-01-01 00:00:$count +0000"
> GIT_COMMITTER_DATE="$GIT_AUTHOR_DATE"
> git "$@" >/dev/null 2>/dev/null || echo "git command error"
> count=`expr $count + 1`
> }
$ glog()
> {
> hg log -G --template '{rev} "{desc|firstline}" files: {files}\n' "$@"
> }
$ convertrepo()
> {
> hg convert --datesort git-repo hg-repo
> }
Build a GIT repo with at least 1 tag
$ mkdir git-repo
$ cd git-repo
$ git init >/dev/null 2>&1
$ echo a > a
$ git add a
$ action commit -m "rev1"
$ action tag -m "tag1" tag1
$ cd ..
Convert without tags
$ hg convert git-repo hg-repo --config convert.skiptags=True
initializing destination hg-repo repository
scanning source...
sorting...
converting...
0 rev1
updating bookmarks
$ hg -R hg-repo tags
$ rm -rf hg-repo
Do a first conversion
$ convertrepo
initializing destination hg-repo repository
scanning source...
sorting...
converting...
0 rev1
updating tags
updating bookmarks
Simulate upstream updates after first conversion
$ cd git-repo
$ echo b > a
$ git add a
$ action commit -m "rev2"
$ action tag -m "tag2" tag2
$ cd ..
Perform an incremental conversion
$ convertrepo
scanning source...
sorting...
converting...
0 rev2
updating tags
updating bookmarks
Print the log
$ cd hg-repo
$ glog
o 3 "update tags" files: .hgtags
o 2 "rev2" files: a
|
| o 1 "update tags" files: .hgtags
|
o 0 "rev1" files: a
$ cd ..

View File

@ -154,24 +154,19 @@
Subversion Source
#################
Subversion source detects classical trunk/branches/tags layouts. By
default, the supplied "svn://repo/path/" source URL is converted as a
single branch. If "svn://repo/path/trunk" exists it replaces the default
branch. If "svn://repo/path/branches" exists, its subdirectories are
listed as possible branches. If "svn://repo/path/tags" exists, it is
looked for tags referencing converted branches. Default "trunk",
"branches" and "tags" values can be overridden with following options. Set
them to paths relative to the source URL, or leave them blank to disable
auto detection.
Subversion source detects classical trunk/branches layouts. By default,
the supplied "svn://repo/path/" source URL is converted as a single
branch. If "svn://repo/path/trunk" exists it replaces the default branch.
If "svn://repo/path/branches" exists, its subdirectories are listed as
possible branches. Default "trunk" and "branches" values can be overridden
with following options. Set them to paths relative to the source URL, or
leave them blank to disable auto detection.
The following options can be set with "--config":
convert.svn.branches
specify the directory containing branches. The default is
"branches".
convert.svn.tags
specify the directory containing tags. The default is
"tags".
convert.svn.trunk
specify the name of the trunk branch. The default is
"trunk".
@ -293,8 +288,6 @@
convert.hg.clonebranches
dispatch source branches in separate clones. The default is
False.
convert.hg.tagsbranch
branch name for tag revisions, defaults to "default".
convert.hg.usebranchnames
preserve branch names. The default is True.
convert.hg.sourcename
@ -302,15 +295,6 @@
on each commit made in the target repository. The default is
None.
All Destinations
################
All destination types accept the following options:
convert.skiptags
does not convert tags from the source repo to the target
repo. The default is False.
Options ([+] can be repeated):
-s --source-type TYPE source repository type