]> Cypherpunks repositories - gostls13.git/commitdiff
create release-branch.r57
authorRuss Cox <rsc@golang.org>
Tue, 3 May 2011 05:07:23 +0000 (01:07 -0400)
committerRuss Cox <rsc@golang.org>
Tue, 3 May 2011 05:07:23 +0000 (01:07 -0400)
lib/codereview/codereview.py

index bfa69fcc08c499a7f540f69f5a8784f5c3093926..36d7df199fea981a3ca7c80519b443e8b9c6944a 100644 (file)
@@ -112,6 +112,7 @@ defaultcc = None
 contributors = {}
 missing_codereview = None
 real_rollback = None
+releaseBranch = None
 
 #######################################################################
 # RE: UNICODE STRING HANDLING
@@ -1049,7 +1050,7 @@ def change(ui, repo, *pats, **opts):
 
        if missing_codereview:
                return missing_codereview
-
+       
        dirty = {}
        if len(pats) > 0 and GoodCLName(pats[0]):
                name = pats[0]
@@ -1062,6 +1063,8 @@ def change(ui, repo, *pats, **opts):
                if not cl.local and (opts["stdin"] or not opts["stdout"]):
                        return "cannot change non-local CL " + name
        else:
+               if repo[None].branch() != "default":
+                       return "cannot run hg change outside default branch"
                name = "new"
                cl = CL("new")
                dirty[cl] = True
@@ -1154,7 +1157,9 @@ def clpatch(ui, repo, clname, **opts):
        Submitting an imported patch will keep the original author's
        name as the Author: line but add your own name to a Committer: line.
        """
-       return clpatch_or_undo(ui, repo, clname, opts)
+       if repo[None].branch() != "default":
+               return "cannot run hg clpatch outside default branch"
+       return clpatch_or_undo(ui, repo, clname, opts, mode="clpatch")
 
 def undo(ui, repo, clname, **opts):
        """undo the effect of a CL
