~abentley/bzrtools/bzrtools.dev

« back to all changes in this revision

Viewing changes to __init__.py

  • Committer: Aaron Bentley
  • Date: 2008-09-12 15:10:58 UTC
  • Revision ID: aaron@aaronbentley.com-20080912151058-23mk98rkpt0ojc4g
Update target for release script

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
#!/usr/bin/python
2
 
"""Shelf - temporarily set aside changes, then bring them back."""
3
 
 
4
 
try:
5
 
    import bzrlib.commands
6
 
    import bzr_shelf
7
 
 
8
 
    def test_suite():
9
 
        from bzrlib.tests.TestUtil import TestLoader
10
 
        import tests
11
 
        return TestLoader().loadTestsFromModule(tests)
12
 
 
13
 
except ImportError:
14
 
    raise Exception("Bzr not found!")
15
 
 
16
 
 
 
1
# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron@aaronbentley.com>
 
2
# Copyright (C) 2005, 2006 Canonical Limited.
 
3
# Copyright (C) 2006 Michael Ellerman.
 
4
#
 
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.
 
9
#
 
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.
 
14
#
 
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
 
18
 
 
19
"""\
 
20
Various useful plugins for working with bzr.
 
21
"""
 
22
 
 
23
import bzrlib
 
24
 
 
25
from bzrlib.lazy_import import lazy_import
 
26
lazy_import(globals(), """
 
27
from bzrlib import help, urlutils
 
28
import shelf
 
29
""")
 
30
 
 
31
from version import version_info, __version__
 
32
from command import BzrToolsCommand
 
33
from errors import CommandError
 
34
from patchsource import BzrPatchSource
 
35
import sys
 
36
import os.path
 
37
 
 
38
import bzrlib.builtins
 
39
import bzrlib.commands
 
40
from bzrlib.branch import Branch
 
41
from bzrlib.bzrdir import BzrDir
 
42
from bzrlib.commands import get_cmd_object
 
43
from bzrlib.errors import BzrCommandError
 
44
import bzrlib.ignores
 
45
from bzrlib.trace import note
 
46
from bzrlib.option import Option
 
47
 
 
48
from command import BzrToolsCommand
 
49
 
 
50
bzrlib.ignores.add_runtime_ignores(['./.shelf'])
 
51
 
 
52
 
 
53
class cmd_clean_tree(BzrToolsCommand):
 
54
    """Remove unwanted files from working tree.
 
55
 
 
56
    By default, only unknown files, not ignored files, are deleted.  Versioned
 
57
    files are never deleted.
 
58
 
 
59
    Another class is 'detritus', which includes files emitted by bzr during
 
60
    normal operations and selftests.  (The value of these files decreases with
 
61
    time.)
 
62
 
 
63
    If no options are specified, unknown files are deleted.  Otherwise, option
 
64
    flags are respected, and may be combined.
 
65
 
 
66
    To check what clean-tree will do, use --dry-run.
 
67
    """
 
68
    takes_options = [Option('ignored', help='Delete all ignored files.'),
 
69
                     Option('detritus', help='Delete conflict files, merge'
 
70
                            ' backups, and failed selftest dirs.'),
 
71
                     Option('unknown',
 
72
                            help='Delete files unknown to bzr (default).'),
 
73
                     Option('dry-run', help='Show files to delete instead of'
 
74
                            ' deleting them.')]
 
75
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False):
 
76
        from clean_tree import clean_tree
 
77
        if not (unknown or ignored or detritus):
 
78
            unknown = True
 
79
        clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus, 
 
80
                   dry_run=dry_run)
 
81
 
 
82
 
 
83
class cmd_graph_ancestry(BzrToolsCommand):
 
