Ignore:
Timestamp:
Feb 22, 2010 5:12:04 PM (15 years ago)
Author:
retracile
Message:

Mergebot: begin refactoring svn layer

File:
1 moved

Legend:

Unmodified
Added
Removed
  • mergebot/trunk/mergebot/svn.py

    r25 r33  
    1010import subprocess
    1111
    12 def shell_quote(string):
    13     """Given a string, escape the characters interpretted by the shell."""
    14     for char in ["\\", "\"", "$"]:
    15         string = string.replace(char, "\\%s" % (char, ))
    16     return '"%s"' % (string, )
     12class SvnLib(object):
     13    def __init__(self):
     14        pass
    1715
    18 def logcmd(cmd, logfile):
    19     """Log the cmd string, then execute it, appending its stdout and stderr to
    20     logfile."""
    21     open(logfile, "a").write("%s: %s\n" % (time.asctime(), cmd))
    22     return os.system("(%s) >>%s 2>&1" % (cmd, logfile))
     16    def shell_quote(self, string):
     17        """Given a string, escape the characters interpretted by the shell."""
     18        for char in ["\\", "\"", "$"]:
     19            string = string.replace(char, "\\%s" % (char, ))
     20        return '"%s"' % (string, )
    2321
    24 def get_rev_from_log(logentry):
    25     """Given a log entry split out of svn log, return its revision number"""
    26     return int(logentry.split()[0][1:])
     22    def logcmd(self, cmd, logfile):
     23        """Log the cmd string, then execute it, appending its stdout and stderr to
     24        logfile."""
     25        open(logfile, "a").write("%s: %s\n" % (time.asctime(), cmd))
     26        return os.system("(%s) >>%s 2>&1" % (cmd, logfile))
    2727
    28 def does_url_exist_14(url):
    29     """Given a subversion url return true if it exists, false otherwise."""
    30     return not subprocess.call(['svn', 'log', '--limit=1', '--non-interactive',
    31                                 url],
    32                     stdout=open('/dev/null', 'w'),
    33                     stderr=open('/dev/null', 'w'))
     28    def get_rev_from_log(self, logentry):
     29        """Given a log entry split out of svn log, return its revision number"""
     30        return int(logentry.split()[0][1:])
    3431
    35 def does_url_exist_15(url):
    36     """Given a subversion url return true if it exists, false otherwise."""
    37     return not subprocess.call(['svn', 'ls', '--depth', 'empty',
    38                                 '--non-interactive', url],
    39                     stdout=open('/dev/null', 'w'),
    40                     stderr=open('/dev/null', 'w'))
     32    def does_url_exist_14(self, url):
     33        """Given a subversion url return true if it exists, false otherwise."""
     34        return not subprocess.call(['svn', 'log', '--limit=1', '--non-interactive',
     35                                    url],
     36                        stdout=open('/dev/null', 'w'),
     37                        stderr=open('/dev/null', 'w'))
    4138
    42 does_url_exist=does_url_exist_14 # default to most compatible form for now
     39    def does_url_exist_15(self, url):
     40        """Given a subversion url return true if it exists, false otherwise."""
     41        return not subprocess.call(['svn', 'ls', '--depth', 'empty',
     42                                    '--non-interactive', url],
     43                        stdout=open('/dev/null', 'w'),
     44                        stderr=open('/dev/null', 'w'))
    4345
    44 def get_branch_info(url, logfile):
    45     """Given a subversion url and a logfile, return (start_revision,
    46     end_revision) or None if it does not exist."""
    47     svncmd = os.popen("svn log --stop-on-copy --non-interactive %s 2>>%s" % \
    48         (url, logfile), "r")
    49     branchlog = svncmd.read()
    50     returnval = svncmd.close()
    51     if returnval:
    52         # This branch apparently doesn't exist
    53         return None
    54     logs = branchlog.split("-"*72 + "\n")
    55     # If there have been no commits on the branch since it was created, there
    56     # will only be one revision listed.... but the log will split into 3 parts.
    57     endrev = get_rev_from_log(logs[1])
    58     startrev = get_rev_from_log(logs[-2])
    59     return (startrev, endrev)
     46    does_url_exist=does_url_exist_14 # default to most compatible form for now
    6047
    61 def create_branch(from_url, to_url, commit_message, logfile):
    62     """Create a branch copying from_url to to_url.  Commit as mergebot, and use
    63     the provided commit message."""
    64     svncmd = \
    65         "svn copy --username=mergebot --password=mergebot -m %s %s %s" \
    66         % (shell_quote(commit_message), from_url, to_url)
    67     return logcmd(svncmd, logfile)
     48    def get_branch_info(self, url, logfile):
     49        """Given a subversion url and a logfile, return (start_revision,
     50        end_revision) or None if it does not exist."""
     51        svncmd = os.popen("svn log --stop-on-copy --non-interactive %s 2>>%s" % \
     52            (url, logfile), "r")
     53        branchlog = svncmd.read()
     54        returnval = svncmd.close()
     55        if returnval:
     56            # This branch apparently doesn't exist
     57            return None
     58        logs = branchlog.split("-"*72 + "\n")
     59        # If there have been no commits on the branch since it was created, there
     60        # will only be one revision listed.... but the log will split into 3 parts.
     61        endrev = get_rev_from_log(logs[1])
     62        startrev = get_rev_from_log(logs[-2])
     63        return (startrev, endrev)
    6864
    69 def delete_branch(url, commit_message, logfile):
    70     """This will generate a new revision.  Return the revision number, or -1 on
    71     failure.
    72     Assumes that the url exists.  You should call get_branch_info() to
    73     determine that first"""
    74     svncmd = "svn rm --no-auth-cache --username=mergebot --password=mergebot " \
    75         "-m %s %s 2>>%s" % (shell_quote(commit_message), url, logfile)
    76     return _svn_new_rev_command(svncmd)
     65    def create_branch(self, from_url, to_url, commit_message, logfile):
     66        """Create a branch copying from_url to to_url.  Commit as mergebot, and use
     67        the provided commit message."""
     68        svncmd = \
     69            "svn copy --username=mergebot --password=mergebot -m %s %s %s" \
     70            % (shell_quote(commit_message), from_url, to_url)
     71        return logcmd(svncmd, logfile)
    7772
    78 def checkout(from_url, workingdir, logfile):
    79     """Checkout from the given url into workingdir"""
    80     return os.system("svn checkout %s %s >>%s 2>&1" % (from_url, workingdir,
    81         logfile))
     73    def delete_branch(self, url, commit_message, logfile):
     74        """This will generate a new revision.  Return the revision number, or -1 on
     75        failure.
     76        Assumes that the url exists.  You should call get_branch_info() to
     77        determine that first"""
     78        svncmd = "svn rm --no-auth-cache --username=mergebot --password=mergebot " \
     79            "-m %s %s 2>>%s" % (shell_quote(commit_message), url, logfile)
     80        return _svn_new_rev_command(svncmd)
    8281
    83 def merge(from_url, workingdir, revision_range, logfile):
    84     """Returns a list (status, filename) tuples"""
    85     # There are a couple of different 'Skipped' messages.
    86     skipped_regex = re.compile("Skipped.* '(.*)'", re.M)
    87     start_rev, end_rev = revision_range
    88     pipe = os.popen("cd %s && svn merge --revision %s:%s %s . 2>>%s" % \
    89         (workingdir, start_rev, end_rev, from_url, logfile))
    90     output = pipe.readlines()
    91     # FIXME: check pipe.close for errors
    92     results = []
    93     for line in output:
    94         if line.startswith("Skipped"):
    95             # This kind of conflict requires special handling.
    96             filename = skipped_regex.findall(line)[0]
    97             status = "C"
     82    def checkout(self, from_url, workingdir, logfile):
     83        """Checkout from the given url into workingdir"""
     84        return os.system("svn checkout %s %s >>%s 2>&1" % (from_url, workingdir,
     85            logfile))
     86
     87    def merge(self, from_url, workingdir, revision_range, logfile):
     88        """Returns a list (status, filename) tuples"""
     89        # There are a couple of different 'Skipped' messages.
     90        skipped_regex = re.compile("Skipped.* '(.*)'", re.M)
     91        start_rev, end_rev = revision_range
     92        pipe = os.popen("cd %s && svn merge --revision %s:%s %s . 2>>%s" % \
     93            (workingdir, start_rev, end_rev, from_url, logfile))
     94        output = pipe.readlines()
     95        # FIXME: check pipe.close for errors
     96        results = []
     97        for line in output:
     98            if line.startswith("Skipped"):
     99                # This kind of conflict requires special handling.
     100                filename = skipped_regex.findall(line)[0]
     101                status = "C"
     102            else:
     103                assert line[4] == ' ', "Unexpected output from svn merge " \
     104                    "operation; the 5th character should always be a space." \
     105                    "  Output was %r." % line
     106                filename = line[5:-1] # (strip trailing newline)
     107                status = line[:4].rstrip()
     108            results.append((status, filename))
     109        return results
     110
     111    def conflicts_from_merge_results(self, results):
     112        "Given the output from merge, return a list of files that had conflicts."
     113        conflicts = [filename for status, filename in results if 'C' in status]
     114        return conflicts
     115
     116    def commit(self, workingdir, commit_message, logfile):
     117        """Returns newly committed revision number, or None if there was nothing to
     118        commit.  -1 on error."""
     119        svncmd = "cd %s && svn commit --no-auth-cache --username=mergebot " \
     120            "--password=mergebot -m %s 2>>%s" % (workingdir,
     121                shell_quote(commit_message), logfile)
     122        return _svn_new_rev_command(svncmd)
     123
     124    def _svn_new_rev_command(self, svncmd):
     125        """Given an svn command that results in a new revision, return the revision
     126        number, or -1 on error."""
     127        pipe = os.popen(svncmd)
     128        output = pipe.read()
     129        retval = pipe.close()
     130        if retval:
     131            new_revision = -1
    98132        else:
    99             assert line[4] == ' ', "Unexpected output from svn merge " \
    100                 "operation; the 5th character should always be a space." \
    101                 "  Output was %r." % line
    102             filename = line[5:-1] # (strip trailing newline)
    103             status = line[:4].rstrip()
    104         results.append((status, filename))
    105     return results
    106 
    107 def conflicts_from_merge_results(results):
    108     "Given the output from merge, return a list of files that had conflicts."
    109     conflicts = [filename for status, filename in results if 'C' in status]
    110     return conflicts
    111 
    112 def commit(workingdir, commit_message, logfile):
    113     """Returns newly committed revision number, or None if there was nothing to
    114     commit.  -1 on error."""
    115     svncmd = "cd %s && svn commit --no-auth-cache --username=mergebot " \
    116         "--password=mergebot -m %s 2>>%s" % (workingdir,
    117             shell_quote(commit_message), logfile)
    118     return _svn_new_rev_command(svncmd)
    119 
    120 def _svn_new_rev_command(svncmd):
    121     """Given an svn command that results in a new revision, return the revision
    122     number, or -1 on error."""
    123     pipe = os.popen(svncmd)
    124     output = pipe.read()
    125     retval = pipe.close()
    126     if retval:
    127         new_revision = -1
    128     else:
    129         new_revisions = re.compile("Committed revision ([0-9]+)\\.",
    130             re.M).findall(output)
    131         if new_revisions:
    132             new_revision = new_revisions[0]
    133         else:
    134             new_revision = None
    135     return new_revision
     133            new_revisions = re.compile("Committed revision ([0-9]+)\\.",
     134                re.M).findall(output)
     135            if new_revisions:
     136                new_revision = new_revisions[0]
     137            else:
     138                new_revision = None
     139        return new_revision
    136140
    137141# vim:foldcolumn=4 foldmethod=indent
Note: See TracChangeset for help on using the changeset viewer.