3
Various useful plugins for working with bzr.
1
# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron@aaronbentley.com>
2
# Copyright (C) 2005, 2006 Canonical Limited.
3
# Copyright (C) 2006 Michael Ellerman.
5
# This program is free software; you can redistribute it and/or modify
6
# it under the terms of the GNU General Public License as published by
7
# the Free Software Foundation; either version 2 of the License, or
8
# (at your option) any later version.
10
# This program is distributed in the hope that it will be useful,
11
# but WITHOUT ANY WARRANTY; without even the implied warranty of
12
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13
# GNU General Public License for more details.
15
# You should have received a copy of the GNU General Public License
16
# along with this program; if not, write to the Free Software
17
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
from bzrlib.lazy_import import lazy_import
24
lazy_import(globals(), """
25
from bzrlib import help, urlutils
29
from command import BzrToolsCommand
6
30
from errors import CommandError
7
31
from patchsource import BzrPatchSource
8
from shelf import Shelf
9
from switch import cmd_switch
13
from bzrlib import DEFAULT_IGNORE
14
35
import bzrlib.builtins
16
36
import bzrlib.commands
37
from bzrlib.branch import Branch
38
from bzrlib.bzrdir import BzrDir
17
39
from bzrlib.commands import get_cmd_object
18
40
from bzrlib.errors import BzrCommandError
19
from bzrlib.help import command_usage
20
from bzrlib.option import Option
21
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__),
25
DEFAULT_IGNORE.append('./.shelf')
26
DEFAULT_IGNORE.append('./.bzr-shelf*')
29
class cmd_clean_tree(bzrlib.commands.Command):
30
"""Remove unwanted files from working tree.
32
By default, only unknown files, not ignored files, are deleted. Versioned
33
files are never deleted.
35
Another class is 'detritus', which includes files emitted by bzr during
36
normal operations and selftests. (The value of these files decreases with
39
If no options are specified, unknown files are deleted. Otherwise, option
40
flags are respected, and may be combined.
42
To check what clean-tree will do, use --dry-run.
44
takes_options = [Option('ignored', help='delete all ignored files.'),
45
Option('detritus', help='delete conflict files, merge'
46
' backups, and failed selftest dirs.'),
48
help='delete files unknown to bzr. (default)'),
49
Option('dry-run', help='show files to delete instead of'
51
def run(self, unknown=False, ignored=False, detritus=False, dry_run=False):
52
from clean_tree import clean_tree
53
if not (unknown or ignored or detritus):
55
clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus,
58
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):
59
50
"""Produce ancestry graphs using dot.
61
52
Output format is detected according to file extension. Some of the more
90
81
If available, rsvg is used to antialias PNG and JPEG output, but this can
91
82
be disabled with --no-antialias.
93
takes_args = ['branch', 'file']
94
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."),
95
86
Option('no-antialias',
96
help="Do not use rsvg to produce antialiased output"),
97
Option('merge-branch', type=str,
98
help="Use this branch to calcuate a merge base"),
99
Option('cluster', help="Use clustered output.")]
100
def run(self, branch, file, no_collapse=False, no_antialias=False,
101
merge_branch=None, cluster=False):
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."),
90
Option('cluster', help="Use clustered output."),
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:
104
106
ranking = "cluster"
106
108
ranking = "forced"
107
graph.write_ancestry_file(branch, file, not no_collapse,
108
not no_antialias, merge_branch, ranking)
110
class cmd_fetch_ghosts(bzrlib.commands.Command):
109
graph.write_ancestry_file(directory, file, not no_collapse,
110
not no_antialias, merge_branch, ranking,
111
max_distance=max_distance)
114
class cmd_fetch_ghosts(BzrToolsCommand):
111
115
"""Attempt to retrieve ghosts from another branch.
112
116
If the other branch is not supplied, the last-pulled branch is used.
114
118
aliases = ['fetch-missing']
115
119
takes_args = ['branch?']
116
takes_options = [Option('no-fix')]
120
takes_options = [Option('no-fix', help="Skip additional synchonization.")]
117
121
def run(self, branch=None, no_fix=False):
118
122
from fetch_ghosts import fetch_ghosts
119
fetch_ghosts(branch, no_fix)
123
fetch_ghosts(branch, do_reconcile=not no_fix)
121
125
strip_help="""Strip the smallest prefix containing num leading slashes from \
122
126
each file name found in the patch file."""
123
Option.OPTIONS['bzrdiff'] = Option('bzrdiff',type=None,
124
help="""Handle extra bzr tags""")
125
class cmd_patch(bzrlib.commands.Command):
129
class cmd_patch(BzrToolsCommand):
126
130
"""Apply a named patch to the current tree.
128
132
takes_args = ['filename?']
129
takes_options = [Option('strip', type=int, help=strip_help)]
130
def run(self, filename=None, strip=-1, bzrdiff=0):
133
takes_options = [Option('strip', type=int, help=strip_help),
134
Option('silent', help='Suppress chatter.')]
135
def run(self, filename=None, strip=None, silent=False):
131
136
from patch import patch
132
137
from bzrlib.workingtree import WorkingTree
133
138
wt = WorkingTree.open_containing('.')[0]
135
if bzrdiff: strip = 0
138
return patch(wt, filename, strip, legacy= not bzrdiff)
140
class cmd_shelve(bzrlib.commands.Command):
141
return patch(wt, filename, strip, silent)
144
class cmd_shelve1(BzrToolsCommand):
141
145
"""Temporarily set aside some changes from the current tree.
143
147
Shelve allows you to temporarily put changes you've made "on the shelf",
144
148
ie. out of the way, until a later time when you can bring them back from
145
the shelf with the 'unshelve' command.
149
the shelf with the 'unshelve1' command.
147
151
Shelve is intended to help separate several sets of text changes that have
148
152
been inappropriately mingled. If you just want to get rid of all changes
149
153
(text and otherwise) and you don't need to restore them later, use revert.
150
If you want to shelve all text changes at once, use shelve --all.
154
If you want to shelve all text changes at once, use shelve1 --all.
152
By default shelve asks you what you want to shelve, press '?' at the
153
prompt to get help. To shelve everything run shelve --all.
156
By default shelve1 asks you what you want to shelve, press '?' at the
157
prompt to get help. To shelve everything run shelve1 --all.
155
159
If filenames are specified, only the changes to those files will be
156
160
shelved, other files will be left untouched.
353
371
If --branch is specified, the branch will be deleted too, but only if the
354
372
the branch has no new commits (relative to its parent).
356
takes_options = [Option("branch", help="Remove associtated branch from"
374
takes_options = [Option("branch", help="Remove associated branch from"
376
Option('force', help='Delete tree even if contents are'
358
378
takes_args = ["checkout"]
359
def run(self, checkout, branch=False):
379
def run(self, checkout, branch=False, force=False):
360
380
from zap import zap
361
return zap(checkout, remove_branch=branch)
364
class cmd_cbranch(bzrlib.commands.Command):
381
return zap(checkout, remove_branch=branch, allow_modified=force)
384
class cmd_cbranch(BzrToolsCommand):
366
386
Create a new checkout, associated with a new repository branch.
368
When you cbranch, bzr looks up the repository associated with your current
369
directory in branches.conf. It creates a new branch in that repository
370
with the same name and relative path as the checkout you request.
372
The branches.conf parameter is "cbranch_root". So if you want
373
cbranch operations in /home/jrandom/bigproject to produce branches in
374
/home/jrandom/bigproject/repository, you'd add this:
376
[/home/jrandom/bigproject]
377
cbranch_root = /home/jrandom/bigproject/repository
379
Note that if "/home/jrandom/bigproject/repository" isn't a repository,
380
standalone branches will be produced. Standalone branches will also
381
be produced if the source branch is in 0.7 format (or earlier).
388
When you cbranch, bzr looks up a target location in locations.conf, and
389
creates the branch there.
391
In your locations.conf, add the following lines:
392
[/working_directory_root]
393
cbranch_target = /branch_root
394
cbranch_target:policy = appendpath
396
This will mean that if you run "bzr cbranch foo/bar foo/baz" in the
397
working directory root, the branch will be created in
398
"/branch_root/foo/baz"
400
NOTE: cbranch also supports "cbranch_root", but that behaviour is
383
takes_options = [Option("lightweight",
384
help="Create a lightweight checkout"), 'revision']
403
takes_options = [Option("lightweight",
404
help="Create a lightweight checkout."), 'revision',
405
Option('files-from', type=unicode,
406
help='Accelerate checkout using files from this'
409
help='Hard-link files from source/files-from tree'
385
411
takes_args = ["source", "target?"]
386
def run(self, source, target=None, lightweight=False, revision=None):
412
def run(self, source, target=None, lightweight=False, revision=None,
413
files_from=None, hardlink=False):
387
414
from cbranch import cbranch
388
return cbranch(source, target, lightweight=lightweight,
392
class cmd_branches(bzrlib.commands.Command):
415
return cbranch(source, target, lightweight=lightweight,
416
revision=revision, files_from=files_from,
420
class cmd_branches(BzrToolsCommand):
393
421
"""Scan a location for branches"""
394
422
takes_args = ["location?"]
395
423
def run(self, location=None):
396
424
from branches import branches
397
425
return branches(location)
427
class cmd_trees(BzrToolsCommand):
428
"""Scan a location for trees"""
429
takes_args = ['location?']
430
def run(self, location='.'):
431
from bzrlib.workingtree import WorkingTree
432
from bzrlib.transport import get_transport
433
t = get_transport(location)
434
for tree in WorkingTree.find_trees(location):
435
self.outf.write('%s\n' % t.relpath(
436
tree.bzrdir.root_transport.base))
400
class cmd_multi_pull(bzrlib.commands.Command):
438
class cmd_multi_pull(BzrToolsCommand):
401
439
"""Pull all the branches under a location, e.g. a repository.
403
441
Both branches present in the directory and the branches of checkouts are
406
444
takes_args = ["location?"]
407
445
def run(self, location=None):
408
from bzrlib.branch import Branch
409
446
from bzrlib.transport import get_transport
410
447
from bzrtools import iter_branch_tree
411
448
if location is None:
413
450
t = get_transport(location)
451
possible_transports = []
414
452
if not t.listable():
415
453
print "Can't list this type of location."
433
471
print "Pulling %s from %s" % (relpath, parent)
435
pullable.pull(Branch.open(parent))
473
branch_t = get_transport(parent, possible_transports)
474
pullable.pull(Branch.open_from_transport(branch_t))
436
475
except Exception, e:
440
class cmd_branch_mark(bzrlib.commands.Command):
442
Add, view or list branch markers <EXPERIMENTAL>
444
To add a mark, do 'bzr branch-mark MARK'.
445
To list marks, do 'bzr branch-mark' (this lists all marks for the branch's
447
To delete a mark, do 'bzr branch-mark --delete MARK'
449
These marks can be used to track a branch's status.
451
takes_args = ['mark?', 'branch?']
452
takes_options = [Option('delete', help='Delete this mark')]
453
def run(self, mark=None, branch=None, delete=False):
454
from branch_mark import branch_mark
455
branch_mark(mark, branch, delete)
457
class cmd_import(bzrlib.commands.Command):
458
"""Import sources from a tarball
460
This command will import a tarball into a bzr tree, replacing any versioned
461
files already present. If a directory is specified, it is used as the
462
target. If the directory does not exist, or is not versioned, it is
480
class cmd_import(BzrToolsCommand):
481
"""Import sources from a directory, tarball or zip file
483
This command will import a directory, tarball or zip file into a bzr
484
tree, replacing any versioned files already present. If a directory is
485
specified, it is used as the target. If the directory does not exist, or
486
is not versioned, it is created.
465
488
Tarballs may be gzip or bzip2 compressed. This is autodetected.
467
If the tarball has a single root directory, that directory is stripped
468
when extracting the tarball.
490
If the tarball or zip has a single root directory, that directory is
491
stripped when extracting the tarball. This is not done for directories.
471
494
takes_args = ['source', 'tree?']
472
495
def run(self, source, tree=None):
473
496
from upstream_import import do_import
474
497
do_import(source, tree)
476
class cmd_shove(bzrlib.commands.Command):
477
"""Apply uncommitted changes to another tree
479
This is useful when you start to make changes in one tree, then realize
480
they should really be done in a different tree.
482
Shove is implemented using merge, so:
483
- All changes, including renames and adds, will be applied.
484
- No changes that have already been applied will be applied.
485
- If the target is significantly different from the source, conflicts may
489
takes_args = ['target', 'source?']
490
def run(self, target, source='.'):
491
from shove import do_shove
492
do_shove(source, target)
494
class cmd_cdiff(bzrlib.commands.Command):
500
class cmd_cdiff(BzrToolsCommand):
495
501
"""A color version of bzr's diff"""
496
502
takes_args = property(lambda x: get_cmd_object('diff').takes_args)
497
takes_options = property(lambda x: get_cmd_object('diff').takes_options)
498
def run(*args, **kwargs):
503
takes_options = list(get_cmd_object('diff').takes_options) + [
504
RegistryOption.from_kwargs('color',
505
'Color mode to use.',
506
title='Color Mode', value_switches=False, enum_switch=True,
507
never='Never colorize output.',
508
auto='Only colorize output if terminal supports it and STDOUT is a'
510
always='Always colorize output (default).'),
511
Option('check-style',
512
help='Warn if trailing whitespace or spurious changes have been'
515
def run(self, color='always', check_style=False, *args, **kwargs):
499
516
from colordiff import colordiff
500
colordiff(*args, **kwargs)
502
commands = [cmd_shelve, cmd_unshelve, cmd_shelf, cmd_clean_tree,
503
cmd_graph_ancestry, cmd_fetch_ghosts, cmd_patch, cmd_shell,
504
cmd_branch_history, cmd_zap, cmd_cbranch, cmd_branches,
505
cmd_multi_pull, cmd_switch, cmd_branch_mark, cmd_import, cmd_shove,
509
commands.append(rspush.cmd_rspush)
511
from errors import NoPyBaz
514
commands.append(baz_import.cmd_baz_import_branch)
515
commands.append(baz_import.cmd_baz_import)
518
class cmd_baz_import_branch(bzrlib.commands.Command):
519
"""Disabled. (Requires PyBaz)"""
520
takes_args = ['to_location?', 'from_branch?', 'reuse_history*']
521
takes_options = ['verbose', Option('max-count', type=int)]
522
def run(self, to_location=None, from_branch=None, fast=False,
523
max_count=None, verbose=False, dry_run=False,
524
reuse_history_list=[]):
525
print "This command is disabled. Please install PyBaz."
528
class cmd_baz_import(bzrlib.commands.Command):
529
"""Disabled. (Requires PyBaz)"""
530
takes_args = ['to_root_dir?', 'from_archive?', 'reuse_history*']
531
takes_options = ['verbose', Option('prefixes', type=str,
532
help="Prefixes of branches to import")]
533
def run(self, to_root_dir=None, from_archive=None, verbose=False,
534
reuse_history_list=[], prefixes=None):
535
print "This command is disabled. Please install PyBaz."
536
commands.extend((cmd_baz_import_branch, cmd_baz_import))
539
if hasattr(bzrlib.commands, 'register_command'):
540
for command in commands:
541
bzrlib.commands.register_command(command)
545
from bzrlib.tests.TestUtil import TestLoader
547
from doctest import DocTestSuite, ELLIPSIS
548
from unittest import TestSuite
549
import tests.clean_tree
550
import upstream_import
552
import tests.blackbox
553
import tests.shelf_tests
555
result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
556
result.addTest(tests.clean_tree.test_suite())
559
result.addTest(DocTestSuite(baz_import))
562
result.addTest(tests.test_suite())
563
result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
564
result.addTest(tests.blackbox.test_suite())
565
result.addTest(upstream_import.test_suite())
566
result.addTest(zap.test_suite())
517
colordiff(color, check_style, *args, **kwargs)
520
class cmd_conflict_diff(BzrToolsCommand):
522
"""Compare a conflicted file against BASE."""
524
encoding_type = 'exact'
525
takes_args = ['file']
527
RegistryOption.from_kwargs('direction', 'Direction of comparison.',
528
value_switches=True, enum_switch=False,
529
other='Compare OTHER against common base.',
530
this='Compare THIS against common base.')]
532
def run(self, file, direction='other'):
533
from bzrlib.plugins.bzrtools.colordiff import DiffWriter
534
from conflict_diff import conflict_diff
535
dw = DiffWriter(self.outf, check_style=False, color='auto')
536
conflict_diff(dw, file, direction)
539
class cmd_rspush(BzrToolsCommand):
540
"""Upload this branch to another location using rsync.
542
If no location is specified, the last-used location will be used. To
543
prevent dirty trees from being uploaded, rspush will error out if there are
544
unknown files or local changes. It will also error out if the upstream
545
directory is non-empty and not an earlier version of the branch.
547
takes_args = ['location?']
548
takes_options = [Option('overwrite', help='Ignore differences between'
549
' branches and overwrite unconditionally.'),
550
Option('no-tree', help='Do not push the working tree,'
553
def run(self, location=None, overwrite=False, no_tree=False):
554
from bzrlib import workingtree
556
cur_branch = workingtree.WorkingTree.open_containing(".")[0]
557
bzrtools.rspush(cur_branch, location, overwrite=overwrite,
558
working_tree=not no_tree)
561
class cmd_link_tree(BzrToolsCommand):
562
"""Hardlink matching files to another tree.
564
Only files with identical content and execute bit will be linked.
566
takes_args = ['location']
568
def run(self, location):
569
from bzrlib import workingtree
570
from bzrlib.plugins.bzrtools.link_tree import link_tree
571
target_tree = workingtree.WorkingTree.open_containing(".")[0]
572
source_tree = workingtree.WorkingTree.open(location)
573
target_tree.lock_write()
575
source_tree.lock_read()
577
link_tree(target_tree, source_tree)
584
class cmd_create_mirror(BzrToolsCommand):
585
"""Create a mirror of another branch.
587
This is similar to `bzr branch`, but copies more settings, including the
588
submit branch and nickname.
590
It sets the public branch and parent of the target to the source location.
593
takes_args = ['source', 'target']
595
def run(self, source, target):
596
source_branch = Branch.open(source)
597
from bzrlib.plugins.bzrtools.mirror import create_mirror
598
create_mirror(source_branch, target, [])