view svncommand.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 ef5d7a7aabb0
children f537eb456cf7
line wrap: on
line source

import os
import pickle
import stat

from mercurial import hg
from mercurial import node
from mercurial import util as merc_util

import svnwrap
import hg_delta_editor
import util
from util import register_subcommand, svn_subcommands
# dirty trick to force demandimport to run my decorator anyway.
from utility_commands import print_wc_url
from fetch_command import fetch_revisions
from push_cmd import commit_from_rev
# shut up, pyflakes, we must import those
__x = [print_wc_url, fetch_revisions, commit_from_rev, ]

mode755 = (stat.S_IXUSR | stat.S_IXGRP| stat.S_IXOTH | stat.S_IRUSR |
           stat.S_IRGRP| stat.S_IROTH | stat.S_IWUSR)
mode644 = (stat.S_IRUSR | stat.S_IRGRP| stat.S_IROTH | stat.S_IWUSR)


def svncmd(ui, repo, subcommand, *args, **opts):
    if subcommand not in svn_subcommands:
        candidates = []
        for c in svn_subcommands:
            if c.startswith(subcommand):
                candidates.append(c)
        if len(candidates) == 1:
            subcommand = candidates[0]
    path = os.path.dirname(repo.path)
    try:
        opts['svn_url'] = open(os.path.join(repo.path, 'svn', 'url')).read()
        return svn_subcommands[subcommand](ui, args=args,
                                           hg_repo_path=path,
                                           repo=repo,
                                           **opts)
    except TypeError, e:
        print e
        print 'Bad arguments for subcommand %s' % subcommand
    except KeyError, e:
        print 'Unknown subcommand %s' % subcommand

@register_subcommand('help')
def help_command(ui, args=None, **opts):
    """Get help on the subsubcommands.
    """
    if args and args[0] in svn_subcommands:
        print svn_subcommands[args[0]].__doc__.strip()
        return
    print 'Valid commands:', ' '.join(sorted(svn_subcommands.keys()))

@register_subcommand('gentags')
def generate_hg_tags(ui, hg_repo_path, **opts):
    """Save tags to .hg/localtags
    """
    hg_editor = hg_delta_editor.HgChangeReceiver(hg_repo_path, ui_=ui)
    f = open(hg_editor.tag_info_file)
    tag_info = pickle.load(f)
    f = open(os.path.join(hg_repo_path, '.hg', 'localtags'), 'w')
    for tag, source in tag_info.iteritems():
        source_ha = hg_editor.get_parent_revision(source[1]+1, source[0])
        f.write('%s tag/%s\n' % (node.hex(source_ha), tag))

@register_subcommand('up')
def update(ui, args, repo, clean=False, **opts):
    """Update to a specified Subversion revision number.
    """
    assert len(args) == 1
    rev = int(args[0])
    path = os.path.join(repo.path, 'svn', 'rev_map')
    answers = []
    for k,v in util.parse_revmap(path).iteritems():
        if k[0] == rev:
            answers.append((v, k[1]))
    if len(answers) == 1:
        if clean:
            return hg.clean(repo, answers[0][0])
        return hg.update(repo, answers[0][0])
    elif len(answers) == 0:
        ui.status('Revision %s did not produce an hg revision.\n' % rev)
        return 1
    else:
        ui.status('Ambiguous revision!\n')
        ui.status('\n'.join(['%s on %s' % (node.hex(a[0]), a[1]) for a in
                             answers]+['']))
    return 1


@register_subcommand('verify_revision')
def verify_revision(ui, args, repo, force=False, **opts):
    """Verify a single converted revision.
    Note: This wipes your working copy and then exports the corresponding
    Subversion into your working copy to verify. Use with caution.
    """
    assert len(args) == 1
    if not force:
        assert repo.status(ignored=True,
                           unknown=True) == ([], [], [], [], [], [], [])
    rev = int(args[0])
    wc_path = os.path.dirname(repo.path)
    svn_url = open(os.path.join(repo.path, 'svn', 'url')).read()
    svn = svnwrap.SubversionRepo(svn_url, username=merc_util.getuser())
    util.wipe_all_files(wc_path)
    if update(ui, args, repo, clean=True) == 0:
        util.wipe_all_files(wc_path)
        br = repo.dirstate.branch()
        if br == 'default':
            br = None
        if br:
            diff_path = 'branches/%s' % br
        else:
            diff_path = 'trunk'
        svn.fetch_all_files_to_dir(diff_path, rev, wc_path)
        stat = repo.status(unknown=True)
        ignored = [s for s in stat[4]
                   if '/.svn/'  not in s and not s.startswith('.svn/')]
        stat = stat[0:4]
        if stat != ([], [], [], [],) or ignored != []:
            ui.status('Something is wrong with this revision.\n')
            return 2
        else:
            ui.status('OK.\n')
            return 0
    return 1

@register_subcommand('verify_all_revisions')
def verify_all_revisions(ui, args, repo, **opts):
    """Verify all the converted revisions, optionally starting at a revision.

    Note: This is *extremely* abusive of the Subversion server. It exports every
    revision of the code one revision at a time.
    """
    assert repo.status(ignored=True,
                       unknown=True) == ([], [], [], [], [], [], [])
    start_rev = 0
    args = list(args)
    if args:
        start_rev = int(args.pop(0))
    revmap = util.parse_revmap(os.path.join(repo.path, 'svn', 'rev_map'))
    revs = sorted(revmap.keys())
    for revnum, br in revs:
        if revnum < start_rev:
            continue
        res = verify_revision(ui, [revnum], repo, force=True)
        if res == 0:
            print revnum, 'verfied'
        elif res == 1:
            print revnum, 'skipped'
        else:
            print revnum, 'failed'
            return 1
    return 0