84
    """Produce ancestry graphs using dot.
 
85
    
 
86
    Output format is detected according to file extension.  Some of the more
 
87
    common output formats are html, png, gif, svg, ps.  An extension of '.dot'
 
88
    will cause a dot graph file to be produced.  HTML output has mouseovers
 
89
    that show the commit message.
 
90
 
 
91
    Branches are labeled r?, where ? is the revno.  If they have no revno,
 
92
    with the last 5 characters of their revision identifier are used instead.
 
93
 
 
94
    The value starting with d is "(maximum) distance from the null revision".
 
95
 
 
96
    If --merge-branch is specified, the two branches are compared and a merge
 
97
    base is selected.
 
98
 
 
99
    Legend:
 
100
    white    normal revision
 
101
    yellow   THIS  history
 
102
    red      OTHER history
 
103
    orange   COMMON history
 
104
    blue     COMMON non-history ancestor
 
105
    green    Merge base (COMMON ancestor farthest from the null revision)
 
106
    dotted   Ghost revision (missing from branch storage)
 
107
 
 
108
    Ancestry is usually collapsed by skipping revisions with a single parent
 
109
    and descendant.  The number of skipped revisions is shown on the arrow.
 
110
    This feature can be disabled with --no-collapse.
 
111
 
 
112
    By default, revisions are ordered by distance from root, but they can be
 
113
    clustered instead using --cluster.
 
114
 
 
115
    If available, rsvg is used to antialias PNG and JPEG output, but this can
 
116
    be disabled with --no-antialias.
 
117
    """
 
118
    takes_args = ['file', 'merge_branch?']
 
119
    takes_options = [Option('no-collapse', help="Do not skip simple nodes."),
 
120
                     Option('no-antialias',
 
121
                     help="Do not use rsvg to produce antialiased output."),
 
122
                     Option('merge-branch', type=str,
 
123
                     help="Use this branch to calcuate a merge base."),
 
124
                     Option('cluster', help="Use clustered output."),
 
125
                     Option('max-distance',
 
126
                            help="Show no nodes farther than this.", type=int),
 
127
                     Option('directory',
 
128
                            help='Source branch to use (default is current'
 
129
                            ' directory).',
 
130
                            short_name='d',
 
131
                            type=unicode),
 
132
                    ]
 
133
    def run(self, file, merge_branch=None, no_collapse=False,
 
134
            no_antialias=False, cluster=False, max_distance=100,
 
135
            directory='.'):
 
136
        if max_distance == -1:
 
137
            max_distance = None
 
138
        import graph
 
139
        if cluster:
 
140
            ranking = "cluster"
 
141
        else:
 
142
            ranking = "forced"
 
143
        graph.write_ancestry_file(directory, file, not no_collapse,
 
144
                                  not no_antialias, merge_branch, ranking,
 
145
                                  max_distance=max_distance)
 
146
 
 
147
 
 
148
class cmd_fetch_ghosts(BzrToolsCommand):
 
149
    """Attempt to retrieve ghosts from another branch.
 
150
    If the other branch is not supplied, the last-pulled branch is used.
 
151
    """
 
152
    aliases = ['fetch-missing']
 
153
    takes_args = ['branch?']
 
154
    takes_options = [Option('no-fix', help="Skip additional synchonization.")]
 
155
    def run(self, branch=None, no_fix=False):
 
156
        from fetch_ghosts import fetch_ghosts
 
157
        fetch_ghosts(branch, no_fix)
 
158
 
 
159
strip_help="""Strip the smallest prefix containing num leading slashes  from \
 
160
each file name found in the patch file."""
 
161
 
 
162
 
 
163
class cmd_patch(BzrToolsCommand):
 
164
    """Apply a named patch to the current tree.
 
165
    """
 
166
    takes_args = ['filename?']
 
167
    takes_options = [Option('strip', type=int, help=strip_help),
 
168
                     Option('silent', help='Suppress chatter.')]
 
169
    def run(self, filename=None, strip=None, silent=False):
 
170
        from patch import patch
 
171
        from bzrlib.workingtree import WorkingTree
 
172
        wt = WorkingTree.open_containing('.')[0]
 
173
        if strip is None:
 
174
            strip = 0
 
175
        return patch(wt, filename, strip, silent)
 
176
 
 
177
 
 
178
class cmd_shelve(BzrToolsCommand):
 
