~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to __init__.py

  • Committer: Aaron Bentley
  • Date: 2006-06-27 14:36:32 UTC
  • Revision ID: abentley@panoramicfeedback.com-20060627143632-0f4114d7b0a8d7d9
Fix zap for checkouts of branches with no parents

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
#!/usr/bin/python
 
2
"""\
 
3
Various useful plugins for working with bzr.
 
4
"""
 
5
import rspush
 
6
from errors import CommandError
 
7
from patchsource import BzrPatchSource
 
8
from shelf import Shelf
 
9
from switch import cmd_switch
 
10
import sys
 
11
import os.path
 
12
 
 
13
from bzrlib import DEFAULT_IGNORE
 
14
import bzrlib.builtins
 
15
import bzrlib.branch
 
16
import bzrlib.commands
 
17
from bzrlib.errors import BzrCommandError
 
18
from bzrlib.help import command_usage
 
19
from bzrlib.option import Option
 
20
sys.path.insert(0, os.path.realpath(os.path.join(os.path.dirname(__file__), 
 
21
                                                 "external")))
 
22
 
 
23
 
 
24
DEFAULT_IGNORE.append('./.shelf')
 
25
DEFAULT_IGNORE.append('./.bzr-shelf*')
 
26
 
 
27
 
 
28
class cmd_clean_tree(bzrlib.commands.Command):
 
29
    """Remove unwanted files from working tree.  <BZRTOOLS>
 
30
    Normally, ignored files are left alone.
 
31
    """
 
32
    takes_options = [Option('ignored', help='delete all ignored files.'), 
 
33
                     Option('detritus', help='delete conflict files merge'
 
34
                            ' backups, and failed selftest dirs.  (*.THIS, '
 
35
                            '*.BASE, *.OTHER, *~, *.tmp)'), 
 
36
                     Option('dry-run', help='show files to delete instead of'
 
37
                            ' deleting them.')]
 
38
    def run(self, ignored=False, detritus=False, dry_run=False):
 
39
        from clean_tree import clean_tree
 
40
        clean_tree('.', ignored=ignored, detritus=detritus, dry_run=dry_run)
 
41
 
 
42
class cmd_graph_ancestry(bzrlib.commands.Command):
 
43
    """Produce ancestry graphs using dot.  <BZRTOOLS>
 
44
    
 
45
    Output format is detected according to file extension.  Some of the more
 
46
    common output formats are html, png, gif, svg, ps.  An extension of '.dot'
 
47
    will cause a dot graph file to be produced.  HTML output has mouseovers
 
48
    that show the commit message.
 
49
 
 
50
    Branches are labeled r?, where ? is the revno.  If they have no revno,
 
51
    with the last 5 characters of their revision identifier are used instead.
 
52
 
 
53
    The value starting with d is "(maximum) distance from the null revision".
 
54
    
 
55
    If --merge-branch is specified, the two branches are compared and a merge
 
56
    base is selected.
 
57
    
 
58
    Legend:
 
59
    white    normal revision
 
60
    yellow   THIS  history
 
61
    red      OTHER history
 
62
    orange   COMMON history
 
63
    blue     COMMON non-history ancestor
 
64
    green    Merge base (COMMON ancestor farthest from the null revision)
 
65
    dotted   Ghost revision (missing from branch storage)
 
66
 
 
67
    Ancestry is usually collapsed by skipping revisions with a single parent
 
68
    and descendant.  The number of skipped revisions is shown on the arrow.
 
69
    This feature can be disabled with --no-collapse.
 
70
 
 
71
    By default, revisions are ordered by distance from root, but they can be
 
72
    clustered instead using --cluster.
 
73
 
 
74
    If available, rsvg is used to antialias PNG and JPEG output, but this can
 
75
    be disabled with --no-antialias.
 
76
    """
 
77
    takes_args = ['branch', 'file']
 
78
    takes_options = [Option('no-collapse', help="Do not skip simple nodes"), 
 
79
                     Option('no-antialias',
 
80
                     help="Do not use rsvg to produce antialiased output"), 
 
81
                     Option('merge-branch', type=str, 
 
82
                     help="Use this branch to calcuate a merge base"), 
 
83
                     Option('cluster', help="Use clustered output.")]
 
