~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to command_classes.py

  • Committer: Aaron Bentley
  • Date: 2009-09-26 16:53:09 UTC
  • mfrom: (723.2.1 shell_improvement_^C)
  • Revision ID: aaron@aaronbentley.com-20090926165309-dbw4ikblf5ce63yi
Merge Benoît Pierre's ^C fix

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron.bentley@utoronto.ca>
 
1
# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron@aaronbentley.com>
2
2
# Copyright (C) 2005, 2006 Canonical Limited.
3
3
# Copyright (C) 2006 Michael Ellerman.
4
4
#
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
18
18
 
19
 
"""\
20
 
Various useful plugins for working with bzr.
21
 
"""
 
19
import errno
22
20
 
23
21
import bzrlib
24
22
 
25
 
 
26
 
__version__ = '0.18.0'
27
 
 
28
 
 
29
 
version_info = tuple(int(n) for n in __version__.split('.'))
30
 
 
31
 
 
32
 
def check_bzrlib_version(desired):
33
 
    """Check that bzrlib is compatible.
34
 
 
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.
39
 
    """
40
 
    desired_plus = (desired[0], desired[1]+1)
41
 
    bzrlib_version = bzrlib.version_info[:2]
42
 
    if bzrlib_version == desired or (bzrlib_version == desired_plus and
43
 
                                     bzrlib.version_info[3] == 'dev'):
44
 
        return
45
 
    try:
46
 
        from bzrlib.trace import warning
47
 
    except ImportError:
48
 
        # get the message out any way we can
49
 
        from warnings import warn as warning
50
 
    if bzrlib_version < desired:
51
 
        warning('Installed Bazaar version %s is too old to be used with'
52
 
                ' plugin \n'
53
 
                '"Bzrtools" %s.' % (bzrlib.__version__, __version__))
54
 
        # Not using BzrNewError, because it may not exist.
55
 
        raise Exception, ('Version mismatch', version_info)
56
 
    else:
57
 
        warning('Plugin "Bzrtools" is not up to date with installed Bazaar'
58
 
                ' version %s.\n'
59
 
                ' There should be a newer version of Bzrtools available, e.g.'
60
 
                ' %i.%i.'
61
 
                % (bzrlib.__version__, bzrlib_version[0], bzrlib_version[1]))
62
 
        if bzrlib_version != desired_plus:
63
 
            raise Exception, 'Version mismatch'
64
 
 
65
 
 
66
 
check_bzrlib_version(version_info[:2])
67
 
 
68
23
from bzrlib.lazy_import import lazy_import
69
24
lazy_import(globals(), """
70
 
from bzrlib import help
 
25
from bzrlib import help, urlutils
71
26
import shelf
72
27
""")
73
28
 
74
 
from errors import CommandError, NoPyBaz
 
29
from command import BzrToolsCommand
 
30
from errors import CommandError
75
31
from patchsource import BzrPatchSource
76
32
import sys
77
33
import os.path
78
34
 
79
35
import bzrlib.builtins
80
36
import bzrlib.commands
 
37
from bzrlib.branch import Branch
 
38
from bzrlib.bzrdir import BzrDir
81
39
from bzrlib.commands import get_cmd_object
82
40
from bzrlib.errors import BzrCommandError
83
41
import bzrlib.ignores
84
 
from bzrlib.option import Option
85
 
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__),
86
 
                                                 "external")))
87
 
 
88
 
import show_paths
89
 
 
90
 
bzrlib.ignores.add_runtime_ignores(['./.shelf'])
91
 
 
92
 
 
93
 
class cmd_clean_tree(bzrlib.commands.Command):
94
 
    """Remove unwanted files from working tree.
95
 
 
96
 
    By default, only unknown files, not ignored files, are deleted.  Versioned
97
 
    files are never deleted.
98
 
 
99
 
    Another class is 'detritus', which includes files emitted by bzr during
100
 
    normal operations and selftests.  (The value of these files decreases with
101
 
    time.)
102
 
 
103
 
    If no options are specified, unknown files are deleted.  Otherwise, option
104
 
    flags are respected, and may be combined.
105
 
 
106
 
    To check what clean-tree will do, use --dry-run.
107
 
    """
108
 
    takes_options = [Option('ignored', help='delete all ignored files.'),
109
 
                     Option('detritus', help='delete conflict files, merge'
110
 
                            ' backups, and failed selftest dirs.'),
111
 
                     Option('unknown',
112
 
                            help='delete files unknown to bzr.  (default)'),
113
 
                     Option('dry-run', help='show files to delete instead of'
114
 
                            ' deleting them.')]
