예제 #1
0
 def test_switch_dirty_repo(self):
     common.run_hg(["branch", "test"])
     f = open("bar", "w")
     f.write("bar")
     f.close()
     common.run_hg(["add", "bar"])
     eq_(False, common.hg_switch_branch("test", "default"))
예제 #2
0
def map_svn_rev_to_hg(svn_rev, hg_rev="tip", local=False):
    """
    Record the mapping from an SVN revision number and an hg revision (default "tip").
    """
    args = ["tag"]
    if local:
        args.append("-l")
    args.extend(["-r", strip_hg_rev(hg_rev), "svn.%d" % svn_rev])
    run_hg(args)
예제 #3
0
def map_svn_rev_to_hg(svn_rev, hg_rev="tip", local=False):
    """
    Record the mapping from an SVN revision number and an hg revision (default "tip").
    """
    args = ["tag"]
    if local:
        args.append("-l")
    args.extend(["-r", strip_hg_rev(hg_rev), "svn.%d" % svn_rev])
    run_hg(args)
예제 #4
0
def get_hg_changes(rev_string):
    """
    Get paths of changed files from a previous revision.
    Returns a tuple of (added files, removed files, modified files, copied files)
    Copied files are dict of (dest=>src), others are lists.
    """
    args = ["st", "-armC", "--rev", rev_string]
    out = run_hg(args, output_is_locale_encoding=True)
    added = []
    removed = []
    modified = []
    copied = {}
    skipnext = False
    for line in out.splitlines():
        st = line[0]
        path = line[2:]
        if st == 'A':
            added.append(path)
            lastadded=path
        elif st == 'R':
            removed.append(path)
        elif st == 'M':
            modified.append(path)
        elif st == ' ':
            added.remove(lastadded)
            copied[lastadded] = path
    #print "added:", added
    #print "removed:", removed
    #print "modified:", modified
    return added, removed, modified, copied
예제 #5
0
def get_hg_changes(rev_string):
    """
    Get paths of changed files from a previous revision.
    Returns a tuple of (added files, removed files, modified files, copied files)
    Copied files are dict of (dest=>src), others are lists.
    """
    args = ["st", "-armC", "--rev", rev_string]
    out = run_hg(args, output_is_locale_encoding=True)
    added = []
    removed = []
    modified = []
    copied = {}
    skipnext = False
    for line in out.splitlines():
        st = line[0]
        path = line[2:]
        if st == 'A':
            added.append(path)
            lastadded = path
        elif st == 'R':
            removed.append(path)
        elif st == 'M':
            modified.append(path)
        elif st == ' ':
            added.remove(lastadded)
            copied[lastadded] = path
    #print "added:", added
    #print "removed:", removed
    #print "modified:", modified
    return added, removed, modified, copied
예제 #6
0
def get_hg_log(start_rev, end_rev):
    """Get log messages for given changeset range."""

    log_args = [
        "log", "--verbose", "--follow", "--rev", start_rev + ":" + end_rev,
        "--prune", start_rev
    ]
    return run_hg(log_args)
예제 #7
0
def get_hg_revs(first_rev, svn_branch, last_rev="tip"):
    """
    Get a chronological list of revisions (changeset IDs) between the two
    revisions (included).
    """
    args = ["log", "--template", r'{rev}:{node|short}\n', "-b", svn_branch,
            '--limit', '1000', '--follow',
            "-r", "%s:%s" % (strip_hg_rev(first_rev),
                             strip_hg_rev(last_rev))]
    out = run_hg(args)
    return [strip_hg_rev(s) for s in out.splitlines()]
예제 #8
0
def get_hg_revs(first_rev, svn_branch, last_rev="tip"):
    """
    Get a chronological list of revisions (changeset IDs) between the two
    revisions (included).
    """
    args = [
        "log", "--template", r'{rev}:{node|short}\n', "-b", svn_branch,
        '--limit', '1000', '--follow', "-r",
        "%s:%s" % (strip_hg_rev(first_rev), strip_hg_rev(last_rev))
    ]
    out = run_hg(args)
    return [strip_hg_rev(s) for s in out.splitlines()]
예제 #9
0
 def test_switch_clean_repo(self):
     common.run_hg(["branch", "test"])
     f = open("bar", "w")
     f.write("bar")
     f.close()
     common.run_hg(["add", "bar"])
     common.run_hg(["commit", "-m", '"bar added."'])
     eq_(True, common.hg_switch_branch("test", "default"))
예제 #10
0
 def setUp(self):
     self._wd = tempfile.mkdtemp()
     self._cwd = os.getcwd()
     os.chdir(self._wd)
     common.run_hg(["init"])
     f = open("foo", "w")
     f.write("foo")
     f.close()
     common.run_hg(["add", "foo"])
     common.run_hg(["commit", "-m", "Initial"])
예제 #11
0
 def test_hg(self):
     s = common.run_hg(['version', '-q'])
     s = s.split()[0]
     eq_(s.lower(), 'mercurial')
예제 #12
0
                        raise
            hg_commit_from_svn_log_entry(log_entry)
        elif unrelated_paths:
            detect_overwritten_svn_branch(wc_url, svn_rev)
    # NOTE: in Python 2.5, KeyboardInterrupt isn't a subclass of Exception anymore
    except (Exception, KeyboardInterrupt), e:
        ui.status("\nInterrupted, please wait for cleanup!\n", level=ui.ERROR)
        # NOTE: at this point, running SVN sometimes gives "write lock failures"
        # which leave the WC in a weird state.
        time.sleep(0.3)
        run_svn(["cleanup"])
        hgsvn_rev = get_svn_rev_from_hg()
        if hgsvn_rev != svn_rev:
            # Unless the tag is there, revert to the last stable state
            run_svn(["up", "--ignore-externals", "-r", current_rev, svn_wc])
            run_hg(["revert", "--all"])
        raise

    return svn_rev


def real_main(options, args):
    if check_for_applied_patches():
        print ("There are applied mq patches. Put them aside before "
               "running hgpullsvn.")
        return 1
    svn_wc = "."

    # Get SVN info
    svn_info = get_svn_info('.')
    current_rev = svn_info['revision']
