Documentation: Rename link macros
[stgit] / stgit / commands / branch.py
index a18ef2a..8748c0a 100644 (file)
@@ -1,6 +1,3 @@
-"""Branch command
-"""
-
 __copyright__ = """
 Copyright (C) 2005, Chuck Lever <cel@netapp.com>
 
@@ -18,56 +15,96 @@ along with this program; if not, write to the Free Software
 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 """
 
-import sys, os, time
-from optparse import OptionParser, make_option
-
+import sys, os, time, re
+from stgit.argparse import opt
 from stgit.commands.common import *
 from stgit.utils import *
 from stgit.out import *
-from stgit import stack, git, basedir
-
-
-help = 'manage patch stacks'
-usage = """%prog [options] branch-name [commit-id]
-
+from stgit import argparse, stack, git, basedir
+from stgit.lib import log
+
+help = 'Branch operations: switch, list, create, rename, delete, ...'
+kind = 'stack'
+usage = ['',
+         '<branch>',
+         '--list',
+         '--create <new-branch> [<committish>]',
+         '--clone [<new-branch>]',
+         '--rename <old-name> <new-name>',
+         '--protect [<branch>]',
+         '--unprotect [<branch>]',
+         '--delete [--force] <branch>',
+         '--description=<description> [<branch>]']
+description = """
 Create, clone, switch between, rename, or delete development branches
-within a git repository.  By default, a single branch called 'master'
-is always created in a new repository.  This subcommand allows you to
-manage several patch series in the same repository via GIT branches.
-
-When displaying the branches, the names can be prefixed with
-'s' (StGIT managed) or 'p' (protected).
-
-If not given any options, switch to the named branch."""
-
-directory = DirectoryHasRepository()
-options = [make_option('-c', '--create',
-                       help = 'create a new development branch',
-                       action = 'store_true'),
-           make_option('--clone',
-                       help = 'clone the contents of the current branch',
-                       action = 'store_true'),
-           make_option('--delete',
-                       help = 'delete an existing development branch',
-                       action = 'store_true'),
-           make_option('-d', '--description',
-                       help = 'set the branch description'),
-           make_option('--force',
-                       help = 'force a delete when the series is not empty',
-                       action = 'store_true'),
-           make_option('-l', '--list',
-                       help = 'list branches contained in this repository',
-                       action = 'store_true'),
-           make_option('-p', '--protect',
-                       help = 'prevent StGIT from modifying this branch',
-                       action = 'store_true'),
-           make_option('-r', '--rename',
-                       help = 'rename an existing development branch',
-                       action = 'store_true'),
-           make_option('-u', '--unprotect',
-                       help = 'allow StGIT to modify this branch',
-                       action = 'store_true')]
-
+within a git repository.
+
+'stg branch'::
+        Display the name of the current branch.
+
+'stg branch' <branch>::
+        Switch to the given branch."""
+
+args = [argparse.all_branches]
+options = [
+    opt('-l', '--list', action = 'store_true',
+        short = 'List the branches contained in this repository', long = """
+        List each branch in the current repository, followed by its
+        branch description (if any). The current branch is prefixed
+        with '>'. Branches that have been initialized for StGit (with
+        linkstg:init[]) are prefixed with 's'. Protected branches are
+        prefixed with 'p'."""),
+    opt('-c', '--create', action = 'store_true',
+        short = 'Create (and switch to) a new branch', long = """
+        Create (and switch to) a new branch. The new branch is already
+        initialized as an StGit patch stack, so you do not have to run
+        linkstg:init[] manually. If you give a committish argument,
+        the new branch is based there; otherwise, it is based at the
+        current HEAD.
+
+        StGit will try to detect the branch off of which the new
+        branch is forked, as well as the remote repository from which
+        that parent branch is taken (if any), so that running
+        linkstg:pull[] will automatically pull new commits from the
+        correct branch. It will warn if it cannot guess the parent
+        branch (e.g. if you do not specify a branch name as
+        committish)."""),
+    opt('--clone', action = 'store_true',
+        short = 'Clone the contents of the current branch', long = """
+        Clone the current branch, under the name <new-branch> if
+        specified, or using the current branch's name plus a
+        timestamp.
+
+        The description of the new branch is set to tell it is a clone
+        of the current branch. The parent information of the new
+        branch is copied from the current branch."""),
+    opt('-r', '--rename', action = 'store_true',
+        short = 'Rename an existing branch'),
+    opt('-p', '--protect', action = 'store_true',
+        short = 'Prevent StGit from modifying a branch', long = """
+        Prevent StGit from modifying a branch -- either the current
+        one, or one named on the command line."""),
+    opt('-u', '--unprotect', action = 'store_true',
+        short = 'Allow StGit to modify a branch', long = """
+        Allow StGit to modify a branch -- either the current one, or
+        one named on the command line. This undoes the effect of an
+        earlier 'stg branch --protect' command."""),
+    opt('--delete', action = 'store_true',
+        short = 'Delete a branch', long = """
+        Delete the named branch. If there are any patches left in the
+        branch, StGit will refuse to delete it unless you give the
+        '--force' flag.
+
+        A protected branch cannot be deleted; it must be unprotected
+        first (see '--unprotect' above).
+
+        If you delete the current branch, you are switched to the
+        "master" branch, if it exists."""),
+    opt('-d', '--description', short = 'Set the branch description'),
+    opt('--force', action = 'store_true',
+        short = 'Force a delete when the series is not empty')]
+
+directory = DirectoryGotoToplevel(log = False)
 
 def __is_current_branch(branch_name):
     return crt_series.get_name() == branch_name