115
 
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False):
116
 
        from clean_tree import clean_tree
117
 
        if not (unknown or ignored or detritus):
118
 
            unknown = True
119
 
        clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus, 
120
 
                   dry_run=dry_run)
121
 
 
122
 
 
123
 
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
 
45
 
 
46
from command import BzrToolsCommand
 
47
 
 
48
 
 
49
class cmd_graph_ancestry(BzrToolsCommand):
124
50
    """Produce ancestry graphs using dot.
125
51
    
126
52
    Output format is detected according to file extension.  Some of the more
156
82
    be disabled with --no-antialias.
157
83
    """
158
84
    takes_args = ['file', 'merge_branch?']
159
 
    takes_options = [Option('no-collapse', help="Do not skip simple nodes"),
 
85
    takes_options = [Option('no-collapse', help="Do not skip simple nodes."),
160
86
                     Option('no-antialias',
161
 
                     help="Do not use rsvg to produce antialiased output"),
 
87
                     help="Do not use rsvg to produce antialiased output."),
162
88
                     Option('merge-branch', type=str,
163
 
                     help="Use this branch to calcuate a merge base"),
 
89
                     help="Use this branch to calcuate a merge base."),
164
90
                     Option('cluster', help="Use clustered output."),
165
91
                     Option('max-distance',
166
 
                            help="Show no nodes farther than this", type=int),
 
92
                            help="Show no nodes farther than this.", type=int),
167
93
                     Option('directory',
168
94
                            help='Source branch to use (default is current'
169
 
                            ' directory)',
 
95
                            ' directory).',
170
96
                            short_name='d',
171
97
                            type=unicode),
172
98
                    ]
185
111
                                  max_distance=max_distance)
186
112
 
187
113
 
188
 
class cmd_fetch_ghosts(bzrlib.commands.Command):
 
114
class cmd_fetch_ghosts(BzrToolsCommand):
189
115
    """Attempt to retrieve ghosts from another branch.
190
116
    If the other branch is not supplied, the last-pulled branch is used.
191
117
    """
192
118
    aliases = ['fetch-missing']
193
119
    takes_args = ['branch?']
194
 
    takes_options = [Option('no-fix')]
 
120
    takes_options = [Option('no-fix', help="Skip additional synchonization.")]
195
121
    def run(self, branch=None, no_fix=False):
196
122
        from fetch_ghosts import fetch_ghosts
197
 
        fetch_ghosts(branch, no_fix)
 
123
        fetch_ghosts(branch, do_reconcile=not no_fix)
198
124
 
199
125
strip_help="""Strip the smallest prefix containing num leading slashes  from \
200
126
each file name found in the patch file."""
201
127
 
202
128
 
203
 
class cmd_patch(bzrlib.commands.Command):
 
129
class cmd_patch(BzrToolsCommand):
204
130
    """Apply a named patch to the current tree.
205
131
    """
206
132
    takes_args = ['filename?']
207
133
    takes_options = [Option('strip', type=int, help=strip_help),
208
 
                     Option('silent', help='Suppress chatter')]
 
134
                     Option('silent', help='Suppress chatter.')]
209
135
    def run(self, filename=None, strip=None, silent=False):
210
136
        from patch import patch
211
137
        from bzrlib.workingtree import WorkingTree
215
141
        return patch(wt, filename, strip, silent)
216
142
 
217
143
 
218
 
class cmd_shelve(bzrlib.commands.Command):
 
144
class cmd_shelve1(BzrToolsCommand):
219
145
    """Temporarily set aside some changes from the current tree.
220
146
 
221
147
    Shelve allows you to temporarily put changes you've made "on the shelf",
222
148
    ie. out of the way, until a later time when you can bring them back from
223
 
    the shelf with the 'unshelve' command.
 
149
    the shelf with the 'unshelve1' command.
224
150
 
225
151
    Shelve is intended to help separate several sets of text changes that have
226
152
    been inappropriately mingled.  If you just want to get rid of all changes
227
153
    (text and otherwise) and you don't need to restore them later, use revert.
228
 
    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.
229
155
 
230
 
    By default shelve asks you what you want to shelve, press '?' at the
231
 
    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.
232
158
 
233
159
    If filenames are specified, only the changes to those files will be
234
160
    shelved, other files will be left untouched.
236
162
    If a revision is specified, changes since that revision will be shelved.
237
163
 
238
164
    You can put multiple items on the shelf. Normally each time you run
239
 
    unshelve the most recently shelved changes will be reinstated. However,
 
165
    unshelve1 the most recently shelved changes will be reinstated. However,
240
166
    you can also unshelve changes in a different order by explicitly
241
 
    specifiying which changes to unshelve. This works best when the changes
 
167
    specifiying which changes to unshelve1. This works best when the changes
242
168
    don't depend on each other.
243
169
 
244
170
    While you have patches on the shelf you can view and manipulate them with
245
 
    the 'shelf' command. Run 'bzr shelf -h' for more info.
 
171
    the 'shelf1' command. Run 'bzr shelf1 -h' for more info.
246
172
    """