84
    def run(self, branch, file, no_collapse=False, no_antialias=False,
 
85
        merge_branch=None, cluster=False):
 
86
        import graph
 
87
        if cluster:
 
88
            ranking = "cluster"
 
89
        else:
 
90
            ranking = "forced"
 
91
        graph.write_ancestry_file(branch, file, not no_collapse, 
 
92
                                  not no_antialias, merge_branch, ranking)
 
93
 
 
94
class cmd_fetch_ghosts(bzrlib.commands.Command):
 
95
    """Attempt to retrieve ghosts from another branch.  <BZRTOOLS>
 
96
    If the other branch is not supplied, the last-pulled branch is used.
 
97
    """
 
98
    aliases = ['fetch-missing']
 
99
    takes_args = ['branch?']
 
100
    takes_options = [Option('no-fix')]
 
101
    def run(self, branch=None, no_fix=False):
 
102
        from fetch_ghosts import fetch_ghosts
 
103
        fetch_ghosts(branch, no_fix)
 
104
 
 
105
strip_help="""Strip the smallest prefix containing num leading slashes  from \
 
106
each file name found in the patch file."""
 
107
Option.OPTIONS['bzrdiff'] = Option('bzrdiff',type=None,
 
108
                                help="""Handle extra bzr tags""")
 
109
class cmd_patch(bzrlib.commands.Command):
 
110
    """Apply a named patch to the current tree.  <BZRTOOLS>
 
111
    """
 
112
    takes_args = ['filename?']
 
113
    takes_options = [Option('strip', type=int, help=strip_help)]
 
114
    def run(self, filename=None, strip=-1, bzrdiff=0):
 
115
        from patch import patch
 
116
        from bzrlib.workingtree import WorkingTree
 
117
        wt = WorkingTree.open_containing('.')[0]
 
118
        if strip == -1:
 
119
            if bzrdiff: strip = 0
 
120
            else:       strip = 0
 
121
 
 
122
        return patch(wt, filename, strip, legacy= not bzrdiff)
 
123
 
 
124
class cmd_shelve(bzrlib.commands.Command):
 
125
    """Temporarily set aside some changes from the current tree.  <BZRTOOLS>
 
126
 
 
127
    Shelve allows you to temporarily put changes you've made "on the shelf",
 
128
    ie. out of the way, until a later time when you can bring them back from
 
129
    the shelf with the 'unshelve' command.
 
130
 
 
131
    Shelve is intended to help separate several sets of text changes that have
 
132
    been inappropriately mingled.  If you just want to get rid of all changes
 
133
    (text and otherwise) and you don't need to restore them later, use revert.
 
134
    If you want to shelve all text changes at once, use shelve --all.
 
135
 
 
136
    By default shelve asks you what you want to shelve, press '?' at the
 
137
    prompt to get help. To shelve everything run shelve --all.
 
138
 
 
139
    If filenames are specified, only the changes to those files will be
 
140
    shelved, other files will be left untouched.
 
141
 
 
142
    If a revision is specified, changes since that revision will be shelved.
 
143
 
 
144
    You can put multiple items on the shelf. Normally each time you run
 
145
    unshelve the most recently shelved changes will be reinstated. However,
 
146
    you can also unshelve changes in a different order by explicitly
 
147
    specifiying which changes to unshelve. This works best when the changes
 
148
    don't depend on each other.
 
149
    """
 
150
 
 
151
    takes_args = ['file*']
 
152
    takes_options = ['message', 'revision',
 
153
            Option('all', help='Shelve all changes without prompting')]
 
154
 
 
155
    def run(self, all=False, file_list=None, message=None, revision=None):
 
156
        if revision is not None and revision:
 
157
            if len(revision) == 1:
 
158
                revision = revision[0]
 
159
            else:
 
160
                raise CommandError("shelve only accepts a single revision "
 
161
                                  "parameter.")
 
162
 
 
163
        source = BzrPatchSource(revision, file_list)
 
164
        s = Shelf(source.base)
 
165
        s.shelve(source, all, message)
 
166
        return 0
 
