source: mergebot/trunk/mergebot/RebranchActor.py

Last change on this file was 37, checked in by retracile, 14 years ago

Mergebot: fix imports

File size: 8.0 KB
Line 
1#!/usr/bin/env python
2"""
3Rebranch a branch from its trunk, pulling in the changes made on the branch if
4possible.
5"""
6
7import shutil
8import time
9import os
10
11from mergebot.svn import SvnLib
12SvnOps = SvnLib()
13from mergebot.Actor import Actor
14
15class RebranchActor(Actor):
16    """Rebranches a ticket from its baseline.
17    """
18    def execute(self):
19        """
20        To rebranch from the baseline, you have to do these steps:
21            delete the branch.
22            recreate the branch.
23            merge in the changes from the deleted branch.
24            if there are no conflicts, commit those changes to the branch.
25        """
26        # FIXME: This desparately needs to be refactored.
27        # Do as much work as we can before affecting the repository so we have
28        # as little cleanup as possible and the fewest cases where we leave a
29        # broken branch.
30
31        results = {}
32
33        # We need to improve the logging of the rebranch stuff.
34        logfile = self.logfilename()
35        open(logfile, "a").write("%s rebranching ticket %s\n" %
36            (time.asctime(), self.ticket))
37
38        # Make sure the various urls we require do exist
39        problems = self.check_required_directories()
40        if problems:
41            return results, problems, False
42
43        rev_info = SvnOps.get_branch_info(self.branch_local_url(), logfile)
44        if not rev_info:
45            comment = \
46                'Branch for ticket %s does not exist in the repository.' % \
47                (self.ticket)
48            return results, comment, False
49        startrev, endrev = rev_info
50
51        workingcopy = self.work_dir
52        if os.path.exists(workingcopy):
53            shutil.rmtree(workingcopy)
54
55        svnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev,
56            self.branch_local_url(), startrev)
57        publicsvnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev,
58            self.branch_public_url(), startrev)
59        # This is used in the ticket comments when there are conflicts.
60        instructioncomment = "\n".join([
61                "You will need to fix this manually by creating the branch and "
62                    "then doing the merge with this command:",
63                "{{{",
64                publicsvnmergecommand,
65                "}}}",
66        ])
67        # These are used in the commit messages.
68        rmmessage = "\n".join([
69            "Ticket #%s: %s" % (self.ticket, self.summary),
70            "    Remove the branch to rebranch from %s for %s." % \
71                (self.version, self.requestor),
72        ])
73        copymessage = "\n".join([
74            "Ticket #%s: %s" % (self.ticket, self.summary),
75            "    Recreate branch from %s for %s." % (self.version,
76                                                     self.requestor),
77            "[log:%s/branches/ticket-%s@%s Previous log]." % \
78                (self.component, self.ticket, endrev),
79        ])
80        # This is a list of commands.  Each of these must complete
81        # successfully, in this order, to complete the rebranch.  On failure,
82        # the given comment needs to be added to the ticket log.
83        commanderrors = (
84            (lambda x: SvnOps.delete_branch(self.branch_local_url(), rmmessage,
85                                            x) == -1,
86                "Rebranch internal error: Unable to delete the old branch."),
87            (lambda x: SvnOps.create_branch(self.baseline_local_url(),
88                    self.branch_local_url(), copymessage, x),
89                "Rebranch internal error: Unable to recreate the branch.  %s" \
90                    % (instructioncomment, )),
91        )
92        for cmd, error_comment in commanderrors:
93            retval = cmd(logfile)
94            if retval:
95                if os.path.exists(workingcopy):
96                    shutil.rmtree(workingcopy)
97                results['mergebotstate'] = 'rebranchfailed'
98                return results, error_comment, False
99
100        # Check to see if there have been no commits on the branch.  If there
101        # have been no commits, we know we don't need to merge anything.
102        if startrev == endrev:
103            ticket_message = "\n".join([
104                "Rebranched from %s for %s." % (self.version,
105                                                self.requestor),
106                "There were no changes to commit to the branch since there "
107                    "were no commits on the branch.",
108                "You will need to update your working copy.",
109            ])
110            results['mergebotstate'] = 'branched'
111            return results, ticket_message, True
112
113        retval = SvnOps.checkout(self.branch_local_url(), workingcopy, logfile)
114        if retval:
115            error_comment = \
116                "Rebranch internal error: Unable to get working copy.  " + \
117                instructioncomment
118            if os.path.exists(workingcopy):
119                shutil.rmtree(workingcopy)
120            results['mergebotstate'] = 'rebranchfailed'
121            return results, error_comment, False
122
123        # On success, we're in the same state as if we had just branched.
124        results['mergebotstate'] = 'branched'
125        success = True
126
127        # Go ahead and try to do the merge.  If we got lucky and there are no
128        # conflicts, commit the changes.
129        # Put a full update on the ticket.
130        merge_results = SvnOps.merge("%s@%s" % (self.branch_local_url(),
131            startrev), workingcopy, (startrev, endrev), logfile)
132        conflicts = SvnOps.conflicts_from_merge_results(merge_results)
133        if conflicts:
134            ticketmessage = "\n".join([
135                "There were conflicts on rebranching.",
136                "Files in conflict:",
137                "{{{",
138                "\n".join(conflicts),
139                "}}}",
140                "You will need to resolve the conflicts manually.",
141                "To do so, update a working copy to the branch, "
142                    "and run this merge command:",
143                "{{{",
144                publicsvnmergecommand,
145                "}}}",
146                "Once you have resolved the conflicts, "
147                    "commit your work to the branch.",
148            ])
149            success = False # the rebranch failed because there were conflicts
150        else: # No conflicts, do the commit.
151            mergemessage = "\n".join([
152                "Ticket #%s: %s" % (self.ticket, self.summary),
153                "    Merge in changes from old branch for %s." % self.requestor,
154                "    %s" % svnmergecommand,
155            ])
156            newrev = SvnOps.commit(workingcopy, mergemessage, logfile)
157            if newrev == None:
158                ticketmessage = "\n".join([
159                    "Rebranched from %s for %s." % (self.version,
160                                                    self.requestor),
161                    "There were no changes to commit to the branch.",
162                    "You will need to update your working copy.",
163                ])
164            elif newrev < 0:
165                ticketmessage = "\n".join([
166                    "Rebranch internal error:  Unable to commit merged "
167                        "changes.",
168                    "You will need to fix this manually by doing the merge "
169                        "with this command:",
170                    "{{{",
171                    publicsvnmergecommand,
172                    "}}}",
173                ])
174                results['mergebotstate'] = 'rebranchfailed'
175                success = False
176            else:
177                ticketmessage = "\n".join([
178                    "Rebranched from %s for %s." % (self.version,
179                                                    self.requestor),
180                    "There were no conflicts, so the changes were "
181                        "automatically merged and committed to the branch.",
182                    "You will need to update your working copy.",
183                ])
184
185        if os.path.exists(workingcopy):
186            shutil.rmtree(workingcopy)
187        return results, ticketmessage, success
188
189# vim:foldcolumn=4 foldmethod=indent
190# vim:tabstop=4 shiftwidth=4 softtabstop=4 expandtab
Note: See TracBrowser for help on using the repository browser.