3 from stgit
.argparse
import opt
4 from stgit
.commands
.common
import (
12 from stgit
.config
import config
13 from stgit
.exception
import StackException
14 from stgit
.lib
import log
15 from stgit
.lib
.git
.branch
import Branch
16 from stgit
.lib
.git
.repository
import DetachedHeadException
17 from stgit
.lib
.stack
import Stack
, StackRepository
18 from stgit
.lib
.transaction
import StackTransaction
, TransactionHalted
19 from stgit
.out
import out
20 from stgit
.run
import RunException
23 Copyright (C) 2005, Chuck Lever <cel@netapp.com>
25 This program is free software; you can redistribute it and/or modify
26 it under the terms of the GNU General Public License version 2 as
27 published by the Free Software Foundation.
29 This program is distributed in the hope that it will be useful,
30 but WITHOUT ANY WARRANTY; without even the implied warranty of
31 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 GNU General Public License for more details.
34 You should have received a copy of the GNU General Public License
35 along with this program; if not, see http://www.gnu.org/licenses/.
38 help = 'Branch operations: switch, list, create, rename, delete, ...'
42 '[--merge] [--] <branch>',
44 '--create [--] <new-branch> [<committish>]',
45 '--clone [--] [<new-branch>]',
46 '--rename [--] [<old-name>] <new-name>',
47 '--protect [--] [<branch>]',
48 '--unprotect [--] [<branch>]',
49 '--delete [--force] [--] <branch>',
50 '--cleanup [--force] [--] [<branch>]',
51 '--description=<description> [--] [<branch>]',
54 Create, clone, switch between, rename, or delete development branches
55 within a git repository.
58 Display the name of the current branch.
60 'stg branch' <branch>::
61 Switch to the given branch."""
63 args
= ['all_branches']
69 short
='List the branches contained in this repository',
71 List each branch in the current repository, followed by its
72 branch description (if any). The current branch is prefixed
73 with '>'. Branches that have been initialized for StGit (with
74 linkstg:init[]) are prefixed with 's'. Protected branches are
75 prefixed with 'p'.""",
81 short
='Create (and switch to) a new branch',
83 Create (and switch to) a new branch. The new branch is already
84 initialized as an StGit patch stack, so you do not have to run
85 linkstg:init[] manually. If you give a committish argument,
86 the new branch is based there; otherwise, it is based at the
89 StGit will try to detect the branch off of which the new
90 branch is forked, as well as the remote repository from which
91 that parent branch is taken (if any), so that running
92 linkstg:pull[] will automatically pull new commits from the
93 correct branch. It will warn if it cannot guess the parent
94 branch (e.g. if you do not specify a branch name as
100 short
='Clone the contents of the current branch',
102 Clone the current branch, under the name <new-branch> if
103 specified, or using the current branch's name plus a
106 The description of the new branch is set to tell it is a clone
107 of the current branch. The parent information of the new
108 branch is copied from the current branch.""",
114 short
='Rename an existing branch',
120 short
='Prevent StGit from modifying a branch',
122 Prevent StGit from modifying a branch -- either the current
123 one, or one named on the command line.""",
129 short
='Allow StGit to modify a branch',
131 Allow StGit to modify a branch -- either the current one, or
132 one named on the command line. This undoes the effect of an
133 earlier 'stg branch --protect' command.""",
138 short
='Delete a branch',
140 Delete the named branch. If there are any patches left in the
141 branch, StGit will refuse to delete it unless you give the
144 A protected branch cannot be deleted; it must be unprotected
145 first (see '--unprotect' above).
147 If you delete the current branch, you are switched to the
148 "master" branch, if it exists.""",
153 short
='Clean up the StGit metadata for a branch',
155 Remove the StGit information for the current or given branch. If there
156 are patches left in the branch, StGit refuses the operation unless
159 A protected branch cannot be cleaned up; it must be unprotected first
160 (see '--unprotect' above).
162 A cleaned up branch can be re-initialised using the 'stg init'
168 short
='Set the branch description',
173 short
='Merge work tree changes into the other branch',
178 short
='Force a delete when the series is not empty',
182 directory
= DirectoryGotoTopLevel()
185 def __is_current_branch(branch_name
):
187 return directory
.repository
.current_branch_name
== branch_name
188 except DetachedHeadException
:
192 def __print_branch(branch_name
, length
):
193 branch
= Branch(directory
.repository
, branch_name
)
194 current
= '>' if __is_current_branch(branch_name
) else ' '
196 stack
= directory
.repository
.get_stack(branch_name
)
197 except StackException
:
198 initialised
= protected
= ' '
201 protected
= 'p' if stack
.protected
else ' '
209 + branch_name
.ljust(length
)
211 + (branch
.get_description() or '')
215 def __delete_branch(doomed_name
, force
=False):
216 if __is_current_branch(doomed_name
):
217 raise CmdException('Cannot delete the current branch')
219 branch
= Branch(directory
.repository
, doomed_name
)
221 stack
= directory
.repository
.get_stack(doomed_name
)
222 except StackException
:
227 raise CmdException('This branch is protected. Delete is not permitted')
228 if not force
and stack
.patchorder
.all
:
229 raise CmdException('Cannot delete: the series still contains patches')
231 out
.start('Deleting branch "%s"' % doomed_name
)
238 def __cleanup_branch(name
, force
=False):
239 stack
= directory
.repository
.get_stack(name
)
241 raise CmdException('This branch is protected. Clean up is not permitted')
242 if not force
and stack
.patchorder
.all
:
243 raise CmdException('Cannot clean up: the series still contains patches')
245 out
.start('Cleaning up branch "%s"' % name
)
250 def __create_branch(branch_name
, committish
):
251 repository
= directory
.repository
254 if committish
is not None:
257 branchpoint
= repository
.run(
258 ['git', 'rev-parse', '--symbolic-full-name', committish
]
261 if branchpoint
.startswith('refs/heads/') or branchpoint
.startswith(
264 # committish is a valid ref from the branchpoint setting above
265 parentbranch
= committish
269 'Do not know how to determine parent branch from "%s"' % committish
271 # exception in branch = rev_parse() leaves branchpoint unbound
274 branch_commit
= git_commit(branchpoint
or committish
, repository
)
277 out
.info('Recording "%s" as parent branch' % parentbranch
)
280 'Do not know how to determine parent branch from "%s"' % committish
284 # branch stack off current branch
285 parentbranch
= repository
.head_ref
286 except DetachedHeadException
:
290 parentremote
= config
.get('branch.%s.remote' % parentbranch
)
292 out
.info('Using remote "%s" to pull parent from' % parentremote
)
294 out
.info('Recording as a local branch')
296 # no known parent branch, can't guess the remote
299 stack
= Stack
.create(
302 create_at
=branch_commit
,
303 parent_remote
=parentremote
,
304 parent_branch
=parentbranch
,
311 def func(parser
, options
, args
):
312 repository
= directory
.repository
315 if len(args
) == 0 or len(args
) > 2:
316 parser
.error('incorrect number of arguments')
318 branch_name
= args
[0]
319 committish
= None if len(args
) < 2 else args
[1]
322 check_local_changes(repository
)
323 check_conflicts(repository
.default_iw
)
325 stack
= repository
.get_stack()
326 except (DetachedHeadException
, StackException
):
329 check_head_top_equal(stack
)
331 stack
= __create_branch(branch_name
, committish
)
333 out
.info('Branch "%s" created' % branch_name
)
334 log
.log_entry(stack
, 'branch --create %s' % stack
.name
)
339 cur_branch
= Branch(repository
, repository
.current_branch_name
)
341 clone_name
= cur_branch
.name
+ time
.strftime('-%C%y%m%d-%H%M%S')
345 parser
.error('incorrect number of arguments')
347 check_local_changes(repository
)
348 check_conflicts(repository
.default_iw
)
350 stack
= repository
.current_stack
351 except StackException
:
353 base
= repository
.refs
.get(repository
.head_ref
)
355 check_head_top_equal(stack
)
358 out
.start('Cloning current branch to "%s"' % clone_name
)
359 clone
= Stack
.create(
363 parent_remote
=cur_branch
.parent_remote
,
364 parent_branch
=cur_branch
.name
,
367 for pn
in stack
.patchorder
.all_visible
:
368 patch
= stack
.patches
.get(pn
)
369 clone
.patches
.new(pn
, patch
.commit
, 'clone %s' % stack
.name
)
370 clone
.patchorder
.set_order(
371 applied
=[], unapplied
=stack
.patchorder
.all_visible
, hidden
=[]
373 trans
= StackTransaction(clone
, 'clone')
375 for pn
in stack
.patchorder
.applied
:
377 except TransactionHalted
:
380 prefix
= 'branch.%s.' % cur_branch
.name
381 new_prefix
= 'branch.%s.' % clone
.name
382 for n
, v
in list(config
.getstartswith(prefix
)):
383 config
.set(n
.replace(prefix
, new_prefix
, 1), v
)
384 clone
.set_description('clone of "%s"' % cur_branch
.name
)
389 StackRepository
.default(), cur_branch
.name
, clone
.name
, 'branch --clone'
396 parser
.error('incorrect number of arguments')
397 __delete_branch(args
[0], options
.force
)
398 log
.delete_log(StackRepository
.default(), args
[0])
401 elif options
.cleanup
:
404 name
= repository
.current_branch_name
408 parser
.error('incorrect number of arguments')
409 __cleanup_branch(name
, options
.force
)
410 log
.delete_log(StackRepository
.default(), name
)
416 parser
.error('incorrect number of arguments')
418 branch_names
= sorted(
419 ref
.replace('refs/heads/', '', 1)
420 for ref
in repository
.refs
421 if ref
.startswith('refs/heads/') and not ref
.endswith('.stgit')
425 out
.info('Available branches:')
426 max_len
= max(len(name
) for name
in branch_names
)
427 for branch_name
in branch_names
:
428 __print_branch(branch_name
, max_len
)
430 out
.info('No branches')
433 elif options
.protect
:
436 branch_name
= repository
.current_branch_name
438 branch_name
= args
[0]
440 parser
.error('incorrect number of arguments')
443 stack
= repository
.get_stack(branch_name
)
444 except StackException
:
445 raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name
)
447 out
.start('Protecting branch "%s"' % branch_name
)
448 stack
.protected
= True
456 stack
= repository
.current_stack
459 stack
= repository
.get_stack(args
[0])
462 parser
.error('incorrect number of arguments')
464 old_name
= stack
.name
465 stack
.rename(new_name
)
467 out
.info('Renamed branch "%s" to "%s"' % (old_name
, new_name
))
470 elif options
.unprotect
:
473 branch_name
= repository
.current_branch_name
475 branch_name
= args
[0]
477 parser
.error('incorrect number of arguments')
480 stack
= repository
.get_stack(branch_name
)
481 except StackException
:
482 raise CmdException('Branch "%s" is not controlled by StGIT' % branch_name
)
484 out
.info('Unprotecting branch "%s"' % branch_name
)
485 stack
.protected
= False
490 elif options
.description
is not None:
493 branch_name
= repository
.current_branch_name
495 branch_name
= args
[0]
497 parser
.error('incorrect number of arguments')
499 Branch(repository
, branch_name
).set_description(options
.description
)
503 branch_name
= args
[0]
504 if branch_name
== repository
.current_branch_name
:
506 'Branch "%s" is already the current branch' % branch_name
509 if not options
.merge
:
510 check_local_changes(repository
)
511 check_conflicts(repository
.default_iw
)
513 stack
= repository
.get_stack()
514 except StackException
:
517 check_head_top_equal(stack
)
519 out
.start('Switching to branch "%s"' % branch_name
)
520 Branch(repository
, branch_name
).switch_to()
524 # default action: print the current branch
526 parser
.error('incorrect number of arguments')
528 out
.stdout(directory
.repository
.current_branch_name
)