247
173
 
248
174
    takes_args = ['file*']
249
 
    takes_options = ['message', 'revision',
250
 
            Option('all', help='Shelve all changes without prompting'),
251
 
            Option('no-color', help='Never display changes in color')]
 
175
    takes_options = [Option('message',
 
176
            help='A message to associate with the shelved changes.',
 
177
            short_name='m', type=unicode),
 
178
            'revision',
 
179
            Option('all', help='Shelve all changes without prompting.'),
 
180
            Option('no-color', help='Never display changes in color.')]
252
181
 
253
182
    def run(self, all=False, file_list=None, message=None, revision=None,
254
183
            no_color=False):
265
194
        return 0
266
195
 
267
196
 
268
 
# The following classes are only used as subcommands for 'shelf', they're
 
197
# The following classes are only used as subcommands for 'shelf1', they're
269
198
# not to be registered directly with bzr.
270
199
 
271
200
class cmd_shelf_list(bzrlib.commands.Command):
307
236
        self.shelf.upgrade()
308
237
 
309
238
 
310
 
class cmd_shelf(bzrlib.commands.Command):
311
 
    """Perform various operations on your shelved patches. See also shelve."""
 
239
class cmd_shelf1(BzrToolsCommand):
 
240
    """Perform various operations on your shelved patches. See also shelve1."""
312
241
    takes_args = ['subcommand', 'args*']
313
242
 
314
243
    subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
373
302
        return text
374
303
 
375
304
 
376
 
class cmd_unshelve(bzrlib.commands.Command):
 
305
class cmd_unshelve1(BzrToolsCommand):
377
306
    """Restore shelved changes.
378
307
 
379
308
    By default the most recently shelved changes are restored. However if you
380
309
    specify a patch by name those changes will be restored instead.
381
310
 
382
 
    See 'shelve' for more information.
 
311
    See 'shelve1' for more information.
383
312
    """
384
313
    takes_options = [
385
 
            Option('all', help='Unshelve all changes without prompting'),
386
 
            Option('force', help='Force unshelving even if errors occur'),
387
 
            Option('no-color', help='Never display changes in color')
 
314
            Option('all', help='Unshelve all changes without prompting.'),
 
315
            Option('force', help='Force unshelving even if errors occur.'),
 
316
            Option('no-color', help='Never display changes in color.')
388
317
        ]
389
318
    takes_args = ['patch?']
390
319
    def run(self, patch=None, all=False, force=False, no_color=False):
394
323
        return 0
395
324
 
396
325
 
397
 
class cmd_shell(bzrlib.commands.Command):
 
326
class cmd_shell(BzrToolsCommand):
398
327
    """Begin an interactive shell tailored for bzr.
399
328
    Bzr commands can be used without typing bzr first, and will be run natively
400
329
    when possible.  Tab completion is tailored for bzr.  The shell prompt shows
417
346
        return shell.run_shell()
418
347
 
419
348
 
420
 
class cmd_branch_history(bzrlib.commands.Command):
 
349
class cmd_branch_history(BzrToolsCommand):
421
350
    """\
422
351
    Display the development history of a branch.
423
352
 
431
360
        return branch_history(branch)
432
361
 
433
362
 
434
 
class cmd_zap(bzrlib.commands.Command):
 
363
class cmd_zap(BzrToolsCommand):
435
364
    """\
436
365
    Remove a lightweight checkout, if it can be done safely.
437
366
 
442
371
    If --branch is specified, the branch will be deleted too, but only if the
443
372
    the branch has no new commits (relative to its parent).
444
373
    """
445
 
    takes_options = [Option("branch", help="Remove associtated branch from"
446
 
                                           " repository")]
 
374
    takes_options = [Option("branch", help="Remove associated branch from"
 
375
                                           " repository."),
 
376
                     Option('force', help='Delete tree even if contents are'
 
377
                     ' modified.')]