167
 
 
168
 
 
169
# The following classes are only used as subcommands for 'shelf', they're
 
170
# not to be registered directly with bzr.
 
171
 
 
172
class cmd_shelf_list(bzrlib.commands.Command):
 
173
    """List the patches on the current shelf."""
 
174
    aliases = ['list', 'ls']
 
175
    def run(self):
 
176
        self.shelf.list()
 
177
 
 
178
 
 
179
class cmd_shelf_delete(bzrlib.commands.Command):
 
180
    """Delete the patch from the current shelf."""
 
181
    aliases = ['delete', 'del']
 
182
    takes_args = ['patch']
 
183
    def run(self, patch):
 
184
        self.shelf.delete(patch)
 
185
 
 
186
 
 
187
class cmd_shelf_switch(bzrlib.commands.Command):
 
188
    """Switch to the other shelf, create it if necessary."""
 
189
    aliases = ['switch']
 
190
    takes_args = ['othershelf']
 
191
    def run(self, othershelf):
 
192
        s = Shelf(self.shelf.base, othershelf)
 
193
        s.make_default()
 
194
 
 
195
 
 
196
class cmd_shelf_show(bzrlib.commands.Command):
 
197
    """Show the contents of the specified or topmost patch."""
 
198
    aliases = ['show', 'cat', 'display']
 
199
    takes_args = ['patch?']
 
200
    def run(self, patch=None):
 
201
        self.shelf.display(patch)
 
202
 
 
203
 
 
204
class cmd_shelf_upgrade(bzrlib.commands.Command):
 
205
    """Upgrade old format shelves."""
 
206
    aliases = ['upgrade']
 
207
    def run(self):
 
208
        self.shelf.upgrade()
 
209
 
 
210
 
 
211
class cmd_shelf(bzrlib.commands.Command):
 
212
    """Perform various operations on your shelved patches. See also shelve."""
 
213
    takes_args = ['subcommand', 'args*']
 
214
 
 
215
    subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
 
216
        cmd_shelf_show, cmd_shelf_upgrade]
 
217
 
 
218
    def run(self, subcommand, args_list):
 
219
        import sys
 
220
 
 
221
        cmd = self._get_cmd_object(subcommand)
 
222
        source = BzrPatchSource()
 
223
        s = Shelf(source.base)
 
224
        cmd.shelf = s
 
225
        return cmd.run_argv_aliases(args_list)
 
226
 
 
227
    def _get_cmd_object(self, cmd_name):
 
228
        for cmd_class in self.subcommands:
 
229
            for alias in cmd_class.aliases:
 
230
                if alias == cmd_name:
 
231
                    return cmd_class()
 
232
        raise CommandError("Unknown shelf subcommand '%s'" % cmd_name)
 
233
 
 
234
    def help(self):
 
235
        text = ["%s\n\nSubcommands:\n" % self.__doc__]
 
236
 
 
237
        for cmd_class in self.subcommands:
 
238
            text.extend(self.sub_help(cmd_class) + ['\n'])
 
239
 
 
240
        return ''.join(text)
 
241
 
 
242
    def sub_help(self, cmd_class):
 
243
        text = []
 
244
        cmd_obj = cmd_class()
 
245
        indent = 2 * ' '
 
246
 
 
247
        usage = command_usage(cmd_obj)
 
248
        usage = usage.replace('bzr shelf-', '')
 
249
        text.append('%s%s\n' % (indent, usage))
 
250
 
 
251
        text.append('%s%s\n' % (2 * indent, cmd_class.__doc__))
 
252
 
 
253
        # Somewhat copied from bzrlib.help.help_on_command_options
 
254
        option_help = []
 
255
        for option_name, option in sorted(cmd_obj.options().items()):
 
256
            if option_name == 'help':
 
257
                continue
 
258
            option_help.append('%s--%s' % (3 * indent, option_name))
 
259
            if option.type is not None:
 
260
                option_help.append(' %s' % option.argname.upper())
 
261
            if option.short_name():
 
262
                option_help.append(', -%s' % option.short_name())
 
263
            option_help.append('%s%s\n' % (2 * indent, option.help))
 