179
    """Temporarily set aside some changes from the current tree.
 
180
 
 
181
    Shelve allows you to temporarily put changes you've made "on the shelf",
 
182
    ie. out of the way, until a later time when you can bring them back from
 
183
    the shelf with the 'unshelve' command.
 
184
 
 
185
    Shelve is intended to help separate several sets of text changes that have
 
186
    been inappropriately mingled.  If you just want to get rid of all changes
 
187
    (text and otherwise) and you don't need to restore them later, use revert.
 
188
    If you want to shelve all text changes at once, use shelve --all.
 
189
 
 
190
    By default shelve asks you what you want to shelve, press '?' at the
 
191
    prompt to get help. To shelve everything run shelve --all.
 
192
 
 
193
    If filenames are specified, only the changes to those files will be
 
194
    shelved, other files will be left untouched.
 
195
 
 
196
    If a revision is specified, changes since that revision will be shelved.
 
197
 
 
198
    You can put multiple items on the shelf. Normally each time you run
 
199
    unshelve the most recently shelved changes will be reinstated. However,
 
200
    you can also unshelve changes in a different order by explicitly
 
201
    specifiying which changes to unshelve. This works best when the changes
 
202
    don't depend on each other.
 
203
 
 
204
    While you have patches on the shelf you can view and manipulate them with
 
205
    the 'shelf' command. Run 'bzr shelf -h' for more info.
 
206
    """
 
207
 
 
208
    takes_args = ['file*']
 
209
    takes_options = [Option('message',
 
210
            help='A message to associate with the shelved changes.',
 
211
            short_name='m', type=unicode),
 
212
            'revision',
 
213
            Option('all', help='Shelve all changes without prompting.'),
 
214
            Option('no-color', help='Never display changes in color.')]
 
215
 
 
216
    def run(self, all=False, file_list=None, message=None, revision=None,
 
217
            no_color=False):
 
218
        if revision is not None and revision:
 
219
            if len(revision) == 1:
 
220
                revision = revision[0]
 
221
            else:
 
222
                raise CommandError("shelve only accepts a single revision "
 
223
                                  "parameter.")
 
224
 
 
225
        source = BzrPatchSource(revision, file_list)
 
226
        s = shelf.Shelf(source.base)
 
227
        s.shelve(source, all, message, no_color)
 
228
        return 0
 
229
 
 
230
 
 
231
# The following classes are only used as subcommands for 'shelf', they're
 
232
# not to be registered directly with bzr.
 
233
 
 
234
class cmd_shelf_list(bzrlib.commands.Command):
 
235
    """List the patches on the current shelf."""
 
236
    aliases = ['list', 'ls']
 
237
    def run(self):
 
238
        self.shelf.list()
 
239
 
 
240
 
 
241
class cmd_shelf_delete(bzrlib.commands.Command):
 
242
    """Delete the patch from the current shelf."""
 
243
    aliases = ['delete', 'del']
 
244
    takes_args = ['patch']
 
245
    def run(self, patch):
 
246
        self.shelf.delete(patch)
 
247
 
 
248
 
 
249
class cmd_shelf_switch(bzrlib.commands.Command):
 
250
    """Switch to the other shelf, create it if necessary."""
 
251
    aliases = ['switch']
 
252
    takes_args = ['othershelf']
 
253
    def run(self, othershelf):
 
254
        s = shelf.Shelf(self.shelf.base, othershelf)
 
255
        s.make_default()
 
256
 
 
257
 
 
258
class cmd_shelf_show(bzrlib.commands.Command):
 
259
    """Show the contents of the specified or topmost patch."""
 
260
    aliases = ['show', 'cat', 'display']
 
261
    takes_args = ['patch?']
 
262
    def run(self, patch=None):
 
263
        self.shelf.display(patch)
 
264
 
 
265
 
 
266
class cmd_shelf_upgrade(bzrlib.commands.Command):
 
267
    """Upgrade old format shelves."""
 
268
    aliases = ['upgrade']
 
269
    def run(self):
 
270
        self.shelf.upgrade()
 
271
 
 
272
 
 
273
class cmd_shelf(BzrToolsCommand):
 