예제 #13
0
def real_main(options, args):
    if run_hg(["st", "-m"]):
        print ("There are uncommitted changes. Either commit them or put "
               "them aside before running hgpushsvn.")
        return 1
    if check_for_applied_patches():
        print ("There are applied mq patches. Put them aside before "
               "running hgpushsvn.")
        return 1
    svn_info = get_svn_info(".")
    svn_current_rev = svn_info["last_changed_rev"]
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted'
    repos_url = svn_info["repos_url"]
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted/branches/xmpp-subprotocols-2178-2'
    wc_url = svn_info["url"]
    assert wc_url.startswith(repos_url)
    # e.g. u'/branches/xmpp-subprotocols-2178-2'
    wc_base = wc_url[len(repos_url):]

    svn_branch = wc_url.split("/")[-1]

    # Get remote SVN info
    svn_greatest_rev = get_svn_info(wc_url)['last_changed_rev']

    if svn_greatest_rev != svn_current_rev:
        # We can't go on if the pristine branch isn't up to date.
        # If the pristine branch lacks some revisions from SVN we are not
        # able to pull them afterwards.
        # For example, if the last SVN revision in out hgsvn repository is
        # r100 and the latest SVN revision is r101, hgpushsvn would create
        # a tag svn.102 on top of svn.100, but svn.101 isn't in hg history.
        print ("Branch '%s' out of date. Run 'hgpullsvn' first."
               % svn_branch)
        return 1

    # Switch branches if necessary.
    orig_branch = run_hg(["branch"]).strip()
    if orig_branch != svn_branch:
        if not hg_switch_branch(orig_branch, svn_branch):
            return 1

    hg_start_rev = "svn.%d" % svn_current_rev
    hg_revs = None
    try:
        hg_start_cset = get_hg_cset(hg_start_rev)
    except RuntimeError:
        if not options.force:
            raise
        hg_start_cset = get_hg_cset("0")
        print "Warning: revision '%s' not found, forcing to first rev '%s'" % (
            hg_start_rev, hg_start_cset)
    else:
        if not options.collapse:
            hg_revs = get_hg_revs(hg_start_cset, svn_branch)
    if hg_revs is None:
        hg_revs = [strip_hg_rev(hg_start_cset), strip_hg_rev(get_hg_cset("tip"))]

    pushed_svn_revs = []
    try:
        if options.dryrun:
            print "Outgoing revisions that would be pushed to SVN:"
        try:
            for prev_rev, next_rev in get_pairs(hg_revs):
                if not options.dryrun:
                    if not options.edit:
                        ui.status("Committing changes up to revision %s",
                                    get_hg_cset(next_rev))
                    username = options.username
                    if options.keep_author:
                        username = run_hg(["log", "-r", next_rev,
                                            "--template", "{author}"])
                    svn_rev = hg_push_svn(prev_rev, next_rev,
                                            edit=options.edit,
                                            username=username,
                                            password=options.password,
                                            cache=options.cache)
                    if svn_rev:
                        # Issue 95 - added update to prevent add/modify/delete crash
                        run_svn(["up"])
                        map_svn_rev_to_hg(svn_rev, next_rev, local=True)
                        pushed_svn_revs.append(svn_rev)
                else:
                    print run_hg(["log", "-r", next_rev,
                              "--template", "{rev}:{node|short} {desc}"])
        except:
            # TODO: Add --no-backup to leave a "clean" repo behind if something
            #   fails?
            run_hg(["revert", "--all"])
            raise

    finally:
        work_branch = orig_branch or svn_branch
        if work_branch != svn_branch:
            run_hg(["up", "-C", work_branch])
            run_hg(["branch", work_branch])

    if pushed_svn_revs:
        if len(pushed_svn_revs) == 1:
            msg = "Pushed one revision to SVN: "
        else:
            msg = "Pushed %d revisions to SVN: " % len(pushed_svn_revs)
        run_svn(["up", "-r", pushed_svn_revs[-1]])
        ui.status("%s %s", msg, ", ".join(str(x) for x in pushed_svn_revs))
        for line in run_hg(["st"]).splitlines():
            if line.startswith("M"):
                ui.status(("Mercurial repository has local changes after "
                           "SVN update."))
                ui.status(("This may happen with SVN keyword expansions."))
                break
    elif not options.dryrun:
        ui.status("Nothing to do.")
