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

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

Attempt to short-circuit the rebranch logic when dealing with a branch with no commits

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 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        problems = self.check_required_directories()
39        if problems:
40            return results, problems, False
41
42        rev_info = SvnOps.get_branch_info(self.branch_local_url(), logfile)
43        if not rev_info:
44            comment = \
45                'Branch for ticket %s does not exist in the repository.' % \
46                (self.ticket)
47            return results, comment, False
48        startrev, endrev = rev_info
49
50        workingcopy = self.work_dir
51        if os.path.exists(workingcopy):
52            shutil.rmtree(workingcopy)
53
54        svnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev,
55            self.branch_local_url(), startrev)
56        publicsvnmergecommand = "svn merge -r %s:%s %s@%s" % (startrev, endrev,
57            self.branch_public_url(), startrev)
58        # This is used in the ticket comments when there are conflicts.
59        instructioncomment = "\n".join([
60                "You will need to fix this manually by creating the branch and "
61                    "then doing the merge with this command:",
62                "{{{",
63                publicsvnmergecommand,
64                "}}}",
65        ])
66        # These are used in the commit messages.
67        rmmessage = "\n".join([
68            "Ticket #%s: %s" % (self.ticket, self.summary),
69            "    Remove the branch to rebranch from %s for %s." % \
70                (self.version, self.requestor),
71        ])
72        copymessage = "\n".join([
73            "Ticket #%s: %s" % (self.ticket, self.summary),
74            "    Recreate branch from %s for %s." % (self.version,
75                                                     self.requestor),
76            "[log:%s/branches/ticket-%s@%s Previous log]." % \
77                (self.component, self.ticket, endrev),
78        ])
79        # This is a list of commands.  Each of these must complete
80        # successfully, in this order, to complete the rebranch.  On failure,
81        # the given comment needs to be added to the ticket log.
82        commanderrors = (
83            (lambda x: SvnOps.delete_branch(self.branch_local_url(), rmmessage,
84                                            x) == -1,
85                "Rebranch internal error: Unable to delete the old branch."),
86            (lambda x: SvnOps.create_branch(self.baseline_local_url(),
87                    self.branch_local_url(), copymessage, x),
88                "Rebranch internal error: Unable to recreate the branch.  %s" \
89                    % (instructioncomment, )),
90        )
91        for cmd, error_comment in commanderrors:
92            retval = cmd(logfile)
93            if retval:
94                if os.path.exists(workingcopy):
95                    shutil.rmtree(workingcopy)
96                results['mergebotstate'] = 'rebranchfailed'
97                return results, error_comment, False
98
99        # Check to see if there have been no commits on the branch.  If there
100        # have been no commits, we know we don't need to merge anything.
101        if startrev == endrev:
102            ticket_message = "\n".join([
103                "Rebranched from %s for %s." % (self.version,
104                                                self.requestor),
105                "There were no changes to commit to the branch since there "
106                    "were no commits on the branch.",
107                "You will need to update your working copy.",
108            ])
109            results['mergebotstate'] = 'branched'
110            return results, ticket_message, True
111
112        retval = SvnOps.checkout(self.branch_local_url(), workingcopy, logfile)
113        if retval:
114            error_comment = \
115                "Rebranch internal error: Unable to get working copy.  " + \
116                instructioncomment
117            if os.path.exists(workingcopy):
118                shutil.rmtree(workingcopy)
119            results['mergebotstate'] = 'rebranchfailed'
120            return results, error_comment, False
121
122        # On success, we're in the same state as if we had just branched.
123        results['mergebotstate'] = 'branched'
124        success = True
125
126        # Go ahead and try to do the merge.  If we got lucky and there are no
127        # conflicts, commit the changes.
128        # Put a full update on the ticket.
129        merge_results = SvnOps.merge("%s@%s" % (self.branch_local_url(),
130            startrev), workingcopy, (startrev, endrev), logfile)
131        conflicts = SvnOps.conflicts_from_merge_results(merge_results)
132        if conflicts:
133            ticketmessage = "\n".join([
134                "There were conflicts on rebranching.",
135                "Files in conflict:",
136                "{{{",
137                "\n".join(conflicts),
138                "}}}",
139                "You will need to resolve the conflicts manually.",
140                "To do so, update a working copy to the branch, "
141                    "and run this merge command:",
142                "{{{",
143                publicsvnmergecommand,
144                "}}}",
145                "Once you have resolved the conflicts, "
146                    "commit your work to the branch.",
147            ])
148            success = False # the rebranch failed because there were conflicts
149        else: # No conflicts, do the commit.
150            mergemessage = "\n".join([
151                "Ticket #%s: %s" % (self.ticket, self.summary),
152                "    Merge in changes from old branch for %s." % self.requestor,
153                "    %s" % svnmergecommand,
154            ])
155            newrev = SvnOps.commit(workingcopy, mergemessage, logfile)
156            if newrev == None:
157                ticketmessage = "\n".join([
158                    "Rebranched from %s for %s." % (self.version,
159                                                    self.requestor),
160                    "There were no changes to commit to the branch.",
161                    "You will need to update your working copy.",
162                ])
163            elif newrev < 0:
164                ticketmessage = "\n".join([
165                    "Rebranch internal error:  Unable to commit merged "
166                        "changes.",
167                    "You will need to fix this manually by doing the merge "
168                        "with this command:",
169                    "{{{",
170                    publicsvnmergecommand,
171                    "}}}",
172                ])
173                results['mergebotstate'] = 'rebranchfailed'
174                success = False
175            else:
176                ticketmessage = "\n".join([
177                    "Rebranched from %s for %s." % (self.version,
178                                                    self.requestor),
179                    "There were no conflicts, so the changes were "
180                        "automatically merged and committed to the branch.",
181                    "You will need to update your working copy.",
182                ])
183
184        if os.path.exists(workingcopy):
185            shutil.rmtree(workingcopy)
186        return results, ticketmessage, success
187
188# vim:foldcolumn=4 foldmethod=indent
189# vim:tabstop=4 shiftwidth=4 softtabstop=4 expandtab
Note: See TracBrowser for help on using the repository browser.