274
    """Perform various operations on your shelved patches. See also shelve."""
 
275
    takes_args = ['subcommand', 'args*']
 
276
 
 
277
    subcommands = [cmd_shelf_list, cmd_shelf_delete, cmd_shelf_switch,
 
278
        cmd_shelf_show, cmd_shelf_upgrade]
 
279
 
 
280
    def run(self, subcommand, args_list):
 
281
        import sys
 
282
 
 
283
        if args_list is None:
 
284
            args_list = []
 
285
        cmd = self._get_cmd_object(subcommand)
 
286
        source = BzrPatchSource()
 
287
        s = shelf.Shelf(source.base)
 
288
        cmd.shelf = s
 
289
 
 
290
        if args_list is None:
 
291
            args_list = []
 
292
        return cmd.run_argv_aliases(args_list)
 
293
 
 
294
    def _get_cmd_object(self, cmd_name):
 
295
        for cmd_class in self.subcommands:
 
296
            for alias in cmd_class.aliases:
 
297
                if alias == cmd_name:
 
298
                    return cmd_class()
 
299
        raise CommandError("Unknown shelf subcommand '%s'" % cmd_name)
 
300
 
 
301
    def help(self):
 
302
        text = ["%s\n\nSubcommands:\n" % self.__doc__]
 
303
 
 
304
        for cmd_class in self.subcommands:
 
305
            text.extend(self.sub_help(cmd_class) + ['\n'])
 
306
 
 
307
        return ''.join(text)
 
308
 
 
309
    def sub_help(self, cmd_class):
 
310
        text = []
 
311
        cmd_obj = cmd_class()
 
312
        indent = 2 * ' '
 
313
 
 
314
        usage = cmd_obj._usage()
 
315
        usage = usage.replace('bzr shelf-', '')
 
316
        text.append('%s%s\n' % (indent, usage))
 
317
 
 
318
        text.append('%s%s\n' % (2 * indent, cmd_class.__doc__))
 
319
 
 
320
        # Somewhat copied from bzrlib.help.help_on_command_options
 
321
        option_help = []
 
322
        for option_name, option in sorted(cmd_obj.options().items()):
 
323
            if option_name == 'help':
 
324
                continue
 
325
            option_help.append('%s--%s' % (3 * indent, option_name))
 
326
            if option.type is not None:
 
327
                option_help.append(' %s' % option.argname.upper())
 
328
            if option.short_name():
 
329
                option_help.append(', -%s' % option.short_name())
 
330
            option_help.append('%s%s\n' % (2 * indent, option.help))
 
331
 
 
332
        if len(option_help) > 0:
 
333
            text.append('%soptions:\n' % (2 * indent))
 
334
            text.extend(option_help)
 
335
 
 
336
        return text
 
337
 
 
338
 
 
339
class cmd_unshelve(BzrToolsCommand):
 
340
    """Restore shelved changes.
 
341
 
 
342
    By default the most recently shelved changes are restored. However if you
 
343
    specify a patch by name those changes will be restored instead.
 
344
 
 
345
    See 'shelve' for more information.
 
346
    """
 
347
    takes_options = [
 
348
            Option('all', help='Unshelve all changes without prompting.'),
 
349
            Option('force', help='Force unshelving even if errors occur.'),
 
350
            Option('no-color', help='Never display changes in color.')
 
351
        ]
 
352
    takes_args = ['patch?']
 
353
    def run(self, patch=None, all=False, force=False, no_color=False):
 
354
        source = BzrPatchSource()
 
355
        s = shelf.Shelf(source.base)
 
356
        s.unshelve(source, patch, all, force, no_color)
 
357
        return 0
 
358
 
 
359
 
 
360
class cmd_shell(BzrToolsCommand):
 
