Changeset 33 for mergebot/trunk/mergebot/svn.py
- Timestamp:
- Feb 22, 2010 5:12:04 PM (15 years ago)
- File:
-
- 1 moved
Legend:
- Unmodified
- Added
- Removed
-
mergebot/trunk/mergebot/svn.py
r25 r33 10 10 import subprocess 11 11 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, ) 12 class SvnLib(object): 13 def __init__(self): 14 pass 17 15 18 def logcmd(cmd, logfile):19 """Log the cmd string, then execute it, appending its stdout and stderr to20 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, ) 23 21 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)) 27 27 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:]) 34 31 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')) 41 38 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')) 43 45 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 60 47 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) 68 64 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) 77 72 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) 82 81 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 98 132 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 136 140 137 141 # vim:foldcolumn=4 foldmethod=indent
Note: See TracChangeset
for help on using the changeset viewer.