source: mergebot/trunk/mergebot/RebranchActor.py @ 17

Last change on this file since 17 was 17, checked in by retracile, 15 years ago

Mergebot: redesigned implementation. Still has rough edges.

File size: 7.6 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 import SvnOps
12from mergebot.Actor import Actor
13
14class RebranchActor(Actor):
15    """Rebranches a ticket from its baseline.
16    """
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.
29
30        results = {}
31
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))
36
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
50
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
58
59        workingcopy = self.work_dir
60        if os.path.exists(workingcopy):
61            shutil.rmtree(workingcopy)
62
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:",
71                "{{{",
72                publicsvnmergecommand,
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,
142            ])
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                ])
171
172        if os.path.exists(workingcopy):
173            shutil.rmtree(workingcopy)
174        return results, ticketmessage, success
175
176# vim:foldcolumn=4 foldmethod=indent
177# vim:tabstop=4 shiftwidth=4 softtabstop=4 expandtab
Note: See TracBrowser for help on using the repository browser.