361
    """Begin an interactive shell tailored for bzr.
 
362
    Bzr commands can be used without typing bzr first, and will be run natively
 
363
    when possible.  Tab completion is tailored for bzr.  The shell prompt shows
 
364
    the branch nick, revno, and path.
 
365
 
 
366
    If it encounters any moderately complicated shell command, it will punt to
 
367
    the system shell.
 
368
 
 
369
    Example:
 
370
    $ bzr shell
 
371
    bzr bzrtools:287/> status
 
372
    modified:
 
373
      __init__.py
 
374
    bzr bzrtools:287/> status --[TAB][TAB]
 
375
    --all        --help       --revision   --show-ids
 
376
    bzr bzrtools:287/> status --
 
377
    """
 
378
    def run(self):
 
379
        import shell
 
380
        return shell.run_shell()
 
381
 
 
382
 
 
383
class cmd_branch_history(BzrToolsCommand):
 
384
    """\
 
385
    Display the development history of a branch.
 
386
 
 
387
    Each different committer or branch nick is considered a different line of
 
388
    development.  Committers are treated as the same if they have the same
 
389
    name, or if they have the same email address.
 
390
    """
 
391
    takes_args = ["branch?"]
 
392
    def run(self, branch=None):
 
393
        from branchhistory import branch_history
 
394
        return branch_history(branch)
 
395
 
 
396
 
 
397
class cmd_zap(BzrToolsCommand):
 
398
    """\
 
399
    Remove a lightweight checkout, if it can be done safely.
 
400
 
 
401
    This command will remove a lightweight checkout without losing data.  That
 
402
    means it only removes lightweight checkouts, and only if they have no
 
403
    uncommitted changes.
 
404
 
 
405
    If --branch is specified, the branch will be deleted too, but only if the
 
406
    the branch has no new commits (relative to its parent).
 
407
    """
 
408
    takes_options = [Option("branch", help="Remove associated branch from"
 
409
                                           " repository."),
 
410
                     Option('force', help='Delete tree even if contents are'
 
411
                     ' modified.')]
 
412
    takes_args = ["checkout"]
 
413
    def run(self, checkout, branch=False, force=False):
 
414
        from zap import zap
 
415
        return zap(checkout, remove_branch=branch, allow_modified=force)
 
416
 
 
417
 
 
418
class cmd_cbranch(BzrToolsCommand):
 
419
    """
 
420
    Create a new checkout, associated with a new repository branch.
 
421
 
 
422
    When you cbranch, bzr looks up a target location in locations.conf, and
 
423
    creates the branch there.
 
424
 
 
425
    In your locations.conf, add the following lines:
 
426
    [/working_directory_root]
 
427
    cbranch_target = /branch_root
 
428
    cbranch_target:policy = appendpath
 
429
 
 
430
    This will mean that if you run "bzr cbranch foo/bar foo/baz" in the
 
431
    working directory root, the branch will be created in
 
432
    "/branch_root/foo/baz"
 
433
 
 
434
    NOTE: cbranch also supports "cbranch_root", but that behaviour is
 
435
    deprecated.
 
436
    """
 
437
    takes_options = [Option("lightweight",
 
438
                            help="Create a lightweight checkout."), 'revision',
 
439
                     Option('files-from', type=unicode,
 
440
                            help='Accelerate checkout using files from this'
 
441
                                 ' tree.'),
 
442
                     Option('hardlink',
 
443
                            help='Hard-link files from source/files-from tree'
 
444
                            ' where posible.')]
 
445
    takes_args = ["source", "target?"]
 
446
    def run(self, source, target=None, lightweight=False, revision=None,
 
447
            files_from=None, hardlink=False):
 
448
        from cbranch import cbranch
 
449
        return cbranch(source, target, lightweight=lightweight,
 
450
                       revision=revision, files_from=files_from,
 
451
                       hardlink=hardlink)
 
452
 
 
453
 
 
454
class cmd_branches(BzrToolsCommand):
 
455
    """Scan a location for branches"""
 
456
    takes_args = ["location?"]
 
457
    def run(self, location=None):
 
458
        from branches import branches
 
459
        return branches(location)
 
460
 
 
461
class cmd_trees(BzrToolsCommand):
 
462
    """Scan a location for trees"""
 
463
    takes_args = ['location?']
 
