2010-01-26 19:36:13 +03:00
|
|
|
import errno
|
2009-06-16 10:43:12 +04:00
|
|
|
import traceback
|
|
|
|
|
2014-02-03 10:55:56 +04:00
|
|
|
import compathacks
|
2009-06-16 10:43:12 +04:00
|
|
|
import svnexternals
|
|
|
|
import util
|
2019-01-30 03:25:33 +03:00
|
|
|
from edenscm.mercurial import context, node, revlog, util as hgutil
|
2009-06-16 10:43:12 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2009-06-16 10:43:12 +04:00
|
|
|
class MissingPlainTextError(Exception):
|
|
|
|
"""Exception raised when the repo lacks a source file required for replaying
|
|
|
|
a txdelta.
|
|
|
|
"""
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2009-06-16 10:43:12 +04:00
|
|
|
class ReplayException(Exception):
|
|
|
|
"""Exception raised when you try and commit but the replay encountered an
|
|
|
|
exception.
|
|
|
|
"""
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2010-11-19 19:07:13 +03:00
|
|
|
def updateexternals(ui, meta, current):
|
|
|
|
# TODO fix and re-enable externals for single-directory clones
|
2018-05-30 12:16:33 +03:00
|
|
|
if not current.externals or meta.layout == "single":
|
2010-11-19 19:07:13 +03:00
|
|
|
return
|
|
|
|
|
|
|
|
# accumulate externals records for all branches
|
|
|
|
revnum = current.rev.revnum
|
|
|
|
branches = {}
|
|
|
|
for path, entry in current.externals.iteritems():
|
|
|
|
if not meta.is_path_valid(path):
|
|
|
|
continue
|
|
|
|
|
|
|
|
p, b, bp = meta.split_branch_path(path)
|
|
|
|
if bp not in branches:
|
|
|
|
parent = meta.get_parent_revision(revnum, b)
|
|
|
|
pctx = meta.repo[parent]
|
2010-11-25 23:55:21 +03:00
|
|
|
branches[bp] = (svnexternals.parse(ui, pctx), pctx)
|
2010-11-19 19:07:13 +03:00
|
|
|
branches[bp][0][p] = entry
|
2010-11-19 19:07:13 +03:00
|
|
|
|
|
|
|
# register externals file changes
|
2010-11-19 19:07:13 +03:00
|
|
|
for bp, (external, pctx) in branches.iteritems():
|
2018-05-30 12:16:33 +03:00
|
|
|
if bp and bp[-1] != "/":
|
|
|
|
bp += "/"
|
2010-11-19 19:07:13 +03:00
|
|
|
updates = svnexternals.getchanges(ui, meta.repo, pctx, external)
|
|
|
|
for fn, data in updates.iteritems():
|
|
|
|
path = (bp and bp + fn) or fn
|
|
|
|
if data is not None:
|
|
|
|
current.set(path, data, False, False)
|
|
|
|
else:
|
|
|
|
current.delete(path)
|
2010-11-19 19:07:13 +03:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2011-02-13 22:10:52 +03:00
|
|
|
def convert_rev(ui, meta, svn, r, tbdelta, firstrun):
|
2012-10-06 11:59:55 +04:00
|
|
|
try:
|
|
|
|
return _convert_rev(ui, meta, svn, r, tbdelta, firstrun)
|
|
|
|
finally:
|
|
|
|
meta.editor.current.close()
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2012-10-06 11:59:55 +04:00
|
|
|
def _convert_rev(ui, meta, svn, r, tbdelta, firstrun):
|
2009-06-16 11:12:04 +04:00
|
|
|
|
|
|
|
editor = meta.editor
|
|
|
|
editor.current.clear()
|
|
|
|
editor.current.rev = r
|
2012-10-21 00:22:02 +04:00
|
|
|
editor.setsvn(svn)
|
2010-07-20 13:55:07 +04:00
|
|
|
|
2016-06-06 04:18:23 +03:00
|
|
|
if firstrun and meta.revmap.firstpulled <= 0:
|
2011-02-13 22:10:52 +03:00
|
|
|
# We know nothing about this project, so fetch everything before
|
|
|
|
# trying to apply deltas.
|
2018-05-30 12:16:33 +03:00
|
|
|
ui.debug("replay: fetching full revision\n")
|
2010-07-20 13:55:07 +04:00
|
|
|
svn.get_revision(r.revnum, editor)
|
|
|
|
else:
|
2016-06-06 04:18:23 +03:00
|
|
|
svn.get_replay(r.revnum, editor, meta.revmap.firstpulled)
|
2012-10-03 23:27:02 +04:00
|
|
|
editor.close()
|
2010-07-20 13:55:07 +04:00
|
|
|
|
2009-06-16 11:12:04 +04:00
|
|
|
current = editor.current
|
|
|
|
|
2010-11-19 19:07:13 +03:00
|
|
|
updateexternals(ui, meta, current)
|
2009-06-16 10:43:12 +04:00
|
|
|
|
2011-10-17 18:44:15 +04:00
|
|
|
if current.exception is not None: # pragma: no cover
|
2009-06-16 10:43:12 +04:00
|
|
|
traceback.print_exception(*current.exception)
|
|
|
|
raise ReplayException()
|
|
|
|
|
2012-10-06 11:59:55 +04:00
|
|
|
files_to_commit = current.files()
|
2009-06-16 10:43:12 +04:00
|
|
|
branch_batches = {}
|
|
|
|
rev = current.rev
|
|
|
|
date = meta.fixdate(rev.date)
|
|
|
|
|
|
|
|
# build up the branches that have files on them
|
2018-05-30 12:16:33 +03:00
|
|
|
failoninvalid = ui.configbool("hgsubversion", "failoninvalidreplayfile", False)
|
2009-06-16 10:43:12 +04:00
|
|
|
for f in files_to_commit:
|
|
|
|
if not meta.is_path_valid(f):
|
2012-10-14 17:51:12 +04:00
|
|
|
if failoninvalid:
|
2018-05-30 12:16:33 +03:00
|
|
|
raise hgutil.Abort("file %s should not be in commit list" % f)
|
2009-06-16 10:43:12 +04:00
|
|
|
continue
|
|
|
|
p, b = meta.split_branch_path(f)[:2]
|
|
|
|
if b not in branch_batches:
|
|
|
|
branch_batches[b] = []
|
2013-11-17 04:16:59 +04:00
|
|
|
if p:
|
|
|
|
branch_batches[b].append((p, f))
|
2009-06-16 10:43:12 +04:00
|
|
|
|
|
|
|
closebranches = {}
|
2018-05-30 12:16:33 +03:00
|
|
|
for branch in tbdelta["branches"][1]:
|
2016-06-15 15:28:40 +03:00
|
|
|
branchedits = meta.revmap.branchedits(branch, rev.revnum)
|
2009-06-16 10:43:12 +04:00
|
|
|
if len(branchedits) < 1:
|
|
|
|
# can't close a branch that never existed
|
|
|
|
continue
|
|
|
|
ha = branchedits[0][1]
|
|
|
|
closebranches[branch] = ha
|
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
extraempty = set(tbdelta["branches"][0]) - (
|
|
|
|
set(current.emptybranches) | set(branch_batches.keys())
|
|
|
|
)
|
2010-02-06 22:01:35 +03:00
|
|
|
current.emptybranches.update([(x, False) for x in extraempty])
|
|
|
|
|
2009-06-16 10:43:12 +04:00
|
|
|
# 1. handle normal commits
|
|
|
|
closedrevs = closebranches.values()
|
|
|
|
for branch, files in branch_batches.iteritems():
|
|
|
|
|
|
|
|
if branch in current.emptybranches and files:
|
|
|
|
del current.emptybranches[branch]
|
|
|
|
|
2014-03-24 20:20:57 +04:00
|
|
|
if meta.skipbranch(branch):
|
|
|
|
# make sure we also get rid of it from emptybranches
|
|
|
|
if branch in current.emptybranches:
|
|
|
|
del current.emptybranches[branch]
|
|
|
|
continue
|
|
|
|
|
2009-06-16 10:43:12 +04:00
|
|
|
files = dict(files)
|
|
|
|
parents = meta.get_parent_revision(rev.revnum, branch), revlog.nullid
|
|
|
|
if parents[0] in closedrevs and branch in meta.closebranches:
|
|
|
|
continue
|
|
|
|
|
|
|
|
extra = meta.genextra(rev.revnum, branch)
|
2010-01-16 19:25:09 +03:00
|
|
|
tag = None
|
2009-06-16 10:43:12 +04:00
|
|
|
if branch is not None:
|
2010-01-16 19:25:09 +03:00
|
|
|
# New regular tag without modifications, it will be committed by
|
|
|
|
# svnmeta.committag(), we can skip the whole branch for now
|
|
|
|
tag = meta.get_path_tag(meta.remotename(branch))
|
2018-05-30 12:16:33 +03:00
|
|
|
if (
|
|
|
|
tag
|
|
|
|
and tag not in meta.tags
|
2010-02-06 19:36:21 +03:00
|
|
|
and branch not in meta.branches
|
2014-02-03 10:55:56 +04:00
|
|
|
and branch not in compathacks.branchset(meta.repo)
|
2018-05-30 12:16:33 +03:00
|
|
|
and not files
|
|
|
|
):
|
2009-06-16 10:43:12 +04:00
|
|
|
continue
|
|
|
|
|
2009-06-24 06:38:27 +04:00
|
|
|
parentctx = meta.repo.changectx(parents[0])
|
|
|
|
if tag:
|
|
|
|
if parentctx.node() == node.nullid:
|
|
|
|
continue
|
2018-05-30 12:16:33 +03:00
|
|
|
extra.update({"branch": parentctx.extra().get("branch", None), "close": 1})
|
2009-06-24 06:38:27 +04:00
|
|
|
|
2009-06-16 10:43:12 +04:00
|
|
|
def filectxfn(repo, memctx, path):
|
|
|
|
current_file = files[path]
|
2014-09-17 03:35:21 +04:00
|
|
|
try:
|
|
|
|
data, isexec, islink, copied = current.pop(current_file)
|
|
|
|
except IOError:
|
|
|
|
return compathacks.filectxfn_deleted_reraise(memctx)
|
2012-10-06 11:59:55 +04:00
|
|
|
if isexec is None or islink is None:
|
|
|
|
flags = parentctx.flags(path)
|
|
|
|
if isexec is None:
|
2018-05-30 12:16:33 +03:00
|
|
|
isexec = "x" in flags
|
2012-10-06 11:59:55 +04:00
|
|
|
if islink is None:
|
2018-05-30 12:16:33 +03:00
|
|
|
islink = "l" in flags
|
2012-10-06 11:59:55 +04:00
|
|
|
|
|
|
|
if data is not None:
|
|
|
|
if islink:
|
2018-05-30 12:16:33 +03:00
|
|
|
if data.startswith("link "):
|
|
|
|
data = data[len("link ") :]
|
2012-10-06 11:59:55 +04:00
|
|
|
else:
|
2018-05-30 12:16:33 +03:00
|
|
|
ui.debug(
|
|
|
|
"file marked as link, but may contain data: "
|
|
|
|
"%s\n" % current_file
|
|
|
|
)
|
2009-06-16 10:43:12 +04:00
|
|
|
else:
|
2009-06-24 06:38:27 +04:00
|
|
|
data = parentctx.filectx(path).data()
|
2018-05-30 12:16:33 +03:00
|
|
|
return compathacks.makememfilectx(
|
|
|
|
repo,
|
|
|
|
memctx=memctx,
|
|
|
|
path=path,
|
|
|
|
data=data,
|
|
|
|
islink=islink,
|
|
|
|
isexec=isexec,
|
|
|
|
copied=copied,
|
|
|
|
)
|
2009-06-16 10:43:12 +04:00
|
|
|
|
2010-03-02 00:10:18 +03:00
|
|
|
meta.mapbranch(extra)
|
2018-05-30 12:16:33 +03:00
|
|
|
if "branch" not in extra:
|
|
|
|
extra["branch"] = "default"
|
|
|
|
current_ctx = context.memctx(
|
|
|
|
meta.repo,
|
|
|
|
parents,
|
|
|
|
util.forceutf8(meta.getmessage(rev)),
|
|
|
|
[util.forceutf8(f) for f in files.keys()],
|
|
|
|
filectxfn,
|
|
|
|
util.forceutf8(meta.authors[rev.author]),
|
|
|
|
date,
|
|
|
|
extra,
|
|
|
|
)
|
2009-06-16 10:43:12 +04:00
|
|
|
|
2011-10-11 07:03:13 +04:00
|
|
|
new_hash = meta.repo.svn_commitctx(current_ctx)
|
2009-06-16 11:12:04 +04:00
|
|
|
util.describe_commit(ui, new_hash, branch)
|
2009-06-24 06:38:27 +04:00
|
|
|
if (rev.revnum, branch) not in meta.revmap and not tag:
|
2009-06-16 10:43:12 +04:00
|
|
|
meta.revmap[rev.revnum, branch] = new_hash
|
2009-06-24 06:38:27 +04:00
|
|
|
if tag:
|
2010-07-11 13:46:19 +04:00
|
|
|
meta.movetag(tag, new_hash, rev, date)
|
2010-02-06 19:36:21 +03:00
|
|
|
meta.addedtags.pop(tag, None)
|
2009-06-16 10:43:12 +04:00
|
|
|
|
|
|
|
# 2. handle branches that need to be committed without any files
|
|
|
|
for branch in current.emptybranches:
|
|
|
|
|
2014-03-24 20:20:57 +04:00
|
|
|
if meta.skipbranch(branch):
|
|
|
|
continue
|
|
|
|
|
2009-06-16 10:43:12 +04:00
|
|
|
ha = meta.get_parent_revision(rev.revnum, branch)
|
|
|
|
if ha == node.nullid:
|
|
|
|
continue
|
|
|
|
|
2018-01-03 22:51:00 +03:00
|
|
|
# Why are we constructing this? Follow up! (T24862348)
|
|
|
|
meta.repo.changectx(ha)
|
|
|
|
|
2013-11-17 04:16:59 +04:00
|
|
|
files = []
|
2018-05-30 12:16:33 +03:00
|
|
|
|
2009-06-16 10:43:12 +04:00
|
|
|
def del_all_files(*args):
|
2018-05-30 12:16:33 +03:00
|
|
|
raise IOError(errno.ENOENT, "deleting all files")
|
2009-06-16 10:43:12 +04:00
|
|
|
|
2013-11-17 04:16:59 +04:00
|
|
|
# True here means nuke all files. This happens when you
|
|
|
|
# replace a branch root with an empty directory
|
|
|
|
if current.emptybranches[branch]:
|
|
|
|
files = meta.repo[ha].files()
|
2009-06-16 10:43:12 +04:00
|
|
|
|
|
|
|
extra = meta.genextra(rev.revnum, branch)
|
2010-07-10 16:19:24 +04:00
|
|
|
meta.mapbranch(extra)
|
2009-06-16 10:43:12 +04:00
|
|
|
|
2018-05-30 12:16:33 +03:00
|
|
|
current_ctx = context.memctx(
|
|
|
|
meta.repo,
|
|
|
|
(ha, node.nullid),
|
|
|
|
util.forceutf8(meta.getmessage(rev)),
|
|
|
|
[util.forceutf8(f) for f in files],
|
|
|
|
del_all_files,
|
|
|
|
util.forceutf8(meta.authors[rev.author]),
|
|
|
|
date,
|
|
|
|
extra,
|
|
|
|
)
|
2011-10-11 07:03:13 +04:00
|
|
|
new_hash = meta.repo.svn_commitctx(current_ctx)
|
2009-06-16 11:12:04 +04:00
|
|
|
util.describe_commit(ui, new_hash, branch)
|
2009-06-16 10:43:12 +04:00
|
|
|
if (rev.revnum, branch) not in meta.revmap:
|
|
|
|
meta.revmap[rev.revnum, branch] = new_hash
|
|
|
|
|
|
|
|
return closebranches
|