~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/commands.py

  • Committer: Aaron Bentley
  • Date: 2005-07-26 14:06:11 UTC
  • mto: (1092.1.41) (1185.3.4) (974.1.47)
  • mto: This revision was merged to the branch mainline in revision 982.
  • Revision ID: abentley@panoramicfeedback.com-20050726140611-403e366f3c79c1f1
Fixed python invocation

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import sys, os
20
20
 
21
21
import bzrlib
22
 
from bzrlib.trace import mutter, note, log_error
 
22
from bzrlib.trace import mutter, note, log_error, warning
23
23
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
24
 
from bzrlib.osutils import quotefn
25
 
from bzrlib import Branch, Inventory, InventoryEntry, BZRDIR, \
26
 
     format_date
 
24
from bzrlib.branch import find_branch
 
25
from bzrlib import BZRDIR
27
26
 
28
27
 
29
28
plugin_cmds = {}
52
51
    assert cmd.startswith("cmd_")
53
52
    return cmd[4:].replace('_','-')
54
53
 
 
54
 
55
55
def _parse_revision_str(revstr):
56
 
    """This handles a revision string -> revno. 
57
 
 
58
 
    There are several possibilities:
59
 
 
60
 
        '234'       -> 234
61
 
        '234:345'   -> [234, 345]
62
 
        ':234'      -> [None, 234]
63
 
        '234:'      -> [234, None]
64
 
 
65
 
    In the future we will also support:
66
 
        'uuid:blah-blah-blah'   -> ?
67
 
        'hash:blahblahblah'     -> ?
68
 
        potentially:
69
 
        'tag:mytag'             -> ?
 
56
    """This handles a revision string -> revno.
 
57
 
 
58
    This always returns a list.  The list will have one element for 
 
59
 
 
60
    It supports integers directly, but everything else it
 
61
    defers for passing to Branch.get_revision_info()
 
62
 
 
63
    >>> _parse_revision_str('234')
 
64
    [234]
 
65
    >>> _parse_revision_str('234..567')
 
66
    [234, 567]
 
67
    >>> _parse_revision_str('..')
 
68
    [None, None]
 
69
    >>> _parse_revision_str('..234')
 
70
    [None, 234]
 
71
    >>> _parse_revision_str('234..')
 
72
    [234, None]
 
73
    >>> _parse_revision_str('234..456..789') # Maybe this should be an error
 
74
    [234, 456, 789]
 
75
    >>> _parse_revision_str('234....789') # Error?
 
76
    [234, None, 789]
 
77
    >>> _parse_revision_str('revid:test@other.com-234234')
 
78
    ['revid:test@other.com-234234']
 
79
    >>> _parse_revision_str('revid:test@other.com-234234..revid:test@other.com-234235')
 
80
    ['revid:test@other.com-234234', 'revid:test@other.com-234235']
 
81
    >>> _parse_revision_str('revid:test@other.com-234234..23')
 
82
    ['revid:test@other.com-234234', 23]
 
83
    >>> _parse_revision_str('date:2005-04-12')
 
84
    ['date:2005-04-12']
 
85
    >>> _parse_revision_str('date:2005-04-12 12:24:33')
 
86
    ['date:2005-04-12 12:24:33']
 
87
    >>> _parse_revision_str('date:2005-04-12T12:24:33')
 
88
    ['date:2005-04-12T12:24:33']
 
89
    >>> _parse_revision_str('date:2005-04-12,12:24:33')
 
90
    ['date:2005-04-12,12:24:33']
 
91
    >>> _parse_revision_str('-5..23')
 
92
    [-5, 23]
 
93
    >>> _parse_revision_str('-5')
 
94
    [-5]
 
95
    >>> _parse_revision_str('123a')
 
96
    ['123a']
 
97
    >>> _parse_revision_str('abc')
 
98
    ['abc']
70
99
    """
71
 
    if revstr.find(':') != -1:
72
 
        revs = revstr.split(':')
73
 
        if len(revs) > 2:
74
 
            raise ValueError('More than 2 pieces not supported for --revision: %r' % revstr)
75
 
 
76
 
        if not revs[0]:
77
 
            revs[0] = None
78
 
        else:
79
 
            revs[0] = int(revs[0])
80
 
 
81
 
        if not revs[1]:
82
 
            revs[1] = None
83
 
        else:
84
 
            revs[1] = int(revs[1])
85
 
    else:
86
 
        revs = int(revstr)
 
100
    import re
 
101
    old_format_re = re.compile('\d*:\d*')
 
102
    m = old_format_re.match(revstr)
 
103
    if m:
 
104
        warning('Colon separator for revision numbers is deprecated.'
 
105
                ' Use .. instead')
 
106
        revs = []
 
107
        for rev in revstr.split(':'):
 
108
            if rev:
 
109
                revs.append(int(rev))
 
110
            else:
 
111
                revs.append(None)
 
112
        return revs
 
113
    revs = []
 
114
    for x in revstr.split('..'):
 
115
        if not x:
 
116
            revs.append(None)
 
117
        else:
 
118
            try:
 
119
                revs.append(int(x))
 
120
            except ValueError:
 
121
                revs.append(x)
87
122
    return revs
88
123
 
89
124
 
165
200
        assert isinstance(arguments, dict)
166
201
        cmdargs = options.copy()
167
202
        cmdargs.update(arguments)
168
 
        assert self.__doc__ != Command.__doc__, \
169
 
               ("No help message set for %r" % self)
 
203
        if self.__doc__ == Command.__doc__:
 
204
            from warnings import warn
 
205
            warn("No help message set for %r" % self)
170
206
        self.status = self.run(**cmdargs)
 
207
        if self.status is None:
 
208
            self.status = 0
171
209
 
172
210
    
173
211
    def run(self):
297
335
    directory is shown.  Otherwise, only the status of the specified
298
336
    files or directories is reported.  If a directory is given, status
299
337
    is reported for everything inside that directory.
 
338
 
 
339
    If a revision is specified, the changes since that revision are shown.
300
340
    """
301
341
    takes_args = ['file*']
302
 
    takes_options = ['all', 'show-ids']
 
342
    takes_options = ['all', 'show-ids', 'revision']
303
343
    aliases = ['st', 'stat']
304
344
    
305
345
    def run(self, all=False, show_ids=False, file_list=None):
306
346
        if file_list:
307
 
            b = Branch(file_list[0])
 
347
            b = find_branch(file_list[0])
308
348
            file_list = [b.relpath(x) for x in file_list]
309
349
            # special case: only one path was given and it's the root
310
350
            # of the branch
311
351
            if file_list == ['']:
312
352
                file_list = None
313
353
        else:
314
 
            b = Branch('.')
315
 
        import status
316
 
        status.show_status(b, show_unchanged=all, show_ids=show_ids,
317
 
                           specific_files=file_list)
 
354
            b = find_branch('.')
 
355
            
 
356
        from bzrlib.status import show_status
 
357
        show_status(b, show_unchanged=all, show_ids=show_ids,
 
358
                    specific_files=file_list)
318
359
 
319
360
 
320
361
class cmd_cat_revision(Command):
324
365
    takes_args = ['revision_id']
325
366
    
326
367
    def run(self, revision_id):
327
 
        Branch('.').get_revision(revision_id).write_xml(sys.stdout)
 
368
        from bzrlib.xml import pack_xml
 
369
        pack_xml(find_branch('.').get_revision(revision_id), sys.stdout)
328
370
 
329
371
 
330
372
class cmd_revno(Command):
332
374
 
333
375
    This is equal to the number of revisions on this branch."""
334
376
    def run(self):
335
 
        print Branch('.').revno()
 
377
        print find_branch('.').revno()
 
378
 
 
379
class cmd_revision_info(Command):
 
380
    """Show revision number and revision id for a given revision identifier.
 
381
    """
 
382
    hidden = True
 
383
    takes_args = ['revision_info*']
 
384
    takes_options = ['revision']
 
385
    def run(self, revision=None, revision_info_list=None):
 
386
        from bzrlib.branch import find_branch
 
387
 
 
388
        revs = []
 
389
        if revision is not None:
 
390
            revs.extend(revision)
 
391
        if revision_info_list is not None:
 
392
            revs.extend(revision_info_list)
 
393
        if len(revs) == 0:
 
394
            raise BzrCommandError('You must supply a revision identifier')
 
395
 
 
396
        b = find_branch('.')
 
397
 
 
398
        for rev in revs:
 
399
            print '%4d %s' % b.get_revision_info(rev)
336
400
 
337
401
    
338
402
class cmd_add(Command):
348
412
    whether already versioned or not, are searched for files or
349
413
    subdirectories that are neither versioned or ignored, and these
350
414
    are added.  This search proceeds recursively into versioned
351
 
    directories.
 
415
    directories.  If no names are given '.' is assumed.
352
416
 
353
 
    Therefore simply saying 'bzr add .' will version all files that
 
417
    Therefore simply saying 'bzr add' will version all files that
354
418
    are currently unknown.
355
419
 
356
420
    TODO: Perhaps adding a file whose directly is not versioned should
357
421
    recursively add that parent, rather than giving an error?
358
422
    """
359
 
    takes_args = ['file+']
 
423
    takes_args = ['file*']
360
424
    takes_options = ['verbose', 'no-recurse']
361
425
    
362
426
    def run(self, file_list, verbose=False, no_recurse=False):
363
 
        bzrlib.add.smart_add(file_list, verbose, not no_recurse)
 
427
        from bzrlib.add import smart_add
 
428
        smart_add(file_list, verbose, not no_recurse)
364
429
 
365
430
 
366
431
 
372
437
    takes_args = ['dir+']
373
438
 
374
439
    def run(self, dir_list):
375
 
        import os
376
 
        import bzrlib.branch
377
 
        
378
440
        b = None
379
441
        
380
442
        for d in dir_list:
381
443
            os.mkdir(d)
382
444
            if not b:
383
 
                b = bzrlib.branch.Branch(d)
 
445
                b = find_branch(d)
384
446
            b.add([d], verbose=True)
385
447
 
386
448
 
390
452
    hidden = True
391
453
    
392
454
    def run(self, filename):
393
 
        print Branch(filename).relpath(filename)
 
455
        print find_branch(filename).relpath(filename)
394
456
 
395
457
 
396
458
 
399
461
    takes_options = ['revision', 'show-ids']
400
462
    
401
463
    def run(self, revision=None, show_ids=False):
402
 
        b = Branch('.')
 
464
        b = find_branch('.')
403
465
        if revision == None:
404
466
            inv = b.read_working_inventory()
405
467
        else:
406
 
            inv = b.get_revision_inventory(b.lookup_revision(revision))
 
468
            if len(revision) > 1:
 
469
                raise BzrCommandError('bzr inventory --revision takes'
 
470
                    ' exactly one revision identifier')
 
471
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
407
472
 
408
473
        for path, entry in inv.entries():
409
474
            if show_ids:
422
487
    """
423
488
    takes_args = ['source$', 'dest']
424
489
    def run(self, source_list, dest):
425
 
        b = Branch('.')
 
490
        b = find_branch('.')
426
491
 
427
492
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
428
493
 
444
509
    takes_args = ['from_name', 'to_name']
445
510
    
446
511
    def run(self, from_name, to_name):
447
 
        b = Branch('.')
 
512
        b = find_branch('.')
448
513
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
449
514
 
450
515
 
469
534
 
470
535
    def run(self, location=None):
471
536
        from bzrlib.merge import merge
 
537
        import tempfile
 
538
        from shutil import rmtree
472
539
        import errno
473
540
        
474
 
        br_to = Branch('.')
 
541
        br_to = find_branch('.')
475
542
        stored_loc = None
476
543
        try:
477
544
            stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
478
545
        except IOError, e:
479
 
            if errno == errno.ENOENT:
 
546
            if e.errno != errno.ENOENT:
480
547
                raise
481
548
        if location is None:
482
549
            if stored_loc is None:
484
551
            else:
485
552
                print "Using last location: %s" % stored_loc
486
553
                location = stored_loc
487
 
        from branch import find_branch, DivergedBranches
 
554
        cache_root = tempfile.mkdtemp()
 
555
        from bzrlib.branch import DivergedBranches
488
556
        br_from = find_branch(location)
489
557
        location = pull_loc(br_from)
490
558
        old_revno = br_to.revno()
491
559
        try:
492
 
            br_to.update_revisions(br_from)
493
 
        except DivergedBranches:
494
 
            raise BzrCommandError("These branches have diverged.  Try merge.")
495
 
            
496
 
        merge(('.', -1), ('.', old_revno), check_clean=False)
497
 
        if location != stored_loc:
498
 
            br_to.controlfile("x-pull", "wb").write(location + "\n")
 
560
            from branch import find_cached_branch, DivergedBranches
 
561
            br_from = find_cached_branch(location, cache_root)
 
562
            location = pull_loc(br_from)
 
563
            old_revno = br_to.revno()
 
564
            try:
 
565
                br_to.update_revisions(br_from)
 
566
            except DivergedBranches:
 
567
                raise BzrCommandError("These branches have diverged."
 
568
                    "  Try merge.")
 
569
                
 
570
            merge(('.', -1), ('.', old_revno), check_clean=False)
 
571
            if location != stored_loc:
 
572
                br_to.controlfile("x-pull", "wb").write(location + "\n")
 
573
        finally:
 
574
            rmtree(cache_root)
499
575
 
500
576
 
501
577
 
514
590
    def run(self, from_location, to_location=None, revision=None):
515
591
        import errno
516
592
        from bzrlib.merge import merge
517
 
        from branch import find_branch, DivergedBranches, NoSuchRevision
 
593
        from bzrlib.branch import DivergedBranches, NoSuchRevision, \
 
594
             find_cached_branch, Branch
518
595
        from shutil import rmtree
519
 
        try:
520
 
            br_from = find_branch(from_location)
521
 
        except OSError, e:
522
 
            if e.errno == errno.ENOENT:
523
 
                raise BzrCommandError('Source location "%s" does not exist.' %
524
 
                                      to_location)
525
 
            else:
526
 
                raise
527
 
 
528
 
        if to_location is None:
529
 
            to_location = os.path.basename(from_location.rstrip("/\\"))
530
 
 
531
 
        try:
532
 
            os.mkdir(to_location)
533
 
        except OSError, e:
534
 
            if e.errno == errno.EEXIST:
535
 
                raise BzrCommandError('Target directory "%s" already exists.' %
536
 
                                      to_location)
537
 
            if e.errno == errno.ENOENT:
538
 
                raise BzrCommandError('Parent of "%s" does not exist.' %
539
 
                                      to_location)
540
 
            else:
541
 
                raise
542
 
        br_to = Branch(to_location, init=True)
543
 
 
544
 
        try:
545
 
            br_to.update_revisions(br_from, stop_revision=revision)
546
 
        except NoSuchRevision:
547
 
            rmtree(to_location)
548
 
            msg = "The branch %s has no revision %d." % (from_location,
549
 
                                                         revision)
550
 
            raise BzrCommandError(msg)
551
 
        merge((to_location, -1), (to_location, 0), this_dir=to_location,
552
 
              check_clean=False, ignore_zero=True)
553
 
        from_location = pull_loc(br_from)
554
 
        br_to.controlfile("x-pull", "wb").write(from_location + "\n")
 
596
        from meta_store import CachedStore
 
597
        import tempfile
 
598
        cache_root = tempfile.mkdtemp()
 
599
 
 
600
        if revision is None:
 
601
            revision = [None]
 
602
        elif len(revision) > 1:
 
603
            raise BzrCommandError('bzr branch --revision takes exactly 1 revision value')
 
604
 
 
605
        try:
 
606
            try:
 
607
                br_from = find_cached_branch(from_location, cache_root)
 
608
            except OSError, e:
 
609
                if e.errno == errno.ENOENT:
 
610
                    raise BzrCommandError('Source location "%s" does not'
 
611
                                          ' exist.' % to_location)
 
612
                else:
 
613
                    raise
 
614
 
 
615
            if to_location is None:
 
616
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
617
 
 
618
            try:
 
619
                os.mkdir(to_location)
 
620
            except OSError, e:
 
621
                if e.errno == errno.EEXIST:
 
622
                    raise BzrCommandError('Target directory "%s" already'
 
623
                                          ' exists.' % to_location)
 
624
                if e.errno == errno.ENOENT:
 
625
                    raise BzrCommandError('Parent of "%s" does not exist.' %
 
626
                                          to_location)
 
627
                else:
 
628
                    raise
 
629
            br_to = Branch(to_location, init=True)
 
630
 
 
631
            br_to.set_root_id(br_from.get_root_id())
 
632
 
 
633
            if revision:
 
634
                if revision[0] is None:
 
635
                    revno = br_from.revno()
 
636
                else:
 
637
                    revno, rev_id = br_from.get_revision_info(revision[0])
 
638
                try:
 
639
                    br_to.update_revisions(br_from, stop_revision=revno)
 
640
                except NoSuchRevision:
 
641
                    rmtree(to_location)
 
642
                    msg = "The branch %s has no revision %d." % (from_location,
 
643
                                                                 revno)
 
644
                    raise BzrCommandError(msg)
 
645
            
 
646
            merge((to_location, -1), (to_location, 0), this_dir=to_location,
 
647
                  check_clean=False, ignore_zero=True)
 
648
            from_location = pull_loc(br_from)
 
649
            br_to.controlfile("x-pull", "wb").write(from_location + "\n")
 
650
        finally:
 
651
            rmtree(cache_root)
555
652
 
556
653
 
557
654
def pull_loc(branch):
574
671
    takes_args = ['dir?']
575
672
 
576
673
    def run(self, dir='.'):
577
 
        b = Branch(dir)
 
674
        b = find_branch(dir)
578
675
        old_inv = b.basis_tree().inventory
579
676
        new_inv = b.read_working_inventory()
580
677
 
591
688
    def run(self, branch=None):
592
689
        import info
593
690
 
594
 
        from branch import find_branch
595
691
        b = find_branch(branch)
596
692
        info.show_info(b)
597
693
 
606
702
    takes_options = ['verbose']
607
703
    
608
704
    def run(self, file_list, verbose=False):
609
 
        b = Branch(file_list[0])
 
705
        b = find_branch(file_list[0])
610
706
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
611
707
 
612
708
 
620
716
    hidden = True
621
717
    takes_args = ['filename']
622
718
    def run(self, filename):
623
 
        b = Branch(filename)
 
719
        b = find_branch(filename)
624
720
        i = b.inventory.path2id(b.relpath(filename))
625
721
        if i == None:
626
722
            raise BzrError("%r is not a versioned file" % filename)
636
732
    hidden = True
637
733
    takes_args = ['filename']
638
734
    def run(self, filename):
639
 
        b = Branch(filename)
 
735
        b = find_branch(filename)
640
736
        inv = b.inventory
641
737
        fid = inv.path2id(b.relpath(filename))
642
738
        if fid == None:
649
745
    """Display list of revision ids on this branch."""
650
746
    hidden = True
651
747
    def run(self):
652
 
        for patchid in Branch('.').revision_history():
 
748
        for patchid in find_branch('.').revision_history():
653
749
            print patchid
654
750
 
655
751
 
656
752
class cmd_directories(Command):
657
753
    """Display list of versioned directories in this branch."""
658
754
    def run(self):
659
 
        for name, ie in Branch('.').read_working_inventory().directories():
 
755
        for name, ie in find_branch('.').read_working_inventory().directories():
660
756
            if name == '':
661
757
                print '.'
662
758
            else:
677
773
        bzr commit -m 'imported project'
678
774
    """
679
775
    def run(self):
 
776
        from bzrlib.branch import Branch
680
777
        Branch('.', init=True)
681
778
 
682
779
 
710
807
 
711
808
    def run(self, revision=None, file_list=None, diff_options=None):
712
809
        from bzrlib.diff import show_diff
713
 
        from bzrlib import find_branch
714
810
 
715
811
        if file_list:
716
812
            b = find_branch(file_list[0])
719
815
                # just pointing to top-of-tree
720
816
                file_list = None
721
817
        else:
722
 
            b = Branch('.')
 
818
            b = find_branch('.')
 
819
 
 
820
        # TODO: Make show_diff support taking 2 arguments
 
821
        base_rev = None
 
822
        if revision is not None:
 
823
            if len(revision) != 1:
 
824
                raise BzrCommandError('bzr diff --revision takes exactly one revision identifier')
 
825
            base_rev = revision[0]
723
826
    
724
 
        show_diff(b, revision, specific_files=file_list,
 
827
        show_diff(b, base_rev, specific_files=file_list,
725
828
                  external_diff_options=diff_options)
726
829
 
727
830
 
734
837
    TODO: Show files deleted since a previous revision, or between two revisions.
735
838
    """
736
839
    def run(self, show_ids=False):
737
 
        b = Branch('.')
 
840
        b = find_branch('.')
738
841
        old = b.basis_tree()
739
842
        new = b.working_tree()
740
843
 
755
858
    """List files modified in working tree."""
756
859
    hidden = True
757
860
    def run(self):
758
 
        import statcache
759
 
        b = Branch('.')
760
 
        inv = b.read_working_inventory()
761
 
        sc = statcache.update_cache(b, inv)
762
 
        basis = b.basis_tree()
763
 
        basis_inv = basis.inventory
764
 
        
765
 
        # We used to do this through iter_entries(), but that's slow
766
 
        # when most of the files are unmodified, as is usually the
767
 
        # case.  So instead we iterate by inventory entry, and only
768
 
        # calculate paths as necessary.
769
 
 
770
 
        for file_id in basis_inv:
771
 
            cacheentry = sc.get(file_id)
772
 
            if not cacheentry:                 # deleted
773
 
                continue
774
 
            ie = basis_inv[file_id]
775
 
            if cacheentry[statcache.SC_SHA1] != ie.text_sha1:
776
 
                path = inv.id2path(file_id)
777
 
                print path
 
861
        from bzrlib.diff import compare_trees
 
862
 
 
863
        b = find_branch('.')
 
864
        td = compare_trees(b.basis_tree(), b.working_tree())
 
865
 
 
866
        for path, id, kind in td.modified:
 
867
            print path
778
868
 
779
869
 
780
870
 
782
872
    """List files added in working tree."""
783
873
    hidden = True
784
874
    def run(self):
785
 
        b = Branch('.')
 
875
        b = find_branch('.')
786
876
        wt = b.working_tree()
787
877
        basis_inv = b.basis_tree().inventory
788
878
        inv = wt.inventory
804
894
    takes_args = ['filename?']
805
895
    def run(self, filename=None):
806
896
        """Print the branch root."""
807
 
        from branch import find_branch
808
897
        b = find_branch(filename)
809
898
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
810
899
 
816
905
    -r revision requests a specific revision, -r :end or -r begin: are
817
906
    also valid.
818
907
 
 
908
    --message allows you to give a regular expression, which will be evaluated
 
909
    so that only matching entries will be displayed.
 
910
 
819
911
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
820
912
  
821
913
    """
822
914
 
823
915
    takes_args = ['filename?']
824
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision']
 
916
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision','long', 'message']
825
917
    
826
918
    def run(self, filename=None, timezone='original',
827
919
            verbose=False,
828
920
            show_ids=False,
829
921
            forward=False,
830
 
            revision=None):
831
 
        from bzrlib import show_log, find_branch
 
922
            revision=None,
 
923
            message=None,
 
924
            long=False):
 
925
        from bzrlib.branch import find_branch
 
926
        from bzrlib.log import log_formatter, show_log
832
927
        import codecs
833
928
 
834
929
        direction = (forward and 'forward') or 'reverse'
844
939
            b = find_branch('.')
845
940
            file_id = None
846
941
 
847
 
        if revision == None:
848
 
            revision = [None, None]
849
 
        elif isinstance(revision, int):
850
 
            revision = [revision, revision]
 
942
        if revision is None:
 
943
            rev1 = None
 
944
            rev2 = None
 
945
        elif len(revision) == 1:
 
946
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
 
947
        elif len(revision) == 2:
 
948
            rev1 = b.get_revision_info(revision[0])[0]
 
949
            rev2 = b.get_revision_info(revision[1])[0]
851
950
        else:
852
 
            # pair of revisions?
853
 
            pass
854
 
            
855
 
        assert len(revision) == 2
 
951
            raise BzrCommandError('bzr log --revision takes one or two values.')
 
952
 
 
953
        if rev1 == 0:
 
954
            rev1 = None
 
955
        if rev2 == 0:
 
956
            rev2 = None
856
957
 
857
958
        mutter('encoding log as %r' % bzrlib.user_encoding)
858
959
 
860
961
        # in e.g. the default C locale.
861
962
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
862
963
 
863
 
        show_log(b, file_id,
864
 
                 show_timezone=timezone,
 
964
        if long:
 
965
            log_format = 'long'
 
966
        else:
 
967
            log_format = 'short'
 
968
        lf = log_formatter(log_format,
 
969
                           show_ids=show_ids,
 
970
                           to_file=outf,
 
971
                           show_timezone=timezone)
 
972
 
 
973
        show_log(b,
 
974
                 lf,
 
975
                 file_id,
865
976
                 verbose=verbose,
866
 
                 show_ids=show_ids,
867
 
                 to_file=outf,
868
977
                 direction=direction,
869
 
                 start_revision=revision[0],
870
 
                 end_revision=revision[1])
 
978
                 start_revision=rev1,
 
979
                 end_revision=rev2,
 
980
                 search=message)
871
981
 
872
982
 
873
983
 
878
988
    hidden = True
879
989
    takes_args = ["filename"]
880
990
    def run(self, filename):
881
 
        b = Branch(filename)
 
991
        b = find_branch(filename)
882
992
        inv = b.read_working_inventory()
883
993
        file_id = inv.path2id(b.relpath(filename))
884
994
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
892
1002
    """
893
1003
    hidden = True
894
1004
    def run(self, revision=None, verbose=False):
895
 
        b = Branch('.')
 
1005
        b = find_branch('.')
896
1006
        if revision == None:
897
1007
            tree = b.working_tree()
898
1008
        else:
916
1026
class cmd_unknowns(Command):
917
1027
    """List unknown files."""
918
1028
    def run(self):
919
 
        for f in Branch('.').unknowns():
 
1029
        from bzrlib.osutils import quotefn
 
1030
        for f in find_branch('.').unknowns():
920
1031
            print quotefn(f)
921
1032
 
922
1033
 
944
1055
        from bzrlib.atomicfile import AtomicFile
945
1056
        import os.path
946
1057
 
947
 
        b = Branch('.')
 
1058
        b = find_branch('.')
948
1059
        ifn = b.abspath('.bzrignore')
949
1060
 
950
1061
        if os.path.exists(ifn):
984
1095
 
985
1096
    See also: bzr ignore"""
986
1097
    def run(self):
987
 
        tree = Branch('.').working_tree()
 
1098
        tree = find_branch('.').working_tree()
988
1099
        for path, file_class, kind, file_id in tree.list_files():
989
1100
            if file_class != 'I':
990
1101
                continue
1008
1119
        except ValueError:
1009
1120
            raise BzrCommandError("not a valid revision-number: %r" % revno)
1010
1121
 
1011
 
        print Branch('.').lookup_revision(revno)
 
1122
        print find_branch('.').lookup_revision(revno)
1012
1123
 
1013
1124
 
1014
1125
class cmd_export(Command):
1017
1128
    If no revision is specified this exports the last committed revision.
1018
1129
 
1019
1130
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
1020
 
    given, exports to a directory (equivalent to --format=dir)."""
 
1131
    given, try to find the format with the extension. If no extension
 
1132
    is found exports to a directory (equivalent to --format=dir).
 
1133
 
 
1134
    Root may be the top directory for tar, tgz and tbz2 formats. If none
 
1135
    is given, the top directory will be the root name of the file."""
1021
1136
    # TODO: list known exporters
1022
1137
    takes_args = ['dest']
1023
 
    takes_options = ['revision', 'format']
1024
 
    def run(self, dest, revision=None, format='dir'):
1025
 
        b = Branch('.')
1026
 
        if revision == None:
1027
 
            rh = b.revision_history()[-1]
 
1138
    takes_options = ['revision', 'format', 'root']
 
1139
    def run(self, dest, revision=None, format=None, root=None):
 
1140
        import os.path
 
1141
        b = find_branch('.')
 
1142
        if revision is None:
 
1143
            rev_id = b.last_patch()
1028
1144
        else:
1029
 
            rh = b.lookup_revision(int(revision))
1030
 
        t = b.revision_tree(rh)
1031
 
        t.export(dest, format)
 
1145
            if len(revision) != 1:
 
1146
                raise BzrError('bzr export --revision takes exactly 1 argument')
 
1147
            revno, rev_id = b.get_revision_info(revision[0])
 
1148
        t = b.revision_tree(rev_id)
 
1149
        root, ext = os.path.splitext(dest)
 
1150
        if not format:
 
1151
            if ext in (".tar",):
 
1152
                format = "tar"
 
1153
            elif ext in (".gz", ".tgz"):
 
1154
                format = "tgz"
 
1155
            elif ext in (".bz2", ".tbz2"):
 
1156
                format = "tbz2"
 
1157
            else:
 
1158
                format = "dir"
 
1159
        t.export(dest, format, root)
1032
1160
 
1033
1161
 
1034
1162
class cmd_cat(Command):
1040
1168
    def run(self, filename, revision=None):
1041
1169
        if revision == None:
1042
1170
            raise BzrCommandError("bzr cat requires a revision number")
1043
 
        b = Branch('.')
1044
 
        b.print_file(b.relpath(filename), int(revision))
 
1171
        elif len(revision) != 1:
 
1172
            raise BzrCommandError("bzr cat --revision takes exactly one number")
 
1173
        b = find_branch('.')
 
1174
        b.print_file(b.relpath(filename), revision[0])
1045
1175
 
1046
1176
 
1047
1177
class cmd_local_time_offset(Command):
1068
1198
    TODO: Strict commit that fails if there are unknown or deleted files.
1069
1199
    """
1070
1200
    takes_args = ['selected*']
1071
 
    takes_options = ['message', 'file', 'verbose']
 
1201
    takes_options = ['message', 'file', 'verbose', 'unchanged']
1072
1202
    aliases = ['ci', 'checkin']
1073
1203
 
1074
 
    def run(self, message=None, file=None, verbose=True, selected_list=None):
1075
 
        from bzrlib.commit import commit
 
1204
    def run(self, message=None, file=None, verbose=True, selected_list=None,
 
1205
            unchanged=False):
 
1206
        from bzrlib.errors import PointlessCommit
1076
1207
        from bzrlib.osutils import get_text_message
1077
1208
 
1078
1209
        ## Warning: shadows builtin file()
1096
1227
            import codecs
1097
1228
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1098
1229
 
1099
 
        b = Branch('.')
1100
 
        commit(b, message, verbose=verbose, specific_files=selected_list)
 
1230
        b = find_branch('.')
 
1231
 
 
1232
        try:
 
1233
            b.commit(message, verbose=verbose,
 
1234
                     specific_files=selected_list,
 
1235
                     allow_pointless=unchanged)
 
1236
        except PointlessCommit:
 
1237
            # FIXME: This should really happen before the file is read in;
 
1238
            # perhaps prepare the commit; get the message; then actually commit
 
1239
            raise BzrCommandError("no changes to commit",
 
1240
                                  ["use --unchanged to commit anyhow"])
1101
1241
 
1102
1242
 
1103
1243
class cmd_check(Command):
1112
1252
    takes_args = ['dir?']
1113
1253
 
1114
1254
    def run(self, dir='.'):
1115
 
        import bzrlib.check
1116
 
        bzrlib.check.check(Branch(dir))
1117
 
 
 
1255
        from bzrlib.check import check
 
1256
        check(find_branch(dir))
 
1257
 
 
1258
 
 
1259
 
 
1260
class cmd_scan_cache(Command):
 
1261
    hidden = True
 
1262
    def run(self):
 
1263
        from bzrlib.hashcache import HashCache
 
1264
        import os
 
1265
 
 
1266
        c = HashCache('.')
 
1267
        c.read()
 
1268
        c.scan()
 
1269
            
 
1270
        print '%6d stats' % c.stat_count
 
1271
        print '%6d in hashcache' % len(c._cache)
 
1272
        print '%6d files removed from cache' % c.removed_count
 
1273
        print '%6d hashes updated' % c.update_count
 
1274
        print '%6d files changed too recently to cache' % c.danger_count
 
1275
 
 
1276
        if c.needs_write:
 
1277
            c.write()
 
1278
            
1118
1279
 
1119
1280
 
1120
1281
class cmd_upgrade(Command):
1127
1288
 
1128
1289
    def run(self, dir='.'):
1129
1290
        from bzrlib.upgrade import upgrade
1130
 
        upgrade(Branch(dir))
 
1291
        upgrade(find_branch(dir))
1131
1292
 
1132
1293
 
1133
1294
 
1145
1306
class cmd_selftest(Command):
1146
1307
    """Run internal test suite"""
1147
1308
    hidden = True
1148
 
    def run(self):
 
1309
    takes_options = ['verbose']
 
1310
    def run(self, verbose=False):
1149
1311
        from bzrlib.selftest import selftest
1150
 
        return int(not selftest())
 
1312
        return int(not selftest(verbose=verbose))
1151
1313
 
1152
1314
 
1153
1315
class cmd_version(Command):
1185
1347
    ['..', -1]
1186
1348
    >>> parse_spec("../f/@35")
1187
1349
    ['../f', 35]
 
1350
    >>> parse_spec('./@revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67')
 
1351
    ['.', 'revid:john@arbash-meinel.com-20050711044610-3ca0327c6a222f67']
1188
1352
    """
1189
1353
    if spec is None:
1190
1354
        return [None, None]
1194
1358
        if parsed[1] == "":
1195
1359
            parsed[1] = -1
1196
1360
        else:
1197
 
            parsed[1] = int(parsed[1])
1198
 
            assert parsed[1] >=0
 
1361
            try:
 
1362
                parsed[1] = int(parsed[1])
 
1363
            except ValueError:
 
1364
                pass # We can allow stuff like ./@revid:blahblahblah
 
1365
            else:
 
1366
                assert parsed[1] >=0
1199
1367
    else:
1200
1368
        parsed = [spec, None]
1201
1369
    return parsed
1259
1427
    """
1260
1428
    takes_options = ['revision']
1261
1429
 
1262
 
    def run(self, revision=-1):
 
1430
    def run(self, revision=None):
1263
1431
        from bzrlib.merge import merge
1264
 
        merge(('.', revision), parse_spec('.'),
 
1432
        if revision is None:
 
1433
            revision = [-1]
 
1434
        elif len(revision) != 1:
 
1435
            raise BzrCommandError('bzr merge-revert --revision takes exactly 1 argument')
 
1436
        merge(('.', revision[0]), parse_spec('.'),
1265
1437
              check_clean=False,
1266
1438
              ignore_zero=True)
1267
1439
 
1285
1457
        help.help(topic)
1286
1458
 
1287
1459
 
1288
 
class cmd_update_stat_cache(Command):
1289
 
    """Update stat-cache mapping inodes to SHA-1 hashes.
1290
 
 
1291
 
    For testing only."""
1292
 
    hidden = True
1293
 
    def run(self):
1294
 
        import statcache
1295
 
        b = Branch('.')
1296
 
        statcache.update_cache(b.base, b.read_working_inventory())
1297
 
 
1298
1460
 
1299
1461
 
1300
1462
class cmd_plugins(Command):
1302
1464
    hidden = True
1303
1465
    def run(self):
1304
1466
        import bzrlib.plugin
 
1467
        from inspect import getdoc
1305
1468
        from pprint import pprint
1306
 
        pprint(bzrlib.plugin.all_plugins)
 
1469
        for plugin in bzrlib.plugin.all_plugins:
 
1470
            print plugin.__path__[0]
 
1471
            d = getdoc(plugin)
 
1472
            if d:
 
1473
                print '\t', d.split('\n')[0]
 
1474
 
 
1475
        #pprint(bzrlib.plugin.all_plugins)
1307
1476
 
1308
1477
 
1309
1478
 
1327
1496
    'verbose':                None,
1328
1497
    'version':                None,
1329
1498
    'email':                  None,
 
1499
    'unchanged':              None,
1330
1500
    'update':                 None,
 
1501
    'long':                   None,
 
1502
    'root':                   str,
1331
1503
    }
1332
1504
 
1333
1505
SHORT_OPTIONS = {
1336
1508
    'm':                      'message',
1337
1509
    'r':                      'revision',
1338
1510
    'v':                      'verbose',
 
1511
    'l':                      'long',
1339
1512
}
1340
1513
 
1341
1514
 
1356
1529
    >>> parse_args('commit --message=biter'.split())
1357
1530
    (['commit'], {'message': u'biter'})
1358
1531
    >>> parse_args('log -r 500'.split())
1359
 
    (['log'], {'revision': 500})
1360
 
    >>> parse_args('log -r500:600'.split())
 
1532
    (['log'], {'revision': [500]})
 
1533
    >>> parse_args('log -r500..600'.split())
1361
1534
    (['log'], {'revision': [500, 600]})
1362
 
    >>> parse_args('log -vr500:600'.split())
 
1535
    >>> parse_args('log -vr500..600'.split())
1363
1536
    (['log'], {'verbose': True, 'revision': [500, 600]})
1364
 
    >>> parse_args('log -rv500:600'.split()) #the r takes an argument
1365
 
    Traceback (most recent call last):
1366
 
    ...
1367
 
    ValueError: invalid literal for int(): v500
 
1537
    >>> parse_args('log -rv500..600'.split()) #the r takes an argument
 
1538
    (['log'], {'revision': ['v500', 600]})
1368
1539
    """
1369
1540
    args = []
1370
1541
    opts = {}
1504
1675
                    This is also a non-master option.
1505
1676
        --help      Run help and exit, also a non-master option (I think that should stay, though)
1506
1677
 
1507
 
    >>> argv, opts = _parse_master_args(['bzr', '--test'])
 
1678
    >>> argv, opts = _parse_master_args(['--test'])
1508
1679
    Traceback (most recent call last):
1509
1680
    ...
1510
1681
    BzrCommandError: Invalid master option: 'test'
1511
 
    >>> argv, opts = _parse_master_args(['bzr', '--version', 'command'])
 
1682
    >>> argv, opts = _parse_master_args(['--version', 'command'])
1512
1683
    >>> print argv
1513
1684
    ['command']
1514
1685
    >>> print opts['version']
1515
1686
    True
1516
 
    >>> argv, opts = _parse_master_args(['bzr', '--profile', 'command', '--more-options'])
 
1687
    >>> argv, opts = _parse_master_args(['--profile', 'command', '--more-options'])
1517
1688
    >>> print argv
1518
1689
    ['command', '--more-options']
1519
1690
    >>> print opts['profile']
1520
1691
    True
1521
 
    >>> argv, opts = _parse_master_args(['bzr', '--no-plugins', 'command'])
 
1692
    >>> argv, opts = _parse_master_args(['--no-plugins', 'command'])
1522
1693
    >>> print argv
1523
1694
    ['command']
1524
1695
    >>> print opts['no-plugins']
1525
1696
    True
1526
1697
    >>> print opts['profile']
1527
1698
    False
1528
 
    >>> argv, opts = _parse_master_args(['bzr', 'command', '--profile'])
 
1699
    >>> argv, opts = _parse_master_args(['command', '--profile'])
1529
1700
    >>> print argv
1530
1701
    ['command', '--profile']
1531
1702
    >>> print opts['profile']
1538
1709
        'help':False
1539
1710
    }
1540
1711
 
1541
 
    # This is the point where we could hook into argv[0] to determine
1542
 
    # what front-end is supposed to be run
1543
 
    # For now, we are just ignoring it.
1544
 
    cmd_name = argv.pop(0)
1545
1712
    for arg in argv[:]:
1546
1713
        if arg[:2] != '--': # at the first non-option, we return the rest
1547
1714
            break
1561
1728
 
1562
1729
    This is similar to main(), but without all the trappings for
1563
1730
    logging and error handling.  
 
1731
    
 
1732
    argv
 
1733
       The command-line arguments, without the program name.
 
1734
    
 
1735
    Returns a command status or raises an exception.
1564
1736
    """
1565
1737
    argv = [a.decode(bzrlib.user_encoding) for a in argv]
 
1738
 
 
1739
    # some options like --builtin and --no-plugins have special effects
 
1740
    argv, master_opts = _parse_master_args(argv)
 
1741
    if not master_opts['no-plugins']:
 
1742
        from bzrlib.plugin import load_plugins
 
1743
        load_plugins()
 
1744
 
 
1745
    args, opts = parse_args(argv)
 
1746
 
 
1747
    if master_opts.get('help') or 'help' in opts:
 
1748
        from bzrlib.help import help
 
1749
        if argv:
 
1750
            help(argv[0])
 
1751
        else:
 
1752
            help()
 
1753
        return 0            
 
1754
        
 
1755
    if 'version' in opts:
 
1756
        show_version()
 
1757
        return 0
 
1758
    
 
1759
    if args and args[0] == 'builtin':
 
1760
        include_plugins=False
 
1761
        args = args[1:]
1566
1762
    
1567
1763
    try:
1568
 
        # some options like --builtin and --no-plugins have special effects
1569
 
        argv, master_opts = _parse_master_args(argv)
1570
 
        if not master_opts['no-plugins']:
1571
 
            bzrlib.load_plugins()
1572
 
 
1573
 
        args, opts = parse_args(argv)
1574
 
 
1575
 
        if master_opts['help']:
1576
 
            from bzrlib.help import help
1577
 
            if argv:
1578
 
                help(argv[0])
1579
 
            else:
1580
 
                help()
1581
 
            return 0            
1582
 
            
1583
 
        if 'help' in opts:
1584
 
            from bzrlib.help import help
1585
 
            if args:
1586
 
                help(args[0])
1587
 
            else:
1588
 
                help()
1589
 
            return 0
1590
 
        elif 'version' in opts:
1591
 
            show_version()
1592
 
            return 0
1593
 
        elif args and args[0] == 'builtin':
1594
 
            include_plugins=False
1595
 
            args = args[1:]
1596
1764
        cmd = str(args.pop(0))
1597
1765
    except IndexError:
1598
 
        import help
1599
 
        help.help()
 
1766
        print >>sys.stderr, "please try 'bzr help' for help"
1600
1767
        return 1
1601
 
          
1602
1768
 
1603
1769
    plugins_override = not (master_opts['builtin'])
1604
1770
    canonical_cmd, cmd_class = get_cmd_class(cmd, plugins_override=plugins_override)
1663
1829
 
1664
1830
 
1665
1831
def main(argv):
1666
 
    import errno
1667
1832
    
1668
 
    bzrlib.open_tracefile(argv)
 
1833
    bzrlib.trace.open_tracefile(argv)
1669
1834
 
1670
1835
    try:
1671
1836
        try:
1672
1837
            try:
1673
 
                return run_bzr(argv)
 
1838
                return run_bzr(argv[1:])
1674
1839
            finally:
1675
1840
                # do this here inside the exception wrappers to catch EPIPE
1676
1841
                sys.stdout.flush()
1692
1857
            _report_exception('interrupted', quiet=True)
1693
1858
            return 2
1694
1859
        except Exception, e:
 
1860
            import errno
1695
1861
            quiet = False
1696
1862
            if (isinstance(e, IOError) 
1697
1863
                and hasattr(e, 'errno')