2009-04-26 03:47:44 +04:00
|
|
|
# hg.py - hg backend for convert extension
|
|
|
|
#
|
|
|
|
# Copyright 2005-2009 Matt Mackall <mpm@selenic.com> and others
|
|
|
|
#
|
|
|
|
# This software may be used and distributed according to the terms of the
|
2010-01-20 07:20:08 +03:00
|
|
|
# GNU General Public License version 2 or any later version.
|
2007-06-11 07:08:47 +04:00
|
|
|
|
2007-11-27 20:44:09 +03:00
|
|
|
# Notes for hg->hg conversion:
|
|
|
|
#
|
|
|
|
# * Old versions of Mercurial didn't trim the whitespace from the ends
|
|
|
|
# of commit messages, but new versions do. Changesets created by
|
|
|
|
# those older versions, then converted, may thus have different
|
|
|
|
# hashes for changesets that are otherwise identical.
|
|
|
|
#
|
2009-05-24 18:27:37 +04:00
|
|
|
# * Using "--config convert.hg.saverev=true" will make the source
|
|
|
|
# identifier to be stored in the converted revision. This will cause
|
|
|
|
# the converted revision to have a different identity than the
|
|
|
|
# source.
|
2016-03-02 18:26:49 +03:00
|
|
|
from __future__ import absolute_import
|
2007-07-27 00:34:36 +04:00
|
|
|
|
2016-03-02 18:26:49 +03:00
|
|
|
import os
|
|
|
|
import re
|
|
|
|
import time
|
|
|
|
|
2019-01-30 03:25:33 +03:00
|
|
|
from edenscm.mercurial import (
|
2016-03-02 18:26:49 +03:00
|
|
|
bookmarks,
|
|
|
|
context,
|
|
|
|
error,
|
|
|
|
exchange,
|
|
|
|
hg,
|
|
|
|
lock as lockmod,
|
|
|
|
merge as mergemod,
|
|
|
|
node as nodemod,
|
|
|
|
phases,
|
|
|
|
scmutil,
|
|
|
|
util,
|
|
|
|
)
|
2019-01-30 03:25:33 +03:00
|
|
|
from edenscm.mercurial.i18n import _
|
2018-07-06 03:45:27 +03:00
|
|
|
|
|
|
|
from . import common
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2016-04-10 23:55:37 +03:00
|
|
|
stringio = util.stringio
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2016-03-02 18:26:49 +03:00
|
|
|
mapfile = common.mapfile
|
|
|
|
NoRepo = common.NoRepo
|
2007-06-11 07:08:47 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
sha1re = re.compile(r"\b[0-9a-f]{12,40}\b")
|
|
|
|
|
2013-04-18 19:05:50 +04:00
|
|
|
|
2016-03-02 18:26:49 +03:00
|
|
|
class mercurial_sink(common.converter_sink):
|
2017-11-23 04:49:01 +03:00
|
|
|
def __init__(self, ui, repotype, path):
|
|
|
|
common.converter_sink.__init__(self, ui, repotype, path)
|
2018-05-30 12:16:33 +03:00
|
|
|
self.branchnames = ui.configbool("convert", "hg.usebranchnames")
|
|
|
|
self.clonebranches = ui.configbool("convert", "hg.clonebranches")
|
|
|
|
self.tagsbranch = ui.config("convert", "hg.tagsbranch")
|
2007-08-16 00:21:23 +04:00
|
|
|
self.lastbranch = None
|
2007-10-11 02:42:00 +04:00
|
|
|
if os.path.isdir(path) and len(os.listdir(path)) > 0:
|
|
|
|
try:
|
|
|
|
self.repo = hg.repository(self.ui, path)
|
2008-01-22 00:24:28 +03:00
|
|
|
if not self.repo.local():
|
2018-05-30 12:16:33 +03:00
|
|
|
raise NoRepo(_("%s is not a local Mercurial repository") % path)
|
2015-06-24 08:20:08 +03:00
|
|
|
except error.RepoError as err:
|
2009-04-27 01:50:44 +04:00
|
|
|
ui.traceback()
|
2007-10-11 02:42:00 +04:00
|
|
|
raise NoRepo(err.args[0])
|
|
|
|
else:
|
|
|
|
try:
|
2018-05-30 12:16:33 +03:00
|
|
|
ui.status(_("initializing destination %s repository\n") % path)
|
2007-10-11 02:42:00 +04:00
|
|
|
self.repo = hg.repository(self.ui, path, create=True)
|
2008-01-22 00:24:28 +03:00
|
|
|
if not self.repo.local():
|
2018-05-30 12:16:33 +03:00
|
|
|
raise NoRepo(_("%s is not a local Mercurial repository") % path)
|
2007-10-11 02:42:00 +04:00
|
|
|
self.created.append(path)
|
2009-03-23 15:13:06 +03:00
|
|
|
except error.RepoError:
|
2009-04-27 01:50:44 +04:00
|
|
|
ui.traceback()
|
2018-05-30 12:16:33 +03:00
|
|
|
raise NoRepo(_("could not create hg repository %s as sink") % path)
|
2007-07-27 00:34:36 +04:00
|
|
|
self.lock = None
|
|
|
|
self.wlock = None
|
convert: add a mode where mercurial_sink skips empty revisions.
The getchanges function of some converter_source classes can return
some false positives. I.e. they sometimes claim that a file "foo"
was changed in some revision, even though its contents are still the
same.
convert_svn is particularly bad, but I think this can also happen with
convert_cvs and, at least in theory, with mercurial_source.
For regular conversions this is not really a problem - as long as
getfile returns the right contents, we'll get a converted revision
with the right contents. But when we use --filemap, this could lead
to superfluous revisions being converted.
Instead of fixing every converter_source, I decided to change
mercurial_sink to work around this problem.
When --filemap is used, we're interested only in revisions that touch
some specific files. If a revision doesn't change any of these files,
then we're not interested in it (at least for revisions with a single
parent; merges are special).
For mercurial_sink, we abuse this property and rollback a commit if
the manifest text hasn't changed. This avoids duplicating the logic
from localrepo.filecommit to detect unchanged files.
2007-10-05 06:21:37 +04:00
|
|
|
self.filemapmode = False
|
2015-05-29 20:25:34 +03:00
|
|
|
self.subrevmaps = {}
|
2007-07-27 00:34:36 +04:00
|
|
|
|
|
|
|
def before(self):
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ui.debug("run hg sink pre-conversion action\n")
|
2007-07-27 00:34:36 +04:00
|
|
|
self.wlock = self.repo.wlock()
|
2007-08-02 08:56:08 +04:00
|
|
|
self.lock = self.repo.lock()
|
2007-07-27 00:34:36 +04:00
|
|
|
|
|
|
|
def after(self):
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ui.debug("run hg sink post-conversion action\n")
|
2009-12-17 01:42:58 +03:00
|
|
|
if self.lock:
|
|
|
|
self.lock.release()
|
|
|
|
if self.wlock:
|
|
|
|
self.wlock.release()
|
2007-06-11 07:08:47 +04:00
|
|
|
|
2007-07-27 00:34:36 +04:00
|
|
|
def revmapfile(self):
|
2018-09-28 17:08:56 +03:00
|
|
|
return self.repo.localvfs.join("shamap")
|
2007-06-11 07:08:47 +04:00
|
|
|
|
2007-06-15 01:25:55 +04:00
|
|
|
def authorfile(self):
|
2018-09-28 17:08:56 +03:00
|
|
|
return self.repo.localvfs.join("authormap")
|
2007-06-15 01:25:55 +04:00
|
|
|
|
2008-01-26 21:55:04 +03:00
|
|
|
def setbranch(self, branch, pbranches):
|
|
|
|
if not self.clonebranches:
|
2007-08-16 00:21:23 +04:00
|
|
|
return
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
setbranch = branch != self.lastbranch
|
2007-08-16 00:21:23 +04:00
|
|
|
self.lastbranch = branch
|
|
|
|
if not branch:
|
2018-05-30 12:16:33 +03:00
|
|
|
branch = "default"
|
|
|
|
pbranches = [(b[0], b[1] and b[1] or "default") for b in pbranches]
|
2015-03-14 00:00:06 +03:00
|
|
|
if pbranches:
|
|
|
|
pbranch = pbranches[0][1]
|
|
|
|
else:
|
2018-05-30 12:16:33 +03:00
|
|
|
pbranch = "default"
|
2007-08-16 00:21:23 +04:00
|
|
|
|
|
|
|
branchpath = os.path.join(self.path, branch)
|
2008-01-26 21:55:04 +03:00
|
|
|
if setbranch:
|
|
|
|
self.after()
|
|
|
|
try:
|
2007-08-16 00:21:23 +04:00
|
|
|
self.repo = hg.repository(self.ui, branchpath)
|
2012-05-12 18:02:46 +04:00
|
|
|
except Exception:
|
2008-01-26 21:55:04 +03:00
|
|
|
self.repo = hg.repository(self.ui, branchpath, create=True)
|
|
|
|
self.before()
|
|
|
|
|
|
|
|
# pbranches may bring revisions from other branches (merge parents)
|
|
|
|
# Make sure we have them, or pull them.
|
|
|
|
missings = {}
|
|
|
|
for b in pbranches:
|
|
|
|
try:
|
|
|
|
self.repo.lookup(b[0])
|
2012-05-12 18:02:46 +04:00
|
|
|
except Exception:
|
2008-01-26 21:55:04 +03:00
|
|
|
missings.setdefault(b[1], []).append(b[0])
|
2008-03-07 02:24:36 +03:00
|
|
|
|
2008-01-26 21:55:04 +03:00
|
|
|
if missings:
|
|
|
|
self.after()
|
2013-01-15 05:59:14 +04:00
|
|
|
for pbranch, heads in sorted(missings.iteritems()):
|
2008-01-26 21:55:04 +03:00
|
|
|
pbranchpath = os.path.join(self.path, pbranch)
|
2011-06-10 20:43:38 +04:00
|
|
|
prepo = hg.peer(self.ui, {}, pbranchpath)
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ui.note(_("pulling from %s into %s\n") % (pbranch, branch))
|
|
|
|
exchange.pull(self.repo, prepo, [prepo.lookup(h) for h in heads])
|
2008-01-26 21:55:04 +03:00
|
|
|
self.before()
|
2007-08-16 00:21:23 +04:00
|
|
|
|
convert: backout 8a62813ea220 and ca6679798c95 - tagmap
Tagmap solves a very specific use case. It would be better to have a more
generic solution than to have to maintain this forever.
Tagmap has not been released yet and removing it now will not break any
backward compatibility contract.
There is no test coverage for tagmap but it seems like the same can be achieved
with a (relatively) simple and much more powerful custom extension:
import hgext.convert.hg
def f(tag):
return tag.replace('some', 'other')
class source(hgext.convert.hg.mercurial_source):
def gettags(self):
return dict((f(tag), node)
for tag, node in in super(source, self).gettags().items())
def getfile(self, name, rev):
data, flags = super(source, self).getfile(name, rev)
if name == '.hgtags':
data = ''.join(l[:41] + f(l[41:]) + '\n' for l in data.splitlines())
return data, flags
hgext.convert.hg.mercurial_source = source
2014-04-16 03:09:49 +04:00
|
|
|
def _rewritetags(self, source, revmap, data):
|
2016-04-10 23:55:37 +03:00
|
|
|
fp = stringio()
|
2009-06-01 19:12:42 +04:00
|
|
|
for line in data.splitlines():
|
2018-05-30 12:16:33 +03:00
|
|
|
s = line.split(" ", 1)
|
2009-06-01 19:12:42 +04:00
|
|
|
if len(s) != 2:
|
|
|
|
continue
|
|
|
|
revid = revmap.get(source.lookuprev(s[0]))
|
|
|
|
if not revid:
|
2016-03-02 18:26:49 +03:00
|
|
|
if s[0] == nodemod.nullhex:
|
2015-05-27 22:28:29 +03:00
|
|
|
revid = s[0]
|
|
|
|
else:
|
|
|
|
continue
|
2018-05-30 12:16:33 +03:00
|
|
|
fp.write("%s %s\n" % (revid, s[1]))
|
2009-06-01 19:12:42 +04:00
|
|
|
return fp.getvalue()
|
|
|
|
|
2015-08-15 01:22:47 +03:00
|
|
|
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.
|
|
|
|
|
|
|
|
This prevents us from losing files that only exist in the target p2 and
|
|
|
|
that don't come from the source repo (like if you're merging multiple
|
|
|
|
repositories together).
|
|
|
|
"""
|
|
|
|
anc = [p1ctx.ancestor(p2ctx)]
|
|
|
|
# Calculate what files are coming from p2
|
|
|
|
actions, diverge, rename = mergemod.calculateupdates(
|
2018-05-30 12:16:33 +03:00
|
|
|
self.repo,
|
|
|
|
p1ctx,
|
|
|
|
p2ctx,
|
|
|
|
anc,
|
2015-08-15 01:22:47 +03:00
|
|
|
True, # branchmerge
|
|
|
|
True, # force
|
2018-05-30 12:16:33 +03:00
|
|
|
False, # acceptremote
|
|
|
|
False, # followcopies
|
2015-08-15 01:22:47 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
for file, (action, info, msg) in actions.iteritems():
|
|
|
|
if source.targetfilebelongstosource(file):
|
|
|
|
# If the file belongs to the source repo, ignore the p2
|
|
|
|
# since it will be covered by the existing fileset.
|
|
|
|
continue
|
|
|
|
|
|
|
|
# If the file requires actual merging, abort. We don't have enough
|
|
|
|
# context to resolve merges correctly.
|
2018-05-30 12:16:33 +03:00
|
|
|
if action in ["m", "dm", "cd", "dc"]:
|
|
|
|
raise error.Abort(
|
|
|
|
_(
|
|
|
|
"unable to convert merge commit "
|
|
|
|
"since target parents do not merge cleanly (file "
|
|
|
|
"%s, parents %s and %s)"
|
|
|
|
)
|
|
|
|
% (file, p1ctx, p2ctx)
|
|
|
|
)
|
|
|
|
elif action == "k":
|
2015-08-15 01:22:47 +03:00
|
|
|
# 'keep' means nothing changed from p1
|
|
|
|
continue
|
|
|
|
else:
|
|
|
|
# Any other change means we want to take the p2 version
|
|
|
|
yield file
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
def putcommit(self, files, copies, parents, commit, source, revmap, full, cleanp2):
|
2008-06-19 02:14:24 +04:00
|
|
|
files = dict(files)
|
2015-03-19 19:40:19 +03:00
|
|
|
|
2008-06-19 02:14:24 +04:00
|
|
|
def getfilectx(repo, memctx, f):
|
2015-08-15 01:22:47 +03:00
|
|
|
if p2ctx and f in p2files and f not in copies:
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ui.debug("reusing %s from p2\n" % f)
|
2015-08-26 01:54:33 +03:00
|
|
|
try:
|
|
|
|
return p2ctx[f]
|
|
|
|
except error.ManifestLookupError:
|
|
|
|
# If the file doesn't exist in p2, then we're syncing a
|
|
|
|
# delete, so just return None.
|
|
|
|
return None
|
convert: introduce --full for converting all files
Convert will normally only process files that were changed in a source
revision, apply the filemap, and record it has a change in the target
repository. (If it ends up not really changing anything, nothing changes.)
That means that _if_ the filemap is changed before continuing an incremental
convert, the change will only kick in when the files it affects are modified in
a source revision and thus processed.
With --full, convert will make a full conversion every time and process
all files in the source repo and remove target repo files that shouldn't be
there. Filemap changes will thus kick in on the first converted revision, no
matter what is changed.
This flag should in most cases not make any difference but will make convert
significantly slower.
Other names has been considered for this feature, such as "resync", "sync",
"checkunmodified", "all" or "allfiles", but I found that they were less obvious
and required more explanation than "full" and were harder to describe
consistently.
2014-08-27 00:03:32 +04:00
|
|
|
try:
|
|
|
|
v = files[f]
|
|
|
|
except KeyError:
|
|
|
|
return None
|
2010-05-09 23:52:34 +04:00
|
|
|
data, mode = source.getfile(f, v)
|
2014-08-27 00:03:32 +04:00
|
|
|
if data is None:
|
|
|
|
return None
|
2018-05-30 12:16:33 +03:00
|
|
|
if f == ".hgtags":
|
convert: backout 8a62813ea220 and ca6679798c95 - tagmap
Tagmap solves a very specific use case. It would be better to have a more
generic solution than to have to maintain this forever.
Tagmap has not been released yet and removing it now will not break any
backward compatibility contract.
There is no test coverage for tagmap but it seems like the same can be achieved
with a (relatively) simple and much more powerful custom extension:
import hgext.convert.hg
def f(tag):
return tag.replace('some', 'other')
class source(hgext.convert.hg.mercurial_source):
def gettags(self):
return dict((f(tag), node)
for tag, node in in super(source, self).gettags().items())
def getfile(self, name, rev):
data, flags = super(source, self).getfile(name, rev)
if name == '.hgtags':
data = ''.join(l[:41] + f(l[41:]) + '\n' for l in data.splitlines())
return data, flags
hgext.convert.hg.mercurial_source = source
2014-04-16 03:09:49 +04:00
|
|
|
data = self._rewritetags(source, revmap, data)
|
2018-05-30 12:16:33 +03:00
|
|
|
return context.memfilectx(
|
|
|
|
self.repo, memctx, f, data, "l" in mode, "x" in mode, copies.get(f)
|
|
|
|
)
|
2008-06-19 02:14:24 +04:00
|
|
|
|
2007-06-11 07:08:47 +04:00
|
|
|
pl = []
|
|
|
|
for p in parents:
|
2008-06-19 02:14:24 +04:00
|
|
|
if p not in pl:
|
2007-06-11 07:08:47 +04:00
|
|
|
pl.append(p)
|
|
|
|
parents = pl
|
convert: add a mode where mercurial_sink skips empty revisions.
The getchanges function of some converter_source classes can return
some false positives. I.e. they sometimes claim that a file "foo"
was changed in some revision, even though its contents are still the
same.
convert_svn is particularly bad, but I think this can also happen with
convert_cvs and, at least in theory, with mercurial_source.
For regular conversions this is not really a problem - as long as
getfile returns the right contents, we'll get a converted revision
with the right contents. But when we use --filemap, this could lead
to superfluous revisions being converted.
Instead of fixing every converter_source, I decided to change
mercurial_sink to work around this problem.
When --filemap is used, we're interested only in revisions that touch
some specific files. If a revision doesn't change any of these files,
then we're not interested in it (at least for revisions with a single
parent; merges are special).
For mercurial_sink, we abuse this property and rollback a commit if
the manifest text hasn't changed. This avoids duplicating the logic
from localrepo.filecommit to detect unchanged files.
2007-10-05 06:21:37 +04:00
|
|
|
nparents = len(parents)
|
|
|
|
if self.filemapmode and nparents == 1:
|
2016-03-02 18:26:49 +03:00
|
|
|
m1node = self.repo.changelog.read(nodemod.bin(parents[0]))[0]
|
convert: add a mode where mercurial_sink skips empty revisions.
The getchanges function of some converter_source classes can return
some false positives. I.e. they sometimes claim that a file "foo"
was changed in some revision, even though its contents are still the
same.
convert_svn is particularly bad, but I think this can also happen with
convert_cvs and, at least in theory, with mercurial_source.
For regular conversions this is not really a problem - as long as
getfile returns the right contents, we'll get a converted revision
with the right contents. But when we use --filemap, this could lead
to superfluous revisions being converted.
Instead of fixing every converter_source, I decided to change
mercurial_sink to work around this problem.
When --filemap is used, we're interested only in revisions that touch
some specific files. If a revision doesn't change any of these files,
then we're not interested in it (at least for revisions with a single
parent; merges are special).
For mercurial_sink, we abuse this property and rollback a commit if
the manifest text hasn't changed. This avoids duplicating the logic
from localrepo.filecommit to detect unchanged files.
2007-10-05 06:21:37 +04:00
|
|
|
parent = parents[0]
|
2007-06-11 07:08:47 +04:00
|
|
|
|
2010-01-25 09:05:27 +03:00
|
|
|
if len(parents) < 2:
|
2016-03-02 18:26:49 +03:00
|
|
|
parents.append(nodemod.nullid)
|
2010-01-25 09:05:27 +03:00
|
|
|
if len(parents) < 2:
|
2016-03-02 18:26:49 +03:00
|
|
|
parents.append(nodemod.nullid)
|
2007-06-11 07:08:47 +04:00
|
|
|
p2 = parents.pop(0)
|
|
|
|
|
|
|
|
text = commit.desc
|
2013-04-18 19:05:50 +04:00
|
|
|
|
|
|
|
sha1s = re.findall(sha1re, text)
|
|
|
|
for sha1 in sha1s:
|
2018-02-05 19:42:25 +03:00
|
|
|
try:
|
|
|
|
oldrev = source.lookuprev(sha1)
|
|
|
|
newrev = revmap.get(oldrev)
|
|
|
|
if newrev is not None:
|
2018-05-30 12:16:33 +03:00
|
|
|
text = text.replace(sha1, newrev[: len(sha1)])
|
2018-02-05 19:42:25 +03:00
|
|
|
except Exception:
|
|
|
|
# Don't crash if we find a bad sha in the message
|
|
|
|
continue
|
2013-04-18 19:05:50 +04:00
|
|
|
|
2007-10-11 02:30:00 +04:00
|
|
|
extra = commit.extra.copy()
|
2014-06-12 06:19:29 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
sourcename = self.repo.ui.config("convert", "hg.sourcename")
|
2015-07-08 20:31:09 +03:00
|
|
|
if sourcename:
|
2018-05-30 12:16:33 +03:00
|
|
|
extra["convert_source"] = sourcename
|
|
|
|
|
|
|
|
for label in (
|
|
|
|
"source",
|
|
|
|
"transplant_source",
|
|
|
|
"rebase_source",
|
|
|
|
"intermediate-source",
|
|
|
|
):
|
2014-06-12 06:19:29 +04:00
|
|
|
node = extra.get(label)
|
|
|
|
|
|
|
|
if node is None:
|
|
|
|
continue
|
|
|
|
|
|
|
|
# Only transplant stores its reference in binary
|
2018-05-30 12:16:33 +03:00
|
|
|
if label == "transplant_source":
|
2016-03-02 18:26:49 +03:00
|
|
|
node = nodemod.hex(node)
|
2014-06-12 06:19:29 +04:00
|
|
|
|
|
|
|
newrev = revmap.get(node)
|
|
|
|
if newrev is not None:
|
2018-05-30 12:16:33 +03:00
|
|
|
if label == "transplant_source":
|
2016-03-02 18:26:49 +03:00
|
|
|
newrev = nodemod.bin(newrev)
|
2014-06-12 06:19:29 +04:00
|
|
|
|
|
|
|
extra[label] = newrev
|
|
|
|
|
2007-08-01 04:18:59 +04:00
|
|
|
if self.branchnames and commit.branch:
|
2018-05-30 12:16:33 +03:00
|
|
|
extra["branch"] = commit.branch
|
2015-06-14 20:04:00 +03:00
|
|
|
if commit.rev and commit.saverev:
|
2018-05-30 12:16:33 +03:00
|
|
|
extra["convert_revision"] = commit.rev
|
2007-06-11 07:08:47 +04:00
|
|
|
|
|
|
|
while parents:
|
|
|
|
p1 = p2
|
|
|
|
p2 = parents.pop(0)
|
2015-08-15 01:22:47 +03:00
|
|
|
p1ctx = self.repo[p1]
|
2015-03-19 19:40:19 +03:00
|
|
|
p2ctx = None
|
2016-03-02 18:26:49 +03:00
|
|
|
if p2 != nodemod.nullid:
|
2015-03-19 19:40:19 +03:00
|
|
|
p2ctx = self.repo[p2]
|
convert: introduce --full for converting all files
Convert will normally only process files that were changed in a source
revision, apply the filemap, and record it has a change in the target
repository. (If it ends up not really changing anything, nothing changes.)
That means that _if_ the filemap is changed before continuing an incremental
convert, the change will only kick in when the files it affects are modified in
a source revision and thus processed.
With --full, convert will make a full conversion every time and process
all files in the source repo and remove target repo files that shouldn't be
there. Filemap changes will thus kick in on the first converted revision, no
matter what is changed.
This flag should in most cases not make any difference but will make convert
significantly slower.
Other names has been considered for this feature, such as "resync", "sync",
"checkunmodified", "all" or "allfiles", but I found that they were less obvious
and required more explanation than "full" and were harder to describe
consistently.
2014-08-27 00:03:32 +04:00
|
|
|
fileset = set(files)
|
|
|
|
if full:
|
2014-08-31 13:11:42 +04:00
|
|
|
fileset.update(self.repo[p1])
|
|
|
|
fileset.update(self.repo[p2])
|
2015-08-15 01:22:47 +03:00
|
|
|
|
|
|
|
if p2ctx:
|
|
|
|
p2files = set(cleanp2)
|
|
|
|
for file in self._calculatemergedfiles(source, p1ctx, p2ctx):
|
|
|
|
p2files.add(file)
|
|
|
|
fileset.add(file)
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
ctx = context.memctx(
|
|
|
|
self.repo,
|
|
|
|
(p1, p2),
|
|
|
|
text,
|
|
|
|
fileset,
|
|
|
|
getfilectx,
|
|
|
|
commit.author,
|
|
|
|
commit.date,
|
|
|
|
extra,
|
|
|
|
)
|
convert: apply the appropriate phases to the destination (issue4165)
If the conversion doesn't change the hash, and the cset is public in the source,
it should be public in the destination. (This can happen if file remapping is
done that doesn't affect the initial commits.) This also propagates the secret
phase from the source, regardless of the hash, because presumably the content is
what is secret. Otherwise, the destination commit stays in the draft phase.
Maybe any draft cset with an unchanged hash should be changed to public, because
it has effectively been shared, but convert pretty strongly implies throwing
away (or at least readonly archiving) the source repo.
The change in the rollback output is because the name of the outer transaction
is now 'convert', which seems more accurate. Unfortunately, the memctx won't
indicate the hash prior to committing, so the proper phase can't be applied with
the commit.
The repo is already write locked in mercurial_sink.before().
2015-06-14 20:08:11 +03:00
|
|
|
|
|
|
|
# We won't know if the conversion changes the node until after the
|
|
|
|
# commit, so copy the source's phase for now.
|
2018-05-30 12:16:33 +03:00
|
|
|
self.repo.ui.setconfig(
|
|
|
|
"phases", "new-commit", phases.phasenames[commit.phase], "convert"
|
|
|
|
)
|
convert: apply the appropriate phases to the destination (issue4165)
If the conversion doesn't change the hash, and the cset is public in the source,
it should be public in the destination. (This can happen if file remapping is
done that doesn't affect the initial commits.) This also propagates the secret
phase from the source, regardless of the hash, because presumably the content is
what is secret. Otherwise, the destination commit stays in the draft phase.
Maybe any draft cset with an unchanged hash should be changed to public, because
it has effectively been shared, but convert pretty strongly implies throwing
away (or at least readonly archiving) the source repo.
The change in the rollback output is because the name of the outer transaction
is now 'convert', which seems more accurate. Unfortunately, the memctx won't
indicate the hash prior to committing, so the proper phase can't be applied with
the commit.
The repo is already write locked in mercurial_sink.before().
2015-06-14 20:08:11 +03:00
|
|
|
|
2016-01-16 00:14:47 +03:00
|
|
|
with self.repo.transaction("convert") as tr:
|
2016-03-02 18:26:49 +03:00
|
|
|
node = nodemod.hex(self.repo.commitctx(ctx))
|
convert: apply the appropriate phases to the destination (issue4165)
If the conversion doesn't change the hash, and the cset is public in the source,
it should be public in the destination. (This can happen if file remapping is
done that doesn't affect the initial commits.) This also propagates the secret
phase from the source, regardless of the hash, because presumably the content is
what is secret. Otherwise, the destination commit stays in the draft phase.
Maybe any draft cset with an unchanged hash should be changed to public, because
it has effectively been shared, but convert pretty strongly implies throwing
away (or at least readonly archiving) the source repo.
The change in the rollback output is because the name of the outer transaction
is now 'convert', which seems more accurate. Unfortunately, the memctx won't
indicate the hash prior to committing, so the proper phase can't be applied with
the commit.
The repo is already write locked in mercurial_sink.before().
2015-06-14 20:08:11 +03:00
|
|
|
|
|
|
|
# If the node value has changed, but the phase is lower than
|
|
|
|
# draft, set it back to draft since it hasn't been exposed
|
|
|
|
# anywhere.
|
|
|
|
if commit.rev != node:
|
|
|
|
ctx = self.repo[node]
|
|
|
|
if ctx.phase() < phases.draft:
|
2018-05-30 12:16:33 +03:00
|
|
|
phases.registernew(self.repo, tr, phases.draft, [ctx.node()])
|
convert: apply the appropriate phases to the destination (issue4165)
If the conversion doesn't change the hash, and the cset is public in the source,
it should be public in the destination. (This can happen if file remapping is
done that doesn't affect the initial commits.) This also propagates the secret
phase from the source, regardless of the hash, because presumably the content is
what is secret. Otherwise, the destination commit stays in the draft phase.
Maybe any draft cset with an unchanged hash should be changed to public, because
it has effectively been shared, but convert pretty strongly implies throwing
away (or at least readonly archiving) the source repo.
The change in the rollback output is because the name of the outer transaction
is now 'convert', which seems more accurate. Unfortunately, the memctx won't
indicate the hash prior to committing, so the proper phase can't be applied with
the commit.
The repo is already write locked in mercurial_sink.before().
2015-06-14 20:08:11 +03:00
|
|
|
|
2007-06-11 07:08:47 +04:00
|
|
|
text = "(octopus merge fixup)\n"
|
2015-06-29 23:39:05 +03:00
|
|
|
p2 = node
|
2007-06-11 07:08:47 +04:00
|
|
|
|
convert: add a mode where mercurial_sink skips empty revisions.
The getchanges function of some converter_source classes can return
some false positives. I.e. they sometimes claim that a file "foo"
was changed in some revision, even though its contents are still the
same.
convert_svn is particularly bad, but I think this can also happen with
convert_cvs and, at least in theory, with mercurial_source.
For regular conversions this is not really a problem - as long as
getfile returns the right contents, we'll get a converted revision
with the right contents. But when we use --filemap, this could lead
to superfluous revisions being converted.
Instead of fixing every converter_source, I decided to change
mercurial_sink to work around this problem.
When --filemap is used, we're interested only in revisions that touch
some specific files. If a revision doesn't change any of these files,
then we're not interested in it (at least for revisions with a single
parent; merges are special).
For mercurial_sink, we abuse this property and rollback a commit if
the manifest text hasn't changed. This avoids duplicating the logic
from localrepo.filecommit to detect unchanged files.
2007-10-05 06:21:37 +04:00
|
|
|
if self.filemapmode and nparents == 1:
|
2018-10-17 18:56:09 +03:00
|
|
|
mfl = self.repo.manifestlog
|
2016-03-02 18:26:49 +03:00
|
|
|
mnode = self.repo.changelog.read(nodemod.bin(p2))[0]
|
2018-05-30 12:16:33 +03:00
|
|
|
closed = "close" in commit.extra
|
2018-10-17 18:56:09 +03:00
|
|
|
if not closed and not mfl[m1node].read().diff(mfl[mnode].read()):
|
2009-05-24 20:30:59 +04:00
|
|
|
self.ui.status(_("filtering out empty revision\n"))
|
2011-10-08 00:36:35 +04:00
|
|
|
self.repo.rollback(force=True)
|
convert: add a mode where mercurial_sink skips empty revisions.
The getchanges function of some converter_source classes can return
some false positives. I.e. they sometimes claim that a file "foo"
was changed in some revision, even though its contents are still the
same.
convert_svn is particularly bad, but I think this can also happen with
convert_cvs and, at least in theory, with mercurial_source.
For regular conversions this is not really a problem - as long as
getfile returns the right contents, we'll get a converted revision
with the right contents. But when we use --filemap, this could lead
to superfluous revisions being converted.
Instead of fixing every converter_source, I decided to change
mercurial_sink to work around this problem.
When --filemap is used, we're interested only in revisions that touch
some specific files. If a revision doesn't change any of these files,
then we're not interested in it (at least for revisions with a single
parent; merges are special).
For mercurial_sink, we abuse this property and rollback a commit if
the manifest text hasn't changed. This avoids duplicating the logic
from localrepo.filecommit to detect unchanged files.
2007-10-05 06:21:37 +04:00
|
|
|
return parent
|
2007-06-11 07:08:47 +04:00
|
|
|
return p2
|
|
|
|
|
|
|
|
def puttags(self, tags):
|
2009-03-23 15:13:27 +03:00
|
|
|
try:
|
|
|
|
parentctx = self.repo[self.tagsbranch]
|
|
|
|
tagparent = parentctx.node()
|
|
|
|
except error.RepoError:
|
|
|
|
parentctx = None
|
2016-03-02 18:26:49 +03:00
|
|
|
tagparent = nodemod.nullid
|
2008-06-19 02:14:24 +04:00
|
|
|
|
2014-01-23 03:38:05 +04:00
|
|
|
oldlines = set()
|
|
|
|
for branch, heads in self.repo.branchmap().iteritems():
|
|
|
|
for h in heads:
|
2018-05-30 12:16:33 +03:00
|
|
|
if ".hgtags" in self.repo[h]:
|
2014-01-23 03:38:05 +04:00
|
|
|
oldlines.update(
|
2018-05-30 12:16:33 +03:00
|
|
|
set(self.repo[h][".hgtags"].data().splitlines(True))
|
|
|
|
)
|
2014-01-23 03:38:05 +04:00
|
|
|
oldlines = sorted(list(oldlines))
|
2009-03-23 15:13:27 +03:00
|
|
|
|
2009-04-27 01:50:44 +04:00
|
|
|
newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags])
|
2009-03-23 15:13:27 +03:00
|
|
|
if newlines == oldlines:
|
2009-09-06 17:29:14 +04:00
|
|
|
return None, None
|
2014-01-23 01:31:24 +04:00
|
|
|
|
|
|
|
# if the old and new tags match, then there is nothing to update
|
|
|
|
oldtags = set()
|
|
|
|
newtags = set()
|
|
|
|
for line in oldlines:
|
2018-05-30 12:16:33 +03:00
|
|
|
s = line.strip().split(" ", 1)
|
2014-01-23 01:31:24 +04:00
|
|
|
if len(s) != 2:
|
|
|
|
continue
|
|
|
|
oldtags.add(s[1])
|
|
|
|
for line in newlines:
|
2018-05-30 12:16:33 +03:00
|
|
|
s = line.strip().split(" ", 1)
|
2014-01-23 01:31:24 +04:00
|
|
|
if len(s) != 2:
|
|
|
|
continue
|
|
|
|
if s[1] not in oldtags:
|
|
|
|
newtags.add(s[1].strip())
|
|
|
|
|
|
|
|
if not newtags:
|
|
|
|
return None, None
|
|
|
|
|
2009-03-23 15:13:27 +03:00
|
|
|
data = "".join(newlines)
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2009-03-23 15:13:27 +03:00
|
|
|
def getfilectx(repo, memctx, f):
|
2017-12-11 20:27:40 +03:00
|
|
|
return context.memfilectx(repo, memctx, f, data, False, False, None)
|
2008-06-19 02:14:24 +04:00
|
|
|
|
2009-03-23 15:13:27 +03:00
|
|
|
self.ui.status(_("updating tags\n"))
|
|
|
|
date = "%s 0" % int(time.mktime(time.gmtime()))
|
2018-05-30 12:16:33 +03:00
|
|
|
extra = {"branch": self.tagsbranch}
|
|
|
|
ctx = context.memctx(
|
|
|
|
self.repo,
|
|
|
|
(tagparent, None),
|
|
|
|
"update tags",
|
|
|
|
[".hgtags"],
|
|
|
|
getfilectx,
|
|
|
|
"convert-repo",
|
|
|
|
date,
|
|
|
|
extra,
|
|
|
|
)
|
2015-06-29 23:39:05 +03:00
|
|
|
node = self.repo.commitctx(ctx)
|
2016-03-02 18:26:49 +03:00
|
|
|
return nodemod.hex(node), nodemod.hex(tagparent)
|
2007-07-27 00:34:36 +04:00
|
|
|
|
convert: add a mode where mercurial_sink skips empty revisions.
The getchanges function of some converter_source classes can return
some false positives. I.e. they sometimes claim that a file "foo"
was changed in some revision, even though its contents are still the
same.
convert_svn is particularly bad, but I think this can also happen with
convert_cvs and, at least in theory, with mercurial_source.
For regular conversions this is not really a problem - as long as
getfile returns the right contents, we'll get a converted revision
with the right contents. But when we use --filemap, this could lead
to superfluous revisions being converted.
Instead of fixing every converter_source, I decided to change
mercurial_sink to work around this problem.
When --filemap is used, we're interested only in revisions that touch
some specific files. If a revision doesn't change any of these files,
then we're not interested in it (at least for revisions with a single
parent; merges are special).
For mercurial_sink, we abuse this property and rollback a commit if
the manifest text hasn't changed. This avoids duplicating the logic
from localrepo.filecommit to detect unchanged files.
2007-10-05 06:21:37 +04:00
|
|
|
def setfilemapmode(self, active):
|
|
|
|
self.filemapmode = active
|
|
|
|
|
2010-07-07 02:06:59 +04:00
|
|
|
def putbookmarks(self, updatedbookmark):
|
|
|
|
if not len(updatedbookmark):
|
|
|
|
return
|
2015-11-17 04:15:36 +03:00
|
|
|
wlock = lock = tr = None
|
|
|
|
try:
|
|
|
|
wlock = self.repo.wlock()
|
|
|
|
lock = self.repo.lock()
|
2018-05-30 12:16:33 +03:00
|
|
|
tr = self.repo.transaction("bookmark")
|
2015-11-17 04:14:15 +03:00
|
|
|
self.ui.status(_("updating bookmarks\n"))
|
|
|
|
destmarks = self.repo._bookmarks
|
2018-05-30 12:16:33 +03:00
|
|
|
changes = [
|
|
|
|
(bookmark, nodemod.bin(updatedbookmark[bookmark]))
|
|
|
|
for bookmark in updatedbookmark
|
|
|
|
]
|
2017-07-10 18:30:20 +03:00
|
|
|
destmarks.applychanges(self.repo, tr, changes)
|
2015-11-17 04:15:36 +03:00
|
|
|
tr.close()
|
|
|
|
finally:
|
|
|
|
lockmod.release(lock, wlock, tr)
|
2010-07-07 02:06:59 +04:00
|
|
|
|
2014-05-20 00:12:30 +04:00
|
|
|
def hascommitfrommap(self, rev):
|
|
|
|
# the exact semantics of clonebranches is unclear so we can't say no
|
|
|
|
return rev in self.repo or self.clonebranches
|
|
|
|
|
2014-05-20 00:11:14 +04:00
|
|
|
def hascommitforsplicemap(self, rev):
|
2012-05-12 18:00:57 +04:00
|
|
|
if rev not in self.repo and self.clonebranches:
|
2018-05-30 12:16:33 +03:00
|
|
|
raise error.Abort(
|
|
|
|
_(
|
|
|
|
"revision %s not found in destination "
|
|
|
|
"repository (lookups with clonebranches=true "
|
|
|
|
"are not implemented)"
|
|
|
|
)
|
|
|
|
% rev
|
|
|
|
)
|
2012-02-11 01:34:13 +04:00
|
|
|
return rev in self.repo
|
2010-07-07 02:06:59 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2016-03-02 18:26:49 +03:00
|
|
|
class mercurial_source(common.converter_source):
|
2017-11-23 04:49:01 +03:00
|
|
|
def __init__(self, ui, repotype, path, revs=None):
|
|
|
|
common.converter_source.__init__(self, ui, repotype, path, revs)
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ignoreerrors = ui.configbool("convert", "hg.ignoreerrors")
|
2009-05-17 05:04:17 +04:00
|
|
|
self.ignored = set()
|
2018-05-30 12:16:33 +03:00
|
|
|
self.saverev = ui.configbool("convert", "hg.saverev")
|
2007-10-03 00:48:52 +04:00
|
|
|
try:
|
|
|
|
self.repo = hg.repository(self.ui, path)
|
2007-10-10 11:15:33 +04:00
|
|
|
# try to provoke an exception if this isn't really a hg
|
|
|
|
# repo, but some other bogus compatible-looking url
|
2007-11-10 01:21:35 +03:00
|
|
|
if not self.repo.local():
|
2012-05-12 18:00:58 +04:00
|
|
|
raise error.RepoError
|
2009-01-12 19:42:31 +03:00
|
|
|
except error.RepoError:
|
2009-04-27 01:50:44 +04:00
|
|
|
ui.traceback()
|
2010-04-18 17:47:49 +04:00
|
|
|
raise NoRepo(_("%s is not a local Mercurial repository") % path)
|
2007-07-27 00:34:36 +04:00
|
|
|
self.lastrev = None
|
|
|
|
self.lastctx = None
|
2014-08-27 00:03:32 +04:00
|
|
|
self._changescache = None, None
|
2007-11-27 04:23:20 +03:00
|
|
|
self.convertfp = None
|
2008-08-14 01:31:10 +04:00
|
|
|
# Restrict converted revisions to startrev descendants
|
2018-05-30 12:16:33 +03:00
|
|
|
startnode = ui.config("convert", "hg.startrev")
|
|
|
|
hgrevs = ui.config("convert", "hg.revs")
|
2013-07-20 02:43:08 +04:00
|
|
|
if hgrevs is None:
|
|
|
|
if startnode is not None:
|
|
|
|
try:
|
|
|
|
startnode = self.repo.lookup(startnode)
|
|
|
|
except error.RepoError:
|
2018-05-30 12:16:33 +03:00
|
|
|
raise error.Abort(_("%s is not a valid start revision") % startnode)
|
2013-07-20 02:43:08 +04:00
|
|
|
startrev = self.repo.changelog.rev(startnode)
|
|
|
|
children = {startnode: 1}
|
|
|
|
for r in self.repo.changelog.descendants([startrev]):
|
|
|
|
children[self.repo.changelog.node(r)] = 1
|
|
|
|
self.keep = children.__contains__
|
|
|
|
else:
|
|
|
|
self.keep = util.always
|
2015-07-08 20:27:43 +03:00
|
|
|
if revs:
|
2015-09-03 20:29:42 +03:00
|
|
|
self._heads = [self.repo[r].node() for r in revs]
|
2013-07-20 02:43:08 +04:00
|
|
|
else:
|
|
|
|
self._heads = self.repo.heads()
|
2013-10-03 20:01:21 +04:00
|
|
|
else:
|
2015-07-08 20:27:43 +03:00
|
|
|
if revs or startnode is not None:
|
2018-05-30 12:16:33 +03:00
|
|
|
raise error.Abort(
|
|
|
|
_("hg.revs cannot be combined with " "hg.startrev or --rev")
|
|
|
|
)
|
2013-07-20 02:43:08 +04:00
|
|
|
nodes = set()
|
|
|
|
parents = set()
|
|
|
|
for r in scmutil.revrange(self.repo, [hgrevs]):
|
|
|
|
ctx = self.repo[r]
|
|
|
|
nodes.add(ctx.node())
|
|
|
|
parents.update(p.node() for p in ctx.parents())
|
|
|
|
self.keep = nodes.__contains__
|
|
|
|
self._heads = nodes - parents
|
2007-07-27 00:34:36 +04:00
|
|
|
|
2016-01-10 08:42:48 +03:00
|
|
|
def _changectx(self, rev):
|
2007-07-27 00:34:36 +04:00
|
|
|
if self.lastrev != rev:
|
2008-06-26 23:35:46 +04:00
|
|
|
self.lastctx = self.repo[rev]
|
2007-07-27 00:34:36 +04:00
|
|
|
self.lastrev = rev
|
|
|
|
return self.lastctx
|
|
|
|
|
2016-01-10 08:42:48 +03:00
|
|
|
def _parents(self, ctx):
|
2009-10-07 12:13:04 +04:00
|
|
|
return [p for p in ctx.parents() if p and self.keep(p.node())]
|
2008-08-14 01:31:10 +04:00
|
|
|
|
2007-07-27 00:34:36 +04:00
|
|
|
def getheads(self):
|
2016-03-02 18:26:49 +03:00
|
|
|
return [nodemod.hex(h) for h in self._heads if self.keep(h)]
|
2007-07-27 00:34:36 +04:00
|
|
|
|
|
|
|
def getfile(self, name, rev):
|
|
|
|
try:
|
2016-01-10 08:42:48 +03:00
|
|
|
fctx = self._changectx(rev)[name]
|
2010-05-09 23:52:34 +04:00
|
|
|
return fctx.data(), fctx.flags()
|
2014-08-27 00:03:32 +04:00
|
|
|
except error.LookupError:
|
|
|
|
return None, None
|
2007-07-27 00:34:36 +04:00
|
|
|
|
2016-01-11 08:07:34 +03:00
|
|
|
def _changedfiles(self, ctx1, ctx2):
|
2016-01-10 09:58:10 +03:00
|
|
|
ma, r = [], []
|
|
|
|
maappend = ma.append
|
|
|
|
rappend = r.append
|
|
|
|
d = ctx1.manifest().diff(ctx2.manifest())
|
|
|
|
for f, ((node1, flag1), (node2, flag2)) in d.iteritems():
|
|
|
|
if node2 is None:
|
|
|
|
rappend(f)
|
|
|
|
else:
|
|
|
|
maappend(f)
|
|
|
|
return ma, r
|
2016-01-11 08:07:34 +03:00
|
|
|
|
convert: introduce --full for converting all files
Convert will normally only process files that were changed in a source
revision, apply the filemap, and record it has a change in the target
repository. (If it ends up not really changing anything, nothing changes.)
That means that _if_ the filemap is changed before continuing an incremental
convert, the change will only kick in when the files it affects are modified in
a source revision and thus processed.
With --full, convert will make a full conversion every time and process
all files in the source repo and remove target repo files that shouldn't be
there. Filemap changes will thus kick in on the first converted revision, no
matter what is changed.
This flag should in most cases not make any difference but will make convert
significantly slower.
Other names has been considered for this feature, such as "resync", "sync",
"checkunmodified", "all" or "allfiles", but I found that they were less obvious
and required more explanation than "full" and were harder to describe
consistently.
2014-08-27 00:03:32 +04:00
|
|
|
def getchanges(self, rev, full):
|
2016-01-10 08:42:48 +03:00
|
|
|
ctx = self._changectx(rev)
|
|
|
|
parents = self._parents(ctx)
|
convert: introduce --full for converting all files
Convert will normally only process files that were changed in a source
revision, apply the filemap, and record it has a change in the target
repository. (If it ends up not really changing anything, nothing changes.)
That means that _if_ the filemap is changed before continuing an incremental
convert, the change will only kick in when the files it affects are modified in
a source revision and thus processed.
With --full, convert will make a full conversion every time and process
all files in the source repo and remove target repo files that shouldn't be
there. Filemap changes will thus kick in on the first converted revision, no
matter what is changed.
This flag should in most cases not make any difference but will make convert
significantly slower.
Other names has been considered for this feature, such as "resync", "sync",
"checkunmodified", "all" or "allfiles", but I found that they were less obvious
and required more explanation than "full" and were harder to describe
consistently.
2014-08-27 00:03:32 +04:00
|
|
|
if full or not parents:
|
2014-08-27 00:03:32 +04:00
|
|
|
files = copyfiles = ctx.manifest()
|
convert: introduce --full for converting all files
Convert will normally only process files that were changed in a source
revision, apply the filemap, and record it has a change in the target
repository. (If it ends up not really changing anything, nothing changes.)
That means that _if_ the filemap is changed before continuing an incremental
convert, the change will only kick in when the files it affects are modified in
a source revision and thus processed.
With --full, convert will make a full conversion every time and process
all files in the source repo and remove target repo files that shouldn't be
there. Filemap changes will thus kick in on the first converted revision, no
matter what is changed.
This flag should in most cases not make any difference but will make convert
significantly slower.
Other names has been considered for this feature, such as "resync", "sync",
"checkunmodified", "all" or "allfiles", but I found that they were less obvious
and required more explanation than "full" and were harder to describe
consistently.
2014-08-27 00:03:32 +04:00
|
|
|
if parents:
|
2014-08-27 00:03:32 +04:00
|
|
|
if self._changescache[0] == rev:
|
2016-01-11 08:07:34 +03:00
|
|
|
ma, r = self._changescache[1]
|
2014-08-27 00:03:32 +04:00
|
|
|
else:
|
2016-01-11 08:07:34 +03:00
|
|
|
ma, r = self._changedfiles(parents[0], ctx)
|
convert: introduce --full for converting all files
Convert will normally only process files that were changed in a source
revision, apply the filemap, and record it has a change in the target
repository. (If it ends up not really changing anything, nothing changes.)
That means that _if_ the filemap is changed before continuing an incremental
convert, the change will only kick in when the files it affects are modified in
a source revision and thus processed.
With --full, convert will make a full conversion every time and process
all files in the source repo and remove target repo files that shouldn't be
there. Filemap changes will thus kick in on the first converted revision, no
matter what is changed.
This flag should in most cases not make any difference but will make convert
significantly slower.
Other names has been considered for this feature, such as "resync", "sync",
"checkunmodified", "all" or "allfiles", but I found that they were less obvious
and required more explanation than "full" and were harder to describe
consistently.
2014-08-27 00:03:32 +04:00
|
|
|
if not full:
|
2016-01-11 08:07:34 +03:00
|
|
|
files = ma + r
|
|
|
|
copyfiles = ma
|
2016-01-10 08:42:48 +03:00
|
|
|
# _getcopies() is also run for roots and before filtering so missing
|
2014-08-27 00:03:32 +04:00
|
|
|
# revlogs are detected early
|
2016-01-10 08:42:48 +03:00
|
|
|
copies = self._getcopies(ctx, parents, copyfiles)
|
2015-03-19 19:40:19 +03:00
|
|
|
cleanp2 = set()
|
|
|
|
if len(parents) == 2:
|
2016-01-10 09:58:10 +03:00
|
|
|
d = parents[1].manifest().diff(ctx.manifest(), clean=True)
|
|
|
|
for f, value in d.iteritems():
|
|
|
|
if value is None:
|
|
|
|
cleanp2.add(f)
|
2014-08-27 00:03:32 +04:00
|
|
|
changes = [(f, rev) for f in files if f not in self.ignored]
|
|
|
|
changes.sort()
|
2015-03-19 19:40:19 +03:00
|
|
|
return changes, copies, cleanp2
|
2007-07-27 00:34:36 +04:00
|
|
|
|
2016-01-10 08:42:48 +03:00
|
|
|
def _getcopies(self, ctx, parents, files):
|
2007-07-27 00:34:36 +04:00
|
|
|
copies = {}
|
2007-09-01 09:49:18 +04:00
|
|
|
for name in files:
|
2008-10-21 23:24:47 +04:00
|
|
|
if name in self.ignored:
|
|
|
|
continue
|
2007-07-27 00:34:36 +04:00
|
|
|
try:
|
2013-07-19 03:40:57 +04:00
|
|
|
copysource, _copynode = ctx.filectx(name).renamed()
|
|
|
|
if copysource in self.ignored:
|
2008-10-21 23:24:47 +04:00
|
|
|
continue
|
2009-10-07 20:52:01 +04:00
|
|
|
# Ignore copy sources not in parent revisions
|
|
|
|
found = False
|
|
|
|
for p in parents:
|
|
|
|
if copysource in p:
|
|
|
|
found = True
|
|
|
|
break
|
|
|
|
if not found:
|
|
|
|
continue
|
2008-10-21 23:24:47 +04:00
|
|
|
copies[name] = copysource
|
2007-07-27 00:34:36 +04:00
|
|
|
except TypeError:
|
|
|
|
pass
|
2015-06-24 08:20:08 +03:00
|
|
|
except error.LookupError as e:
|
2008-10-21 23:24:47 +04:00
|
|
|
if not self.ignoreerrors:
|
|
|
|
raise
|
2009-05-17 05:04:17 +04:00
|
|
|
self.ignored.add(name)
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ui.warn(_("ignoring: %s\n") % e)
|
2007-07-27 00:34:36 +04:00
|
|
|
return copies
|
2007-08-07 12:28:43 +04:00
|
|
|
|
2007-07-27 00:34:36 +04:00
|
|
|
def getcommit(self, rev):
|
2016-01-10 08:42:48 +03:00
|
|
|
ctx = self._changectx(rev)
|
2016-04-13 01:16:21 +03:00
|
|
|
_parents = self._parents(ctx)
|
|
|
|
parents = [p.hex() for p in _parents]
|
|
|
|
optparents = [p.hex() for p in ctx.parents() if p and p not in _parents]
|
2015-06-14 20:04:00 +03:00
|
|
|
crev = rev
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
return common.commit(
|
|
|
|
author=ctx.user(),
|
|
|
|
date=util.datestr(ctx.date(), "%Y-%m-%d %H:%M:%S %1%2"),
|
|
|
|
desc=ctx.description(),
|
|
|
|
rev=crev,
|
|
|
|
parents=parents,
|
|
|
|
optparents=optparents,
|
|
|
|
branch=ctx.branch(),
|
|
|
|
extra=ctx.extra(),
|
|
|
|
sortkey=ctx.rev(),
|
|
|
|
saverev=self.saverev,
|
|
|
|
phase=ctx.phase(),
|
|
|
|
)
|
2007-07-27 00:34:36 +04:00
|
|
|
|
|
|
|
def gettags(self):
|
2014-05-16 04:30:27 +04:00
|
|
|
# This will get written to .hgtags, filter non global tags out.
|
2018-05-30 12:16:33 +03:00
|
|
|
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)]
|
|
|
|
)
|
2007-10-05 06:21:37 +04:00
|
|
|
|
|
|
|
def getchangedfiles(self, rev, i):
|
2016-01-10 08:42:48 +03:00
|
|
|
ctx = self._changectx(rev)
|
|
|
|
parents = self._parents(ctx)
|
2008-08-14 01:31:10 +04:00
|
|
|
if not parents and i is None:
|
|
|
|
i = 0
|
2016-01-11 08:07:34 +03:00
|
|
|
ma, r = ctx.manifest().keys(), []
|
2008-08-14 01:31:10 +04:00
|
|
|
else:
|
|
|
|
i = i or 0
|
2016-01-11 08:07:34 +03:00
|
|
|
ma, r = self._changedfiles(parents[i], ctx)
|
|
|
|
ma, r = [[f for f in l if f not in self.ignored] for l in (ma, r)]
|
2007-10-05 06:21:37 +04:00
|
|
|
|
|
|
|
if i == 0:
|
2016-01-11 08:07:34 +03:00
|
|
|
self._changescache = (rev, (ma, r))
|
2007-10-05 06:21:37 +04:00
|
|
|
|
2016-01-11 08:07:34 +03:00
|
|
|
return ma + r
|
2007-10-05 06:21:37 +04:00
|
|
|
|
2007-11-27 04:23:20 +03:00
|
|
|
def converted(self, rev, destrev):
|
|
|
|
if self.convertfp is None:
|
2018-09-28 17:08:56 +03:00
|
|
|
self.convertfp = open(self.repo.localvfs.join("shamap"), "a")
|
2018-05-30 12:16:33 +03:00
|
|
|
self.convertfp.write("%s %s\n" % (destrev, rev))
|
2007-11-27 04:23:20 +03:00
|
|
|
self.convertfp.flush()
|
2008-01-05 01:43:55 +03:00
|
|
|
|
|
|
|
def before(self):
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ui.debug("run hg source pre-conversion action\n")
|
2008-01-05 01:43:55 +03:00
|
|
|
|
|
|
|
def after(self):
|
2018-05-30 12:16:33 +03:00
|
|
|
self.ui.debug("run hg source post-conversion action\n")
|
2009-06-01 19:12:39 +04:00
|
|
|
|
|
|
|
def hasnativeorder(self):
|
|
|
|
return True
|
2009-06-01 19:12:42 +04:00
|
|
|
|
2013-03-23 21:06:52 +04:00
|
|
|
def hasnativeclose(self):
|
|
|
|
return True
|
|
|
|
|
2009-06-01 19:12:42 +04:00
|
|
|
def lookuprev(self, rev):
|
|
|
|
try:
|
2016-03-02 18:26:49 +03:00
|
|
|
return nodemod.hex(self.repo.lookup(rev))
|
2015-01-19 06:21:53 +03:00
|
|
|
except (error.RepoError, error.LookupError):
|
2009-06-01 19:12:42 +04:00
|
|
|
return None
|
2011-03-25 03:38:47 +03:00
|
|
|
|
|
|
|
def getbookmarks(self):
|
|
|
|
return bookmarks.listbookmarks(self.repo)
|
2013-04-25 22:50:26 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
def checkrevformat(self, revstr, mapname="splicemap"):
|
2013-04-25 22:50:26 +04:00
|
|
|
""" Mercurial, revision string is a 40 byte hex """
|
2014-01-21 21:34:55 +04:00
|
|
|
self.checkhexformat(revstr, mapname)
|