16
16
# along with this program; if not, write to the Free Software
17
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
Various useful plugins for working with bzr.
26
__version__ = '0.14.0'
29
version_info = tuple(int(n) for n in __version__.split('.'))
32
def check_bzrlib_version(desired):
33
"""Check that bzrlib is compatible.
35
If version is < bzrtools version, assume incompatible.
36
If version == bzrtools version, assume completely compatible
37
If version == bzrtools version + 1, assume compatible, with deprecations
38
Otherwise, assume incompatible.
40
desired_plus = (desired[0], desired[1]+1)
41
bzrlib_version = bzrlib.version_info[:2]
42
if bzrlib_version == desired:
45
from bzrlib.trace import warning
47
# get the message out any way we can
48
from warnings import warn as warning
49
if bzrlib_version < desired:
50
warning('Installed bzr version %s is too old to be used with bzrtools'
51
' %s.' % (bzrlib.__version__, __version__))
52
# Not using BzrNewError, because it may not exist.
53
raise Exception, ('Version mismatch', version_info)
55
warning('Bzrtools is not up to date with installed bzr version %s.'
56
' \nThere should be a newer version available, e.g. %i.%i.'
57
% (bzrlib.__version__, bzrlib_version[0], bzrlib_version[1]))
58
if bzrlib_version != desired_plus:
59
raise Exception, 'Version mismatch'
62
check_bzrlib_version(version_info[:2])
65
from errors import CommandError, NoPyBaz
23
from bzrlib.lazy_import import lazy_import
24
lazy_import(globals(), """
25
from bzrlib import help, urlutils
29
from command import BzrToolsCommand
30
from errors import CommandError
66
31
from patchsource import BzrPatchSource
67
from shelf import Shelf
71
35
import bzrlib.builtins
72
36
import bzrlib.commands
37
from bzrlib.branch import Branch
38
from bzrlib.bzrdir import BzrDir
73
39
from bzrlib.commands import get_cmd_object
74
40
from bzrlib.errors import BzrCommandError
75
from bzrlib.help import command_usage
76
41
import bzrlib.ignores
77
from bzrlib.option import Option
78
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__),
83
bzrlib.ignores.add_runtime_ignores(['./.shelf'])
86
class cmd_clean_tree(bzrlib.commands.Command):
87
"""Remove unwanted files from working tree.
89
By default, only unknown files, not ignored files, are deleted. Versioned
90
files are never deleted.
92
Another class is 'detritus', which includes files emitted by bzr during
93
normal operations and selftests. (The value of these files decreases with
96
If no options are specified, unknown files are deleted. Otherwise, option
97
flags are respected, and may be combined.
99
To check what clean-tree will do, use --dry-run.
101
takes_options = [Option('ignored', help='delete all ignored files.'),
102
Option('detritus', help='delete conflict files, merge'
103
' backups, and failed selftest dirs.'),
105
help='delete files unknown to bzr. (default)'),
106
Option('dry-run', help='show files to delete instead of'
108
def run(self, unknown=False, ignored=False, detritus=False, dry_run=False):
109
from clean_tree import clean_tree
110
if not (unknown or ignored or detritus):
112
clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus,
116
class cmd_graph_ancestry(bzrlib.commands.Command):
42
from bzrlib.trace import note
43
from bzrlib.option import Option, RegistryOption
44
from bzrlib.workingtree import WorkingTree
46
from command import BzrToolsCommand
49
class cmd_graph_ancestry(BzrToolsCommand):
117
50
"""Produce ancestry graphs using dot.
119
52
Output format is detected according to file extension. Some of the more
120
53
common output formats are html, png, gif, svg, ps. An extension of '.dot'
121
54
will cause a dot graph file to be produced. HTML output has mouseovers
148
81
If available, rsvg is used to antialias PNG and JPEG output, but this can
149
82
be disabled with --no-antialias.
151
takes_args = ['branch', 'file']
152
takes_options = [Option('no-collapse', help="Do not skip simple nodes"),
84
takes_args = ['file', 'merge_branch?']
85
takes_options = [Option('no-collapse', help="Do not skip simple nodes."),
153
86
Option('no-antialias',
154
help="Do not use rsvg to produce antialiased output"),
155
Option('merge-branch', type=str,
156
help="Use this branch to calcuate a merge base"),
87
help="Do not use rsvg to produce antialiased output."),
88
Option('merge-branch', type=str,
89
help="Use this branch to calcuate a merge base."),
157
90
Option('cluster', help="Use clustered output."),
158
Option('max-distance', help="Show no nodes farther than this",
160
def run(self, branch, file, no_collapse=False, no_antialias=False,
161
merge_branch=None, cluster=False, max_distance=None):
91
Option('max-distance',
92
help="Show no nodes farther than this.", type=int),
94
help='Source branch to use (default is current'
99
def run(self, file, merge_branch=None, no_collapse=False,
100
no_antialias=False, cluster=False, max_distance=100,
102
if max_distance == -1:
164
106
ranking = "cluster"
166
108
ranking = "forced"
167
graph.write_ancestry_file(branch, file, not no_collapse,
168
not no_antialias, merge_branch, ranking,
109
graph.write_ancestry_file(directory, file, not no_collapse,
110
not no_antialias, merge_branch, ranking,
169
111
max_distance=max_distance)
172
class cmd_fetch_ghosts(bzrlib.commands.Command):
114
class cmd_fetch_ghosts(BzrToolsCommand):
173
115
"""Attempt to retrieve ghosts from another branch.
174
116
If the other branch is not supplied, the last-pulled branch is used.
176
118
aliases = ['fetch-missing']
177
119
takes_args = ['branch?']
178
takes_options = [Option('no-fix')]
120
takes_options = [Option('no-fix', help="Skip additional synchonization.")]
179
121
def run(self, branch=None, no_fix=False):
180
122
from fetch_ghosts import fetch_ghosts
181
fetch_ghosts(branch, no_fix)
123
fetch_ghosts(branch, do_reconcile=not no_fix)
183
125
strip_help="""Strip the smallest prefix containing num leading slashes from \
184
126
each file name found in the patch file."""
187
class cmd_patch(bzrlib.commands.Command):
129
class cmd_patch(BzrToolsCommand):
188
130
"""Apply a named patch to the current tree.
190
132
takes_args = ['filename?']
191
takes_options = [Option('strip', type=int, help=strip_help)]
192
def run(self, filename=None, strip=None):
133
takes_options = [Option('strip', type=int, short_name='p',
135
Option('silent', help='Suppress chatter.')]
136
def run(self, filename=None, strip=None, silent=False):
193
137
from patch import patch
194
138
from bzrlib.workingtree import WorkingTree
195
139
wt = WorkingTree.open_containing('.')[0]
196
140
if strip is None:
198
return patch(wt, filename, strip)
201
class cmd_shelve(bzrlib.commands.Command):
142
return patch(wt, filename, strip, silent)
145
class cmd_shelve1(BzrToolsCommand):
202
146
"""Temporarily set aside some changes from the current tree.
204
148
Shelve allows you to temporarily put changes you've made "on the shelf",
205
149
ie. out of the way, until a later time when you can bring them back from
206
the shelf with the 'unshelve' command.
150
the shelf with the 'unshelve1' command.
208
152
Shelve is intended to help separate several sets of text changes that have
209
153
been inappropriately mingled. If you just want to get rid of all changes
210
154
(text and otherwise) and you don't need to restore them later, use revert.
211
If you want to shelve all text changes at once, use shelve --all.
155
If you want to shelve all text changes at once, use shelve1 --all.
213
By default shelve asks you what you want to shelve, press '?' at the
214
prompt to get help. To shelve everything run shelve --all.
157
By default shelve1 asks you what you want to shelve, press '?' at the
158
prompt to get help. To shelve everything run shelve1 --all.
216
160
If filenames are specified, only the changes to those files will be
217
161
shelved, other files will be left untouched.
422
380
If --branch is specified, the branch will be deleted too, but only if the
423
381
the branch has no new commits (relative to its parent).
383
If bzr-pipeline is also installed, the --store option will store changes
384
in the branch before deleting the tree. To restore the changes, do::
386
bzr checkout --lightweight $BRANCH $CHECKOUT
387
bzr switch-pipe -d $CHECKOUT `bzr nick -d $CHECKOUT`
425
takes_options = [Option("branch", help="Remove associtated branch from"
389
takes_options = [Option("branch", help="Remove associated branch from"
391
RegistryOption('change_policy',
392
'How to handle changed files',
394
('bzrlib.plugins.bzrtools.zap',
395
'change_policy_registry'),
427
398
takes_args = ["checkout"]
428
def run(self, checkout, branch=False):
430
return zap(checkout, remove_branch=branch)
433
class cmd_cbranch(bzrlib.commands.Command):
399
def run(self, checkout, branch=False, change_policy=None):
401
change_policy_registry,
405
if change_policy is None:
406
change_policy = change_policy_registry.get()
407
if change_policy is StoreChanges:
409
import bzrlib.plugins.pipeline
411
raise BzrCommandError('--store requires bzr-pipeline.')
412
return zap(checkout, remove_branch=branch, policy=change_policy)
415
class cmd_cbranch(BzrToolsCommand):
435
417
Create a new checkout, associated with a new repository branch.
437
When you cbranch, bzr looks up the repository associated with your current
438
directory in locations.conf. It creates a new branch in that repository
439
with the same name and relative path as the checkout you request.
441
The locations.conf parameter is "cbranch_root". So if you want
442
cbranch operations in /home/jrandom/bigproject to produce branches in
443
/home/jrandom/bigproject/repository, you'd add this:
445
[/home/jrandom/bigproject]
446
cbranch_root = /home/jrandom/bigproject/repository
448
Note that if "/home/jrandom/bigproject/repository" isn't a repository,
449
standalone branches will be produced. Standalone branches will also
450
be produced if the source branch is in 0.7 format (or earlier).
419
When you cbranch, bzr looks up a target location in locations.conf, and
420
creates the branch there.
422
In your locations.conf, add the following lines:
423
[/working_directory_root]
424
cbranch_target = /branch_root
425
cbranch_target:policy = appendpath
427
This will mean that if you run "bzr cbranch foo/bar foo/baz" in the
428
working directory root, the branch will be created in
429
"/branch_root/foo/baz"
431
NOTE: cbranch also supports "cbranch_root", but that behaviour is
452
takes_options = [Option("lightweight",
453
help="Create a lightweight checkout"), 'revision']
434
takes_options = [Option("lightweight",
435
help="Create a lightweight checkout."), 'revision',
436
Option('files-from', type=unicode,
437
help='Accelerate checkout using files from this'
440
help='Hard-link files from source/files-from tree'
454
442
takes_args = ["source", "target?"]
455
def run(self, source, target=None, lightweight=False, revision=None):
443
def run(self, source, target=None, lightweight=False, revision=None,
444
files_from=None, hardlink=False):
456
445
from cbranch import cbranch
457
return cbranch(source, target, lightweight=lightweight,
461
class cmd_branches(bzrlib.commands.Command):
446
return cbranch(source, target, lightweight=lightweight,
447
revision=revision, files_from=files_from,
451
class cmd_branches(BzrToolsCommand):
462
452
"""Scan a location for branches"""
463
453
takes_args = ["location?"]
464
454
def run(self, location=None):
465
455
from branches import branches
466
456
return branches(location)
458
class cmd_trees(BzrToolsCommand):
459
"""Scan a location for trees"""
460
takes_args = ['location?']
461
def run(self, location='.'):
462
from bzrlib.workingtree import WorkingTree
463
from bzrlib.transport import get_transport
464
t = get_transport(location)
465
for tree in WorkingTree.find_trees(location):
466
self.outf.write('%s\n' % t.relpath(
467
tree.bzrdir.root_transport.base))
469
class cmd_multi_pull(bzrlib.commands.Command):
469
class cmd_multi_pull(BzrToolsCommand):
470
470
"""Pull all the branches under a location, e.g. a repository.
472
472
Both branches present in the directory and the branches of checkouts are
475
475
takes_args = ["location?"]
476
476
def run(self, location=None):
477
from bzrlib.branch import Branch
478
477
from bzrlib.transport import get_transport
479
478
from bzrtools import iter_branch_tree
480
479
if location is None:
482
481
t = get_transport(location)
482
possible_transports = []
483
483
if not t.listable():
484
484
print "Can't list this type of location."
502
502
print "Pulling %s from %s" % (relpath, parent)
504
pullable.pull(Branch.open(parent))
504
branch_t = get_transport(parent, possible_transports)
505
pullable.pull(Branch.open_from_transport(branch_t))
505
506
except Exception, e:
509
class cmd_branch_mark(bzrlib.commands.Command):
511
Add, view or list branch markers <EXPERIMENTAL>
513
To add a mark, do 'bzr branch-mark MARK'.
514
To list marks, do 'bzr branch-mark' (this lists all marks for the branch's
516
To delete a mark, do 'bzr branch-mark --delete MARK'
518
These marks can be used to track a branch's status.
520
takes_args = ['mark?', 'branch?']
521
takes_options = [Option('delete', help='Delete this mark')]
522
def run(self, mark=None, branch=None, delete=False):
523
from branch_mark import branch_mark
524
branch_mark(mark, branch, delete)
527
class cmd_import(bzrlib.commands.Command):
528
"""Import sources from a tarball or zip file
530
This command will import a tarball or zip file into a bzr tree, replacing
531
any versioned files already present. If a directory is specified, it is
532
used as the target. If the directory does not exist, or is not versioned,
511
class cmd_import(BzrToolsCommand):
512
"""Import sources from a directory, tarball or zip file
514
This command will import a directory, tarball or zip file into a bzr
515
tree, replacing any versioned files already present. If a directory is
516
specified, it is used as the target. If the directory does not exist, or
517
is not versioned, it is created.
535
519
Tarballs may be gzip or bzip2 compressed. This is autodetected.
537
If the tarball has a single root directory, that directory is stripped
538
when extracting the tarball.
521
If the tarball or zip has a single root directory, that directory is
522
stripped when extracting the tarball. This is not done for directories.
541
525
takes_args = ['source', 'tree?']
542
526
def run(self, source, tree=None):
543
527
from upstream_import import do_import
544
528
do_import(source, tree)
547
class cmd_cdiff(bzrlib.commands.Command):
531
class cmd_cdiff(BzrToolsCommand):
548
532
"""A color version of bzr's diff"""
549
533
takes_args = property(lambda x: get_cmd_object('diff').takes_args)
550
takes_options = property(lambda x: get_cmd_object('diff').takes_options)
551
def run(*args, **kwargs):
534
takes_options = list(get_cmd_object('diff').takes_options) + [
535
RegistryOption.from_kwargs('color',
536
'Color mode to use.',
537
title='Color Mode', value_switches=False, enum_switch=True,
538
never='Never colorize output.',
539
auto='Only colorize output if terminal supports it and STDOUT is a'
541
always='Always colorize output (default).'),
542
Option('check-style',
543
help='Warn if trailing whitespace or spurious changes have been'
546
def run(self, color='always', check_style=False, *args, **kwargs):
552
547
from colordiff import colordiff
553
colordiff(*args, **kwargs)
556
class cmd_baz_import(bzrlib.commands.Command):
557
"""Import an Arch or Baz archive into a bzr repository.
559
This command should be used on local archives (or mirrors) only. It is
560
quite slow on remote archives.
562
reuse_history allows you to specify any previous imports you
563
have done of different archives, which this archive has branches
564
tagged from. This will dramatically reduce the time to convert
565
the archive as it will not have to convert the history already
566
converted in that other branch.
568
If you specify prefixes, only branches whose names start with that prefix
569
will be imported. Skipped branches will be listed, so you can import any
570
branches you missed by accident. Here's an example of doing a partial
571
import from thelove@canonical.com:
572
bzr baz-import thelove thelove@canonical.com --prefixes dists:talloc-except
574
WARNING: Encoding should not be specified unless necessary, because if you
575
specify an encoding, your converted branch will not interoperate with
576
independently-converted branches, unless the other branches were converted
577
with exactly the same encoding. Any encoding recognized by Python may
578
be specified. Aliases are not detected, so 'utf_8', 'U8', 'UTF' and 'utf8'
581
takes_args = ['to_root_dir', 'from_archive', 'reuse_history*']
582
takes_options = ['verbose', Option('prefixes', type=str,
583
help="Prefixes of branches to import, colon-separated"),
584
Option('encoding', type=str,
585
help='Force encoding to specified value. See WARNING.')]
587
def run(self, to_root_dir, from_archive, encoding=None, verbose=False,
588
reuse_history_list=[], prefixes=None):
589
from errors import NoPyBaz
592
baz_import.baz_import(to_root_dir, from_archive, encoding,
593
verbose, reuse_history_list, prefixes)
595
print "This command is disabled. Please install PyBaz."
598
class cmd_baz_import_branch(bzrlib.commands.Command):
599
"""Import an Arch or Baz branch into a bzr branch.
601
WARNING: Encoding should not be specified unless necessary, because if you
602
specify an encoding, your converted branch will not interoperate with
603
independently-converted branches, unless the other branches were converted
604
with exactly the same encoding. Any encoding recognized by Python may
605
be specified. Aliases are not detected, so 'utf_8', 'U8', 'UTF' and 'utf8'
608
takes_args = ['to_location', 'from_branch?', 'reuse_history*']
609
takes_options = ['verbose', Option('max-count', type=int),
610
Option('encoding', type=str,
611
help='Force encoding to specified value. See WARNING.')]
613
def run(self, to_location, from_branch=None, fast=False, max_count=None,
614
encoding=None, verbose=False, dry_run=False,
615
reuse_history_list=[]):
616
from errors import NoPyBaz
619
baz_import.baz_import_branch(to_location, from_branch, fast,
620
max_count, verbose, encoding, dry_run,
623
print "This command is disabled. Please install PyBaz."
626
class cmd_rspush(bzrlib.commands.Command):
548
colordiff(color, check_style, *args, **kwargs)
551
class cmd_conflict_diff(BzrToolsCommand):
553
"""Compare a conflicted file against BASE."""
555
encoding_type = 'exact'
556
takes_args = ['file*']
558
RegistryOption.from_kwargs('direction', 'Direction of comparison.',
559
value_switches=True, enum_switch=False,
560
other='Compare OTHER against common base.',
561
this='Compare THIS against common base.')]
563
def run(self, file_list, direction='other'):
564
from bzrlib.plugins.bzrtools.colordiff import DiffWriter
565
from conflict_diff import ConflictDiffer
566
dw = DiffWriter(self.outf, check_style=False, color='auto')
567
ConflictDiffer().run(dw, file_list, direction)
570
class cmd_rspush(BzrToolsCommand):
627
571
"""Upload this branch to another location using rsync.
629
If no location is specified, the last-used location will be used. To
630
prevent dirty trees from being uploaded, rspush will error out if there are
631
unknown files or local changes. It will also error out if the upstream
632
directory is non-empty and not an earlier version of the branch.
573
If no location is specified, the last-used location will be used. To
574
prevent dirty trees from being uploaded, rspush will error out if there are
575
unknown files or local changes. It will also error out if the upstream
576
directory is non-empty and not an earlier version of the branch.
634
578
takes_args = ['location?']
635
579
takes_options = [Option('overwrite', help='Ignore differences between'
636
' branches and overwrite unconditionally'),
580
' branches and overwrite unconditionally.'),
637
581
Option('no-tree', help='Do not push the working tree,'
638
582
' just the .bzr.')]
641
585
from bzrlib import workingtree
643
587
cur_branch = workingtree.WorkingTree.open_containing(".")[0]
644
bzrtools.rspush(cur_branch, location, overwrite=overwrite,
588
bzrtools.rspush(cur_branch, location, overwrite=overwrite,
645
589
working_tree=not no_tree)
648
class cmd_switch(bzrlib.commands.Command):
649
"""Set the branch of a lightweight checkout and update."""
651
takes_args = ['to_location']
653
def run(self, to_location):
654
from switch import cmd_switch
655
cmd_switch().run(to_location)
660
cmd_baz_import_branch,
682
if hasattr(bzrlib.commands, 'register_command'):
683
for command in commands:
684
bzrlib.commands.register_command(command)
688
from bzrlib.tests.TestUtil import TestLoader
690
from doctest import DocTestSuite, ELLIPSIS
691
from unittest import TestSuite
693
import tests.clean_tree
694
import upstream_import
696
import tests.blackbox
697
import tests.shelf_tests
699
result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
700
result.addTest(tests.clean_tree.test_suite())
703
result.addTest(DocTestSuite(baz_import))
706
result.addTest(tests.test_suite())
707
result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
708
result.addTest(tests.blackbox.test_suite())
709
result.addTest(upstream_import.test_suite())
710
result.addTest(zap.test_suite())
592
class cmd_link_tree(BzrToolsCommand):
593
"""Hardlink matching files to another tree.
595
Only files with identical content and execute bit will be linked.
597
takes_args = ['location']
599
def run(self, location):
600
from bzrlib import workingtree
601
from bzrlib.plugins.bzrtools.link_tree import link_tree
602
target_tree = workingtree.WorkingTree.open_containing(".")[0]
603
source_tree = workingtree.WorkingTree.open(location)
604
target_tree.lock_write()
606
source_tree.lock_read()
608
link_tree(target_tree, source_tree)
615
class cmd_create_mirror(BzrToolsCommand):
616
"""Create a mirror of another branch.
618
This is similar to `bzr branch`, but copies more settings, including the
619
submit branch and nickname.
621
It sets the public branch and parent of the target to the source location.
624
takes_args = ['source', 'target']
626
def run(self, source, target):
627
source_branch = Branch.open(source)
628
from bzrlib.plugins.bzrtools.mirror import create_mirror
629
create_mirror(source_branch, target, [])