Changeset 17 for mergebot/trunk/mergebot/RebranchActor.py
- Timestamp:
- Jun 8, 2009 3:07:47 AM (15 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
mergebot/trunk/mergebot/RebranchActor.py
r16 r17 1 1 #!/usr/bin/env python 2 2 """ 3 Syntax: RebranchActor.py ticketnum component version requestor4 5 3 Rebranch a branch from its trunk, pulling in the changes made on the branch if 6 4 possible. 7 5 """ 8 6 7 import shutil 8 import time 9 9 import os 10 import sys11 import time12 import trac.env13 10 14 import SvnOps 15 from WorkQueue import MergeBotActor, VersionToDir 16 from TrackerTools import GetRepositoryPublicUrl, GetRepositoryLocalUrl, Task, \ 17 GetWorkDir, GetLogFile 11 from mergebot import SvnOps 12 from mergebot.Actor import Actor 18 13 19 def rebranch_action(trac_env, ticketnum, component, version, requestor): 14 class RebranchActor(Actor): 15 """Rebranches a ticket from its baseline. 20 16 """ 21 To rebranch from the baseline, you have to do these steps:22 delete the branch.23 recreate the branch.24 merge in the changes from the deletedbranch.25 if there are no conflicts, commit those changes tothe branch.26 """27 # FIXME: This desparately needs to be refactored.28 # Do as much work as we can before affecting the repository so we have as29 # little cleanup as possible and the fewest cases where we leave a broken30 # branch.31 task_obj = Task(trac_env, ticketnum)32 summary = task_obj.GetSummary()17 def execute(self): 18 """ 19 To rebranch from the baseline, you have to do these steps: 20 delete the branch. 21 recreate the branch. 22 merge in the changes from the deleted branch. 23 if there are no conflicts, commit those changes to the branch. 24 """ 25 # FIXME: This desparately needs to be refactored. 26 # Do as much work as we can before affecting the repository so we have 27 # as little cleanup as possible and the fewest cases where we leave a 28 # broken branch. 33 29 34 # We need to improve the logging of the rebranch stuff. 35 logfile = GetLogFile(trac_env, ticketnum) 36 open(logfile, "a").write("%s rebranching ticket %s\n" % (time.asctime(), 37 ticketnum)) 30 results = {} 38 31 39 baserepositoryurl = str(GetRepositoryLocalUrl(trac_env)) 40 branchurl = os.path.join(baserepositoryurl, component, "branches", 41 "ticket-%s" % ticketnum) 42 baselineurl = os.path.join(baserepositoryurl, component, 43 VersionToDir(version)) 44 publicbranchurl = os.path.join(GetRepositoryPublicUrl(trac_env), component, 45 "branches", "ticket-%s" % ticketnum) 32 # We need to improve the logging of the rebranch stuff. 33 logfile = self.logfilename() 34 open(logfile, "a").write("%s rebranching ticket %s\n" % 35 (time.asctime(), self.ticket)) 46 36 47 # Determine the revision range for the branch: startrev:endrev 48 branchinfo = SvnOps.get_branch_info(branchurl, logfile) 49 if not branchinfo: 50 open(logfile, "a").write( 51 "Unable to get %s branch revision information.\n" % (branchurl)) 52 return "rebranchfailed", task_obj 53 startrev, endrev = branchinfo 37 # Make sure the various urls we require do exist 38 if not SvnOps.get_branch_info(self.local_url(), logfile): 39 comment = 'Component %s does not exist in the repository.' \ 40 % self.component 41 return results, comment, False 42 if not SvnOps.get_branch_info(self.local_url() + '/branches', logfile): 43 comment = 'No directory in which to create branches for ' \ 44 'component %s in the repository.' % self.component 45 return results, comment, False 46 if not SvnOps.get_branch_info(self.baseline_local_url(), logfile): 47 comment = 'Version %s for component %s does not exist in the ' \ 48 'repository.' % (self.version, self.component) 49 return results, comment, False 54 50 55 workingcopy = GetWorkDir(trac_env, ticketnum, __name__) 51 rev_info = SvnOps.get_branch_info(self.branch_local_url(), logfile) 52 if not rev_info: 53 comment = \ 54 'Branch for ticket %s does not exist in the repository.' % \ 55 (self.ticket) 56 return results, comment, False 57 startrev, endrev = rev_info 56 58 57 svnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev, 58 branchurl, startrev) 59 publicsvnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev, 60 publicbranchurl, startrev) 61 # This is used in the ticket comments. 62 instructioncomment = "\n".join([ 63 "You will need to fix this manually by creating the branch and " 64 "then doing the merge with this command:", 65 "{{{", 66 publicsvnmergecommand, 67 "}}}", 68 ]) 69 # These are used in the commit messages. 70 rmmessage = "\n".join([ 71 "Ticket #%s: %s" % (ticketnum, summary), 72 " Remove the branch to rebranch from %s for %s." % \ 73 (version, requestor), 74 ]) 75 copymessage = "\n".join([ 76 "Ticket #%s: %s" % (ticketnum, summary), 77 " Recreate branch from %s for %s." % (version, requestor), 78 "[log:%s/branches/ticket-%s@%s Previous log]." % \ 79 (component, ticketnum, endrev), 80 ]) 81 # This is a list of commands. Each of these must complete successfully, in 82 # this order, to complete the rebranch. On failure, the given comment 83 # needs to be added to the ticket log. 84 commanderrors = ( 85 (lambda x: SvnOps.logcmd("rm -rf \"%s\"" % (workingcopy), x), 86 "Rebranch internal error: Unable to cleanup work area."), 87 (lambda x: SvnOps.delete_branch(branchurl, rmmessage, x) == -1, 88 "Rebranch internal error: Unable to delete the old branch."), 89 (lambda x: SvnOps.create_branch(baselineurl, branchurl, copymessage, x), 90 "Rebranch internal error: Unable to recreate the branch. %s" % \ 91 (instructioncomment, )), 92 (lambda x: SvnOps.checkout(branchurl, workingcopy, x), 93 "Rebranch internal error: Unable to get working copy. %s" % \ 94 (instructioncomment, )), 95 ) 96 for cmd, errorcomment in commanderrors: 97 retval = cmd(logfile) 98 if retval: 99 task_obj.AddComment(errorcomment) 100 os.system("rm -rf \"%s\"" % (workingcopy, )) 101 return "rebranchfailed", task_obj 59 workingcopy = self.work_dir 60 if os.path.exists(workingcopy): 61 shutil.rmtree(workingcopy) 102 62 103 # On success, we're in the same state as if we had just branched. 104 status = "branched" 105 # Go ahead and try to do the merge. If we got lucky and there are no 106 # conflicts, commit the changes. 107 # Put a full update on the ticket. 108 results = SvnOps.merge("%s@%s" % (branchurl, startrev), workingcopy, 109 (startrev, endrev), logfile) 110 conflicts = SvnOps.conflicts_from_merge_results(results) 111 if conflicts: 112 ticketmessage = "\n".join([ 113 "There were conflicts on rebranching.", 114 "Files in conflict:", 115 "{{{", 116 "\n".join(conflicts), 117 "}}}", 118 "You will need to resolve the conflicts manually.", 119 "To do so, update a working copy to the branch, " 120 "and run this merge command:", 121 "{{{", 122 publicsvnmergecommand, 123 "}}}", 124 "Once you have resolved the conflicts, " 125 "commit your work to the branch.", 126 ]) 127 else: # No conflicts, do the commit. 128 mergemessage = "\n".join([ 129 "Ticket #%s: %s" % (ticketnum, summary), 130 " Merge in changes from old branch for %s." % (requestor), 131 " %s" % (svnmergecommand), 132 ]) 133 newrev = SvnOps.commit(workingcopy, mergemessage, logfile) 134 if newrev == None: 135 ticketmessage = "\n".join([ 136 "Rebranched from %s for %s." % (version, requestor), 137 "There were no changes to commit to the branch.", 138 "You will need to update your working copy.", 139 ]) 140 elif newrev < 0: 141 ticketmessage = "\n".join([ 142 "Rebranch internal error: Unable to commit merged changes.", 143 "You will need to fix this manually by doing the merge with " 144 "this command:", 63 svnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev, 64 self.branch_local_url(), startrev) 65 publicsvnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev, 66 self.branch_public_url(), startrev) 67 # This is used in the ticket comments when there are conflicts. 68 instructioncomment = "\n".join([ 69 "You will need to fix this manually by creating the branch and " 70 "then doing the merge with this command:", 145 71 "{{{", 146 72 publicsvnmergecommand, 147 73 "}}}", 74 ]) 75 # These are used in the commit messages. 76 rmmessage = "\n".join([ 77 "Ticket #%s: %s" % (self.ticket, self.summary), 78 " Remove the branch to rebranch from %s for %s." % \ 79 (self.version, self.requestor), 80 ]) 81 copymessage = "\n".join([ 82 "Ticket #%s: %s" % (self.ticket, self.summary), 83 " Recreate branch from %s for %s." % (self.version, 84 self.requestor), 85 "[log:%s/branches/ticket-%s@%s Previous log]." % \ 86 (self.component, self.ticket, endrev), 87 ]) 88 # This is a list of commands. Each of these must complete 89 # successfully, in this order, to complete the rebranch. On failure, 90 # the given comment needs to be added to the ticket log. 91 commanderrors = ( 92 (lambda x: SvnOps.delete_branch(self.branch_local_url(), rmmessage, 93 x) == -1, 94 "Rebranch internal error: Unable to delete the old branch."), 95 (lambda x: SvnOps.create_branch(self.baseline_local_url(), 96 self.branch_local_url(), copymessage, x), 97 "Rebranch internal error: Unable to recreate the branch. %s" \ 98 % (instructioncomment, )), 99 (lambda x: SvnOps.checkout(self.branch_local_url(), workingcopy, x), 100 "Rebranch internal error: Unable to get working copy. %s" % \ 101 (instructioncomment, )), 102 ) 103 for cmd, error_comment in commanderrors: 104 retval = cmd(logfile) 105 if retval: 106 if os.path.exists(workingcopy): 107 shutil.rmtree(workingcopy) 108 results['mergebotstate'] = 'rebranchfailed' 109 return results, error_comment, False 110 111 # On success, we're in the same state as if we had just branched. 112 results['mergebotstate'] = 'branched' 113 success = True 114 115 # Go ahead and try to do the merge. If we got lucky and there are no 116 # conflicts, commit the changes. 117 # Put a full update on the ticket. 118 merge_results = SvnOps.merge("%s@%s" % (self.branch_local_url(), 119 startrev), workingcopy, (startrev, endrev), logfile) 120 conflicts = SvnOps.conflicts_from_merge_results(merge_results) 121 if conflicts: 122 ticketmessage = "\n".join([ 123 "There were conflicts on rebranching.", 124 "Files in conflict:", 125 "{{{", 126 "\n".join(conflicts), 127 "}}}", 128 "You will need to resolve the conflicts manually.", 129 "To do so, update a working copy to the branch, " 130 "and run this merge command:", 131 "{{{", 132 publicsvnmergecommand, 133 "}}}", 134 "Once you have resolved the conflicts, " 135 "commit your work to the branch.", 136 ]) 137 else: # No conflicts, do the commit. 138 mergemessage = "\n".join([ 139 "Ticket #%s: %s" % (self.ticket, self.summary), 140 " Merge in changes from old branch for %s." % self.requestor, 141 " %s" % svnmergecommand, 148 142 ]) 149 status = "rebranchfailed" 150 else: 151 ticketmessage = "\n".join([ 152 "Rebranched from %s for %s." % (version, requestor), 153 "There were no conflicts, so the changes were automatically " 154 "merged and committed to the branch.", 155 "You will need to update your working copy.", 156 ]) 143 newrev = SvnOps.commit(workingcopy, mergemessage, logfile) 144 if newrev == None: 145 ticketmessage = "\n".join([ 146 "Rebranched from %s for %s." % (self.version, 147 self.requestor), 148 "There were no changes to commit to the branch.", 149 "You will need to update your working copy.", 150 ]) 151 elif newrev < 0: 152 ticketmessage = "\n".join([ 153 "Rebranch internal error: Unable to commit merged " 154 "changes.", 155 "You will need to fix this manually by doing the merge " 156 "with this command:", 157 "{{{", 158 publicsvnmergecommand, 159 "}}}", 160 ]) 161 results['mergebotstate'] = 'rebranchfailed' 162 success = False 163 else: 164 ticketmessage = "\n".join([ 165 "Rebranched from %s for %s." % (self.version, 166 self.requestor), 167 "There were no conflicts, so the changes were " 168 "automatically merged and committed to the branch.", 169 "You will need to update your working copy.", 170 ]) 157 171 158 task_obj.AddComment( ticketmessage ) 159 os.system("rm -rf \"%s\"" % (workingcopy, )) 160 return status, task_obj 161 162 class RebranchActor(MergeBotActor): 163 "Actor wrapper for rebranch_action" 164 def __init__(self, trac_env): 165 MergeBotActor.__init__(self, trac_env, "rebranch", rebranch_action) 166 167 def main(): 168 tracdir = sys.argv[1] 169 trac_env = trac.env.open_environment(tracdir) 170 rebranchingActor = RebranchActor(trac_env) 171 rebranchingActor.AddTask(sys.argv[2:]) 172 rebranchingActor.Run() 173 174 if __name__ == "__main__": 175 main() 172 if os.path.exists(workingcopy): 173 shutil.rmtree(workingcopy) 174 return results, ticketmessage, success 176 175 177 176 # vim:foldcolumn=4 foldmethod=indent
Note: See TracChangeset
for help on using the changeset viewer.