264
 
 
265
        if len(option_help) > 0:
 
266
            text.append('%soptions:\n' % (2 * indent))
 
267
            text.extend(option_help)
 
268
 
 
269
        return text
 
270
 
 
271
 
 
272
 
 
273
class cmd_unshelve(bzrlib.commands.Command):
 
274
    """Restore shelved changes.  <BZRTOOLS>
 
275
 
 
276
    By default the most recently shelved changes are restored. However if you
 
277
    specify a patch by name those changes will be restored instead.
 
278
 
 
279
    See 'shelve' for more information.
 
280
    """
 
281
    takes_options = [
 
282
            Option('all', help='Unshelve all changes without prompting'),
 
283
            Option('force', help='Force unshelving even if errors occur'),
 
284
    ]
 
285
    takes_args = ['patch?']
 
286
    def run(self, patch=None, all=False, force=False):
 
287
        source = BzrPatchSource()
 
288
        s = Shelf(source.base)
 
289
        s.unshelve(source, patch, all, force)
 
290
        return 0
 
291
 
 
292
 
 
293
class cmd_shell(bzrlib.commands.Command):
 
294
    """Begin an interactive shell tailored for bzr.  <BZRTOOLS>
 
295
    Bzr commands can be used without typing bzr first, and will be run natively
 
296
    when possible.  Tab completion is tailored for bzr.  The shell prompt shows
 
297
    the branch nick, revno, and path.
 
298
 
 
299
    If it encounters any moderately complicated shell command, it will punt to
 
300
    the system shell.
 
301
 
 
302
    Example:
 
303
    $ bzr shell
 
304
    bzr bzrtools:287/> status
 
305
    modified:
 
306
      __init__.py
 
307
    bzr bzrtools:287/> status --[TAB][TAB]
 
308
    --all        --help       --revision   --show-ids
 
309
    bzr bzrtools:287/> status --
 
310
    """
 
311
    def run(self):
 
312
        import shell
 
313
        return shell.run_shell()
 
314
 
 
315
class cmd_branch_history(bzrlib.commands.Command):
 
316
    """\
 
317
    Display the development history of a branch  <BZRTOOLS>.
 
318
 
 
319
    Each different committer or branch nick is considered a different line of
 
320
    development.  Committers are treated as the same if they have the same
 
321
    name, or if they have the same email address.
 
322
    """
 
323
    takes_args = ["branch?"]
 
324
    def run(self, branch=None):
 
325
        from branchhistory import branch_history 
 
326
        return branch_history(branch)
 
327
 
 
328
 
 
329
class cmd_zap(bzrlib.commands.Command):
 
330
    """\
 
331
    Remove a checkout, if it can be done safely. <BZRTOOLS>
 
332
 
 
333
    This command will remove a checkout without losing data.  That means
 
334
    it only removes checkouts, and only if they have no uncommitted changes.
 
335
    """
 
336
    takes_options = [Option("branch", help="Remove associtated branch from"
 
337
                                           " repository")]
 
338
    takes_args = ["checkout"]
 
339
    def run(self, checkout, branch=False):
 
340
        from zap import zap
 
341
        return zap(checkout, remove_branch=branch)
 
342
 
 
343
 
 
344
class cmd_cbranch(bzrlib.commands.Command):
 
345
    """
 
346
    Create a new checkout, associated with a new repository branch. <BZRTOOLS>
 
347
    
 
348
    When you cbranch, bzr looks up the repository associated with your current
 
349
    directory in branches.conf.  It creates a new branch in that repository
 
350
    with the same name and relative path as the checkout you request.
 
351
 
 
352
    The branches.conf parameter is "cbranch_root".  So if you want 
 
353
    cbranch operations in /home/jrandom/bigproject to produce branches in 
 
354
    /home/jrandom/bigproject/repository, you'd add this:
 
355
 
 
356
    [/home/jrandom/bigproject]
 
357
    cbranch_root = /home/jrandom/bigproject/repository
 
358
 
 
359
    Note that if "/home/jrandom/bigproject/repository" isn't a repository,
 
360
    standalone branches will be produced.  Standalone branches will also
 
361
    be produced if the source branch is in 0.7 format (or earlier).
 
362
    """
 
363
    takes_options = [Option("lightweight", 
 
364
                            help="Create a lightweight checkout")]
 
365
    takes_args = ["source", "target?"]
 
366
    def run(self, source, target=None, lightweight=False):
 
367
        from cbranch import cbranch
 
368
        return cbranch(source, target, lightweight=lightweight)
 
369
 
 
370
 
 
371
class cmd_branches(bzrlib.commands.Command):
 
372
    """Scan a location for branches <BZRTOOLS>"""
 
373
    takes_args = ["location?"]
 
374
    def run(self, location=None):
 
375
        from branches import branches
 
376
        return branches(location)
 
377
 
 
378
 
 
379
class cmd_multi_pull(bzrlib.commands.Command):
 
380
    """Pull all the branches under a location, e.g. a repository. <BZRTOOLS>
 
381
    
 
382
    Both branches present in the directory and the branches of checkouts are
 
383
    pulled.
 
384
    """
 
385
    takes_args = ["location?"]
 
386
    def run(self, location=None):
 
387
        from bzrlib.branch import Branch
 
388
        from bzrlib.transport import get_transport
 
389
        from bzrtools import iter_branch_tree
 
390
        if location is None:
 
391
            location = '.'
 
392
        t = get_transport(location)
 
393
        if not t.listable():
 
394
            print "Can't list this type of location."
 
395
            return 3
 
396
        for branch, wt in iter_branch_tree(t):
 
397
            if wt is None:
 
398
                pullable = branch
 
399
            else:
 
400
                pullable = wt
 
401
            parent = branch.get_parent()
 
402
            if parent is None:
 
403
                continue
 
404
            if wt is not None:
 
405
                base = wt.basedir
 
406
            else:
 
407
                base = branch.base
 
408
            if base.startswith(t.base):
 
409
                relpath = base[len(t.base):].rstrip('/')
 
410
            else:
 
411
                relpath = base
 
412
            print "Pulling %s from %s" % (relpath, parent)
 
413
            try:
 
414
                pullable.pull(Branch.open(parent))
 
415
            except Exception, e:
 
416
                print e
 
417
 
 
418
 
 
419
class cmd_branch_mark(bzrlib.commands.Command):
 
420
    """
 
421
    Add, view or list branch markers <EXPERIMENTAL>
 
422
 
 
423
    To add a mark, do 'bzr branch-mark MARK'.
 
424
    To list marks, do 'bzr branch-mark' (this lists all marks for the branch's
 
425
    repository).
 
426
    To delete a mark, do 'bzr branch-mark --delete MARK'
 
427
 
 
428
    These marks can be used to track a branch's status.
 
429
    """
 
430
    takes_args = ['mark?', 'branch?']
 
431
    takes_options = [Option('delete', help='Delete this mark')]
 
432
    def run(self, mark=None, branch=None, delete=False):
 
433
        from branch_mark import branch_mark
 
434
        branch_mark(mark, branch, delete)
 
435
 
 
436
class cmd_import(bzrlib.commands.Command):
 
437
    """Import sources from a tarball <BZRTOOLS>
 
438
    
 
439
    This command will import a tarball into a bzr tree, replacing any versioned
 
440
    files already present.  If a directory is specified, it is used as the
 
441
    target.  If the directory does not exist, or is not versioned, it is
 
442
    created.
 
443
 
 
444
    Tarballs may be gzip or bzip2 compressed.  This is autodetected.
 
445
 
 
446
    If the tarball has a single root directory, that directory is stripped
 
447
    when extracting the tarball.
 
448
    """
 
449
    
 
450
    takes_args = ['source', 'tree?']
 
451
    def run(self, source, tree=None):
 
452
        from upstream_import import do_import
 
453
        do_import(source, tree)
 
454
 
 
455
class cmd_shove(bzrlib.commands.Command):
 
456
    """Apply uncommitted changes to another tree <BZRTOOLS>
 
457
    
 
458
    This is useful when you start to make changes in one tree, then realize
 
459
    they should really be done in a different tree.
 
460
 
 
461
    Shove is implemented using merge, so:
 
462
     - All changes, including renames and adds, will be applied.
 
463
     - No changes that have already been applied will be applied.
 
464
     - If the target is significantly different from the source, conflicts may
 
465
       be produced.
 
466
    """
 