447
378
    takes_args = ["checkout"]
448
 
    def run(self, checkout, branch=False):
 
379
    def run(self, checkout, branch=False, force=False):
449
380
        from zap import zap
450
 
        return zap(checkout, remove_branch=branch)
451
 
 
452
 
 
453
 
class cmd_cbranch(bzrlib.commands.Command):
 
381
        return zap(checkout, remove_branch=branch, allow_modified=force)
 
382
 
 
383
 
 
384
class cmd_cbranch(BzrToolsCommand):
454
385
    """
455
386
    Create a new checkout, associated with a new repository branch.
456
387
 
470
401
    deprecated.
471
402
    """
472
403
    takes_options = [Option("lightweight",
473
 
                            help="Create a lightweight checkout"), 'revision']
 
404
                            help="Create a lightweight checkout."), 'revision',
 
405
                     Option('files-from', type=unicode,
 
406
                            help='Accelerate checkout using files from this'
 
407
                                 ' tree.'),
 
408
                     Option('hardlink',
 
409
                            help='Hard-link files from source/files-from tree'
 
410
                            ' where posible.')]
474
411
    takes_args = ["source", "target?"]
475
 
    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):
476
414
        from cbranch import cbranch
477
415
        return cbranch(source, target, lightweight=lightweight,
478
 
                       revision=revision)
479
 
 
480
 
 
481
 
class cmd_branches(bzrlib.commands.Command):
 
416
                       revision=revision, files_from=files_from,
 
417
                       hardlink=hardlink)
 
418
 
 
419
 
 
420
class cmd_branches(BzrToolsCommand):
482
421
    """Scan a location for branches"""
483
422
    takes_args = ["location?"]
484
423
    def run(self, location=None):
485
424
        from branches import branches
486
425
        return branches(location)
487
426
 
 
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))
488
437
 
489
 
class cmd_multi_pull(bzrlib.commands.Command):
 
438
class cmd_multi_pull(BzrToolsCommand):
490
439
    """Pull all the branches under a location, e.g. a repository.
491
440
 
492
441
    Both branches present in the directory and the branches of checkouts are
494
443
    """
495
444
    takes_args = ["location?"]
496
445
    def run(self, location=None):
497
 
        from bzrlib.branch import Branch
498
446
        from bzrlib.transport import get_transport
499
447
        from bzrtools import iter_branch_tree
500
448
        if location is None:
501
449
            location = '.'
502
450
        t = get_transport(location)
 
451
        possible_transports = []
503
452
        if not t.listable():
504
453
            print "Can't list this type of location."
505
454
            return 3
521
470
                relpath = base
522
471
            print "Pulling %s from %s" % (relpath, parent)
523
472
            try:
524
 
                pullable.pull(Branch.open(parent))
 
473
                branch_t = get_transport(parent, possible_transports)
 
474
                pullable.pull(Branch.open_from_transport(branch_t))
525
475
            except Exception, e:
526
476
                print e
527
477
 
528
478
 
529
 
class cmd_branch_mark(bzrlib.commands.Command):
530
 
    """
531
 
    Add, view or list branch markers <EXPERIMENTAL>
532
 
 
533
 
    To add a mark, do 'bzr branch-mark MARK'.
534
 
    To list marks, do 'bzr branch-mark' (this lists all marks for the branch's
535
 
    repository).
536
 
    To delete a mark, do 'bzr branch-mark --delete MARK'
537
 
 
538
 
    These marks can be used to track a branch's status.
539
 
    """
540
 
    takes_args = ['mark?', 'branch?']
541
 
    takes_options = [Option('delete', help='Delete this mark')]
542
 
    def run(self, mark=None, branch=None, delete=False):
543
 
        from branch_mark import branch_mark
544
 
        branch_mark(mark, branch, delete)
545
 
 
546
 
 
547
 
class cmd_import(bzrlib.commands.Command):
 
479
 
 
480
class cmd_import(BzrToolsCommand):
548
481
    """Import sources from a directory, tarball or zip file
549
482
 
550
483
    This command will import a directory, tarball or zip file into a bzr
564
497
        do_import(source, tree)
565
498
 
566
499
 
567
 
class cmd_cdiff(bzrlib.commands.Command):
 
500
class cmd_cdiff(BzrToolsCommand):
568
501
    """A color version of bzr's diff"""
569
502
    takes_args = property(lambda x: get_cmd_object('diff').takes_args)
570
 
 
571
 
    def _takes_options(self):
