##// END OF EJS Templates
typing: lock in correct changes from pytype 2023.04.11 -> 2023.06.16...
typing: lock in correct changes from pytype 2023.04.11 -> 2023.06.16 There were a handful of other changes to the pyi files generated when updating pytype locally (and jumping from python 3.8.0 to python 3.10.11), but they were not as clear (e.g. the embedded type in a list changing from `nothing` to `Any` or similar). These looked obviously correct, and agreed with PyCharm's thoughts on the signatures. Oddly, even though pytype starting inferring `obsutil._getfilteredreason()` as returning bytes, it (correctly) complained about the None path when it was typed that way. Instead, raise a ProgrammingError if an unhandled fate is calculated. (Currently, all possibilities are handled, so this isn't reachable unless another fate is added in the future.)

File last commit:

r51887:a97f2b50 default
r52708:460e8048 default
Show More
fastexport.py
216 lines | 6.7 KiB | text/x-python | PythonLexer
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 # Copyright 2020 Joerg Sonnenberger <joerg@bec.de>
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""export repositories as git fast-import stream"""
# The format specification for fast-import streams can be found at
# https://git-scm.com/docs/git-fast-import#_input_format
import re
from mercurial.i18n import _
from mercurial.node import hex, nullrev
from mercurial.utils import stringutil
from mercurial import (
error,
Martin von Zweigbergk
errors: raise InputError on bad revset to revrange() iff provided by the user...
r48928 logcmdutil,
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 registrar,
scmutil,
)
from .convert import convcmd
# Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
testedwith = b"ships-with-hg-core"
cmdtable = {}
command = registrar.command(cmdtable)
GIT_PERSON_PROHIBITED = re.compile(b'[<>\n"]')
GIT_EMAIL_PROHIBITED = re.compile(b"[<> \n]")
def convert_to_git_user(authormap, user, rev):
mapped_user = authormap.get(user, user)
user_person = stringutil.person(mapped_user)
user_email = stringutil.email(mapped_user)
if GIT_EMAIL_PROHIBITED.match(user_email) or GIT_PERSON_PROHIBITED.match(
user_person
):
raise error.Abort(
Joerg Sonnenberger
fastexport: make a diagnostics message more localizable...
r45181 _(b"Unable to parse user into person and email for revision %s")
% rev
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 )
if user_person:
Joerg Sonnenberger
fastexport: simplify code
r51887 return b'"%s" <%s>' % (user_person, user_email)
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 else:
Joerg Sonnenberger
fastexport: simplify code
r51887 return b"<%s>" % user_email
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761
def convert_to_git_date(date):
timestamp, utcoff = date
Joerg Sonnenberger
fastexport: adjust output to be more canonical...
r45345 tzsign = b"+" if utcoff <= 0 else b"-"
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 if utcoff % 60 != 0:
raise error.Abort(
_(b"UTC offset in %b is not an integer number of seconds") % (date,)
)
utcoff = abs(utcoff) // 60
tzh = utcoff // 60
tzmin = utcoff % 60
return b"%d " % int(timestamp) + tzsign + b"%02d%02d" % (tzh, tzmin)
def convert_to_git_ref(branch):
# XXX filter/map depending on git restrictions
return b"refs/heads/" + branch
Felipe Contreras
fastexport: rework newline logic...
r51219 def write_data(buf, data, add_newline=False):
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 buf.append(b"data %d\n" % len(data))
buf.append(data)
Felipe Contreras
fastexport: rework newline logic...
r51219 if add_newline or data[-1:] != b"\n":
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 buf.append(b"\n")
def export_commit(ui, repo, rev, marks, authormap):
ctx = repo[rev]
revid = ctx.hex()
if revid in marks:
Joerg Sonnenberger
fastexport: downgrade message about already exported changesets to debug...
r45329 ui.debug(b"warning: revision %s already exported, skipped\n" % revid)
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 return
parents = [p for p in ctx.parents() if p.rev() != nullrev]
for p in parents:
if p.hex() not in marks:
ui.warn(
_(b"warning: parent %s of %s has not been exported, skipped\n")
% (p, revid)
)
return
# For all files modified by the commit, check if they have already
# been exported and otherwise dump the blob with the new mark.
for fname in ctx.files():
if fname not in ctx:
continue
filectx = ctx.filectx(fname)
filerev = hex(filectx.filenode())
if filerev not in marks:
mark = len(marks) + 1
marks[filerev] = mark
data = filectx.data()
buf = [b"blob\n", b"mark :%d\n" % mark]
Felipe Contreras
fastexport: rework newline logic...
r51219 write_data(buf, data, True)
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 ui.write(*buf, keepprogressbar=True)
del buf
# Assign a mark for the current revision for references by
# latter merge commits.
mark = len(marks) + 1
marks[revid] = mark
ref = convert_to_git_ref(ctx.branch())
buf = [
b"commit %s\n" % ref,
b"mark :%d\n" % mark,
b"committer %s %s\n"
% (
convert_to_git_user(authormap, ctx.user(), revid),
convert_to_git_date(ctx.date()),
),
]
Felipe Contreras
fastexport: rework newline logic...
r51219 write_data(buf, ctx.description())
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 if parents:
buf.append(b"from :%d\n" % marks[parents[0].hex()])
if len(parents) == 2:
buf.append(b"merge :%d\n" % marks[parents[1].hex()])
p0ctx = repo[parents[0]]
files = ctx.manifest().diff(p0ctx.manifest())
else:
files = ctx.files()
filebuf = []
for fname in files:
if fname not in ctx:
filebuf.append((fname, b"D %s\n" % fname))
else:
filectx = ctx.filectx(fname)
filerev = filectx.filenode()
fileperm = b"755" if filectx.isexec() else b"644"
changed = b"M %s :%d %s\n" % (fileperm, marks[hex(filerev)], fname)
filebuf.append((fname, changed))
filebuf.sort()
buf.extend(changed for (fname, changed) in filebuf)
del filebuf
buf.append(b"\n")
ui.write(*buf, keepprogressbar=True)
del buf
isrev = re.compile(b"^[0-9a-f]{40}$")
@command(
b"fastexport",
[
(b"r", b"rev", [], _(b"revisions to export"), _(b"REV")),
(b"i", b"import-marks", b"", _(b"old marks file to read"), _(b"FILE")),
(b"e", b"export-marks", b"", _(b"new marks file to write"), _(b"FILE")),
(
b"A",
b"authormap",
b"",
_(b"remap usernames using this file"),
_(b"FILE"),
),
],
_(b"[OPTION]... [REV]..."),
helpcategory=command.CATEGORY_IMPORT_EXPORT,
)
def fastexport(ui, repo, *revs, **opts):
"""export repository as git fast-import stream
This command lets you dump a repository as a human-readable text stream.
It can be piped into corresponding import routines like "git fast-import".
Incremental dumps can be created by using marks files.
"""
Matt Harbison
fastexport: migrate `opts` to native kwargs
r51768 revs += tuple(opts.get("rev", []))
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 if not revs:
revs = scmutil.revrange(repo, [b":"])
else:
Martin von Zweigbergk
errors: raise InputError on bad revset to revrange() iff provided by the user...
r48928 revs = logcmdutil.revrange(repo, revs)
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 if not revs:
raise error.Abort(_(b"no revisions matched"))
Matt Harbison
fastexport: migrate `opts` to native kwargs
r51768 authorfile = opts.get("authormap")
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 if authorfile:
authormap = convcmd.readauthormap(ui, authorfile)
else:
authormap = {}
Matt Harbison
fastexport: migrate `opts` to native kwargs
r51768 import_marks = opts.get("import_marks")
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 marks = {}
if import_marks:
with open(import_marks, "rb") as import_marks_file:
for line in import_marks_file:
line = line.strip()
if not isrev.match(line) or line in marks:
raise error.Abort(_(b"Corrupted marks file"))
marks[line] = len(marks) + 1
revs.sort()
with ui.makeprogress(
_(b"exporting"), unit=_(b"revisions"), total=len(revs)
) as progress:
for rev in revs:
export_commit(ui, repo, rev, marks, authormap)
progress.increment()
Matt Harbison
fastexport: migrate `opts` to native kwargs
r51768 export_marks = opts.get("export_marks")
Joerg Sonnenberger
hgext: initial version of fastexport extension...
r44761 if export_marks:
with open(export_marks, "wb") as export_marks_file:
output_marks = [None] * len(marks)
for k, v in marks.items():
output_marks[v - 1] = k
for k in output_marks:
export_marks_file.write(k + b"\n")