467
 
 
468
    takes_args = ['source?', 'target']
 
469
    def run(self, target, source='.'):
 
470
        from shove import do_shove
 
471
        do_shove(source, target)
 
472
 
 
473
class cmd_cdiff(bzrlib.commands.Command):
 
474
    """A color version of bzr's diff <BZRTOOLS>"""
 
475
    takes_args = bzrlib.builtins.cmd_diff.takes_args
 
476
    takes_options = bzrlib.builtins.cmd_diff.takes_options
 
477
    def run(*args, **kwargs):
 
478
        from colordiff import colordiff
 
479
        colordiff(*args, **kwargs)
 
480
 
 
481
commands = [cmd_shelve, cmd_unshelve, cmd_shelf, cmd_clean_tree,
 
482
            cmd_graph_ancestry, cmd_fetch_ghosts, cmd_patch, cmd_shell,
 
483
            cmd_branch_history, cmd_zap, cmd_cbranch, cmd_branches, 
 
484
            cmd_multi_pull, cmd_switch, cmd_branch_mark, cmd_import, cmd_shove,
 
485
            cmd_cdiff]
 
486
 
 
487
 
 
488
commands.append(rspush.cmd_rspush)
 
489
 
 
490
from errors import NoPyBaz
 
491
try:
 
492
    import baz_import
 
493
    commands.append(baz_import.cmd_baz_import_branch)
 
494
    commands.append(baz_import.cmd_baz_import)
 
495
 
 
496
except NoPyBaz:
 
497
    class cmd_baz_import_branch(bzrlib.commands.Command):
 
498
        """Disabled. (Requires PyBaz)   <BZRTOOLS>"""
 
499
        takes_args = ['to_location?', 'from_branch?', 'reuse_history*']
 
500
        takes_options = ['verbose', Option('max-count', type=int)]
 
501
        def run(self, to_location=None, from_branch=None, fast=False, 
 
502
                max_count=None, verbose=False, dry_run=False,
 
503
                reuse_history_list=[]):
 
504
            print "This command is disabled.  Please install PyBaz."
 
505
 
 
506
 
 
507
    class cmd_baz_import(bzrlib.commands.Command):
 
508
        """Disabled. (Requires PyBaz)   <BZRTOOLS>"""
 
509
        takes_args = ['to_root_dir?', 'from_archive?', 'reuse_history*']
 
510
        takes_options = ['verbose', Option('prefixes', type=str,
 
511
                         help="Prefixes of branches to import")]
 
512
        def run(self, to_root_dir=None, from_archive=None, verbose=False,
 
513
                reuse_history_list=[], prefixes=None):
 
514
                print "This command is disabled.  Please install PyBaz."
 
515
    commands.extend((cmd_baz_import_branch, cmd_baz_import))
 
516
 
 
517
 
 
518
if hasattr(bzrlib.commands, 'register_command'):
 
519
    for command in commands:
 
520
        bzrlib.commands.register_command(command)
 
521
 
 
522
 
 
523
def test_suite():
 
524
    from bzrlib.tests.TestUtil import TestLoader
 
525
    import tests
 
526
    from doctest import DocTestSuite, ELLIPSIS
 
527
    from unittest import TestSuite
 
528
    import tests.clean_tree
 
529
    import upstream_import
 
530
    import zap
 
531
    import tests.blackbox
 
532
    import tests.shelf_tests
 
533
    result = TestSuite()
 
534
    result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
 
535
    result.addTest(tests.clean_tree.test_suite())
 
536
    try:
 
537
        import baz_import
 
538
        result.addTest(DocTestSuite(baz_import))
 
539
    except NoPyBaz:
 
540
        pass
 
541
    result.addTest(tests.test_suite())
 
542
    result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
 
543
    result.addTest(tests.blackbox.test_suite())
 
544
    result.addTest(upstream_import.test_suite())
 
545
    result.addTest(zap.test_suite())
 
546
    return result