@@ -91,16 +128,13 @@ def __print_branch(branch_name, length):
 def __delete_branch(doomed_name, force = False):
     doomed = stack.Series(doomed_name)
 
+    if __is_current_branch(doomed_name):
+        raise CmdException('Cannot delete the current branch')
     if doomed.get_protected():
         raise CmdException, 'This branch is protected. Delete is not permitted'
 
     out.start('Deleting branch "%s"' % doomed_name)
-
-    if __is_current_branch(doomed_name):
-        raise CmdException('Cannot delete the current branch')
-
     doomed.delete(force)
-
     out.done()
 
 def func(parser, options, args):
@@ -120,19 +154,15 @@ def func(parser, options, args):
             try:
                 branchpoint = git.rev_parse(args[1])
 
-                # first, look for branchpoint in well-known branch namespaces
-                for namespace in ('refs/heads/', 'remotes/'):
-                    # check if branchpoint exists in namespace
-                    try:
-                        maybehead = git.rev_parse(namespace + args[1])
-                    except git.GitException:
-                        maybehead = None
-
-                    # check if git resolved branchpoint to this namespace
-                    if maybehead and branchpoint == maybehead:
-                        # we are for sure referring to a branch
-                        parentbranch = namespace + args[1]
-
+                # parent branch?
+                head_re = re.compile('refs/(heads|remotes)/')
+                ref_re = re.compile(args[1] + '$')
+                for ref in git.all_refs():
+                    if head_re.match(ref) and ref_re.search(ref):
+                        # args[1] is a valid ref from the branchpoint
+                        # setting above
+                        parentbranch = args[1]
+                        break;
             except git.GitException:
                 # should use a more specific exception to catch only
                 # non-git refs ?
@@ -168,6 +198,7 @@ def func(parser, options, args):
                                    parent_branch = parentbranch)
 
         out.info('Branch "%s" created' % args[0])
+        log.compat_log_entry('branch --create')
         return
 
     elif options.clone:
@@ -188,6 +219,8 @@ def func(parser, options, args):
         crt_series.clone(clone)
         out.done()
 
+        log.copy_log(log.default_repo(), crt_series.get_name(), clone,
+                     'branch --clone')
         return
 
     elif options.delete:
@@ -195,6 +228,7 @@ def func(parser, options, args):
         if len(args) != 1:
             parser.error('incorrect number of arguments')
         __delete_branch(args[0], options.force)
+        log.delete_log(log.default_repo(), args[0])
         return
 
     elif options.list:
@@ -202,13 +236,16 @@ def func(parser, options, args):
         if len(args) != 0:
             parser.error('incorrect number of arguments')
 
-        branches = git.get_heads()
-        branches.sort()
+        branches = set(git.get_heads())
+        for br in set(branches):
+            m = re.match(r'^(.*)\.stgit$', br)
+            if m and m.group(1) in branches:
+                branches.remove(br)
 
         if branches:
             out.info('Available branches:')
             max_len = max([len(i) for i in branches])
-            for i in branches:
+            for i in sorted(branches):
                 __print_branch(i, max_len)
         else:
             out.info('No branches')
@@ -245,7 +282,7 @@ def func(parser, options, args):
         stack.Series(args[0]).rename(args[1])
 
         out.info('Renamed branch "%s" to "%s"' % (args[0], args[1]))
-
+        log.rename_log(log.default_repo(), args[0], args[1], 'branch --rename')
         return
 
     elif options.unprotect: