~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Robert Collins
  • Date: 2010-07-04 06:22:11 UTC
  • mto: This revision was merged to the branch mainline in revision 5332.
  • Revision ID: robertc@robertcollins.net-20100704062211-tk9hw6bnsn5x47fm
``bzrlib.lsprof.profile`` will no longer silently generate bad threaded
profiles when concurrent profile requests are made. Instead the profile
requests will be serialised. Reentrant requests will now deadlock.
(Robert Collins)

Show diffs side-by-side

added added

removed removed

Lines of Context:
232
232
    return view_info
233
233
 
234
234
 
 
235
def _open_directory_or_containing_tree_or_branch(filename, directory):
 
236
    """Open the tree or branch containing the specified file, unless
 
237
    the --directory option is used to specify a different branch."""
 
238
    if directory is not None:
 
239
        return (None, Branch.open(directory), filename)
 
240
    return bzrdir.BzrDir.open_containing_tree_or_branch(filename)
 
241
 
 
242
 
235
243
# TODO: Make sure no commands unconditionally use the working directory as a
236
244
# branch.  If a filename argument is used, the first of them should be used to
237
245
# specify the branch.  (Perhaps this can be factored out into some kind of
340
348
 
341
349
    hidden = True
342
350
    takes_args = ['revision_id?']
343
 
    takes_options = ['revision']
 
351
    takes_options = ['directory', 'revision']
344
352
    # cat-revision is more for frontends so should be exact
345
353
    encoding = 'strict'
346
354
 
353
361
        self.outf.write(revtext.decode('utf-8'))
354
362
 
355
363
    @display_command
356
 
    def run(self, revision_id=None, revision=None):
 
364
    def run(self, revision_id=None, revision=None, directory=u'.'):
357
365
        if revision_id is not None and revision is not None:
358
366
            raise errors.BzrCommandError('You can only supply one of'
359
367
                                         ' revision_id or --revision')
360
368
        if revision_id is None and revision is None:
361
369
            raise errors.BzrCommandError('You must supply either'
362
370
                                         ' --revision or a revision_id')
363
 
        b = WorkingTree.open_containing(u'.')[0].branch
 
371
        b = WorkingTree.open_containing(directory)[0].branch
364
372
 
365
373
        revisions = b.repository.revisions
366
374
        if revisions is None:
483
491
    takes_options = [
484
492
        Option('force',
485
493
               help='Remove the working tree even if it has '
486
 
                    'uncommitted changes.'),
 
494
                    'uncommitted or shelved changes.'),
487
495
        ]
488
496
 
489
497
    def run(self, location_list, force=False):
503
511
            if not force:
504
512
                if (working.has_changes()):
505
513
                    raise errors.UncommittedChanges(working)
 
514
                if working.get_shelf_manager().last_shelf() is not None:
 
515
                    raise errors.ShelvedChanges(working)
506
516
 
507
517
            if working.user_url != working.branch.user_url:
508
518
                raise errors.BzrCommandError("You cannot remove the working tree"
528
538
        if tree:
529
539
            try:
530
540
                wt = WorkingTree.open_containing(location)[0]
531
 
                wt.lock_read()
 
541
                self.add_cleanup(wt.lock_read().unlock)
532
542
            except (errors.NoWorkingTree, errors.NotLocalUrl):
533
543
                raise errors.NoWorkingTree(location)
534
 
            self.add_cleanup(wt.unlock)
535
544
            revid = wt.last_revision()
536
545
            try:
537
546
                revno_t = wt.branch.revision_id_to_dotted_revno(revid)
540
549
            revno = ".".join(str(n) for n in revno_t)
541
550
        else:
542
551
            b = Branch.open_containing(location)[0]
543
 
            b.lock_read()
544
 
            self.add_cleanup(b.unlock)
 
552
            self.add_cleanup(b.lock_read().unlock)
545
553
            revno = b.revno()
546
554
        self.cleanup_now()
547
555
        self.outf.write(str(revno) + '\n')
554
562
    takes_args = ['revision_info*']
555
563
    takes_options = [
556
564
        'revision',
557
 
        Option('directory',
 
565
        custom_help('directory',
558
566
            help='Branch to examine, '
559
 
                 'rather than the one containing the working directory.',
560
 
            short_name='d',
561
 
            type=unicode,
562
 
            ),
 
567
                 'rather than the one containing the working directory.'),
563
568
        Option('tree', help='Show revno of working tree'),
564
569
        ]
565
570
 
570
575
        try:
571
576
            wt = WorkingTree.open_containing(directory)[0]
572
577
            b = wt.branch
573
 
            wt.lock_read()
574
 
            self.add_cleanup(wt.unlock)
 
578
            self.add_cleanup(wt.lock_read().unlock)
575
579
        except (errors.NoWorkingTree, errors.NotLocalUrl):
576
580
            wt = None
577
581
            b = Branch.open_containing(directory)[0]
578
 
            b.lock_read()
579
 
            self.add_cleanup(b.unlock)
 
582
            self.add_cleanup(b.lock_read().unlock)
580
583
        revision_ids = []
581
584
        if revision is not None:
582
585
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
681
684
                should_print=(not is_quiet()))
682
685
 
683
686
        if base_tree:
684
 
            base_tree.lock_read()
685
 
            self.add_cleanup(base_tree.unlock)
 
687
            self.add_cleanup(base_tree.lock_read().unlock)
686
688
        tree, file_list = tree_files_for_add(file_list)
687
689
        added, ignored = tree.smart_add(file_list, not
688
690
            no_recurse, action=action, save=not dry_run)
760
762
 
761
763
        revision = _get_one_revision('inventory', revision)
762
764
        work_tree, file_list = tree_files(file_list)
763
 
        work_tree.lock_read()
764
 
        self.add_cleanup(work_tree.unlock)
 
765
        self.add_cleanup(work_tree.lock_read().unlock)
765
766
        if revision is not None:
766
767
            tree = revision.as_tree(work_tree.branch)
767
768
 
768
769
            extra_trees = [work_tree]
769
 
            tree.lock_read()
770
 
            self.add_cleanup(tree.unlock)
 
770
            self.add_cleanup(tree.lock_read().unlock)
771
771
        else:
772
772
            tree = work_tree
773
773
            extra_trees = []
833
833
        if len(names_list) < 2:
834
834
            raise errors.BzrCommandError("missing file argument")
835
835
        tree, rel_names = tree_files(names_list, canonicalize=False)
836
 
        tree.lock_tree_write()
837
 
        self.add_cleanup(tree.unlock)
 
836
        self.add_cleanup(tree.lock_tree_write().unlock)
838
837
        self._run(tree, names_list, rel_names, after)
839
838
 
840
839
    def run_auto(self, names_list, after, dry_run):
845
844
            raise errors.BzrCommandError('--after cannot be specified with'
846
845
                                         ' --auto.')
847
846
        work_tree, file_list = tree_files(names_list, default_branch='.')
848
 
        work_tree.lock_tree_write()
849
 
        self.add_cleanup(work_tree.unlock)
 
847
        self.add_cleanup(work_tree.lock_tree_write().unlock)
850
848
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
851
849
 
852
850
    def _run(self, tree, names_list, rel_names, after):
960
958
    takes_options = ['remember', 'overwrite', 'revision',
961
959
        custom_help('verbose',
962
960
            help='Show logs of pulled revisions.'),
963
 
        Option('directory',
 
961
        custom_help('directory',
964
962
            help='Branch to pull into, '
965
 
                 'rather than the one containing the working directory.',
966
 
            short_name='d',
967
 
            type=unicode,
968
 
            ),
 
963
                 'rather than the one containing the working directory.'),
969
964
        Option('local',
970
965
            help="Perform a local pull in a bound "
971
966
                 "branch.  Local pulls are not applied to "
986
981
        try:
987
982
            tree_to = WorkingTree.open_containing(directory)[0]
988
983
            branch_to = tree_to.branch
989
 
            tree_to.lock_write()
990
 
            self.add_cleanup(tree_to.unlock)
 
984
            self.add_cleanup(tree_to.lock_write().unlock)
991
985
        except errors.NoWorkingTree:
992
986
            tree_to = None
993
987
            branch_to = Branch.open_containing(directory)[0]
994
 
            branch_to.lock_write()
995
 
            self.add_cleanup(branch_to.unlock)
 
988
            self.add_cleanup(branch_to.lock_write().unlock)
996
989
 
997
990
        if local and not branch_to.get_bound_location():
998
991
            raise errors.LocalRequiresBoundBranch()
1029
1022
        else:
1030
1023
            branch_from = Branch.open(location,
1031
1024
                possible_transports=possible_transports)
1032
 
            branch_from.lock_read()
1033
 
            self.add_cleanup(branch_from.unlock)
 
1025
            self.add_cleanup(branch_from.lock_read().unlock)
1034
1026
 
1035
1027
            if branch_to.get_parent() is None or remember:
1036
1028
                branch_to.set_parent(branch_from.base)
1088
1080
        Option('create-prefix',
1089
1081
               help='Create the path leading up to the branch '
1090
1082
                    'if it does not already exist.'),
1091
 
        Option('directory',
 
1083
        custom_help('directory',
1092
1084
            help='Branch to push from, '
1093
 
                 'rather than the one containing the working directory.',
1094
 
            short_name='d',
1095
 
            type=unicode,
1096
 
            ),
 
1085
                 'rather than the one containing the working directory.'),
1097
1086
        Option('use-existing-dir',
1098
1087
               help='By default push will fail if the target'
1099
1088
                    ' directory exists, but does not already'
1219
1208
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1220
1209
            from_location)
1221
1210
        revision = _get_one_revision('branch', revision)
1222
 
        br_from.lock_read()
1223
 
        self.add_cleanup(br_from.unlock)
 
1211
        self.add_cleanup(br_from.lock_read().unlock)
1224
1212
        if revision is not None:
1225
1213
            revision_id = revision.as_revision_id(br_from)
1226
1214
        else:
1366
1354
    @display_command
1367
1355
    def run(self, dir=u'.'):
1368
1356
        tree = WorkingTree.open_containing(dir)[0]
1369
 
        tree.lock_read()
1370
 
        self.add_cleanup(tree.unlock)
 
1357
        self.add_cleanup(tree.lock_read().unlock)
1371
1358
        new_inv = tree.inventory
1372
1359
        old_tree = tree.basis_tree()
1373
 
        old_tree.lock_read()
1374
 
        self.add_cleanup(old_tree.unlock)
 
1360
        self.add_cleanup(old_tree.lock_read().unlock)
1375
1361
        old_inv = old_tree.inventory
1376
1362
        renames = []
1377
1363
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
1415
1401
        master = branch.get_master_branch(
1416
1402
            possible_transports=possible_transports)
1417
1403
        if master is not None:
1418
 
            tree.lock_write()
1419
1404
            branch_location = master.base
 
1405
            tree.lock_write()
1420
1406
        else:
 
1407
            branch_location = tree.branch.base
1421
1408
            tree.lock_tree_write()
1422
 
            branch_location = tree.branch.base
1423
1409
        self.add_cleanup(tree.unlock)
1424
1410
        # get rid of the final '/' and be ready for display
1425
1411
        branch_location = urlutils.unescape_for_display(
1545
1531
        if file_list is not None:
1546
1532
            file_list = [f for f in file_list]
1547
1533
 
1548
 
        tree.lock_write()
1549
 
        self.add_cleanup(tree.unlock)
 
1534
        self.add_cleanup(tree.lock_write().unlock)
1550
1535
        # Heuristics should probably all move into tree.remove_smart or
1551
1536
        # some such?
1552
1537
        if new:
1985
1970
         old_branch, new_branch,
1986
1971
         specific_files, extra_trees) = get_trees_and_branches_to_diff_locked(
1987
1972
            file_list, revision, old, new, self.add_cleanup, apply_view=True)
 
1973
        # GNU diff on Windows uses ANSI encoding for filenames
 
1974
        path_encoding = osutils.get_diff_header_encoding()
1988
1975
        return show_diff_trees(old_tree, new_tree, sys.stdout,
1989
1976
                               specific_files=specific_files,
1990
1977
                               external_diff_options=diff_options,
1991
1978
                               old_label=old_label, new_label=new_label,
1992
 
                               extra_trees=extra_trees, using=using,
 
1979
                               extra_trees=extra_trees,
 
1980
                               path_encoding=path_encoding,
 
1981
                               using=using,
1993
1982
                               format_cls=format)
1994
1983
 
1995
1984
 
2003
1992
    # level of effort but possibly much less IO.  (Or possibly not,
2004
1993
    # if the directories are very large...)
2005
1994
    _see_also = ['status', 'ls']
2006
 
    takes_options = ['show-ids']
 
1995
    takes_options = ['directory', 'show-ids']
2007
1996
 
2008
1997
    @display_command
2009
 
    def run(self, show_ids=False):
2010
 
        tree = WorkingTree.open_containing(u'.')[0]
2011
 
        tree.lock_read()
2012
 
        self.add_cleanup(tree.unlock)
 
1998
    def run(self, show_ids=False, directory=u'.'):
 
1999
        tree = WorkingTree.open_containing(directory)[0]
 
2000
        self.add_cleanup(tree.lock_read().unlock)
2013
2001
        old = tree.basis_tree()
2014
 
        old.lock_read()
2015
 
        self.add_cleanup(old.unlock)
 
2002
        self.add_cleanup(old.lock_read().unlock)
2016
2003
        for path, ie in old.inventory.iter_entries():
2017
2004
            if not tree.has_id(ie.file_id):
2018
2005
                self.outf.write(path)
2028
2015
 
2029
2016
    hidden = True
2030
2017
    _see_also = ['status', 'ls']
2031
 
    takes_options = [
2032
 
            Option('null',
2033
 
                   help='Write an ascii NUL (\\0) separator '
2034
 
                   'between files rather than a newline.')
2035
 
            ]
 
2018
    takes_options = ['directory', 'null']
2036
2019
 
2037
2020
    @display_command
2038
 
    def run(self, null=False):
2039
 
        tree = WorkingTree.open_containing(u'.')[0]
 
2021
    def run(self, null=False, directory=u'.'):
 
2022
        tree = WorkingTree.open_containing(directory)[0]
2040
2023
        td = tree.changes_from(tree.basis_tree())
2041
2024
        for path, id, kind, text_modified, meta_modified in td.modified:
2042
2025
            if null:
2051
2034
 
2052
2035
    hidden = True
2053
2036
    _see_also = ['status', 'ls']
2054
 
    takes_options = [
2055
 
            Option('null',
2056
 
                   help='Write an ascii NUL (\\0) separator '
2057
 
                   'between files rather than a newline.')
2058
 
            ]
 
2037
    takes_options = ['directory', 'null']
2059
2038
 
2060
2039
    @display_command
2061
 
    def run(self, null=False):
2062
 
        wt = WorkingTree.open_containing(u'.')[0]
2063
 
        wt.lock_read()
2064
 
        self.add_cleanup(wt.unlock)
 
2040
    def run(self, null=False, directory=u'.'):
 
2041
        wt = WorkingTree.open_containing(directory)[0]
 
2042
        self.add_cleanup(wt.lock_read().unlock)
2065
2043
        basis = wt.basis_tree()
2066
 
        basis.lock_read()
2067
 
        self.add_cleanup(basis.unlock)
 
2044
        self.add_cleanup(basis.lock_read().unlock)
2068
2045
        basis_inv = basis.inventory
2069
2046
        inv = wt.inventory
2070
2047
        for file_id in inv:
2073
2050
            if inv.is_root(file_id) and len(basis_inv) == 0:
2074
2051
                continue
2075
2052
            path = inv.id2path(file_id)
2076
 
            if not os.access(osutils.abspath(path), os.F_OK):
 
2053
            if not os.access(osutils.pathjoin(wt.basedir, path), os.F_OK):
2077
2054
                continue
2078
2055
            if null:
2079
2056
                self.outf.write(path + '\0')
2279
2256
                   help='Show just the specified revision.'
2280
2257
                   ' See also "help revisionspec".'),
2281
2258
            'log-format',
 
2259
            RegistryOption('authors',
 
2260
                'What names to list as authors - first, all or committer.',
 
2261
                title='Authors',
 
2262
                lazy_registry=('bzrlib.log', 'author_list_registry'),
 
2263
            ),
2282
2264
            Option('levels',
2283
2265
                   short_name='n',
2284
2266
                   help='Number of levels to display - 0 for all, 1 for flat.',
2319
2301
            limit=None,
2320
2302
            show_diff=False,
2321
2303
            include_merges=False,
 
2304
            authors=None,
2322
2305
            exclude_common_ancestry=False,
2323
2306
            ):
2324
2307
        from bzrlib.log import (
2352
2335
        if file_list:
2353
2336
            # find the file ids to log and check for directory filtering
2354
2337
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2355
 
                revision, file_list)
2356
 
            self.add_cleanup(b.unlock)
 
2338
                revision, file_list, self.add_cleanup)
2357
2339
            for relpath, file_id, kind in file_info_list:
2358
2340
                if file_id is None:
2359
2341
                    raise errors.BzrCommandError(
2377
2359
                location = '.'
2378
2360
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2379
2361
            b = dir.open_branch()
2380
 
            b.lock_read()
2381
 
            self.add_cleanup(b.unlock)
 
2362
            self.add_cleanup(b.lock_read().unlock)
2382
2363
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2383
2364
 
2384
2365
        # Decide on the type of delta & diff filtering to use
2404
2385
                        show_timezone=timezone,
2405
2386
                        delta_format=get_verbosity_level(),
2406
2387
                        levels=levels,
2407
 
                        show_advice=levels is None)
 
2388
                        show_advice=levels is None,
 
2389
                        author_list_handler=authors)
2408
2390
 
2409
2391
        # Choose the algorithm for doing the logging. It's annoying
2410
2392
        # having multiple code paths like this but necessary until
2508
2490
        tree, relpath = WorkingTree.open_containing(filename)
2509
2491
        file_id = tree.path2id(relpath)
2510
2492
        b = tree.branch
2511
 
        b.lock_read()
2512
 
        self.add_cleanup(b.unlock)
 
2493
        self.add_cleanup(b.lock_read().unlock)
2513
2494
        touching_revs = log.find_touching_revisions(b, file_id)
2514
2495
        for revno, revision_id, what in touching_revs:
2515
2496
            self.outf.write("%6d %s\n" % (revno, what))
2528
2509
                   help='Recurse into subdirectories.'),
2529
2510
            Option('from-root',
2530
2511
                   help='Print paths relative to the root of the branch.'),
2531
 
            Option('unknown', help='Print unknown files.'),
 
2512
            Option('unknown', short_name='u',
 
2513
                help='Print unknown files.'),
2532
2514
            Option('versioned', help='Print versioned files.',
2533
2515
                   short_name='V'),
2534
 
            Option('ignored', help='Print ignored files.'),
2535
 
            Option('null',
2536
 
                   help='Write an ascii NUL (\\0) separator '
2537
 
                   'between files rather than a newline.'),
2538
 
            Option('kind',
 
2516
            Option('ignored', short_name='i',
 
2517
                help='Print ignored files.'),
 
2518
            Option('kind', short_name='k',
2539
2519
                   help='List entries of a particular kind: file, directory, symlink.',
2540
2520
                   type=unicode),
 
2521
            'null',
2541
2522
            'show-ids',
 
2523
            'directory',
2542
2524
            ]
2543
2525
    @display_command
2544
2526
    def run(self, revision=None, verbose=False,
2545
2527
            recursive=False, from_root=False,
2546
2528
            unknown=False, versioned=False, ignored=False,
2547
 
            null=False, kind=None, show_ids=False, path=None):
 
2529
            null=False, kind=None, show_ids=False, path=None, directory=None):
2548
2530
 
2549
2531
        if kind and kind not in ('file', 'directory', 'symlink'):
2550
2532
            raise errors.BzrCommandError('invalid kind specified')
2562
2544
                raise errors.BzrCommandError('cannot specify both --from-root'
2563
2545
                                             ' and PATH')
2564
2546
            fs_path = path
2565
 
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
2566
 
            fs_path)
 
2547
        tree, branch, relpath = \
 
2548
            _open_directory_or_containing_tree_or_branch(fs_path, directory)
2567
2549
 
2568
2550
        # Calculate the prefix to use
2569
2551
        prefix = None
2584
2566
                view_str = views.view_display_str(view_files)
2585
2567
                note("Ignoring files outside view. View is %s" % view_str)
2586
2568
 
2587
 
        tree.lock_read()
2588
 
        self.add_cleanup(tree.unlock)
 
2569
        self.add_cleanup(tree.lock_read().unlock)
2589
2570
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2590
2571
            from_dir=relpath, recursive=recursive):
2591
2572
            # Apply additional masking
2638
2619
 
2639
2620
    hidden = True
2640
2621
    _see_also = ['ls']
 
2622
    takes_options = ['directory']
2641
2623
 
2642
2624
    @display_command
2643
 
    def run(self):
2644
 
        for f in WorkingTree.open_containing(u'.')[0].unknowns():
 
2625
    def run(self, directory=u'.'):
 
2626
        for f in WorkingTree.open_containing(directory)[0].unknowns():
2645
2627
            self.outf.write(osutils.quotefn(f) + '\n')
2646
2628
 
2647
2629
 
2712
2694
 
2713
2695
    _see_also = ['status', 'ignored', 'patterns']
2714
2696
    takes_args = ['name_pattern*']
2715
 
    takes_options = [
 
2697
    takes_options = ['directory',
2716
2698
        Option('default-rules',
2717
2699
               help='Display the default ignore rules that bzr uses.')
2718
2700
        ]
2719
2701
 
2720
 
    def run(self, name_pattern_list=None, default_rules=None):
 
2702
    def run(self, name_pattern_list=None, default_rules=None,
 
2703
            directory=u'.'):
2721
2704
        from bzrlib import ignores
2722
2705
        if default_rules is not None:
2723
2706
            # dump the default rules and exit
2734
2717
                (len(name_pattern) > 1 and name_pattern[1] == ':')):
2735
2718
                raise errors.BzrCommandError(
2736
2719
                    "NAME_PATTERN should not be an absolute path")
2737
 
        tree, relpath = WorkingTree.open_containing(u'.')
 
2720
        tree, relpath = WorkingTree.open_containing(directory)
2738
2721
        ignores.tree_ignores_add_patterns(tree, name_pattern_list)
2739
2722
        ignored = globbing.Globster(name_pattern_list)
2740
2723
        matches = []
2741
 
        tree.lock_read()
 
2724
        self.add_cleanup(tree.lock_read().unlock)
2742
2725
        for entry in tree.list_files():
2743
2726
            id = entry[3]
2744
2727
            if id is not None:
2745
2728
                filename = entry[0]
2746
2729
                if ignored.match(filename):
2747
2730
                    matches.append(filename)
2748
 
        tree.unlock()
2749
2731
        if len(matches) > 0:
2750
2732
            self.outf.write("Warning: the following files are version controlled and"
2751
2733
                  " match your ignore pattern:\n%s"
2766
2748
 
2767
2749
    encoding_type = 'replace'
2768
2750
    _see_also = ['ignore', 'ls']
 
2751
    takes_options = ['directory']
2769
2752
 
2770
2753
    @display_command
2771
 
    def run(self):
2772
 
        tree = WorkingTree.open_containing(u'.')[0]
2773
 
        tree.lock_read()
2774
 
        self.add_cleanup(tree.unlock)
 
2754
    def run(self, directory=u'.'):
 
2755
        tree = WorkingTree.open_containing(directory)[0]
 
2756
        self.add_cleanup(tree.lock_read().unlock)
2775
2757
        for path, file_class, kind, file_id, entry in tree.list_files():
2776
2758
            if file_class != 'I':
2777
2759
                continue
2788
2770
    """
2789
2771
    hidden = True
2790
2772
    takes_args = ['revno']
 
2773
    takes_options = ['directory']
2791
2774
 
2792
2775
    @display_command
2793
 
    def run(self, revno):
 
2776
    def run(self, revno, directory=u'.'):
2794
2777
        try:
2795
2778
            revno = int(revno)
2796
2779
        except ValueError:
2797
2780
            raise errors.BzrCommandError("not a valid revision-number: %r"
2798
2781
                                         % revno)
2799
 
        revid = WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
 
2782
        revid = WorkingTree.open_containing(directory)[0].branch.get_rev_id(revno)
2800
2783
        self.outf.write("%s\n" % revid)
2801
2784
 
2802
2785
 
2829
2812
      =================       =========================
2830
2813
    """
2831
2814
    takes_args = ['dest', 'branch_or_subdir?']
2832
 
    takes_options = [
 
2815
    takes_options = ['directory',
2833
2816
        Option('format',
2834
2817
               help="Type of file to export to.",
2835
2818
               type=unicode),
2844
2827
                    'revision in which it was changed.'),
2845
2828
        ]
2846
2829
    def run(self, dest, branch_or_subdir=None, revision=None, format=None,
2847
 
        root=None, filters=False, per_file_timestamps=False):
 
2830
        root=None, filters=False, per_file_timestamps=False, directory=u'.'):
2848
2831
        from bzrlib.export import export
2849
2832
 
2850
2833
        if branch_or_subdir is None:
2851
 
            tree = WorkingTree.open_containing(u'.')[0]
 
2834
            tree = WorkingTree.open_containing(directory)[0]
2852
2835
            b = tree.branch
2853
2836
            subdir = None
2854
2837
        else:
2873
2856
    """
2874
2857
 
2875
2858
    _see_also = ['ls']
2876
 
    takes_options = [
 
2859
    takes_options = ['directory',
2877
2860
        Option('name-from-revision', help='The path name in the old tree.'),
2878
2861
        Option('filters', help='Apply content filters to display the '
2879
2862
                'convenience form.'),
2884
2867
 
2885
2868
    @display_command
2886
2869
    def run(self, filename, revision=None, name_from_revision=False,
2887
 
            filters=False):
 
2870
            filters=False, directory=None):
2888
2871
        if revision is not None and len(revision) != 1:
2889
2872
            raise errors.BzrCommandError("bzr cat --revision takes exactly"
2890
2873
                                         " one revision specifier")
2891
2874
        tree, branch, relpath = \
2892
 
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2893
 
        branch.lock_read()
2894
 
        self.add_cleanup(branch.unlock)
 
2875
            _open_directory_or_containing_tree_or_branch(filename, directory)
 
2876
        self.add_cleanup(branch.lock_read().unlock)
2895
2877
        return self._run(tree, branch, relpath, filename, revision,
2896
2878
                         name_from_revision, filters)
2897
2879
 
2900
2882
        if tree is None:
2901
2883
            tree = b.basis_tree()
2902
2884
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
2903
 
        rev_tree.lock_read()
2904
 
        self.add_cleanup(rev_tree.unlock)
 
2885
        self.add_cleanup(rev_tree.lock_read().unlock)
2905
2886
 
2906
2887
        old_file_id = rev_tree.path2id(relpath)
2907
2888
 
3169
3150
        def get_message(commit_obj):
3170
3151
            """Callback to get commit message"""
3171
3152
            if file:
3172
 
                my_message = codecs.open(
3173
 
                    file, 'rt', osutils.get_user_encoding()).read()
 
3153
                f = codecs.open(file, 'rt', osutils.get_user_encoding())
 
3154
                try:
 
3155
                    my_message = f.read()
 
3156
                finally:
 
3157
                    f.close()
3174
3158
            elif message is not None:
3175
3159
                my_message = message
3176
3160
            else:
3330
3314
 
3331
3315
            bzr whoami "Frank Chu <fchu@example.com>"
3332
3316
    """
3333
 
    takes_options = [ Option('email',
 
3317
    takes_options = [ 'directory',
 
3318
                      Option('email',
3334
3319
                             help='Display email address only.'),
3335
3320
                      Option('branch',
3336
3321
                             help='Set identity for the current branch instead of '
3340
3325
    encoding_type = 'replace'
3341
3326
 
3342
3327
    @display_command
3343
 
    def run(self, email=False, branch=False, name=None):
 
3328
    def run(self, email=False, branch=False, name=None, directory=None):
3344
3329
        if name is None:
3345
 
            # use branch if we're inside one; otherwise global config
3346
 
            try:
3347
 
                c = Branch.open_containing('.')[0].get_config()
3348
 
            except errors.NotBranchError:
3349
 
                c = config.GlobalConfig()
 
3330
            if directory is None:
 
3331
                # use branch if we're inside one; otherwise global config
 
3332
                try:
 
3333
                    c = Branch.open_containing(u'.')[0].get_config()
 
3334
                except errors.NotBranchError:
 
3335
                    c = config.GlobalConfig()
 
3336
            else:
 
3337
                c = Branch.open(directory).get_config()
3350
3338
            if email:
3351
3339
                self.outf.write(c.user_email() + '\n')
3352
3340
            else:
3362
3350
 
3363
3351
        # use global config unless --branch given
3364
3352
        if branch:
3365
 
            c = Branch.open_containing('.')[0].get_config()
 
3353
            if directory is None:
 
3354
                c = Branch.open_containing(u'.')[0].get_config()
 
3355
            else:
 
3356
                c = Branch.open(directory).get_config()
3366
3357
        else:
3367
3358
            c = config.GlobalConfig()
3368
3359
        c.set_user_option('email', name)
3380
3371
 
3381
3372
    _see_also = ['info']
3382
3373
    takes_args = ['nickname?']
3383
 
    def run(self, nickname=None):
3384
 
        branch = Branch.open_containing(u'.')[0]
 
3374
    takes_options = ['directory']
 
3375
    def run(self, nickname=None, directory=u'.'):
 
3376
        branch = Branch.open_containing(directory)[0]
3385
3377
        if nickname is None:
3386
3378
            self.printme(branch)
3387
3379
        else:
3612
3604
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunner
3613
3605
            # On Windows, disable automatic conversion of '\n' to '\r\n' in
3614
3606
            # stdout, which would corrupt the subunit stream. 
3615
 
            if sys.platform == "win32" and sys.stdout.fileno() >= 0:
 
3607
            # FIXME: This has been fixed in subunit trunk (>0.0.5) so the
 
3608
            # following code can be deleted when it's sufficiently deployed
 
3609
            # -- vila/mgz 20100514
 
3610
            if (sys.platform == "win32"
 
3611
                and getattr(sys.stdout, 'fileno', None) is not None):
3616
3612
                import msvcrt
3617
3613
                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
3618
3614
        if parallel:
3690
3686
 
3691
3687
        branch1 = Branch.open_containing(branch)[0]
3692
3688
        branch2 = Branch.open_containing(other)[0]
3693
 
        branch1.lock_read()
3694
 
        self.add_cleanup(branch1.unlock)
3695
 
        branch2.lock_read()
3696
 
        self.add_cleanup(branch2.unlock)
 
3689
        self.add_cleanup(branch1.lock_read().unlock)
 
3690
        self.add_cleanup(branch2.lock_read().unlock)
3697
3691
        last1 = ensure_null(branch1.last_revision())
3698
3692
        last2 = ensure_null(branch2.last_revision())
3699
3693
 
3793
3787
                ' completely merged into the source, pull from the'
3794
3788
                ' source rather than merging.  When this happens,'
3795
3789
                ' you do not need to commit the result.'),
3796
 
        Option('directory',
 
3790
        custom_help('directory',
3797
3791
               help='Branch to merge into, '
3798
 
                    'rather than the one containing the working directory.',
3799
 
               short_name='d',
3800
 
               type=unicode,
3801
 
               ),
 
3792
                    'rather than the one containing the working directory.'),
3802
3793
        Option('preview', help='Instead of merging, show a diff of the'
3803
3794
               ' merge.'),
3804
3795
        Option('interactive', help='Select changes interactively.',
3837
3828
            unversioned_filter=tree.is_ignored, view_info=view_info)
3838
3829
        pb = ui.ui_factory.nested_progress_bar()
3839
3830
        self.add_cleanup(pb.finished)
3840
 
        tree.lock_write()
3841
 
        self.add_cleanup(tree.unlock)
 
3831
        self.add_cleanup(tree.lock_write().unlock)
3842
3832
        if location is not None:
3843
3833
            try:
3844
3834
                mergeable = bundle.read_mergeable_from_url(location,
3905
3895
    def _do_preview(self, merger):
3906
3896
        from bzrlib.diff import show_diff_trees
3907
3897
        result_tree = self._get_preview(merger)
 
3898
        path_encoding = osutils.get_diff_header_encoding()
3908
3899
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3909
 
                        old_label='', new_label='')
 
3900
                        old_label='', new_label='',
 
3901
                        path_encoding=path_encoding)
3910
3902
 
3911
3903
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
3912
3904
        merger.change_reporter = change_reporter
4100
4092
        if merge_type is None:
4101
4093
            merge_type = _mod_merge.Merge3Merger
4102
4094
        tree, file_list = tree_files(file_list)
4103
 
        tree.lock_write()
4104
 
        self.add_cleanup(tree.unlock)
 
4095
        self.add_cleanup(tree.lock_write().unlock)
4105
4096
        parents = tree.get_parent_ids()
4106
4097
        if len(parents) != 2:
4107
4098
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
4217
4208
    def run(self, revision=None, no_backup=False, file_list=None,
4218
4209
            forget_merges=None):
4219
4210
        tree, file_list = tree_files(file_list)
4220
 
        tree.lock_tree_write()
4221
 
        self.add_cleanup(tree.unlock)
 
4211
        self.add_cleanup(tree.lock_tree_write().unlock)
4222
4212
        if forget_merges:
4223
4213
            tree.set_parent_ids(tree.get_parent_ids()[:1])
4224
4214
        else:
4313
4303
    _see_also = ['merge', 'pull']
4314
4304
    takes_args = ['other_branch?']
4315
4305
    takes_options = [
 
4306
        'directory',
4316
4307
        Option('reverse', 'Reverse the order of revisions.'),
4317
4308
        Option('mine-only',
4318
4309
               'Display changes in the local branch only.'),
4340
4331
            theirs_only=False,
4341
4332
            log_format=None, long=False, short=False, line=False,
4342
4333
            show_ids=False, verbose=False, this=False, other=False,
4343
 
            include_merges=False, revision=None, my_revision=None):
 
4334
            include_merges=False, revision=None, my_revision=None,
 
4335
            directory=u'.'):
4344
4336
        from bzrlib.missing import find_unmerged, iter_log_revisions
4345
4337
        def message(s):
4346
4338
            if not is_quiet():
4359
4351
        elif theirs_only:
4360
4352
            restrict = 'remote'
4361
4353
 
4362
 
        local_branch = Branch.open_containing(u".")[0]
4363
 
        local_branch.lock_read()
4364
 
        self.add_cleanup(local_branch.unlock)
 
4354
        local_branch = Branch.open_containing(directory)[0]
 
4355
        self.add_cleanup(local_branch.lock_read().unlock)
4365
4356
 
4366
4357
        parent = local_branch.get_parent()
4367
4358
        if other_branch is None:
4378
4369
        if remote_branch.base == local_branch.base:
4379
4370
            remote_branch = local_branch
4380
4371
        else:
4381
 
            remote_branch.lock_read()
4382
 
            self.add_cleanup(remote_branch.unlock)
 
4372
            self.add_cleanup(remote_branch.lock_read().unlock)
4383
4373
 
4384
4374
        local_revid_range = _revision_range_to_revid_range(
4385
4375
            _get_revision_range(my_revision, local_branch,
4440
4430
            message("Branches are up to date.\n")
4441
4431
        self.cleanup_now()
4442
4432
        if not status_code and parent is None and other_branch is not None:
4443
 
            local_branch.lock_write()
4444
 
            self.add_cleanup(local_branch.unlock)
 
4433
            self.add_cleanup(local_branch.lock_write().unlock)
4445
4434
            # handle race conditions - a parent might be set while we run.
4446
4435
            if local_branch.get_parent() is None:
4447
4436
                local_branch.set_parent(remote_branch.base)
4547
4536
            b = Branch.open_containing(branch)[0]
4548
4537
        else:
4549
4538
            b = Branch.open(branch)
4550
 
        b.lock_read()
4551
 
        self.add_cleanup(b.unlock)
 
4539
        self.add_cleanup(b.lock_read().unlock)
4552
4540
        if revision is None:
4553
4541
            rev_id = b.last_revision()
4554
4542
        else:
4578
4566
                     Option('long', help='Show commit date in annotations.'),
4579
4567
                     'revision',
4580
4568
                     'show-ids',
 
4569
                     'directory',
4581
4570
                     ]
4582
4571
    encoding_type = 'exact'
4583
4572
 
4584
4573
    @display_command
4585
4574
    def run(self, filename, all=False, long=False, revision=None,
4586
 
            show_ids=False):
 
4575
            show_ids=False, directory=None):
4587
4576
        from bzrlib.annotate import annotate_file, annotate_file_tree
4588
4577
        wt, branch, relpath = \
4589
 
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
 
4578
            _open_directory_or_containing_tree_or_branch(filename, directory)
4590
4579
        if wt is not None:
4591
 
            wt.lock_read()
4592
 
            self.add_cleanup(wt.unlock)
 
4580
            self.add_cleanup(wt.lock_read().unlock)
4593
4581
        else:
4594
 
            branch.lock_read()
4595
 
            self.add_cleanup(branch.unlock)
 
4582
            self.add_cleanup(branch.lock_read().unlock)
4596
4583
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
4597
 
        tree.lock_read()
4598
 
        self.add_cleanup(tree.unlock)
 
4584
        self.add_cleanup(tree.lock_read().unlock)
4599
4585
        if wt is not None:
4600
4586
            file_id = wt.path2id(relpath)
4601
4587
        else:
4619
4605
 
4620
4606
    hidden = True # is this right ?
4621
4607
    takes_args = ['revision_id*']
4622
 
    takes_options = ['revision']
 
4608
    takes_options = ['directory', 'revision']
4623
4609
 
4624
 
    def run(self, revision_id_list=None, revision=None):
 
4610
    def run(self, revision_id_list=None, revision=None, directory=u'.'):
4625
4611
        if revision_id_list is not None and revision is not None:
4626
4612
            raise errors.BzrCommandError('You can only supply one of revision_id or --revision')
4627
4613
        if revision_id_list is None and revision is None:
4628
4614
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4629
 
        b = WorkingTree.open_containing(u'.')[0].branch
4630
 
        b.lock_write()
4631
 
        self.add_cleanup(b.unlock)
 
4615
        b = WorkingTree.open_containing(directory)[0].branch
 
4616
        self.add_cleanup(b.lock_write().unlock)
4632
4617
        return self._run(b, revision_id_list, revision)
4633
4618
 
4634
4619
    def _run(self, b, revision_id_list, revision):
4693
4678
 
4694
4679
    _see_also = ['checkouts', 'unbind']
4695
4680
    takes_args = ['location?']
4696
 
    takes_options = []
 
4681
    takes_options = ['directory']
4697
4682
 
4698
 
    def run(self, location=None):
4699
 
        b, relpath = Branch.open_containing(u'.')
 
4683
    def run(self, location=None, directory=u'.'):
 
4684
        b, relpath = Branch.open_containing(directory)
4700
4685
        if location is None:
4701
4686
            try:
4702
4687
                location = b.get_old_bound_location()
4729
4714
 
4730
4715
    _see_also = ['checkouts', 'bind']
4731
4716
    takes_args = []
4732
 
    takes_options = []
 
4717
    takes_options = ['directory']
4733
4718
 
4734
 
    def run(self):
4735
 
        b, relpath = Branch.open_containing(u'.')
 
4719
    def run(self, directory=u'.'):
 
4720
        b, relpath = Branch.open_containing(directory)
4736
4721
        if not b.unbind():
4737
4722
            raise errors.BzrCommandError('Local branch is not bound')
4738
4723
 
4784
4769
            b = control.open_branch()
4785
4770
 
4786
4771
        if tree is not None:
4787
 
            tree.lock_write()
4788
 
            self.add_cleanup(tree.unlock)
 
4772
            self.add_cleanup(tree.lock_write().unlock)
4789
4773
        else:
4790
 
            b.lock_write()
4791
 
            self.add_cleanup(b.unlock)
 
4774
            self.add_cleanup(b.lock_write().unlock)
4792
4775
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
4793
4776
 
4794
4777
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4902
4885
                    'result in a dynamically allocated port.  The default port '
4903
4886
                    'depends on the protocol.',
4904
4887
               type=str),