464
    def run(self, location='.'):
 
465
        from bzrlib.workingtree import WorkingTree
 
466
        from bzrlib.transport import get_transport
 
467
        t = get_transport(location)
 
468
        for tree in WorkingTree.find_trees(location):
 
469
            self.outf.write('%s\n' % t.relpath(
 
470
                tree.bzrdir.root_transport.base))
 
471
 
 
472
class cmd_multi_pull(BzrToolsCommand):
 
473
    """Pull all the branches under a location, e.g. a repository.
 
474
 
 
475
    Both branches present in the directory and the branches of checkouts are
 
476
    pulled.
 
477
    """
 
478
    takes_args = ["location?"]
 
479
    def run(self, location=None):
 
480
        from bzrlib.transport import get_transport
 
481
        from bzrtools import iter_branch_tree
 
482
        if location is None:
 
483
            location = '.'
 
484
        t = get_transport(location)
 
485
        possible_transports = []
 
486
        if not t.listable():
 
487
            print "Can't list this type of location."
 
488
            return 3
 
489
        for branch, wt in iter_branch_tree(t):
 
490
            if wt is None:
 
491
                pullable = branch
 
492
            else:
 
493
                pullable = wt
 
494
            parent = branch.get_parent()
 
495
            if parent is None:
 
496
                continue
 
497
            if wt is not None:
 
498
                base = wt.basedir
 
499
            else:
 
500
                base = branch.base
 
501
            if base.startswith(t.base):
 
502
                relpath = base[len(t.base):].rstrip('/')
 
503
            else:
 
504
                relpath = base
 
505
            print "Pulling %s from %s" % (relpath, parent)
 
506
            try:
 
507
                branch_t = get_transport(parent, possible_transports)
 
508
                pullable.pull(Branch.open_from_transport(branch_t))
 
509
            except Exception, e:
 
510
                print e
 
511
 
 
512
 
 
513
 
 
514
class cmd_import(BzrToolsCommand):
 
515
    """Import sources from a directory, tarball or zip file
 
516
 
 
517
    This command will import a directory, tarball or zip file into a bzr
 
518
    tree, replacing any versioned files already present.  If a directory is
 
519
    specified, it is used as the target.  If the directory does not exist, or
 
520
    is not versioned, it is created.
 
521
 
 
522
    Tarballs may be gzip or bzip2 compressed.  This is autodetected.
 
523
 
 
524
    If the tarball or zip has a single root directory, that directory is
 
525
    stripped when extracting the tarball.  This is not done for directories.
 
526
    """
 
527
 
 
528
    takes_args = ['source', 'tree?']
 
529
    def run(self, source, tree=None):
 
530
        from upstream_import import do_import
 
531
        do_import(source, tree)
 
532
 
 
533
 
 
534
class cmd_cdiff(BzrToolsCommand):
 
535
    """A color version of bzr's diff"""
 
536
    takes_args = property(lambda x: get_cmd_object('diff').takes_args)
 
537
    takes_options = list(get_cmd_object('diff').takes_options) + [
 
538
        Option('check-style',
 
539
            help='Warn if trailing whitespace or spurious changes have been'
 
540
                 ' added.')]
 
541
 
 
542
    def run(self, check_style=False, *args, **kwargs):
 
543
        from colordiff import colordiff
 
544
        colordiff(check_style, *args, **kwargs)
 
545
 
 
546
 
 
547
class cmd_rspush(BzrToolsCommand):
 
548
    """Upload this branch to another location using rsync.
 
549
 
 
550
    If no location is specified, the last-used location will be used.  To
 
551
    prevent dirty trees from being uploaded, rspush will error out if there are
 
552
    unknown files or local changes.  It will also error out if the upstream
 
553
    directory is non-empty and not an earlier version of the branch.
 
554
    """
 
555
    takes_args = ['location?']
 
556
    takes_options = [Option('overwrite', help='Ignore differences between'
 
557
                            ' branches and overwrite unconditionally.'),
 
558
                     Option('no-tree', help='Do not push the working tree,'
 
559
                            ' just the .bzr.')]
 