572
 
        options = list(get_cmd_object('diff').takes_options)
573
 
        options.append(Option('check-style',
 
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'
 
509
            ' TTY.',
 
510
            always='Always colorize output (default).'),
 
511
        Option('check-style',
574
512
            help='Warn if trailing whitespace or spurious changes have been'
575
 
                 ' added.'))
576
 
        return options
577
 
 
578
 
    takes_options = property(_takes_options)
579
 
 
580
 
    def run(self, check_style=False, *args, **kwargs):
 
513
                 ' added.')]
 
514
 
 
515
    def run(self, color='always', check_style=False, *args, **kwargs):
581
516
        from colordiff import colordiff
582
 
        colordiff(check_style, *args, **kwargs)
583
 
 
584
 
 
585
 
class cmd_baz_import(bzrlib.commands.Command):
586
 
    """Import an Arch or Baz archive into a bzr repository.
587
 
 
588
 
    This command should be used on local archives (or mirrors) only.  It is
589
 
    quite slow on remote archives.
590
 
 
591
 
    reuse_history allows you to specify any previous imports you
592
 
    have done of different archives, which this archive has branches
593
 
    tagged from. This will dramatically reduce the time to convert
594
 
    the archive as it will not have to convert the history already
595
 
    converted in that other branch.
596
 
 
597
 
    If you specify prefixes, only branches whose names start with that prefix
598
 
    will be imported.  Skipped branches will be listed, so you can import any
599
 
    branches you missed by accident.  Here's an example of doing a partial
600
 
    import from thelove@canonical.com:
601
 
    bzr baz-import thelove thelove@canonical.com --prefixes dists:talloc-except
602
 
 
603
 
    WARNING: Encoding should not be specified unless necessary, because if you
604
 
    specify an encoding, your converted branch will not interoperate with
605
 
    independently-converted branches, unless the other branches were converted
606
 
    with exactly the same encoding.  Any encoding recognized by Python may
607
 
    be specified.  Aliases are not detected, so 'utf_8', 'U8', 'UTF' and 'utf8'
608
 
    are incompatible.
609
 
    """
610
 
    takes_args = ['to_root_dir', 'from_archive', 'reuse_history*']
611
 
    takes_options = ['verbose', Option('prefixes', type=str,
612
 
                     help="Prefixes of branches to import, colon-separated"),
613
 
                     Option('encoding', type=str,
614
 
                     help='Force encoding to specified value.  See WARNING.')]
615
 
 
616
 
    def run(self, to_root_dir, from_archive, encoding=None, verbose=False,
617
 
            reuse_history_list=[], prefixes=None):
618
 
        from errors import NoPyBaz
619
 
        try:
620
 
            import baz_import
621
 
            baz_import.baz_import(to_root_dir, from_archive, encoding,
622
 
                                  verbose, reuse_history_list, prefixes)
623
 
        except NoPyBaz:
624
 
            print "This command is disabled.  Please install PyBaz."
625
 
 
626
 
 
627
 
class cmd_baz_import_branch(bzrlib.commands.Command):
628
 
    """Import an Arch or Baz branch into a bzr branch.
629
 
 
630
 
    WARNING: Encoding should not be specified unless necessary, because if you
631
 
    specify an encoding, your converted branch will not interoperate with
632
 
    independently-converted branches, unless the other branches were converted
633
 
    with exactly the same encoding.  Any encoding recognized by Python may
634
 
    be specified.  Aliases are not detected, so 'utf_8', 'U8', 'UTF' and 'utf8'
635
 
    are incompatible.
636
 
    """
637
 
    takes_args = ['to_location', 'from_branch?', 'reuse_history*']
638
 
    takes_options = ['verbose', Option('max-count', type=int),
639
 
                     Option('encoding', type=str,
640
 
                     help='Force encoding to specified value.  See WARNING.')]
641
 
 
642
 
    def run(self, to_location, from_branch=None, fast=False, max_count=None,
643
 
            encoding=None, verbose=False, dry_run=False,
644
 
            reuse_history_list=[]):
645
 
        from errors import NoPyBaz
646
 
        try:
647
 
            import baz_import
648
 
            baz_import.baz_import_branch(to_location, from_branch, fast,
649
 
                                         max_count, verbose, encoding, dry_run,
650
 
                                         reuse_history_list)
651
 
        except NoPyBaz:
652
 
            print "This command is disabled.  Please install PyBaz."
653
 
 
654
 
 
655
 
class cmd_rspush(bzrlib.commands.Command):
 