예제 #14
0
def main():
    usage = """1. %prog [-r SVN rev] [-p SVN peg rev] <SVN URL> [local checkout]
       2. %prog --local-only <local checkout>"""
    parser = OptionParser(usage)
    parser.add_option("-r", "--svn-rev", type="int", dest="svn_rev",
        help="SVN revision to checkout from")
    parser.add_option("-p", "--svn-peg", type="int", dest="svn_peg",
        help="SVN peg revision to locate checkout URL")
    parser.add_option("--no-hgignore", dest="hgignore", action="store_false",
        default=True, help="Don't autogenerate .hgignore")
    parser.add_option("--local-only", action="store_true", default=False,
        help="Don't use the server, just build from a local checkout")
    parser.add_option("--branch", type="string", dest="svn_branch",
        help="Override branch name (defaults to last path component of <SVN URL>)")
    (options, args) = run_parser(parser, __doc__)
    if not 1 <= len(args) <= 2:
        display_parser_error(parser, "incorrect number of arguments")

    target_svn_url = args.pop(0).rstrip("/")
    local_repo = args and args.pop(0) or None
    if options.svn_peg:
       target_svn_url += "@" + str(options.svn_peg)

    if options.local_only:
        if options.svn_peg:
            display_parser_error(parser,
                    "--local-only and --svn-peg are mutually exclusive")
        if options.svn_rev:
            display_parser_error(parser,
                    "--local-only and --svn-rev are mutually exclusive")
        if local_repo:
            display_parser_error(parser,
                    "--local-only does not accept a target directory")



    # Get SVN info
    svn_info = get_svn_info(target_svn_url, options.svn_rev)
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted'
    repos_url = svn_info['repos_url']
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted/branches/xmpp-subprotocols-2178-2'
    actual_svn_url = svn_info['url']
    assert actual_svn_url.startswith(repos_url)

    # if we are allowed to go to the server, lets use it
    if options.local_only:
        target_svn_url = os.path.abspath(target_svn_url)
        local_repo = target_svn_url

    # e.g. u'/branches/xmpp-subprotocols-2178-2'
    svn_path = actual_svn_url[len(repos_url):]
    # e.g. 'xmpp-subprotocols-2178-2'
    svn_branch = actual_svn_url.split("/")[-1]

    # if --branch was passed, override the branch name derived above
    if options.svn_branch:
        svn_branch = options.svn_branch

    svn_greatest_rev = svn_info['last_changed_rev']
    if options.svn_peg:
       actual_svn_url += "@" + str(options.svn_peg)

    if not local_repo:
        local_repo = actual_svn_url.split("/")[-1]
    if os.path.exists(local_repo):
        if not os.path.isdir(local_repo):
            raise ValueError("%s is not a directory" % local_repo)
        is_svn_dir = os.path.isdir(os.path.join(local_repo, '.svn'))
        if is_svn_dir and not options.local_only:
            nlocal_repo = os.path.abspath(local_repo)
            print "%s is already a SVN checkout." % nlocal_repo
            sys.exit(1)
        elif not is_svn_dir and options.local_only:
            nlocal_repo = os.path.abspath(local_repo)
            print "%s must be a SVN checkout for --local-only" % nlocal_repo
            sys.exit(1)
        elif options.local_only and is_svn_dir:
            status = get_svn_status(local_repo, quiet=True)
            if status:
                print ("There are uncommitted changes. Either commit them "
                       "or put them aside before running hgimportsvn.")
                sys.exit(1)
    else:
        os.mkdir(local_repo)
    fixup_hgsvn_dir(local_repo)
    os.chdir(local_repo)

    # Get log entry for the SVN revision we will check out
    svn_copyfrom_path = None
    svn_copyfrom_revision = None
    if options.local_only or svn_greatest_rev == 0:
        # fake up a log message for the initial commit
        svn_start_log = {}
        svn_start_log['message'] = 'initial import by hgimportsvn'
        svn_start_log['revision'] = svn_info['last_changed_rev']
        svn_start_log['author'] = svn_info.get('last_changed_author')
        svn_start_log['date'] = svn_info['last_changed_date']
    elif options.svn_rev:
        # If a specific rev was requested, get log entry just before or at rev
        svn_start_log = get_last_svn_log_entry(actual_svn_url, 1, options.svn_rev)
    else:
        # Otherwise, get log entry of branch creation
        svn_start_log = get_first_svn_log_entry(actual_svn_url, 1, svn_greatest_rev)
        for p in svn_start_log['changed_paths']:
            if p['path'] == svn_path:
                svn_copyfrom_path = p['copyfrom_path']
                svn_copyfrom_revision = p['copyfrom_revision']
                break
        if svn_copyfrom_path:
            print "SVN branch was copied from '%s' at rev %s" % (
                svn_copyfrom_path, svn_copyfrom_revision)
        else:
            print "SVN branch isn't a copy"
    # This is the revision we will checkout from
    svn_rev = svn_start_log['revision']

    # Initialize hg repo
    if not os.path.exists(".hg"):
        run_hg(["init"])
    if svn_copyfrom_path:
        # Try to find an hg repo tracking the SVN branch which was copied
        copyfrom_branch = svn_copyfrom_path.split("/")[-1]
        hg_copyfrom = os.path.join("..", copyfrom_branch)
        if (os.path.exists(os.path.join(hg_copyfrom, ".hg")) and
            os.path.exists(os.path.join(hg_copyfrom, svn_private_dir))):
            u = get_svn_info(hg_copyfrom)['url']
            if u != repos_url + svn_copyfrom_path:
                print "SVN URL %s in working copy %s doesn't match, ignoring" % (u, hg_copyfrom)
            else:
                # Find closest hg tag before requested SVN rev
                best_tag = None
                for line in run_hg(["tags", "-R", hg_copyfrom]).splitlines():
                    if not line.startswith("svn."):
                        continue
                    tag = line.split(None, 1)[0]
                    tag_num = int(tag.split(".")[1])
                    if tag_num <= svn_copyfrom_revision and (not best_tag or best_tag < tag_num):
                        best_tag = tag_num
                if not best_tag:
                    print "No hg tag matching rev %s in %s" % (svn_rev, hg_copyfrom)
                else:
                    # Don't use "pull -u" because it fails with hg 0.9.5
                    # ("branch default not found")
                    run_hg(["pull", "-r", "svn.%d" % best_tag, hg_copyfrom])
                    # Not specifying "tip" fails with hg 0.9.5
                    # ("branch default not found")
                    run_hg(["up", "tip"])
    run_hg(["branch", svn_branch])


    # Stay on the same filesystem so as to have fast moves
    if options.local_only:
        checkout_dir = target_svn_url
    else:
        checkout_dir = tempfile.mkdtemp(dir=".")

    try:
        # Get SVN manifest and checkout
        if not options.local_only:
            svn_checkout(target_svn_url, checkout_dir, svn_rev)
        svn_manifest = get_svn_versioned_files(checkout_dir)

        svn_files = set(skip_dirs(svn_manifest, checkout_dir))
        svn_dirs = sorted(set(svn_manifest) - svn_files)
        svn_files = list(svn_files)

        # in the local case the files here already
        if not options.local_only:
            # All directories must exist, including empty ones
            # (both for hg and for moving .svn dirs later)
            for d in svn_dirs:
                if not os.path.isdir(d):
                    if os.path.exists(d):
                        os.remove(d)
                    os.mkdir(d)
            # Replace checked out files
            for f in svn_files:
                if os.path.exists(f):
                    os.remove(f)
                os.rename(os.path.join(checkout_dir, f), f)

        try:
            # Add/remove new/old files
            if svn_files:
                run_hg(["addremove"] + hg_exclude_options, svn_files)
            hg_commit_from_svn_log_entry(svn_start_log)
            #hg_commit_from_svn_log_entry(svn_start_log, svn_files)
        except:
            run_hg(["revert", "--all"])
            raise

        # in the local case the files here already
        if not options.local_only:
            # Move SVN working copy here (don't forget base directory)
            for d in chain([""], svn_dirs):
                os.rename(os.path.join(checkout_dir, d, svn_private_dir), os.path.join(d, svn_private_dir))

    finally:
        # in the local case the checkout_dir is the target, so don't delete it
        if not options.local_only:
            rmtree(checkout_dir)

    if options.hgignore:
        svn_ignores = []
        # we use '.' because that it the location of the hg repo that we are
        # building and at this point we have already copied all of svn to the
        # checkout (for both the local and non-local case)
        for (path, dirs, files) in os.walk('.'):
            if '.svn' in dirs:
                dirs.remove('.svn')

                # the [2:] strips off the initial './'
                local_ignores = [os.path.join(path, s.strip())[2:]
                    for s in run_svn(['propget', 'svn:ignore', path],
                                     mask_atsign=True).splitlines()
                    if bool(s.strip())
                ]
                svn_ignores += local_ignores
            else:
                # if we are not inside an svn world
                # we can just bail
                del dirs[:]


        # Generate .hgignore file to ignore .svn and .hgsvn directories
        f = open(".hgignore", "a")
        try:
            f.write("\n# Automatically generated by `hgimportsvn`\n")
            f.write("syntax:glob\n%s\n" %
                "\n".join([svn_private_dir, hgsvn_private_dir]))
            f.write("\n# These lines are suggested according to the svn:ignore property")
            f.write("\n# Feel free to enable them by uncommenting them")
            f.write("\nsyntax:glob\n")
            f.write("".join("#%s\n" % s for s in svn_ignores))
        finally:
            f.close()

    print "Finished! You can now pull all SVN history with 'hgpullsvn'."
예제 #15
0
def get_hg_cset(rev_string):
    """
    Given a string identifying an hg revision, get the canonical changeset ID.
    """
    args = ["log", "--template", r"{rev}:{node|short}\n", "-r", rev_string]
    return run_hg(args).strip()
예제 #16
0
def get_hg_cset(rev_string):
    """
    Given a string identifying an hg revision, get the canonical changeset ID.
    """
    args = ["log", "--template", r"{rev}:{node|short}\n", "-r", rev_string]
    return run_hg(args).strip()
예제 #17
0
def get_hg_cset_description(rev_string):
    """Get description of a given changeset."""
    return run_hg(["log", "--template", "{desc|strip}", "-r", rev_string])