@@ -1163,7 +1168,66 @@ def undo(ui, repo, clname, **opts):
        After creating the CL, opens the CL text for editing so that
        you can add the reason for the undo to the description.
        """
-       return clpatch_or_undo(ui, repo, clname, opts, undo=True)
+       if repo[None].branch() != "default":
+               return "cannot run hg undo outside default branch"
+       return clpatch_or_undo(ui, repo, clname, opts, mode="undo")
+
+def release_apply(ui, repo, clname, **opts):
+       """apply a CL to the release branch
+
+       Creates a new CL copying a previously committed change
+       from the main branch to the release branch.
+       The current client must either be clean or already be in
+       the release branch.
+       
+       The release branch must be created by starting with a
+       clean client, disabling the code review plugin, and running:
+       
+               hg update weekly.YYYY-MM-DD
+               hg branch release-branch.rNN
+               hg commit -m 'create release-branch.rNN'
+               hg push --new-branch
+       
+       Then re-enable the code review plugin.
+       
+       People can test the release branch by running
+       
+               hg update release-branch.rNN
+       
+       in a clean client.  To return to the normal tree,
+       
+               hg update default
+       
+       Move changes since the weekly into the release branch 
+       using hg release-apply followed by the usual code review
+       process and hg submit.
+
+       When it comes time to tag the release, record the
+       final long-form tag of the release-branch.rNN
+       in the *default* branch's .hgtags file.  That is, run
+       
+               hg update default
+       
+       and then edit .hgtags as you would for a weekly.
+               
+       """
+       c = repo[None]
+       if not releaseBranch:
+               return "no active release branches"
+       if c.branch() != releaseBranch:
+               if c.modified() or c.added() or c.removed():
+                       raise util.Abort("uncommitted local changes - cannot switch branches")
+               err = hg.clean(repo, releaseBranch)
+               if err:
+                       return err
+       try:
+               err = clpatch_or_undo(ui, repo, clname, opts, mode="backport")
+               if err:
+                       raise util.Abort(err)
+       except Exception, e:
+               hg.clean(repo, "default")
+               raise e
+       return None
 
 def rev2clname(rev):
        # Extract CL name from revision description.
@@ -1185,15 +1249,24 @@ undoFooter = """
 »»»
 """
 
+backportHeader = """[%s] %s
+
+««« CL %s / %s
+"""
+
+backportFooter = """
+»»»
+"""
+
 # Implementation of clpatch/undo.
-def clpatch_or_undo(ui, repo, clname, opts, undo=False):
+def clpatch_or_undo(ui, repo, clname, opts, mode):
        if missing_codereview:
                return missing_codereview
 
-       if undo:
+       if mode == "undo" or mode == "backport":
                if hgversion < '1.4':
                        # Don't have cmdutil.match (see implementation of sync command).
-                       return "hg is too old to run hg undo - update to 1.4 or newer"
+                       return "hg is too old to run hg %s - update to 1.4 or newer" % mode
 
                # Find revision in Mercurial repository.
                # Assume CL number is 7+ decimal digits.
@@ -1227,8 +1300,19 @@ def clpatch_or_undo(ui, repo, clname, opts, undo=False):
                # Create fresh CL and start with patch that would reverse the change.
                vers = short(rev.node())
                cl = CL("new")
-               cl.desc = (undoHeader % (clname, vers)) + rev.description() + undoFooter
-               patch = RunShell(["hg", "diff", "--git", "-r", vers + ":" + short(rev.parents()[0].node())])
+               desc = rev.description()
+               if mode == "undo":
+                       cl.desc = (undoHeader % (clname, vers)) + desc + undoFooter
+               else:
+                       cl.desc = (backportHeader % (releaseBranch, line1(desc), clname, vers)) + desc + undoFooter
+               v1 = vers
+               v0 = short(rev.parents()[0].node())
+               if mode == "undo":
+                       arg = v1 + ":" + v0
+               else:
+                       vers = v0
+                       arg = v0 + ":" + v1
+               patch = RunShell(["hg", "diff", "--git", "-r", arg])
 
        else:  # clpatch
                cl, vers, patch, err = DownloadCL(ui, repo, clname)
@@ -1249,10 +1333,10 @@ def clpatch_or_undo(ui, repo, clname, opts, undo=False):
        if id != vers:
                patch, err = portPatch(repo, patch, vers, id)
                if err != "":
-                       return "codereview issue %s is out of date: %s" % (clname, err)
+                       return "codereview issue %s is out of date: %s (%s->%s)" % (clname, err, vers, id)
 
        argv = ["hgpatch"]
-       if opts["no_incoming"]:
+       if opts["no_incoming"] or mode == "backport":
                argv += ["--checksync=false"]
        try:
                cmd = subprocess.Popen(argv, shell=False, stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=None, close_fds=sys.platform != "win32")
@@ -1271,7 +1355,7 @@ def clpatch_or_undo(ui, repo, clname, opts, undo=False):
        if extra:
                ui.warn("warning: these files were listed in the patch but not changed:\n\t" + "\n\t".join(extra) + "\n")
        cl.Flush(ui, repo)
-       if undo:
+       if mode == "undo":
                err = EditCL(ui, repo, cl)
                if err != "":
                        return "CL created, but error editing: " + err
@@ -1506,7 +1590,7 @@ def reposetup(ui, repo):
 
 def CheckContributor(ui, repo, user=None):
        set_status("checking CONTRIBUTORS file")
-       _, userline = FindContributor(ui, repo, user, warn=False)
+       user, userline = FindContributor(ui, repo, user, warn=False)
        if not userline:
                raise util.Abort("cannot find %s in CONTRIBUTORS" % (user,))
        return userline
@@ -1524,7 +1608,7 @@ def FindContributor(ui, repo, user=None, warn=True):
        if user not in contributors:
                if warn:
                        ui.warn("warning: cannot find %s in CONTRIBUTORS\n" % (user,))
-               return None, None
+               return user, None
        
        user, email = contributors[user]
        return email, "%s <%s>" % (user, email)
@@ -1650,6 +1734,14 @@ def submit(ui, repo, *pats, **opts):
        if not cl.copied_from:
                EditDesc(cl.name, closed=True, private=cl.private)
        cl.Delete(ui, repo)
+       
+       c = repo[None]
+       if c.branch() == releaseBranch and not c.modified() and not c.added() and not c.removed():
+               ui.write("switching from %s to default branch.\n" % releaseBranch)
+               err = hg.clean(repo, "default")
+               if err:
+                       return err
+       return None
 
 def sync(ui, repo, **opts):
        """synchronize with remote repository
@@ -1822,6 +1914,15 @@ cmdtable = {
                ] + commands.walkopts,
                "[-r reviewer] [--cc cc] [change# | file ...]"
        ),
+       "^release-apply": (
+               release_apply,
+               [
+                       ('', 'ignore_hgpatch_failure', None, 'create CL metadata even if hgpatch fails'),
+                       ('', 'no_incoming', None, 'disable check for incoming changes'),
+               ],
+               "change#"
+       ),
+       # TODO: release-start, release-tag, weekly-tag
        "^submit": (
                submit,
                review_opts + [
@@ -2263,6 +2364,19 @@ def RietveldSetup(ui, repo):
                upload_options.email = "test@example.com"
 
        rpc = None
+       
+       global releaseBranch
+       tags = repo.branchtags().keys()
+       if 'release-branch.r100' in tags:
+               # NOTE(rsc): This tags.sort is going to get the wrong
+               # answer when comparing release-branch.r99 with
+               # release-branch.r100.  If we do ten releases a year
+               # that gives us 4 years before we have to worry about this.
+               raise util.Abort('tags.sort needs to be fixed for release-branch.r100')
+       tags.sort()
+       for t in tags:
+               if t.startswith('release-branch.'):
+                       releaseBranch = t                       
 
 #######################################################################
 # http://codereview.appspot.com/static/upload.py, heavily edited.