def detect_overwritten_svn_branch(wc_url, svn_rev): """ Detect whether the current SVN branch was in a different location at the given revision. This means the current branch was later overwritten by another one. """ remote_url = get_svn_info(wc_url, svn_rev)['url'] if remote_url != wc_url: msg = ("The current branch (%s) has been\noverwritten with contents from %s.\n" + "hgsvn is unable to fetch history of the original branch.\n" + "Also, you will have to do a separate 'hgsvnimport' to pull recent history.\n" ) % (wc_url, remote_url) raise OverwrittenSVNBranch(msg)
def detect_overwritten_svn_branch(wc_url, svn_rev): """ Detect whether the current SVN branch was in a different location at the given revision. This means the current branch was later overwritten by another one. """ remote_url = get_svn_info(wc_url, svn_rev)['url'] if remote_url != wc_url: msg = ( "The current branch (%s) has been\noverwritten with contents from %s.\n" + "hgsvn is unable to fetch history of the original branch.\n" + "Also, you will have to do a separate 'hgsvnimport' to pull recent history.\n" ) % (wc_url, remote_url) raise OverwrittenSVNBranch(msg)
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)
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)
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'."
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.")
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.")