Mercurial > hgsubversion
view utility_commands.py @ 69:63ece4ea25c9
hg_delta_editor: register copies only if files are unchanged between source and dest
Handle copies of items from revision X into revision Y where X is not the
parent of Y. This cannot happen in Mercurial because copies always happen
between parents and children. A file copy is recorded if:
1- Source and destination revs are in the same branch.
2- The file is unchanged (content, type, removal) through all revisions between
destination and source, not including source and destination.
Directory copies are registered only if the previous rules apply on all copied
items.
[1] is there because file copies across branches are meaningless in Mercurial
world. We could have tried to remap the source rev to a similar one in the
correct branch, but anyway the intent is wrong.
[2] is more questionable but I think it's better this way for we live in a
non-perfect svn world. In theory, 99% of copies out there should come from the
direct parent. But the direct parent is a fuzzy notion when you can have a
working directory composed of different directory at different revisions. So we
assume that stuff copied from past revisions exactly matching the content of
the direct parent revision is really copied from the parent revision. The
alternative would be to discard the copy, which would always happen unless
people kept updating the working directory after every commit (see
tests).
author | Patrick Mezard <pmezard@gmail.com> |
---|---|
date | Wed, 05 Nov 2008 13:37:08 +0100 |
parents | 08be8ee73551 |
children | 10dd34deac3b |
line wrap: on
line source
from mercurial import cmdutil from mercurial import node from mercurial import util as mutil from hgext import rebase import util import hg_delta_editor @util.register_subcommand('url') def print_wc_url(ui, repo, hg_repo_path, **opts): hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui) ui.status(hge.url, '\n') @util.register_subcommand('info') def run_svn_info(ui, repo, hg_repo_path, **opts): hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui) svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys())) o_r = outgoing_revisions(ui, repo, hge, svn_commit_hashes) ha = repo.parents()[0] if o_r: ha = repo[o_r[-1]].parents()[0] r, br = svn_commit_hashes[ha.node()] if br == None: branchpath = '/trunk' else: branchpath = '/branches/%s' % br url = hge.url if url[-1] == '/': url = url[:-1] url = '%s%s' % (url, branchpath) author = '@'.join(ha.user().split('@')[:-1]) ui.status('''URL: %(url)s Repository Root: %(reporoot)s Repository UUID: %(uuid)s Revision: %(revision)s Node Kind: directory Last Changed Author: %(author)s Last Changed Rev: %(revision)s Last Changed Date: %(date)s\n''' % {'reporoot': None, 'uuid': open(hge.uuid_file).read(), 'url': url, 'author': author, 'revision': r, # TODO I'd like to format this to the user's local TZ if possible 'date': mutil.datestr(ha.date(), '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)') }) @util.register_subcommand('parent') def print_parent_revision(ui, repo, hg_repo_path, **opts): """Prints the hg hash and svn revision info for the nearest svn parent of the current revision""" hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui) svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys())) ha = repo.parents()[0] o_r = outgoing_revisions(ui, repo, hge, svn_commit_hashes) if o_r: ha = repo[o_r[-1]].parents()[0] if ha.node() != node.nullid: r, br = svn_commit_hashes[ha.node()] ui.status('Working copy parent revision is %s: r%s on %s\n' % (ha, r, br or 'trunk')) else: ui.status('Working copy seems to have no parent svn revision.\n') return 0 @util.register_subcommand('rebase') def rebase_commits(ui, repo, hg_repo_path, **opts): """Rebases the current unpushed revisions onto the top of the Subversion branch. This moves a line of development from making its own head to the top of Subversion development, linearizing the changes. In order to make sure you rebase on top of the current top of Subversion work, you should probably run 'hg svn pull' before running this. """ hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui) svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys())) o_r = outgoing_revisions(ui, repo, hge, svn_commit_hashes) if not o_r: ui.status('Nothing to rebase!\n') return 0 if len(repo.parents()[0].children()): ui.status('Refusing to rebase non-head commit like a coward\n') return 0 parent_rev = repo[o_r[-1]].parents()[0] target_rev = parent_rev p_n = parent_rev.node() exhausted_choices = False while target_rev.children() and not exhausted_choices: for c in target_rev.children(): exhausted_choices = True n = c.node() if (n in svn_commit_hashes and svn_commit_hashes[n][1] == svn_commit_hashes[p_n][1]): target_rev = c exhausted_choices = False break if parent_rev == target_rev: ui.status('Already up to date!\n') return 0 # TODO this is really hacky, there must be a more direct way return rebase.rebase(ui, repo, dest=node.hex(target_rev.node()), base=node.hex(repo.parents()[0].node())) @util.register_subcommand('outgoing') def show_outgoing_to_svn(ui, repo, hg_repo_path, **opts): """Commit the current revision and any required parents back to svn. """ hge = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui) svn_commit_hashes = dict(zip(hge.revmap.itervalues(), hge.revmap.iterkeys())) o_r = outgoing_revisions(ui, repo, hge, svn_commit_hashes) if not (o_r and len(o_r)): ui.status('No outgoing changes found.\n') return 0 displayer = cmdutil.show_changeset(ui, repo, opts, buffered=False) for rev in reversed(o_r): displayer.show(changenode=rev) def outgoing_revisions(ui, repo, hg_editor, reverse_map): """Given a repo and an hg_editor, determines outgoing revisions for the current working copy state. """ outgoing_rev_hashes = [] working_rev = repo.parents() assert len(working_rev) == 1 working_rev = working_rev[0] if working_rev.node() in reverse_map: return while (not working_rev.node() in reverse_map and working_rev.node() != node.nullid): outgoing_rev_hashes.append(working_rev.node()) working_rev = working_rev.parents() assert len(working_rev) == 1 working_rev = working_rev[0] if working_rev.node() != node.nullid: return outgoing_rev_hashes