예제 #18
0
def real_main(options, args):
    if check_for_applied_patches():
        print("There are applied mq patches. Put them aside before "
              "running hgpullsvn.")
        return 1
    svn_wc = "."

    # Get SVN info
    svn_info = get_svn_info('.')
    current_rev = svn_info['revision']
    next_rev = current_rev + 1
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted'
    repos_url = svn_info['repos_url']
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted/branches/xmpp-subprotocols-2178-2'
    wc_url = svn_info['url']
    assert wc_url.startswith(repos_url)
    # e.g. u'/branches/xmpp-subprotocols-2178-2'
    wc_base = wc_url[len(repos_url):]
    # e.g. 'xmpp-subprotocols-2178-2'
    svn_branch = wc_url.split("/")[-1]

    # if --branch was passed, override the branch name derived above
    if options.svn_branch:
        svn_branch = options.svn_branch

    if options.svn_peg:
        wc_url += "@" + str(options.svn_peg)

    # Get remote SVN info
    ui.status("Retrieving remote SVN info...", level=ui.VERBOSE)
    svn_greatest_rev = get_svn_info(wc_url)['last_changed_rev']
    if svn_greatest_rev < next_rev:
        ui.status("No revisions after %s in SVN repo, nothing to do",
                  svn_greatest_rev)
        return
    elif options.svn_rev != None:
        if options.svn_rev < next_rev:
            ui.status(
                "All revisions up to %s are already pulled in, "
                "nothing to do", options.svn_rev)
            return
        svn_greatest_rev = options.svn_rev

    # Show incoming changes if in dry-run mode.
    if options.dryrun:
        ui.status("Incoming SVN revisions:")
        for entry in iter_svn_log_entries(wc_url, next_rev, svn_greatest_rev,
                                          options.svnretry):
            if entry["message"]:
                msg = entry["message"].splitlines()[0].strip()
            else:
                msg = ""
            line = "[%d] %s (%s)" % (entry["revision"], msg, entry["author"])
            ui.status(line)
        return

    # Prepare and/or switch named branches
    orig_branch = run_hg(["branch"]).strip()
    if orig_branch != svn_branch:
        # Update to or create the "pristine" branch
        if not hg_switch_branch(orig_branch, svn_branch):
            return 1
    elif not hg_is_clean(svn_branch):
        return 1

    # Detect that a previously aborted hgpullsvn retrieved an SVN revision
    # without committing it to hg.
    # If there is no SVN tag in current head, we may have been interrupted
    # during the previous "hg tag".
    hgsvn_rev = get_svn_rev_from_hg()
    if hgsvn_rev is not None and hgsvn_rev != current_rev:
        ui.status(("\nNote: hgsvn repository is in an unclean state "
                   "(probably because of an aborted hgpullsvn). \n"
                   "Let's first update to the last good SVN rev."),
                  level=ui.VERBOSE)
        run_svn(["revert", "-R", "."])
        run_svn(["up", "--ignore-externals", "-r", hgsvn_rev, svn_wc])
        next_rev = hgsvn_rev + 1

    # Reset working branch to last svn head to have a clean and linear SVN
    # history.
    heads_before = None
    if hgsvn_rev is None:
        heads_before = run_hg(["heads", "--template",
                               "{node}%s" % os.linesep]).splitlines()
        run_hg(["update", "-C", "svn.%d" % current_rev])

    # Load SVN log starting from current rev
    it_log_entries = iter_svn_log_entries(wc_url, next_rev, svn_greatest_rev,
                                          options.svnretry)

    try:
        try:
            for log_entry in it_log_entries:
                current_rev = pull_svn_rev(log_entry, current_rev, svn_wc,
                                           wc_url, wc_base, options.svnretry)
                if current_rev is None:
                    return 1
                ui.status("Pulled r%d %s (%s)", log_entry["revision"],
                          log_entry["message"], log_entry["author"])

        # TODO: detect externals with "svn status" and update them as well

        finally:
            if heads_before is not None:  # we have reset the SVN branch
                heads_now = run_hg(
                    ["heads", "--template",
                     "{node}%s" % os.linesep]).splitlines()
                if len(heads_now) != len(heads_before):
                    ui.status("created new head in branch '%s'", svn_branch)
            work_branch = orig_branch or svn_branch
            if work_branch != svn_branch:
                run_hg(["up", '-C', work_branch])
                run_hg(["branch", work_branch])

    except KeyboardInterrupt:
        ui.status("\nStopped by user.", level=ui.ERROR)
    except ExternalCommandFailed, e:
        ui.status(str(e), level=ui.ERROR)
예제 #19
0
def hg_push_svn(start_rev, end_rev, edit, username, password, cache):
    """
    Commit the changes between two hg revisions into the SVN repository.
    Returns the SVN revision object, or None if nothing needed checking in.
    """
    added, removed, modified, copied = get_hg_changes(start_rev+':'+end_rev)

    # Before replicating changes revert directory to previous state...
    run_hg(['revert', '--all', '--no-backup', '-r', end_rev])

    # ... and restore .svn directories, if we lost some of them due to removes
    run_svn(['revert', '--recursive', '.'])

    # Do the rest over up-to-date working copy
    # Issue 94 - moved this line to prevent do_svn_copy crashing when its not the first changeset
    run_hg(["up", "-C", end_rev])

    # Record copyies into svn
    for dest, src in copied.iteritems():
        do_svn_copy(src,dest)

    # Add new files and dirs
    if added:
        bulk_args = get_ordered_dirs(added) + added
        run_svn(["add", "--depth=empty"], bulk_args,
                mask_atsign=True)
    removed = cleanup_svn_unversioned(removed)
    modified = cleanup_svn_unversioned(modified)
    # Remove old files and empty dirs
    if removed:
        empty_dirs = [d for d in reversed(get_ordered_dirs(removed))
                      if not run_hg(["st", "-c", "%s" %d])]
        # When running "svn rm" all files within removed folders needs to
        # be removed from "removed". Otherwise "svn rm" will fail. For example
        # instead of "svn rm foo/bar foo" it should be "svn rm foo".
        # See issue15.
        svn_removed = strip_nested_removes(removed + empty_dirs)
        run_svn(["rm"], svn_removed, mask_atsign=True)
    if added or removed or modified:
        svn_sep_line = "--This line, and those below, will be ignored--\n"
        adjust_executable_property(added+modified)  # issue24
        description = get_hg_csets_description(start_rev, end_rev)
        fname = os.path.join(hgsvn_private_dir, 'commit-%s.txt' % end_rev)
        lines = description.splitlines()+[""]
        lines.append(svn_sep_line)
        lines.append("To cancel commit just delete text in top message part")
        lines.append("")
        lines.append("Changes to be committed into svn:")
        lines.extend([item.decode("utf-8")
                      for item in run_svn(["st", "-q"]).splitlines()])
        lines.append("")
        lines.append(("These changes are produced from the following "
                      "Hg changesets:"))
        lines.extend(get_hg_log(start_rev, end_rev).splitlines())
        f = codecs.open(fname, "wb", "utf-8")
        f.write(os.linesep.join(lines))
        f.close()

        try:
            if edit:
                editor=(os.environ.get("HGEDITOR") or
                        os.environ.get("SVNEDITOR") or
                        os.environ.get("VISUAL") or
                        os.environ.get("EDITOR", "vi"))

                rc = os.system("%s \"%s\"" % (editor, fname) )
                if(rc):
                    raise ExternalCommandFailed("Can't launch editor")

                empty = True

                f=open(fname, "r")
                for line in f:
                    if(line == svn_sep_line):
                        break

                    if(line.strip() != ""):
                        empty = False
                        break
                f.close()

                if(empty):
                    raise EmptySVNLog("Commit cancelled by user\n")

            svn_rev = None
            svn_commands = ["commit", "-F", fname, "--encoding", get_encoding()]
            if username is not None:
                svn_commands += ["--username", username]
            if password is not None:
                svn_commands += ["--password", password]
            if cache:
                svn_commands.append("--no-auth-cache")
            out = run_svn(svn_commands)

            outlines = out.splitlines()
            outlines.reverse()
            for line in outlines:
                # one of the last lines holds the revision.
                # The previous approach to set LC_ALL to C and search
                # for "Committed revision XXX" doesn't work since
                # svn is unable to convert filenames containing special
                # chars:
                # http://svnbook.red-bean.com/en/1.2/svn.advanced.l10n.html
                match = re.search("(\d+)", line)
                if match:
                    svn_rev = int(match.group(1))
                    break

            return svn_rev
        finally:
            # Exceptions are handled by main().
            os.remove(fname)
    else:
        print "*", "svn: nothing to do"
        return None