560
 
 
561
    def run(self, location=None, overwrite=False, no_tree=False):
 
562
        from bzrlib import workingtree
 
563
        import bzrtools
 
564
        cur_branch = workingtree.WorkingTree.open_containing(".")[0]
 
565
        bzrtools.rspush(cur_branch, location, overwrite=overwrite,
 
566
                      working_tree=not no_tree)
 
567
 
 
568
 
 
569
class cmd_link_tree(BzrToolsCommand):
 
570
    """Hardlink matching files to another tree.
 
571
 
 
572
    Only files with identical content and execute bit will be linked.
 
573
    """
 
574
    takes_args = ['location']
 
575
 
 
576
    def run(self, location):
 
577
        from bzrlib import workingtree
 
578
        from bzrlib.plugins.bzrtools.link_tree import link_tree
 
579
        target_tree = workingtree.WorkingTree.open_containing(".")[0]
 
580
        source_tree = workingtree.WorkingTree.open(location)
 
581
        target_tree.lock_write()
 
582
        try:
 
583
            source_tree.lock_read()
 
584
            try:
 
585
                link_tree(target_tree, source_tree)
 
586
            finally:
 
587
                source_tree.unlock()
 
588
        finally:
 
589
            target_tree.unlock()
 
590
 
 
591
from heads import cmd_heads
 
592
commands = [
 
593
            cmd_branches,
 
594
            cmd_branch_history,
 
595
            cmd_cbranch,
 
596
            cmd_cdiff,
 
597
            cmd_clean_tree,
 
598
            cmd_fetch_ghosts,
 
599
            cmd_graph_ancestry,
 
600
            cmd_heads,
 
601
            cmd_import,
 
602
            cmd_link_tree,
 
603
            cmd_multi_pull,
 
604
            cmd_patch,
 
605
            cmd_rspush,
 
606
            cmd_shelf,
 
607
            cmd_shell,
 
608
            cmd_shelve,
 
609
            cmd_trees,
 
610
            cmd_unshelve,
 
611
            cmd_zap,
 
612
            ]
 
613
 
 
614
 
 
615
if hasattr(bzrlib.commands, 'register_command'):
 
616
    for command in commands:
 
617
        bzrlib.commands.register_command(command)
 
618
 
 
619
 
 
620
def test_suite():
 
621
    from bzrlib.tests.TestUtil import TestLoader
 
622
    import tests
 
623
    from doctest import DocTestSuite, ELLIPSIS
 
624
    from unittest import TestSuite
 
625
    import bzrtools
 
626
    import tests.clean_tree
 
627
    import tests.test_dotgraph
 
628
    import tests.is_clean
 
629
    import tests.test_cbranch
 
630
    import tests.test_link_tree
 
631
    import tests.test_patch
 
632
    import tests.test_rspush
 
633
    import tests.upstream_import
 
634
    import zap
 
635
    import tests.blackbox
 
636
    import tests.shelf_tests
 
637
    result = TestSuite()
 
638
    result.addTest(DocTestSuite(bzrtools, optionflags=ELLIPSIS))
 
639
    result.addTest(tests.clean_tree.test_suite())
 
640
    result.addTest(tests.test_suite())
 
641
    result.addTest(TestLoader().loadTestsFromModule(tests.shelf_tests))
 
642
    result.addTest(tests.blackbox.test_suite())
 
643
    result.addTest(tests.upstream_import.test_suite())
 
644
    result.addTest(zap.test_suite())
 
645
    result.addTest(TestLoader().loadTestsFromModule(tests.test_dotgraph))
 
646
    result.addTest(TestLoader().loadTestsFromModule(tests.is_clean))
 
647
    result.addTest(TestLoader().loadTestsFromModule(tests.test_link_tree))
 
648
    result.addTest(TestLoader().loadTestsFromModule(tests.test_patch))
 
649
    result.addTest(TestLoader().loadTestsFromModule(tests.test_rspush))
 
650
    result.addTest(TestLoader().loadTestsFromModule(tests.test_cbranch))
 
651
    return result