517
        colordiff(color, check_style, *args, **kwargs)
 
518
 
 
519
 
 
520
class cmd_conflict_diff(BzrToolsCommand):
 
521
 
 
522
    """Compare a conflicted file against BASE."""
 
523
 
 
524
    encoding_type = 'exact'
 
525
    takes_args = ['file']
 
526
    takes_options = [
 
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.')]
 
531
 
 
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)
 
537
 
 
538
 
 
539
class cmd_rspush(BzrToolsCommand):
656
540
    """Upload this branch to another location using rsync.
657
541
 
658
542
    If no location is specified, the last-used location will be used.  To
662
546
    """
663
547
    takes_args = ['location?']
664
548
    takes_options = [Option('overwrite', help='Ignore differences between'
665
 
                            ' branches and overwrite unconditionally'),
 
549
                            ' branches and overwrite unconditionally.'),
666
550
                     Option('no-tree', help='Do not push the working tree,'
667
551
                            ' just the .bzr.')]
668
552
 
674
558
                      working_tree=not no_tree)
675
559
 
676
560
 
677
 
class cmd_switch(bzrlib.commands.Command):
678
 
    """Set the branch of a lightweight checkout and update."""
679
 
 
680
 
    takes_args = ['to_location']
681
 
 
682
 
    def run(self, to_location):
683
 
        from switch import cmd_switch
684
 
        cmd_switch().run(to_location)
685
 
 
686
 
 
687
 
commands = [
688
 
            cmd_baz_import,
689
 
            cmd_baz_import_branch,
690
 
            cmd_branches,
691
 
            cmd_branch_history,
692
 
            cmd_branch_mark,
693
 
            cmd_cbranch,
694
 
            cmd_cdiff,
695
 
            cmd_clean_tree,
696
 
            cmd_fetch_ghosts,
697
 
            cmd_graph_ancestry,
698
 
            cmd_import,
699
 
            cmd_multi_pull,
700
 
            cmd_patch,
701
 
            cmd_rspush,
702
 
            cmd_shelf,
703
 
            cmd_shell,
704
 
            cmd_shelve,
705
 
            cmd_switch,
706
 
            cmd_unshelve,
707
 
            cmd_zap,
708
 
            ]
709
 
 
710
 
 
711
 
if hasattr(bzrlib.commands, 'register_command'):
712
 
    for command in commands:
713
 
        bzrlib.commands.register_command(command)
714
 
 
715
 
 
716
 
def test_suite():
717
 
    from bzrlib.tests.TestUtil import TestLoader
718
 
    import tests
719
 
    from doctest import DocTestSuite, ELLIPSIS
720
 
    from unittest import TestSuite
721
 
    import bzrtools
722
 
    import tests.clean_tree
723
 
    import tests.is_clean
724
 
    import tests.upstream_import
725
 
    import zap
726
 
    import tests.blackbox
727
 
    import tests.shelf_tests
728
 
    result = TestSuite()
729
 
    result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
730
 
    result.addTest(tests.clean_tree.test_suite())
731
 
    try:
732
 
        import baz_import
733
 
        result.addTest(DocTestSuite(baz_import))
734
 
    except NoPyBaz:
735
 
        pass
736
 
    result.addTest(tests.test_suite())
737
 
    result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
738
 
    result.addTest(tests.blackbox.test_suite())
739
 
    result.addTest(tests.upstream_import.test_suite())
740
 
    result.addTest(zap.test_suite())
741
 
    result.addTest(TestLoader().loadTestsFromModule(tests.is_clean))
742
 
    return result
 
561
class cmd_link_tree(BzrToolsCommand):
 
562
    """Hardlink matching files to another tree.
 
563
 
 
564
    Only files with identical content and execute bit will be linked.
 
565
    """
 
566
    takes_args = ['location']
 
567
 
 
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()
 
574
        try:
 
575
            source_tree.lock_read()
 
576
            try:
 
577
                link_tree(target_tree, source_tree)
 
578
            finally:
 
579
                source_tree.unlock()
 
580
        finally:
 
581
            target_tree.unlock()
 
582
 
 
583
 
 
584
class cmd_create_mirror(BzrToolsCommand):
 
585
    """Create a mirror of another branch.
 
586
 
 
587
    This is similar to `bzr branch`, but copies more settings, including the
 
588
    submit branch and nickname.
 
589
 
 
590
    It sets the public branch and parent of the target to the source location.
 
591
    """
 
592
 
 
593
    takes_args = ['source', 'target']
 
594
 
 
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, [])