예제 #20
0
def get_hg_csets_description(start_rev, end_rev):
    """Get description of a given changeset range."""
    return run_hg([
        "log", "--template", r"{desc|strip}\n", "--follow", "--rev",
        start_rev + ":" + end_rev, "--prune", start_rev
    ])
예제 #21
0
def get_hg_cset_description(rev_string):
    """Get description of a given changeset."""
    return run_hg(["log", "--template", "{desc|strip}", "-r", rev_string])
예제 #22
0
def pull_svn_rev(log_entry, current_rev, svn_wc, wc_url, wc_base, retry):
    """
    Pull SVN changes from the given log entry.
    Returns the new SVN revision. If an exception occurs, it will rollback
    to revision 'current_rev'.
    """
    svn_rev = log_entry['revision']

    added_paths = []
    copied_paths = []
    removed_paths = []
    changed_paths = []
    unrelated_paths = []
    replaced_paths = {}

    # 1. Prepare for the `svn up` changes that are pulled in the second step
    #    by analyzing log_entry for the changeset
    for d in log_entry['changed_paths']:
        # e.g. u'/branches/xmpp-subprotocols-2178-2/twisted/words/test/test_jabberxmlstream.py'
        p = d['path']
        if not p.startswith(wc_base + "/"):
            # Ignore changed files that are not part of this subdir
            if p != wc_base:
                unrelated_paths.append(p)
            continue
        action = d['action']
        if action not in 'MARD':
            raise UnsupportedSVNAction("In SVN rev. %d: action '%s' not supported. Please report a bug!"
                % (svn_rev, action))
        # e.g. u'twisted/words/test/test_jabberxmlstream.py'
        p = p[len(wc_base):].strip("/")
        # Record for commit
        changed_paths.append(p)
        # Detect special cases
        old_p = d['copyfrom_path']
        if old_p and old_p.startswith(wc_base + "/"):
            old_p = old_p[len(wc_base):].strip("/")
            # Both paths can be identical if copied from an old rev.
            # We treat like it a normal change.
            if old_p != p:
                # Try to hint hg about file and dir copies
                if not os.path.isdir(old_p):
                    copied_paths.append((old_p, p))
                    if action == 'R':
                        removed_paths.append(old_p)
                else:
                    # Extract actual copied files (hg doesn't track dirs
                    # and will refuse "hg copy -A" with dirs)
                    r = run_hg(["st", "-nc"], [old_p], output_is_locale_encoding=True)
                    for old_f in r.splitlines():
                        f = p + old_f[len(old_p):]
                        copied_paths.append((old_f, f))
                        if action == 'R':
                            removed_paths.append(old_f)
                continue
        if d['action'] == 'A':
            added_paths.append(p)
        elif d['action'] == 'D':
            # Same as above: unfold directories into their affected files
            if not os.path.isdir(p):
                removed_paths.append(p)
            else:
                r = run_hg(["st", "-nc"], [p], output_is_locale_encoding=True)
                for f in r.splitlines():
                    removed_paths.append(f)
        elif d['action'] == 'R':
            # (R)eplaced directories can have added and removed files without
            # them being mentioned in the SVN log => we must detect those files
            # ourselves.
            # (http://svn.python.org/projects/python/branches/py3k, rev 59625)
            if os.path.isdir(p):
                replaced_paths[p] = get_svn_versioned_files(
                    os.path.join(svn_wc, p))
            else:
                # We never know what twisty semantics (R) can have. addremove
                # is safest.
                added_paths.append(p)

    # 2. Update SVN + add/remove/commit hg
    try:
        if changed_paths:
            args = ["up", "--ignore-externals"]
            if get_svn_client_version() >= (1, 5):
                args.extend(['--accept', 'postpone'])
            ui.status('Attempting to update to revision %s...', str(svn_rev))
            once_or_more("SVN update", retry, run_svn, args + ["-r", svn_rev, svn_wc])
            conflicts = []
            for status_entry in get_svn_status('.'):
                if status_entry['status'] == 'conflicted':
                    conflicts.append(status_entry['path'])
            if conflicts:
                ui.status('SVN updated resulted in conflicts!', level=ui.ERROR)
                ui.status('Conflicted files: %s', ','.join(conflicts))
                ui.status('Please report a bug.')
                return None
            for p, old_contents in replaced_paths.items():
                old_contents = set(old_contents)
                new_contents = set(get_svn_versioned_files(
                    os.path.join(svn_wc, p)))
                added_paths.extend(p + '/' + f for f in new_contents - old_contents)
                removed_paths.extend(p + '/' + f for f in old_contents - new_contents)
            if added_paths:
                # Use 'addremove' because an added SVN directory may
                # overwrite a previous directory with the same name.
                # XXX what about untracked files in those directories?
                run_hg(["addremove"] + hg_exclude_options, added_paths)
            for old, new in copied_paths:
                try:
                    run_hg(["copy", "-A"], [old, new])
                except ExternalCommandFailed:
                    # Maybe the "old" path is obsolete, i.e. it comes from an
                    # old SVN revision and was later removed.
                    s = run_hg(['st', '-nd'], [old], output_is_locale_encoding=True)
                    if s.strip():
                        # The old path is known by hg, something else happened.
                        raise
                    run_hg(["add"], [new])
            if removed_paths:
                try: 
                    for file_path in removed_paths:
                       if os.path.exists(file_path):
                           run_hg(["remove", "-A"], file_path)
                except (ExternalCommandFailed), e:
                    if str(e).find("file is untracked") > 0:
                        ui.status("Ignoring warnings about untracked files: '%s'" % str(e), level=ui.VERBOSE)
                    else:
                        raise
            hg_commit_from_svn_log_entry(log_entry)
        elif unrelated_paths:
            detect_overwritten_svn_branch(wc_url, svn_rev)
