hg.py
651 lines
| 23.6 KiB
| text/x-python
|
PythonLexer
Martin Geisler
|
r8250 | # 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 | ||||
Matt Mackall
|
r10263 | # GNU General Public License version 2 or any later version. | ||
Brendan Cully
|
r4536 | |||
Bryan O'Sullivan
|
r5556 | # 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. | ||||
# | ||||
Patrick Mezard
|
r8596 | # * 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. | ||||
timeless
|
r28370 | from __future__ import absolute_import | ||
Bryan O'Sullivan
|
r5013 | |||
timeless
|
r28370 | import os | ||
import re | ||||
import time | ||||
Bryan O'Sullivan
|
r5013 | |||
Yuya Nishihara
|
r29205 | from mercurial.i18n import _ | ||
timeless
|
r28370 | from mercurial import ( | ||
bookmarks, | ||||
context, | ||||
error, | ||||
exchange, | ||||
hg, | ||||
lock as lockmod, | ||||
merge as mergemod, | ||||
node as nodemod, | ||||
phases, | ||||
scmutil, | ||||
util, | ||||
) | ||||
Boris Feld
|
r36625 | from mercurial.utils import dateutil | ||
timeless
|
r28861 | stringio = util.stringio | ||
timeless
|
r28370 | from . import common | ||
mapfile = common.mapfile | ||||
NoRepo = common.NoRepo | ||||
Brendan Cully
|
r4536 | |||
Augie Fackler
|
r36151 | sha1re = re.compile(br'\b[0-9a-f]{12,40}\b') | ||
Sean Farley
|
r20372 | |||
timeless
|
r28370 | class mercurial_sink(common.converter_sink): | ||
Matt Harbison
|
r35168 | def __init__(self, ui, repotype, path): | ||
common.converter_sink.__init__(self, ui, repotype, path) | ||||
Boris Feld
|
r34171 | self.branchnames = ui.configbool('convert', 'hg.usebranchnames') | ||
Boris Feld
|
r34164 | self.clonebranches = ui.configbool('convert', 'hg.clonebranches') | ||
Boris Feld
|
r34170 | self.tagsbranch = ui.config('convert', 'hg.tagsbranch') | ||
Brendan Cully
|
r5173 | self.lastbranch = None | ||
Bryan O'Sullivan
|
r5441 | if os.path.isdir(path) and len(os.listdir(path)) > 0: | ||
try: | ||||
self.repo = hg.repository(self.ui, path) | ||||
Patrick Mezard
|
r5918 | if not self.repo.local(): | ||
Martin Geisler
|
r10938 | raise NoRepo(_('%s is not a local Mercurial repository') | ||
% path) | ||||
Gregory Szorc
|
r25660 | except error.RepoError as err: | ||
Matt Mackall
|
r8206 | ui.traceback() | ||
Bryan O'Sullivan
|
r5441 | raise NoRepo(err.args[0]) | ||
else: | ||||
try: | ||||
ui.status(_('initializing destination %s repository\n') % path) | ||||
self.repo = hg.repository(self.ui, path, create=True) | ||||
Patrick Mezard
|
r5918 | if not self.repo.local(): | ||
Martin Geisler
|
r10938 | raise NoRepo(_('%s is not a local Mercurial repository') | ||
% path) | ||||
Bryan O'Sullivan
|
r5441 | self.created.append(path) | ||
Peter Arrenbrecht
|
r7875 | except error.RepoError: | ||
Matt Mackall
|
r8206 | ui.traceback() | ||
Martin Geisler
|
r10939 | raise NoRepo(_("could not create hg repository %s as sink") | ||
Martin Geisler
|
r10938 | % path) | ||
Bryan O'Sullivan
|
r5014 | self.lock = None | ||
self.wlock = None | ||||
Alexis S. L. Carvalho
|
r5378 | self.filemapmode = False | ||
Matt Harbison
|
r25558 | self.subrevmaps = {} | ||
Bryan O'Sullivan
|
r5014 | |||
def before(self): | ||||
Martin Geisler
|
r9467 | self.ui.debug('run hg sink pre-conversion action\n') | ||
Alexis S. L. Carvalho
|
r5052 | self.wlock = self.repo.wlock() | ||
Bryan O'Sullivan
|
r5014 | self.lock = self.repo.lock() | ||
def after(self): | ||||
Martin Geisler
|
r9467 | self.ui.debug('run hg sink post-conversion action\n') | ||
Matt Mackall
|
r10086 | if self.lock: | ||
self.lock.release() | ||||
if self.wlock: | ||||
self.wlock.release() | ||||
Brendan Cully
|
r4536 | |||
Bryan O'Sullivan
|
r5011 | def revmapfile(self): | ||
Pierre-Yves David
|
r31327 | return self.repo.vfs.join("shamap") | ||
Brendan Cully
|
r4536 | |||
Edouard Gomez
|
r4589 | def authorfile(self): | ||
Pierre-Yves David
|
r31327 | return self.repo.vfs.join("authormap") | ||
Edouard Gomez
|
r4589 | |||
Patrick Mezard
|
r5934 | def setbranch(self, branch, pbranches): | ||
if not self.clonebranches: | ||||
Brendan Cully
|
r5173 | return | ||
Patrick Mezard
|
r5934 | setbranch = (branch != self.lastbranch) | ||
Brendan Cully
|
r5173 | self.lastbranch = branch | ||
if not branch: | ||||
branch = 'default' | ||||
Patrick Mezard
|
r5934 | pbranches = [(b[0], b[1] and b[1] or 'default') for b in pbranches] | ||
Brendan Cully
|
r5173 | |||
branchpath = os.path.join(self.path, branch) | ||||
Patrick Mezard
|
r5934 | if setbranch: | ||
self.after() | ||||
try: | ||||
self.repo = hg.repository(self.ui, branchpath) | ||||
Brodie Rao
|
r16689 | except Exception: | ||
Brendan Cully
|
r5173 | self.repo = hg.repository(self.ui, branchpath, create=True) | ||
Patrick Mezard
|
r5934 | 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]) | ||||
Brodie Rao
|
r16689 | except Exception: | ||
Patrick Mezard
|
r5934 | missings.setdefault(b[1], []).append(b[0]) | ||
Thomas Arendsen Hein
|
r6210 | |||
Patrick Mezard
|
r5934 | if missings: | ||
self.after() | ||||
Mads Kiilerich
|
r18373 | for pbranch, heads in sorted(missings.iteritems()): | ||
Patrick Mezard
|
r5934 | pbranchpath = os.path.join(self.path, pbranch) | ||
Matt Mackall
|
r14556 | prepo = hg.peer(self.ui, {}, pbranchpath) | ||
Patrick Mezard
|
r5934 | self.ui.note(_('pulling from %s into %s\n') % (pbranch, branch)) | ||
Pierre-Yves David
|
r22698 | exchange.pull(self.repo, prepo, | ||
[prepo.lookup(h) for h in heads]) | ||||
Patrick Mezard
|
r5934 | self.before() | ||
Brendan Cully
|
r5173 | |||
Mads Kiilerich
|
r21076 | def _rewritetags(self, source, revmap, data): | ||
timeless
|
r28861 | fp = stringio() | ||
Patrick Mezard
|
r8693 | for line in data.splitlines(): | ||
s = line.split(' ', 1) | ||||
if len(s) != 2: | ||||
Matt Harbison
|
r39132 | self.ui.warn(_('invalid tag entry: "%s"\n') % line) | ||
fp.write('%s\n' % line) # Bogus, but keep for hash stability | ||||
Patrick Mezard
|
r8693 | continue | ||
revid = revmap.get(source.lookuprev(s[0])) | ||||
if not revid: | ||||
timeless
|
r28370 | if s[0] == nodemod.nullhex: | ||
Matt Mackall
|
r25305 | revid = s[0] | ||
else: | ||||
Matt Harbison
|
r39132 | # missing, but keep for hash stability | ||
self.ui.warn(_('missing tag entry: "%s"\n') % line) | ||||
fp.write('%s\n' % line) | ||||
Matt Mackall
|
r25305 | continue | ||
Mads Kiilerich
|
r21076 | fp.write('%s %s\n' % (revid, s[1])) | ||
Patrick Mezard
|
r8693 | return fp.getvalue() | ||
Matt Harbison
|
r25558 | def _rewritesubstate(self, source, data): | ||
timeless
|
r28861 | fp = stringio() | ||
Matt Harbison
|
r25558 | for line in data.splitlines(): | ||
s = line.split(' ', 1) | ||||
if len(s) != 2: | ||||
continue | ||||
revid = s[0] | ||||
subpath = s[1] | ||||
timeless
|
r28370 | if revid != nodemod.nullhex: | ||
Matt Harbison
|
r25558 | revmap = self.subrevmaps.get(subpath) | ||
if revmap is None: | ||||
revmap = mapfile(self.ui, | ||||
self.repo.wjoin(subpath, '.hg/shamap')) | ||||
self.subrevmaps[subpath] = revmap | ||||
# It is reasonable that one or more of the subrepos don't | ||||
# need to be converted, in which case they can be cloned | ||||
# into place instead of converted. Therefore, only warn | ||||
# once. | ||||
msg = _('no ".hgsubstate" updates will be made for "%s"\n') | ||||
if len(revmap) == 0: | ||||
sub = self.repo.wvfs.reljoin(subpath, '.hg') | ||||
if self.repo.wvfs.exists(sub): | ||||
self.ui.warn(msg % subpath) | ||||
newid = revmap.get(revid) | ||||
if not newid: | ||||
if len(revmap) > 0: | ||||
self.ui.warn(_("%s is missing from %s/.hg/shamap\n") % | ||||
(revid, subpath)) | ||||
else: | ||||
revid = newid | ||||
fp.write('%s %s\n' % (revid, subpath)) | ||||
return fp.getvalue() | ||||
Durham Goode
|
r26037 | 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( | ||||
self.repo, p1ctx, p2ctx, anc, | ||||
True, # branchmerge | ||||
True, # force | ||||
False, # acceptremote | ||||
False, # followcopies | ||||
) | ||||
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. | ||||
if action in ['m', 'dm', 'cd', 'dc']: | ||||
Pierre-Yves David
|
r26587 | raise error.Abort(_("unable to convert merge commit " | ||
Durham Goode
|
r26037 | "since target parents do not merge cleanly (file " | ||
"%s, parents %s and %s)") % (file, p1ctx, | ||||
p2ctx)) | ||||
elif action == 'k': | ||||
# 'keep' means nothing changed from p1 | ||||
continue | ||||
else: | ||||
# Any other change means we want to take the p2 version | ||||
yield file | ||||
Mads Kiilerich
|
r24395 | def putcommit(self, files, copies, parents, commit, source, revmap, full, | ||
cleanp2): | ||||
Patrick Mezard
|
r6717 | files = dict(files) | ||
Mads Kiilerich
|
r24395 | |||
Patrick Mezard
|
r6717 | def getfilectx(repo, memctx, f): | ||
Durham Goode
|
r26037 | if p2ctx and f in p2files and f not in copies: | ||
Mads Kiilerich
|
r24395 | self.ui.debug('reusing %s from p2\n' % f) | ||
Durham Goode
|
r26078 | 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 | ||||
Mads Kiilerich
|
r22300 | try: | ||
v = files[f] | ||||
except KeyError: | ||||
return None | ||||
Patrick Mezard
|
r11134 | data, mode = source.getfile(f, v) | ||
Mads Kiilerich
|
r22296 | if data is None: | ||
return None | ||||
Patrick Mezard
|
r8693 | if f == '.hgtags': | ||
Mads Kiilerich
|
r21076 | data = self._rewritetags(source, revmap, data) | ||
Matt Harbison
|
r25558 | if f == '.hgsubstate': | ||
data = self._rewritesubstate(source, data) | ||||
Martin von Zweigbergk
|
r35401 | return context.memfilectx(self.repo, memctx, f, data, 'l' in mode, | ||
Sean Farley
|
r21689 | 'x' in mode, copies.get(f)) | ||
Patrick Mezard
|
r6717 | |||
Brendan Cully
|
r4536 | pl = [] | ||
for p in parents: | ||||
Patrick Mezard
|
r6717 | if p not in pl: | ||
Brendan Cully
|
r4536 | pl.append(p) | ||
parents = pl | ||||
Alexis S. L. Carvalho
|
r5378 | nparents = len(parents) | ||
if self.filemapmode and nparents == 1: | ||||
timeless
|
r28370 | m1node = self.repo.changelog.read(nodemod.bin(parents[0]))[0] | ||
Alexis S. L. Carvalho
|
r5378 | parent = parents[0] | ||
Brendan Cully
|
r4536 | |||
Matt Mackall
|
r10282 | if len(parents) < 2: | ||
timeless
|
r28370 | parents.append(nodemod.nullid) | ||
Matt Mackall
|
r10282 | if len(parents) < 2: | ||
timeless
|
r28370 | parents.append(nodemod.nullid) | ||
Brendan Cully
|
r4536 | p2 = parents.pop(0) | ||
text = commit.desc | ||||
Sean Farley
|
r20372 | |||
sha1s = re.findall(sha1re, text) | ||||
for sha1 in sha1s: | ||||
oldrev = source.lookuprev(sha1) | ||||
newrev = revmap.get(oldrev) | ||||
if newrev is not None: | ||||
text = text.replace(sha1, newrev[:len(sha1)]) | ||||
Bryan O'Sullivan
|
r5439 | extra = commit.extra.copy() | ||
Matt Harbison
|
r21765 | |||
Durham Goode
|
r25750 | sourcename = self.repo.ui.config('convert', 'hg.sourcename') | ||
if sourcename: | ||||
extra['convert_source'] = sourcename | ||||
Matt Harbison
|
r25589 | for label in ('source', 'transplant_source', 'rebase_source', | ||
'intermediate-source'): | ||||
Matt Harbison
|
r21765 | node = extra.get(label) | ||
if node is None: | ||||
continue | ||||
# Only transplant stores its reference in binary | ||||
if label == 'transplant_source': | ||||
timeless
|
r28370 | node = nodemod.hex(node) | ||
Matt Harbison
|
r21765 | |||
newrev = revmap.get(node) | ||||
if newrev is not None: | ||||
if label == 'transplant_source': | ||||
timeless
|
r28370 | newrev = nodemod.bin(newrev) | ||
Matt Harbison
|
r21765 | |||
extra[label] = newrev | ||||
Bryan O'Sullivan
|
r5038 | if self.branchnames and commit.branch: | ||
Brendan Cully
|
r4873 | extra['branch'] = commit.branch | ||
Matt Harbison
|
r25570 | if commit.rev and commit.saverev: | ||
Brendan Cully
|
r4873 | extra['convert_revision'] = commit.rev | ||
Thomas Arendsen Hein
|
r4957 | |||
Brendan Cully
|
r4536 | while parents: | ||
p1 = p2 | ||||
p2 = parents.pop(0) | ||||
Durham Goode
|
r26037 | p1ctx = self.repo[p1] | ||
Mads Kiilerich
|
r24395 | p2ctx = None | ||
timeless
|
r28370 | if p2 != nodemod.nullid: | ||
Mads Kiilerich
|
r24395 | p2ctx = self.repo[p2] | ||
Mads Kiilerich
|
r22300 | fileset = set(files) | ||
if full: | ||||
Mads Kiilerich
|
r22360 | fileset.update(self.repo[p1]) | ||
fileset.update(self.repo[p2]) | ||||
Durham Goode
|
r26037 | |||
if p2ctx: | ||||
p2files = set(cleanp2) | ||||
for file in self._calculatemergedfiles(source, p1ctx, p2ctx): | ||||
p2files.add(file) | ||||
fileset.add(file) | ||||
Mads Kiilerich
|
r22300 | ctx = context.memctx(self.repo, (p1, p2), text, fileset, | ||
Matt Mackall
|
r10282 | getfilectx, commit.author, commit.date, extra) | ||
Matt Harbison
|
r25571 | |||
# We won't know if the conversion changes the node until after the | ||||
# commit, so copy the source's phase for now. | ||||
self.repo.ui.setconfig('phases', 'new-commit', | ||||
phases.phasenames[commit.phase], 'convert') | ||||
Bryan O'Sullivan
|
r27863 | with self.repo.transaction("convert") as tr: | ||
timeless
|
r28370 | node = nodemod.hex(self.repo.commitctx(ctx)) | ||
Matt Harbison
|
r25571 | |||
# 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: | ||||
Boris Feld
|
r33455 | phases.registernew(self.repo, tr, phases.draft, | ||
[ctx.node()]) | ||||
Matt Harbison
|
r25571 | |||
Brendan Cully
|
r4536 | text = "(octopus merge fixup)\n" | ||
Durham Goode
|
r25697 | p2 = node | ||
Brendan Cully
|
r4536 | |||
Alexis S. L. Carvalho
|
r5378 | if self.filemapmode and nparents == 1: | ||
Gregory Szorc
|
r39280 | man = self.repo.manifestlog.getstorage(b'') | ||
timeless
|
r28370 | mnode = self.repo.changelog.read(nodemod.bin(p2))[0] | ||
Matt Mackall
|
r11673 | closed = 'close' in commit.extra | ||
if not closed and not man.cmp(m1node, man.revision(mnode)): | ||||
Patrick Mezard
|
r8611 | self.ui.status(_("filtering out empty revision\n")) | ||
Matt Mackall
|
r15193 | self.repo.rollback(force=True) | ||
Alexis S. L. Carvalho
|
r5378 | return parent | ||
Brendan Cully
|
r4536 | return p2 | ||
def puttags(self, tags): | ||||
Martin von Zweigbergk
|
r37413 | tagparent = self.repo.branchtip(self.tagsbranch, ignoremissing=True) | ||
tagparent = tagparent or nodemod.nullid | ||||
Brendan Cully
|
r4536 | |||
Sean Farley
|
r20376 | 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)) | ||||
Brendan Cully
|
r4536 | |||
Matt Mackall
|
r8209 | newlines = sorted([("%s %s\n" % (tags[tag], tag)) for tag in tags]) | ||
Peter Arrenbrecht
|
r7877 | if newlines == oldlines: | ||
Patrick Mezard
|
r9431 | return None, None | ||
Sean Farley
|
r20377 | |||
# 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 | ||||
Peter Arrenbrecht
|
r7877 | data = "".join(newlines) | ||
def getfilectx(repo, memctx, f): | ||||
Martin von Zweigbergk
|
r35401 | return context.memfilectx(repo, memctx, f, data, False, False, None) | ||
Patrick Mezard
|
r6717 | |||
Peter Arrenbrecht
|
r7877 | self.ui.status(_("updating tags\n")) | ||
Pulkit Goyal
|
r37597 | date = "%d 0" % int(time.mktime(time.gmtime())) | ||
Peter Arrenbrecht
|
r7877 | extra = {'branch': self.tagsbranch} | ||
ctx = context.memctx(self.repo, (tagparent, None), "update tags", | ||||
[".hgtags"], getfilectx, "convert-repo", date, | ||||
extra) | ||||
Durham Goode
|
r25697 | node = self.repo.commitctx(ctx) | ||
timeless
|
r28370 | return nodemod.hex(node), nodemod.hex(tagparent) | ||
Bryan O'Sullivan
|
r5013 | |||
Alexis S. L. Carvalho
|
r5378 | def setfilemapmode(self, active): | ||
self.filemapmode = active | ||||
Edouard Gomez
|
r13746 | def putbookmarks(self, updatedbookmark): | ||
if not len(updatedbookmark): | ||||
return | ||||
Laurent Charignon
|
r26974 | wlock = lock = tr = None | ||
try: | ||||
wlock = self.repo.wlock() | ||||
lock = self.repo.lock() | ||||
tr = self.repo.transaction('bookmark') | ||||
Laurent Charignon
|
r26973 | self.ui.status(_("updating bookmarks\n")) | ||
destmarks = self.repo._bookmarks | ||||
Boris Feld
|
r33487 | changes = [(bookmark, nodemod.bin(updatedbookmark[bookmark])) | ||
for bookmark in updatedbookmark] | ||||
destmarks.applychanges(self.repo, tr, changes) | ||||
Laurent Charignon
|
r26974 | tr.close() | ||
finally: | ||||
lockmod.release(lock, wlock, tr) | ||||
Edouard Gomez
|
r13746 | |||
Mads Kiilerich
|
r21635 | 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 | ||||
Mads Kiilerich
|
r21634 | def hascommitforsplicemap(self, rev): | ||
Brodie Rao
|
r16686 | if rev not in self.repo and self.clonebranches: | ||
Pierre-Yves David
|
r26587 | raise error.Abort(_('revision %s not found in destination ' | ||
Patrick Mezard
|
r16106 | 'repository (lookups with clonebranches=true ' | ||
'are not implemented)') % rev) | ||||
return rev in self.repo | ||||
Edouard Gomez
|
r13746 | |||
timeless
|
r28370 | class mercurial_source(common.converter_source): | ||
Matt Harbison
|
r35168 | def __init__(self, ui, repotype, path, revs=None): | ||
common.converter_source.__init__(self, ui, repotype, path, revs) | ||||
Boris Feld
|
r34165 | self.ignoreerrors = ui.configbool('convert', 'hg.ignoreerrors') | ||
Benoit Boissinot
|
r8456 | self.ignored = set() | ||
Boris Feld
|
r34167 | self.saverev = ui.configbool('convert', 'hg.saverev') | ||
Bryan O'Sullivan
|
r5358 | try: | ||
self.repo = hg.repository(self.ui, path) | ||||
Bryan O'Sullivan
|
r5437 | # try to provoke an exception if this isn't really a hg | ||
# repo, but some other bogus compatible-looking url | ||||
Alexis S. L. Carvalho
|
r5522 | if not self.repo.local(): | ||
Brodie Rao
|
r16687 | raise error.RepoError | ||
Matt Mackall
|
r7637 | except error.RepoError: | ||
Matt Mackall
|
r8206 | ui.traceback() | ||
Martin Geisler
|
r10939 | raise NoRepo(_("%s is not a local Mercurial repository") % path) | ||
Bryan O'Sullivan
|
r5013 | self.lastrev = None | ||
self.lastctx = None | ||||
Mads Kiilerich
|
r22299 | self._changescache = None, None | ||
Bryan O'Sullivan
|
r5554 | self.convertfp = None | ||
Patrick Mezard
|
r6885 | # Restrict converted revisions to startrev descendants | ||
startnode = ui.config('convert', 'hg.startrev') | ||||
Mads Kiilerich
|
r19891 | hgrevs = ui.config('convert', 'hg.revs') | ||
if hgrevs is None: | ||||
if startnode is not None: | ||||
try: | ||||
startnode = self.repo.lookup(startnode) | ||||
except error.RepoError: | ||||
Pierre-Yves David
|
r26587 | raise error.Abort(_('%s is not a valid start revision') | ||
Mads Kiilerich
|
r19891 | % startnode) | ||
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 | ||||
Durham Goode
|
r25748 | if revs: | ||
Martin von Zweigbergk
|
r37379 | self._heads = [self.repo.lookup(r) for r in revs] | ||
Mads Kiilerich
|
r19891 | else: | ||
self._heads = self.repo.heads() | ||||
Patrick Mezard
|
r6885 | else: | ||
Durham Goode
|
r25748 | if revs or startnode is not None: | ||
Pierre-Yves David
|
r26587 | raise error.Abort(_('hg.revs cannot be combined with ' | ||
Mads Kiilerich
|
r19891 | 'hg.startrev or --rev')) | ||
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 | ||||
Bryan O'Sullivan
|
r5013 | |||
Martin von Zweigbergk
|
r27717 | def _changectx(self, rev): | ||
Bryan O'Sullivan
|
r5013 | if self.lastrev != rev: | ||
Matt Mackall
|
r6747 | self.lastctx = self.repo[rev] | ||
Bryan O'Sullivan
|
r5013 | self.lastrev = rev | ||
return self.lastctx | ||||
Martin von Zweigbergk
|
r27717 | def _parents(self, ctx): | ||
Patrick Mezard
|
r9531 | return [p for p in ctx.parents() if p and self.keep(p.node())] | ||
Patrick Mezard
|
r6885 | |||
Bryan O'Sullivan
|
r5013 | def getheads(self): | ||
timeless
|
r28370 | return [nodemod.hex(h) for h in self._heads if self.keep(h)] | ||
Bryan O'Sullivan
|
r5013 | |||
def getfile(self, name, rev): | ||||
try: | ||||
Martin von Zweigbergk
|
r27717 | fctx = self._changectx(rev)[name] | ||
Patrick Mezard
|
r11134 | return fctx.data(), fctx.flags() | ||
Mads Kiilerich
|
r22296 | except error.LookupError: | ||
return None, None | ||||
Bryan O'Sullivan
|
r5013 | |||
Martin von Zweigbergk
|
r27718 | def _changedfiles(self, ctx1, ctx2): | ||
Martin von Zweigbergk
|
r27719 | 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 | ||||
Martin von Zweigbergk
|
r27718 | |||
Mads Kiilerich
|
r22300 | def getchanges(self, rev, full): | ||
Martin von Zweigbergk
|
r27717 | ctx = self._changectx(rev) | ||
parents = self._parents(ctx) | ||||
Mads Kiilerich
|
r22300 | if full or not parents: | ||
Mads Kiilerich
|
r22299 | files = copyfiles = ctx.manifest() | ||
Mads Kiilerich
|
r22300 | if parents: | ||
Mads Kiilerich
|
r22299 | if self._changescache[0] == rev: | ||
Martin von Zweigbergk
|
r27718 | ma, r = self._changescache[1] | ||
Mads Kiilerich
|
r22299 | else: | ||
Martin von Zweigbergk
|
r27718 | ma, r = self._changedfiles(parents[0], ctx) | ||
Mads Kiilerich
|
r22300 | if not full: | ||
Martin von Zweigbergk
|
r27718 | files = ma + r | ||
copyfiles = ma | ||||
Martin von Zweigbergk
|
r27717 | # _getcopies() is also run for roots and before filtering so missing | ||
Mads Kiilerich
|
r22299 | # revlogs are detected early | ||
Martin von Zweigbergk
|
r27717 | copies = self._getcopies(ctx, parents, copyfiles) | ||
Mads Kiilerich
|
r24395 | cleanp2 = set() | ||
if len(parents) == 2: | ||||
Martin von Zweigbergk
|
r27719 | d = parents[1].manifest().diff(ctx.manifest(), clean=True) | ||
for f, value in d.iteritems(): | ||||
if value is None: | ||||
cleanp2.add(f) | ||||
Mads Kiilerich
|
r22299 | changes = [(f, rev) for f in files if f not in self.ignored] | ||
changes.sort() | ||||
Mads Kiilerich
|
r24395 | return changes, copies, cleanp2 | ||
Bryan O'Sullivan
|
r5013 | |||
Martin von Zweigbergk
|
r27717 | def _getcopies(self, ctx, parents, files): | ||
Bryan O'Sullivan
|
r5013 | copies = {} | ||
Alexis S. L. Carvalho
|
r5280 | for name in files: | ||
Patrick Mezard
|
r7231 | if name in self.ignored: | ||
continue | ||||
Bryan O'Sullivan
|
r5013 | try: | ||
Mads Kiilerich
|
r19457 | copysource, _copynode = ctx.filectx(name).renamed() | ||
if copysource in self.ignored: | ||||
Patrick Mezard
|
r7231 | continue | ||
Patrick Mezard
|
r9532 | # Ignore copy sources not in parent revisions | ||
Martin von Zweigbergk
|
r36358 | if not any(copysource in p for p in parents): | ||
Patrick Mezard
|
r9532 | continue | ||
Patrick Mezard
|
r7231 | copies[name] = copysource | ||
Bryan O'Sullivan
|
r5013 | except TypeError: | ||
pass | ||||
Gregory Szorc
|
r25660 | except error.LookupError as e: | ||
Patrick Mezard
|
r7231 | if not self.ignoreerrors: | ||
raise | ||||
Benoit Boissinot
|
r8456 | self.ignored.add(name) | ||
Patrick Mezard
|
r7231 | self.ui.warn(_('ignoring: %s\n') % e) | ||
Bryan O'Sullivan
|
r5013 | return copies | ||
Thomas Arendsen Hein
|
r5143 | |||
Bryan O'Sullivan
|
r5013 | def getcommit(self, rev): | ||
Martin von Zweigbergk
|
r27717 | ctx = self._changectx(rev) | ||
Mads Kiilerich
|
r28900 | _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] | ||||
Matt Harbison
|
r25570 | crev = rev | ||
timeless
|
r28370 | return common.commit(author=ctx.user(), | ||
Boris Feld
|
r36625 | date=dateutil.datestr(ctx.date(), | ||
timeless
|
r28370 | '%Y-%m-%d %H:%M:%S %1%2'), | ||
desc=ctx.description(), | ||||
rev=crev, | ||||
parents=parents, | ||||
Mads Kiilerich
|
r28900 | optparents=optparents, | ||
timeless
|
r28370 | branch=ctx.branch(), | ||
extra=ctx.extra(), | ||||
sortkey=ctx.rev(), | ||||
saverev=self.saverev, | ||||
phase=ctx.phase()) | ||||
Bryan O'Sullivan
|
r5013 | |||
Matt Harbison
|
r41215 | def numcommits(self): | ||
return len(self.repo) | ||||
Bryan O'Sullivan
|
r5013 | def gettags(self): | ||
Mads Kiilerich
|
r21498 | # 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'] | ||||
timeless
|
r28370 | return dict([(name, nodemod.hex(node)) for name, node in tags | ||
Patrick Mezard
|
r6885 | if self.keep(node)]) | ||
Alexis S. L. Carvalho
|
r5379 | |||
def getchangedfiles(self, rev, i): | ||||
Martin von Zweigbergk
|
r27717 | ctx = self._changectx(rev) | ||
parents = self._parents(ctx) | ||||
Patrick Mezard
|
r6885 | if not parents and i is None: | ||
i = 0 | ||||
Martin von Zweigbergk
|
r27718 | ma, r = ctx.manifest().keys(), [] | ||
Patrick Mezard
|
r6885 | else: | ||
i = i or 0 | ||||
Martin von Zweigbergk
|
r27718 | 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)] | ||||
Alexis S. L. Carvalho
|
r5379 | |||
if i == 0: | ||||
Martin von Zweigbergk
|
r27718 | self._changescache = (rev, (ma, r)) | ||
Alexis S. L. Carvalho
|
r5379 | |||
Martin von Zweigbergk
|
r27718 | return ma + r | ||
Alexis S. L. Carvalho
|
r5379 | |||
Bryan O'Sullivan
|
r5554 | def converted(self, rev, destrev): | ||
if self.convertfp is None: | ||||
Augie Fackler
|
r36149 | self.convertfp = open(self.repo.vfs.join('shamap'), 'ab') | ||
Yuya Nishihara
|
r36166 | self.convertfp.write(util.tonativeeol('%s %s\n' % (destrev, rev))) | ||
Bryan O'Sullivan
|
r5554 | self.convertfp.flush() | ||
Patrick Mezard
|
r5805 | |||
def before(self): | ||||
Martin Geisler
|
r9467 | self.ui.debug('run hg source pre-conversion action\n') | ||
Patrick Mezard
|
r5805 | |||
def after(self): | ||||
Martin Geisler
|
r9467 | self.ui.debug('run hg source post-conversion action\n') | ||
Patrick Mezard
|
r8691 | |||
def hasnativeorder(self): | ||||
return True | ||||
Patrick Mezard
|
r8693 | |||
Constantine Linnick
|
r18819 | def hasnativeclose(self): | ||
return True | ||||
Patrick Mezard
|
r8693 | def lookuprev(self, rev): | ||
try: | ||||
timeless
|
r28370 | return nodemod.hex(self.repo.lookup(rev)) | ||
Matt Harbison
|
r23926 | except (error.RepoError, error.LookupError): | ||
Patrick Mezard
|
r8693 | return None | ||
Edouard Gomez
|
r13757 | |||
def getbookmarks(self): | ||||
return bookmarks.listbookmarks(self.repo) | ||||
Ben Goswami
|
r19120 | |||
Sean Farley
|
r20373 | def checkrevformat(self, revstr, mapname='splicemap'): | ||
Ben Goswami
|
r19120 | """ Mercurial, revision string is a 40 byte hex """ | ||
Sean Farley
|
r20373 | self.checkhexformat(revstr, mapname) | ||