4905
 
        Option('directory',
4906
 
               help='Serve contents of this directory.',
4907
 
               type=unicode),
 
4888
        custom_help('directory',
 
4889
               help='Serve contents of this directory.'),
4908
4890
        Option('allow-writes',
4909
4891
               help='By default the server is a readonly server.  Supplying '
4910
4892
                    '--allow-writes enables write access to the contents of '
4937
4919
 
4938
4920
    def run(self, port=None, inet=False, directory=None, allow_writes=False,
4939
4921
            protocol=None):
4940
 
        from bzrlib.transport import get_transport, transport_server_registry
 
4922
        from bzrlib import transport
4941
4923
        if directory is None:
4942
4924
            directory = os.getcwd()
4943
4925
        if protocol is None:
4944
 
            protocol = transport_server_registry.get()
 
4926
            protocol = transport.transport_server_registry.get()
4945
4927
        host, port = self.get_host_and_port(port)
4946
4928
        url = urlutils.local_path_to_url(directory)
4947
4929
        if not allow_writes:
4948
4930
            url = 'readonly+' + url
4949
 
        transport = get_transport(url)
4950
 
        protocol(transport, host, port, inet)
 
4931
        t = transport.get_transport(url)
 
4932
        protocol(t, host, port, inet)
4951
4933
 
4952
4934
 
4953
4935
class cmd_join(Command):
5046
5028
    _see_also = ['send']
5047
5029
 
5048
5030
    takes_options = [
 
5031
        'directory',
5049
5032
        RegistryOption.from_kwargs('patch-type',
5050
5033
            'The type of patch to include in the directive.',
5051
5034
            title='Patch type',
5064
5047
    encoding_type = 'exact'
5065
5048
 
5066
5049
    def run(self, submit_branch=None, public_branch=None, patch_type='bundle',
5067
 
            sign=False, revision=None, mail_to=None, message=None):
 
5050
            sign=False, revision=None, mail_to=None, message=None,
 
5051
            directory=u'.'):
5068
5052
        from bzrlib.revision import ensure_null, NULL_REVISION
5069
5053
        include_patch, include_bundle = {
5070
5054
            'plain': (False, False),
5071
5055
            'diff': (True, False),
5072
5056
            'bundle': (True, True),
5073
5057
            }[patch_type]
5074
 
        branch = Branch.open('.')
 
5058
        branch = Branch.open(directory)
5075
5059
        stored_submit_branch = branch.get_submit_branch()
5076
5060
        if submit_branch is None:
5077
5061
            submit_branch = stored_submit_branch
5162
5146
    given, in which case it is sent to a file.
5163
5147
 
5164
5148
    Mail is sent using your preferred mail program.  This should be transparent
5165
 
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
 
5149
    on Windows (it uses MAPI).  On Unix, it requires the xdg-email utility.
5166
5150
    If the preferred client can't be found (or used), your editor will be used.
5167
5151
 
5168
5152
    To use a specific mail program, set the mail_client configuration option.
5339
5323
        Option('delete',
5340
5324
            help='Delete this tag rather than placing it.',
5341
5325
            ),
5342
 
        Option('directory',
5343
 
            help='Branch in which to place the tag.',
5344
 
            short_name='d',
5345
 
            type=unicode,
5346
 
            ),
 
5326
        custom_help('directory',
 
5327
            help='Branch in which to place the tag.'),
5347
5328
        Option('force',
5348
5329
            help='Replace existing tags.',
5349
5330
            ),
5357
5338
            revision=None,
5358
5339
            ):
5359
5340
        branch, relpath = Branch.open_containing(directory)
5360
 
        branch.lock_write()
5361
 
        self.add_cleanup(branch.unlock)
 
5341
        self.add_cleanup(branch.lock_write().unlock)
5362
5342
        if delete:
5363
5343
            if tag_name is None:
5364
5344
                raise errors.BzrCommandError("No tag specified to delete.")
5392
5372
 
5393
5373
    _see_also = ['tag']
5394
5374
    takes_options = [
5395
 
        Option('directory',
5396
 
            help='Branch whose tags should be displayed.',
5397
 
            short_name='d',
5398
 
            type=unicode,
5399
 
            ),
 
5375
        custom_help('directory',
 
5376
            help='Branch whose tags should be displayed.'),
5400
5377
        RegistryOption.from_kwargs('sort',
5401
5378
            'Sort tags by different criteria.', title='Sorting',
5402
5379
            alpha='Sort tags lexicographically (default).',
5419
5396
        if not tags:
5420
5397
            return
5421
5398
 
5422
 
        branch.lock_read()
5423
 
        self.add_cleanup(branch.unlock)
 
5399
        self.add_cleanup(branch.lock_read().unlock)
5424
5400
        if revision:
5425
5401
            graph = branch.repository.get_graph()
5426
5402
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
5573
5549
    """
5574
5550
 
5575
5551
    takes_args = ['to_location?']
5576
 
    takes_options = [Option('force',
 
5552
    takes_options = ['directory',
 
5553
                     Option('force',
5577
5554
                        help='Switch even if local commits will be lost.'),
5578
5555
                     'revision',
5579
5556
                     Option('create-branch', short_name='b',
5582
5559
                    ]
5583
5560
 
5584
5561
    def run(self, to_location=None, force=False, create_branch=False,
5585
 
            revision=None):
 
5562
            revision=None, directory=u'.'):
5586
5563
        from bzrlib import switch
5587
 
        tree_location = '.'
 
5564
        tree_location = directory
5588
5565
        revision = _get_one_revision('switch', revision)
5589
5566
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5590
5567
        if to_location is None:
5591
5568
            if revision is None:
5592
5569
                raise errors.BzrCommandError('You must supply either a'
5593
5570
                                             ' revision or a location')
5594
 
            to_location = '.'
 
5571
            to_location = tree_location
5595
5572
        try:
5596
5573
            branch = control_dir.open_branch()
5597
5574
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5872
5849
    takes_args = ['file*']
5873
5850
 
5874
5851
    takes_options = [
 
5852
        'directory',
5875
5853
        'revision',
5876
5854
        Option('all', help='Shelve all changes.'),
5877
5855
        'message',
5886
5864
    _see_also = ['unshelve']
5887
5865
 
5888
5866
    def run(self, revision=None, all=False, file_list=None, message=None,
5889
 
            writer=None, list=False, destroy=False):
 
5867
            writer=None, list=False, destroy=False, directory=u'.'):
5890
5868
        if list:
5891
5869
            return self.run_for_list()
5892
5870
        from bzrlib.shelf_ui import Shelver
5894
5872
            writer = bzrlib.option.diff_writer_registry.get()
5895
5873
        try:
5896
5874
            shelver = Shelver.from_args(writer(sys.stdout), revision, all,
5897
 
                file_list, message, destroy=destroy)
 
5875
                file_list, message, destroy=destroy, directory=directory)
5898
5876
            try:
5899
5877
                shelver.run()
5900
5878
            finally:
5904
5882
 
5905
5883
    def run_for_list(self):
5906
5884
        tree = WorkingTree.open_containing('.')[0]
5907
 
        tree.lock_read()
5908
 
        self.add_cleanup(tree.unlock)
 
5885
        self.add_cleanup(tree.lock_read().unlock)
5909
5886
        manager = tree.get_shelf_manager()
5910
5887
        shelves = manager.active_shelves()
5911
5888
        if len(shelves) == 0:
5929
5906
 
5930
5907
    takes_args = ['shelf_id?']
5931
5908
    takes_options = [
 
5909
        'directory',
5932
5910
        RegistryOption.from_kwargs(
5933
5911
            'action', help="The action to perform.",
5934
5912
            enum_switch=False, value_switches=True,
5942
5920
    ]
5943
5921
    _see_also = ['shelve']
5944
5922
 
5945
 
    def run(self, shelf_id=None, action='apply'):
 
5923
    def run(self, shelf_id=None, action='apply', directory=u'.'):
5946
5924
        from bzrlib.shelf_ui import Unshelver
5947
 
        unshelver = Unshelver.from_args(shelf_id, action)
 
5925
        unshelver = Unshelver.from_args(shelf_id, action, directory=directory)
5948
5926
        try:
5949
5927
            unshelver.run()
5950
5928
        finally:
5966
5944
 
5967
5945
    To check what clean-tree will do, use --dry-run.
5968
5946
    """
5969
 
    takes_options = [Option('ignored', help='Delete all ignored files.'),
 
5947
    takes_options = ['directory',
 
5948
                     Option('ignored', help='Delete all ignored files.'),
5970
5949
                     Option('detritus', help='Delete conflict files, merge'
5971
5950
                            ' backups, and failed selftest dirs.'),
5972
5951
                     Option('unknown',
5975
5954
                            ' deleting them.'),
5976
5955
                     Option('force', help='Do not prompt before deleting.')]
5977
5956
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
5978
 
            force=False):
 
5957
            force=False, directory=u'.'):
5979
5958
        from bzrlib.clean_tree import clean_tree
5980
5959
        if not (unknown or ignored or detritus):
5981
5960
            unknown = True
5982
5961
        if dry_run:
5983
5962
            force = True
5984
 
        clean_tree('.', unknown=unknown, ignored=ignored, detritus=detritus,
5985
 
                   dry_run=dry_run, no_prompt=force)
 
5963
        clean_tree(directory, unknown=unknown, ignored=ignored,
 
5964
                   detritus=detritus, dry_run=dry_run, no_prompt=force)
5986
5965
 
5987
5966
 
5988
5967
class cmd_reference(Command):