예제 #23
0
def get_hg_log(start_rev, end_rev):
    """Get log messages for given changeset range."""

    log_args=["log", "--verbose", "--follow", "--rev", start_rev+":"+end_rev, "--prune", start_rev]
    return run_hg(log_args)
예제 #24
0
def real_main(options, args):
    if check_for_applied_patches():
        print ("There are applied mq patches. Put them aside before "
               "running hgpullsvn.")
        return 1
    svn_wc = "."

    # Get SVN info
    svn_info = get_svn_info('.')
    current_rev = svn_info['revision']
    next_rev = current_rev + 1
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted'
    repos_url = svn_info['repos_url']
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted/branches/xmpp-subprotocols-2178-2'
    wc_url = svn_info['url']
    assert wc_url.startswith(repos_url)
    # e.g. u'/branches/xmpp-subprotocols-2178-2'
    wc_base = wc_url[len(repos_url):]
    # e.g. 'xmpp-subprotocols-2178-2'
    svn_branch = wc_url.split("/")[-1]

    # if --branch was passed, override the branch name derived above
    if options.svn_branch:
        svn_branch = options.svn_branch

    if options.svn_peg:
       wc_url += "@" + str(options.svn_peg)

    # Get remote SVN info
    ui.status("Retrieving remote SVN info...", level=ui.VERBOSE)
    svn_greatest_rev = get_svn_info(wc_url)['last_changed_rev']
    if svn_greatest_rev < next_rev:
        ui.status("No revisions after %s in SVN repo, nothing to do",
                  svn_greatest_rev)
        return
    elif options.svn_rev != None:
        if options.svn_rev < next_rev:
            ui.status("All revisions up to %s are already pulled in, "
                      "nothing to do",
                      options.svn_rev)
            return
        svn_greatest_rev = options.svn_rev

    # Show incoming changes if in dry-run mode.
    if options.dryrun:
        ui.status("Incoming SVN revisions:")
        for entry in iter_svn_log_entries(wc_url, next_rev, svn_greatest_rev,
                                          options.svnretry):
            if entry["message"]:
                msg = entry["message"].splitlines()[0].strip()
            else:
                msg = ""
            line = "[%d] %s (%s)" % (entry["revision"], msg, entry["author"])
            ui.status(line)
        return

    # Prepare and/or switch named branches
    orig_branch = run_hg(["branch"]).strip()
    if orig_branch != svn_branch:
        # Update to or create the "pristine" branch
        if not hg_switch_branch(orig_branch, svn_branch):
            return 1
    elif not hg_is_clean(svn_branch):
        return 1

    # Detect that a previously aborted hgpullsvn retrieved an SVN revision
    # without committing it to hg.
    # If there is no SVN tag in current head, we may have been interrupted
    # during the previous "hg tag".
    hgsvn_rev = get_svn_rev_from_hg()
    if hgsvn_rev is not None and hgsvn_rev != current_rev:
        ui.status(("\nNote: hgsvn repository is in an unclean state "
                   "(probably because of an aborted hgpullsvn). \n"
                   "Let's first update to the last good SVN rev."),
                  level=ui.VERBOSE)
        run_svn(["revert", "-R", "."])
        run_svn(["up", "--ignore-externals", "-r", hgsvn_rev, svn_wc])
        next_rev = hgsvn_rev + 1

    # Reset working branch to last svn head to have a clean and linear SVN
    # history.
    heads_before = None
    if hgsvn_rev is None:
        heads_before = run_hg(["heads", "--template",
                               "{node}%s" % os.linesep]).splitlines()
        run_hg(["update", "-C", "svn.%d" % current_rev])

    # Load SVN log starting from current rev
    it_log_entries = iter_svn_log_entries(wc_url, next_rev, svn_greatest_rev, options.svnretry)

    try:
        try:
            for log_entry in it_log_entries:
                current_rev = pull_svn_rev(log_entry, current_rev,
                                           svn_wc, wc_url, wc_base, options.svnretry)
                if current_rev is None:
                    return 1
                ui.status("Pulled r%d %s (%s)", log_entry["revision"],
                          log_entry["message"], log_entry["author"])

        # TODO: detect externals with "svn status" and update them as well

        finally:
            if heads_before is not None:  # we have reset the SVN branch
                heads_now = run_hg(["heads", "--template",
                                    "{node}%s" % os.linesep]).splitlines()
                if len(heads_now) != len(heads_before):
                    ui.status("created new head in branch '%s'", svn_branch)
            work_branch = orig_branch or svn_branch
            if work_branch != svn_branch:
                run_hg(["up", '-C', work_branch])
                run_hg(["branch", work_branch])

    except KeyboardInterrupt:
        ui.status("\nStopped by user.", level=ui.ERROR)
    except ExternalCommandFailed, e:
        ui.status(str(e), level=ui.ERROR)
