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

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

Mergebot: Codebase as released with permission from CommProve?, plus cleanups to remove traces of that environment.

File size: 7.0 KB
Line 
1#!/usr/bin/env python
2"""
3Syntax: RebranchActor.py ticketnum component version requestor
4
5Rebranch a branch from its trunk, pulling in the changes made on the branch if
6possible.
7"""
8
9import os
10import sys
11import time
12import trac.env
13
14import SvnOps
15from WorkQueue import MergeBotActor, VersionToDir
16from TrackerTools import GetRepositoryPublicUrl, GetRepositoryLocalUrl, Task, \
17    GetWorkDir, GetLogFile
18
19def rebranch_action(trac_env, ticketnum, component, version, requestor):
20    """
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 deleted branch.
25        if there are no conflicts, commit those changes to the 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 as
29    # little cleanup as possible and the fewest cases where we leave a broken
30    # branch.
31    task_obj = Task(trac_env, ticketnum)
32    summary = task_obj.GetSummary()
33
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))
38
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)
46
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
54
55    workingcopy = GetWorkDir(trac_env, ticketnum, __name__)
56
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
102
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:",
145                "{{{",
146                publicsvnmergecommand,
147                "}}}",
148            ])
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            ])
157
158    task_obj.AddComment( ticketmessage )
159    os.system("rm -rf \"%s\"" % (workingcopy, ))
160    return status, task_obj
161
162class RebranchActor(MergeBotActor):
163    "Actor wrapper for rebranch_action"
164    def __init__(self, trac_env):
165        MergeBotActor.__init__(self, trac_env, "rebranch", rebranch_action)
166
167def 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
174if __name__ == "__main__":
175    main()
176
177# vim:foldcolumn=4 foldmethod=indent
178# vim:tabstop=4 shiftwidth=4 softtabstop=4 expandtab
Note: See TracBrowser for help on using the repository browser.