예제 #25
0
def hg_push_svn(start_rev, end_rev, edit, username, password, cache):
    """
    Commit the changes between two hg revisions into the SVN repository.
    Returns the SVN revision object, or None if nothing needed checking in.
    """
    added, removed, modified, copied = get_hg_changes(start_rev + ':' +
                                                      end_rev)

    # Before replicating changes revert directory to previous state...
    run_hg(['revert', '--all', '--no-backup', '-r', end_rev])

    # ... and restore .svn directories, if we lost some of them due to removes
    run_svn(['revert', '--recursive', '.'])

    # Do the rest over up-to-date working copy
    # Issue 94 - moved this line to prevent do_svn_copy crashing when its not the first changeset
    run_hg(["up", "-C", end_rev])

    # Record copyies into svn
    for dest, src in copied.iteritems():
        do_svn_copy(src, dest)

    # Add new files and dirs
    if added:
        bulk_args = get_ordered_dirs(added) + added
        run_svn(["add", "--depth=empty"], bulk_args, mask_atsign=True)
    removed = cleanup_svn_unversioned(removed)
    modified = cleanup_svn_unversioned(modified)
    # Remove old files and empty dirs
    if removed:
        empty_dirs = [
            d for d in reversed(get_ordered_dirs(removed))
            if not run_hg(["st", "-c", "%s" % d])
        ]
        # When running "svn rm" all files within removed folders needs to
        # be removed from "removed". Otherwise "svn rm" will fail. For example
        # instead of "svn rm foo/bar foo" it should be "svn rm foo".
        # See issue15.
        svn_removed = strip_nested_removes(removed + empty_dirs)
        run_svn(["rm"], svn_removed, mask_atsign=True)
    if added or removed or modified:
        svn_sep_line = "--This line, and those below, will be ignored--\n"
        adjust_executable_property(added + modified)  # issue24
        description = get_hg_csets_description(start_rev, end_rev)
        fname = os.path.join(hgsvn_private_dir, 'commit-%s.txt' % end_rev)
        lines = description.splitlines() + [""]
        lines.append(svn_sep_line)
        lines.append("To cancel commit just delete text in top message part")
        lines.append("")
        lines.append("Changes to be committed into svn:")
        lines.extend([
            item.decode("utf-8")
            for item in run_svn(["st", "-q"]).splitlines()
        ])
        lines.append("")
        lines.append(("These changes are produced from the following "
                      "Hg changesets:"))
        lines.extend(get_hg_log(start_rev, end_rev).splitlines())
        f = codecs.open(fname, "wb", "utf-8")
        f.write(os.linesep.join(lines))
        f.close()

        try:
            if edit:
                editor = (os.environ.get("HGEDITOR")
                          or os.environ.get("SVNEDITOR")
                          or os.environ.get("VISUAL")
                          or os.environ.get("EDITOR", "vi"))

                rc = os.system("%s \"%s\"" % (editor, fname))
                if (rc):
                    raise ExternalCommandFailed("Can't launch editor")

                empty = True

                f = open(fname, "r")
                for line in f:
                    if (line == svn_sep_line):
                        break

                    if (line.strip() != ""):
                        empty = False
                        break
                f.close()

                if (empty):
                    raise EmptySVNLog("Commit cancelled by user\n")

            svn_rev = None
            svn_commands = [
                "commit", "-F", fname, "--encoding",
                get_encoding()
            ]
            if username is not None:
                svn_commands += ["--username", username]
            if password is not None:
                svn_commands += ["--password", password]
            if cache:
                svn_commands.append("--no-auth-cache")
            out = run_svn(svn_commands)

            outlines = out.splitlines()
            outlines.reverse()
            for line in outlines:
                # one of the last lines holds the revision.
                # The previous approach to set LC_ALL to C and search
                # for "Committed revision XXX" doesn't work since
                # svn is unable to convert filenames containing special
                # chars:
                # http://svnbook.red-bean.com/en/1.2/svn.advanced.l10n.html
                match = re.search("(\d+)", line)
                if match:
                    svn_rev = int(match.group(1))
                    break

            return svn_rev
        finally:
            # Exceptions are handled by main().
            os.remove(fname)
    else:
        print "*", "svn: nothing to do"
        return None
예제 #26
0
                        raise
            hg_commit_from_svn_log_entry(log_entry)
        elif unrelated_paths:
            detect_overwritten_svn_branch(wc_url, svn_rev)
    # NOTE: in Python 2.5, KeyboardInterrupt isn't a subclass of Exception anymore
    except (Exception, KeyboardInterrupt), e:
        ui.status("\nInterrupted, please wait for cleanup!\n", level=ui.ERROR)
        # NOTE: at this point, running SVN sometimes gives "write lock failures"
        # which leave the WC in a weird state.
        time.sleep(0.3)
        run_svn(["cleanup"])
        hgsvn_rev = get_svn_rev_from_hg()
        if hgsvn_rev != svn_rev:
            # Unless the tag is there, revert to the last stable state
            run_svn(["up", "--ignore-externals", "-r", current_rev, svn_wc])
            run_hg(["revert", "--all"])
        raise

    return svn_rev


def real_main(options, args):
    if check_for_applied_patches():
        print("There are applied mq patches. Put them aside before "
              "running hgpullsvn.")
        return 1
    svn_wc = "."

    # Get SVN info
    svn_info = get_svn_info('.')
    current_rev = svn_info['revision']
예제 #27
0
def real_main(options, args):
    if run_hg(["st", "-m"]):
        print(
            "There are uncommitted changes. Either commit them or put "
            "them aside before running hgpushsvn.")
        return 1
    if check_for_applied_patches():
        print(
            "There are applied mq patches. Put them aside before "
            "running hgpushsvn.")
        return 1
    svn_info = get_svn_info(".")
    svn_current_rev = svn_info["last_changed_rev"]
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted'
    repos_url = svn_info["repos_url"]
    # e.g. u'svn://svn.twistedmatrix.com/svn/Twisted/branches/xmpp-subprotocols-2178-2'
    wc_url = svn_info["url"]
    assert wc_url.startswith(repos_url)
    # e.g. u'/branches/xmpp-subprotocols-2178-2'
    wc_base = wc_url[len(repos_url):]

    svn_branch = wc_url.split("/")[-1]

    # Get remote SVN info
    svn_greatest_rev = get_svn_info(wc_url)['last_changed_rev']

    if svn_greatest_rev != svn_current_rev:
        # We can't go on if the pristine branch isn't up to date.
        # If the pristine branch lacks some revisions from SVN we are not
        # able to pull them afterwards.
        # For example, if the last SVN revision in out hgsvn repository is
        # r100 and the latest SVN revision is r101, hgpushsvn would create
        # a tag svn.102 on top of svn.100, but svn.101 isn't in hg history.
        print("Branch '%s' out of date. Run 'hgpullsvn' first." % svn_branch)
        return 1

    # Switch branches if necessary.
    orig_branch = run_hg(["branch"]).strip()
    if orig_branch != svn_branch:
        if not hg_switch_branch(orig_branch, svn_branch):
            return 1

    hg_start_rev = "svn.%d" % svn_current_rev
    hg_revs = None
    try:
        hg_start_cset = get_hg_cset(hg_start_rev)
    except RuntimeError:
        if not options.force:
            raise
        hg_start_cset = get_hg_cset("0")
        print "Warning: revision '%s' not found, forcing to first rev '%s'" % (
            hg_start_rev, hg_start_cset)
    else:
        if not options.collapse:
            hg_revs = get_hg_revs(hg_start_cset, svn_branch)
    if hg_revs is None:
        hg_revs = [
            strip_hg_rev(hg_start_cset),
            strip_hg_rev(get_hg_cset("tip"))
        ]

    pushed_svn_revs = []
    try:
        if options.dryrun:
            print "Outgoing revisions that would be pushed to SVN:"
        try:
            for prev_rev, next_rev in get_pairs(hg_revs):
                if not options.dryrun:
                    if not options.edit:
                        ui.status("Committing changes up to revision %s",
                                  get_hg_cset(next_rev))
                    username = options.username
                    if options.keep_author:
                        username = run_hg(
                            ["log", "-r", next_rev, "--template", "{author}"])
                    svn_rev = hg_push_svn(prev_rev,
                                          next_rev,
                                          edit=options.edit,
                                          username=username,
                                          password=options.password,
                                          cache=options.cache)
                    if svn_rev:
                        # Issue 95 - added update to prevent add/modify/delete crash
                        run_svn(["up"])
                        map_svn_rev_to_hg(svn_rev, next_rev, local=True)
                        pushed_svn_revs.append(svn_rev)
                else:
                    print run_hg([
                        "log", "-r", next_rev, "--template",
                        "{rev}:{node|short} {desc}"
                    ])
        except:
            # TODO: Add --no-backup to leave a "clean" repo behind if something
            #   fails?
            run_hg(["revert", "--all"])
            raise

    finally:
        work_branch = orig_branch or svn_branch
        if work_branch != svn_branch:
            run_hg(["up", "-C", work_branch])
            run_hg(["branch", work_branch])

    if pushed_svn_revs:
        if len(pushed_svn_revs) == 1:
            msg = "Pushed one revision to SVN: "
        else:
            msg = "Pushed %d revisions to SVN: " % len(pushed_svn_revs)
        run_svn(["up", "-r", pushed_svn_revs[-1]])
        ui.status("%s %s", msg, ", ".join(str(x) for x in pushed_svn_revs))
        for line in run_hg(["st"]).splitlines():
            if line.startswith("M"):
                ui.status(("Mercurial repository has local changes after "
                           "SVN update."))
                ui.status(("This may happen with SVN keyword expansions."))
                break
    elif not options.dryrun:
        ui.status("Nothing to do.")
예제 #28
0
def pull_svn_rev(log_entry, current_rev, svn_wc, wc_url, wc_base, retry):
    """
    Pull SVN changes from the given log entry.
    Returns the new SVN revision. If an exception occurs, it will rollback
    to revision 'current_rev'.
    """
    svn_rev = log_entry['revision']

    added_paths = []
    copied_paths = []
    removed_paths = []
    changed_paths = []
    unrelated_paths = []
    replaced_paths = {}

    # 1. Prepare for the `svn up` changes that are pulled in the second step
    #    by analyzing log_entry for the changeset
    for d in log_entry['changed_paths']:
        # e.g. u'/branches/xmpp-subprotocols-2178-2/twisted/words/test/test_jabberxmlstream.py'
        p = d['path']
        if not p.startswith(wc_base + "/"):
            # Ignore changed files that are not part of this subdir
            if p != wc_base:
                unrelated_paths.append(p)
            continue
        action = d['action']
        if action not in 'MARD':
            raise UnsupportedSVNAction(
                "In SVN rev. %d: action '%s' not supported. Please report a bug!"
                % (svn_rev, action))
        # e.g. u'twisted/words/test/test_jabberxmlstream.py'
        p = p[len(wc_base):].strip("/")
        # Record for commit
        changed_paths.append(p)
        # Detect special cases
        old_p = d['copyfrom_path']
        if old_p and old_p.startswith(wc_base + "/"):
            old_p = old_p[len(wc_base):].strip("/")
            # Both paths can be identical if copied from an old rev.
            # We treat like it a normal change.
            if old_p != p:
                # Try to hint hg about file and dir copies
                if not os.path.isdir(old_p):
                    copied_paths.append((old_p, p))
                    if action == 'R':
                        removed_paths.append(old_p)
                else:
                    # Extract actual copied files (hg doesn't track dirs
                    # and will refuse "hg copy -A" with dirs)
                    r = run_hg(["st", "-nc"], [old_p],
                               output_is_locale_encoding=True)
                    for old_f in r.splitlines():
                        f = p + old_f[len(old_p):]
                        copied_paths.append((old_f, f))
                        if action == 'R':
                            removed_paths.append(old_f)
                continue
        if d['action'] == 'A':
            added_paths.append(p)
        elif d['action'] == 'D':
            # Same as above: unfold directories into their affected files
            if not os.path.isdir(p):
                removed_paths.append(p)
            else:
                r = run_hg(["st", "-nc"], [p], output_is_locale_encoding=True)
                for f in r.splitlines():
                    removed_paths.append(f)
        elif d['action'] == 'R':
            # (R)eplaced directories can have added and removed files without
            # them being mentioned in the SVN log => we must detect those files
            # ourselves.
            # (http://svn.python.org/projects/python/branches/py3k, rev 59625)
            if os.path.isdir(p):
                replaced_paths[p] = get_svn_versioned_files(
                    os.path.join(svn_wc, p))
            else:
                # We never know what twisty semantics (R) can have. addremove
                # is safest.
                added_paths.append(p)

    # 2. Update SVN + add/remove/commit hg
    try:
        if changed_paths:
            args = ["up", "--ignore-externals"]
            if get_svn_client_version() >= (1, 5):
                args.extend(['--accept', 'postpone'])
            ui.status('Attempting to update to revision %s...', str(svn_rev))
            once_or_more("SVN update", retry, run_svn,
                         args + ["-r", svn_rev, svn_wc])
            conflicts = []
            for status_entry in get_svn_status('.'):
                if status_entry['status'] == 'conflicted':
                    conflicts.append(status_entry['path'])
            if conflicts:
                ui.status('SVN updated resulted in conflicts!', level=ui.ERROR)
                ui.status('Conflicted files: %s', ','.join(conflicts))
                ui.status('Please report a bug.')
                return None
            for p, old_contents in replaced_paths.items():
                old_contents = set(old_contents)
                new_contents = set(
                    get_svn_versioned_files(os.path.join(svn_wc, p)))
                added_paths.extend(p + '/' + f
                                   for f in new_contents - old_contents)
                removed_paths.extend(p + '/' + f
                                     for f in old_contents - new_contents)
            if added_paths:
                # Use 'addremove' because an added SVN directory may
                # overwrite a previous directory with the same name.
                # XXX what about untracked files in those directories?
                run_hg(["addremove"] + hg_exclude_options, added_paths)
            for old, new in copied_paths:
                try:
                    run_hg(["copy", "-A"], [old, new])
                except ExternalCommandFailed:
                    # Maybe the "old" path is obsolete, i.e. it comes from an
                    # old SVN revision and was later removed.
                    s = run_hg(['st', '-nd'], [old],
                               output_is_locale_encoding=True)
                    if s.strip():
                        # The old path is known by hg, something else happened.
                        raise
                    run_hg(["add"], [new])
            if removed_paths:
                try:
                    for file_path in removed_paths:
                        if os.path.exists(file_path):
                            run_hg(["remove", "-A"], file_path)
                except (ExternalCommandFailed), e:
                    if str(e).find("file is untracked") > 0:
                        ui.status(
                            "Ignoring warnings about untracked files: '%s'" %
                            str(e),
                            level=ui.VERBOSE)
                    else:
                        raise
            hg_commit_from_svn_log_entry(log_entry)
        elif unrelated_paths:
            detect_overwritten_svn_branch(wc_url, svn_rev)
예제 #29
0
def get_hg_csets_description(start_rev, end_rev):
    """Get description of a given changeset range."""
    return run_hg(["log", "--template", r"{desc|strip}\n", "--follow", "--rev",
                   start_rev+":"+end_rev, "--prune", start_rev])