~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2005-09-01 11:53:02 UTC
  • Revision ID: mbp@sourcefrog.net-20050901115302-2fcc6c750f0abe34
- make external commands work again

  code is now much simpler; no translation to objects and back again

Show diffs side-by-side

added added

removed removed

Lines of Context:
21
21
import bzrlib
22
22
import bzrlib.trace
23
23
from bzrlib.trace import mutter, note, log_error, warning
24
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
25
 
from bzrlib.errors import DivergedBranches
26
 
from bzrlib.branch import Branch
 
24
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
 
25
from bzrlib.branch import find_branch
27
26
from bzrlib import BZRDIR
28
27
from bzrlib.commands import Command
29
28
 
64
63
    files or directories is reported.  If a directory is given, status
65
64
    is reported for everything inside that directory.
66
65
 
67
 
    If a revision argument is given, the status is calculated against
68
 
    that revision, or between two revisions if two are provided.
 
66
    If a revision is specified, the changes since that revision are shown.
69
67
    """
70
 
    # XXX: FIXME: bzr status should accept a -r option to show changes
71
 
    # relative to a revision, or between revisions
72
 
 
73
68
    takes_args = ['file*']
74
 
    takes_options = ['all', 'show-ids']
 
69
    takes_options = ['all', 'show-ids', 'revision']
75
70
    aliases = ['st', 'stat']
76
71
    
77
 
    def run(self, all=False, show_ids=False, file_list=None, revision=None):
 
72
    def run(self, all=False, show_ids=False, file_list=None):
78
73
        if file_list:
79
 
            b = Branch.open_containing(file_list[0])
 
74
            b = find_branch(file_list[0])
80
75
            file_list = [b.relpath(x) for x in file_list]
81
76
            # special case: only one path was given and it's the root
82
77
            # of the branch
83
78
            if file_list == ['']:
84
79
                file_list = None
85
80
        else:
86
 
            b = Branch.open_containing('.')
 
81
            b = find_branch('.')
87
82
            
88
83
        from bzrlib.status import show_status
89
84
        show_status(b, show_unchanged=all, show_ids=show_ids,
90
 
                    specific_files=file_list, revision=revision)
 
85
                    specific_files=file_list)
91
86
 
92
87
 
93
88
class cmd_cat_revision(Command):
94
 
    """Write out metadata for a revision.
95
 
    
96
 
    The revision to print can either be specified by a specific
97
 
    revision identifier, or you can use --revision.
98
 
    """
 
89
    """Write out metadata for a revision."""
99
90
 
100
91
    hidden = True
101
 
    takes_args = ['revision_id?']
102
 
    takes_options = ['revision']
 
92
    takes_args = ['revision_id']
103
93
    
104
 
    def run(self, revision_id=None, revision=None):
105
 
        from bzrlib.revisionspec import RevisionSpec
 
94
    def run(self, revision_id):
 
95
        from bzrlib.xml import pack_xml
 
96
        pack_xml(find_branch('.').get_revision(revision_id), sys.stdout)
106
97
 
107
 
        if revision_id is not None and revision is not None:
108
 
            raise BzrCommandError('You can only supply one of revision_id or --revision')
109
 
        if revision_id is None and revision is None:
110
 
            raise BzrCommandError('You must supply either --revision or a revision_id')
111
 
        b = Branch.open_containing('.')
112
 
        if revision_id is not None:
113
 
            sys.stdout.write(b.get_revision_xml_file(revision_id).read())
114
 
        elif revision is not None:
115
 
            for rev in revision:
116
 
                if rev is None:
117
 
                    raise BzrCommandError('You cannot specify a NULL revision.')
118
 
                revno, rev_id = rev.in_history(b)
119
 
                sys.stdout.write(b.get_revision_xml_file(rev_id).read())
120
 
    
121
98
 
122
99
class cmd_revno(Command):
123
100
    """Show current revision number.
124
101
 
125
102
    This is equal to the number of revisions on this branch."""
126
103
    def run(self):
127
 
        print Branch.open_containing('.').revno()
128
 
 
 
104
        print find_branch('.').revno()
129
105
 
130
106
class cmd_revision_info(Command):
131
107
    """Show revision number and revision id for a given revision identifier.
133
109
    hidden = True
134
110
    takes_args = ['revision_info*']
135
111
    takes_options = ['revision']
136
 
    def run(self, revision=None, revision_info_list=[]):
137
 
        from bzrlib.revisionspec import RevisionSpec
 
112
    def run(self, revision=None, revision_info_list=None):
 
113
        from bzrlib.branch import find_branch
138
114
 
139
115
        revs = []
140
116
        if revision is not None:
141
117
            revs.extend(revision)
142
118
        if revision_info_list is not None:
143
 
            for rev in revision_info_list:
144
 
                revs.append(RevisionSpec(rev))
 
119
            revs.extend(revision_info_list)
145
120
        if len(revs) == 0:
146
121
            raise BzrCommandError('You must supply a revision identifier')
147
122
 
148
 
        b = Branch.open_containing('.')
 
123
        b = find_branch('.')
149
124
 
150
125
        for rev in revs:
151
 
            revinfo = rev.in_history(b)
152
 
            if revinfo.revno is None:
153
 
                print '     %s' % revinfo.rev_id
154
 
            else:
155
 
                print '%4d %s' % (revinfo.revno, revinfo.rev_id)
 
126
            print '%4d %s' % b.get_revision_info(rev)
156
127
 
157
128
    
158
129
class cmd_add(Command):
173
144
    Therefore simply saying 'bzr add' will version all files that
174
145
    are currently unknown.
175
146
 
176
 
    Adding a file whose parent directory is not versioned will
177
 
    implicitly add the parent, and so on up to the root. This means
178
 
    you should never need to explictly add a directory, they'll just
179
 
    get added when you add a file in the directory.
 
147
    TODO: Perhaps adding a file whose directly is not versioned should
 
148
    recursively add that parent, rather than giving an error?
180
149
    """
181
150
    takes_args = ['file*']
182
 
    takes_options = ['no-recurse', 'quiet']
 
151
    takes_options = ['verbose', 'no-recurse']
183
152
    
184
 
    def run(self, file_list, no_recurse=False, quiet=False):
185
 
        from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
186
 
        if quiet:
187
 
            reporter = add_reporter_null
188
 
        else:
189
 
            reporter = add_reporter_print
190
 
        smart_add(file_list, not no_recurse, reporter)
 
153
    def run(self, file_list, verbose=False, no_recurse=False):
 
154
        # verbose currently has no effect
 
155
        from bzrlib.add import smart_add, add_reporter_print
 
156
        smart_add(file_list, not no_recurse, add_reporter_print)
 
157
 
191
158
 
192
159
 
193
160
class cmd_mkdir(Command):
203
170
        for d in dir_list:
204
171
            os.mkdir(d)
205
172
            if not b:
206
 
                b = Branch.open_containing(d)
 
173
                b = find_branch(d)
207
174
            b.add([d])
208
175
            print 'added', d
209
176
 
214
181
    hidden = True
215
182
    
216
183
    def run(self, filename):
217
 
        print Branch.open_containing(filename).relpath(filename)
 
184
        print find_branch(filename).relpath(filename)
218
185
 
219
186
 
220
187
 
223
190
    takes_options = ['revision', 'show-ids']
224
191
    
225
192
    def run(self, revision=None, show_ids=False):
226
 
        b = Branch.open_containing('.')
227
 
        if revision is None:
 
193
        b = find_branch('.')
 
194
        if revision == None:
228
195
            inv = b.read_working_inventory()
229
196
        else:
230
197
            if len(revision) > 1:
231
198
                raise BzrCommandError('bzr inventory --revision takes'
232
199
                    ' exactly one revision identifier')
233
 
            inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
 
200
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
234
201
 
235
202
        for path, entry in inv.entries():
236
203
            if show_ids:
249
216
    """
250
217
    takes_args = ['source$', 'dest']
251
218
    def run(self, source_list, dest):
252
 
        b = Branch.open_containing('.')
 
219
        b = find_branch('.')
253
220
 
254
221
        # TODO: glob expansion on windows?
255
222
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
266
233
 
267
234
    See also the 'move' command, which moves files into a different
268
235
    directory without changing their name.
269
 
    """
270
 
    # TODO: Some way to rename multiple files without invoking 
271
 
    # bzr for each one?"""
 
236
 
 
237
    TODO: Some way to rename multiple files without invoking bzr for each
 
238
    one?"""
272
239
    takes_args = ['from_name', 'to_name']
273
240
    
274
241
    def run(self, from_name, to_name):
275
 
        b = Branch.open_containing('.')
 
242
        b = find_branch('.')
276
243
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
277
244
 
278
245
 
294
261
    def run(self, names_list):
295
262
        if len(names_list) < 2:
296
263
            raise BzrCommandError("missing file argument")
297
 
        b = Branch.open_containing(names_list[0])
 
264
        b = find_branch(names_list[0])
298
265
 
299
266
        rel_names = [b.relpath(x) for x in names_list]
300
267
        
306
273
            if len(names_list) != 2:
307
274
                raise BzrCommandError('to mv multiple files the destination '
308
275
                                      'must be a versioned directory')
309
 
            b.rename_one(rel_names[0], rel_names[1])
310
 
            print "%s => %s" % (rel_names[0], rel_names[1])
 
276
            for pair in b.move(rel_names[0], rel_names[1]):
 
277
                print "%s => %s" % pair
311
278
            
312
279
    
313
280
 
326
293
    If branches have diverged, you can use 'bzr merge' to pull the text changes
327
294
    from one into the other.
328
295
    """
329
 
    takes_options = ['remember']
330
296
    takes_args = ['location?']
331
297
 
332
 
    def run(self, location=None, remember=False):
 
298
    def run(self, location=None):
333
299
        from bzrlib.merge import merge
334
300
        import tempfile
335
301
        from shutil import rmtree
336
302
        import errno
 
303
        from bzrlib.branch import pull_loc
337
304
        
338
 
        br_to = Branch.open_containing('.')
339
 
        stored_loc = br_to.get_parent()
 
305
        br_to = find_branch('.')
 
306
        stored_loc = None
 
307
        try:
 
308
            stored_loc = br_to.controlfile("x-pull", "rb").read().rstrip('\n')
 
309
        except IOError, e:
 
310
            if e.errno != errno.ENOENT:
 
311
                raise
340
312
        if location is None:
341
313
            if stored_loc is None:
342
314
                raise BzrCommandError("No pull location known or specified.")
343
315
            else:
344
 
                print "Using saved location: %s" % stored_loc
 
316
                print "Using last location: %s" % stored_loc
345
317
                location = stored_loc
346
318
        cache_root = tempfile.mkdtemp()
347
 
        br_from = Branch.open(location)
348
 
        br_from.lock_read()
 
319
        from bzrlib.branch import DivergedBranches
 
320
        br_from = find_branch(location)
 
321
        location = pull_loc(br_from)
 
322
        old_revno = br_to.revno()
349
323
        try:
350
 
            br_from.setup_caching(cache_root)
351
 
            location = br_from.base
 
324
            from branch import find_cached_branch, DivergedBranches
 
325
            br_from = find_cached_branch(location, cache_root)
 
326
            location = pull_loc(br_from)
352
327
            old_revno = br_to.revno()
353
 
            old_revision_history = br_to.revision_history()
354
328
            try:
355
329
                br_to.update_revisions(br_from)
356
330
            except DivergedBranches:
357
331
                raise BzrCommandError("These branches have diverged."
358
332
                    "  Try merge.")
359
 
            new_revision_history = br_to.revision_history()
360
 
            if new_revision_history != old_revision_history:
361
 
                merge(('.', -1), ('.', old_revno), check_clean=False)
362
 
            if stored_loc is None or remember:
363
 
                br_to.set_parent(location)
 
333
                
 
334
            merge(('.', -1), ('.', old_revno), check_clean=False)
 
335
            if location != stored_loc:
 
336
                br_to.controlfile("x-pull", "wb").write(location + "\n")
364
337
        finally:
365
 
            br_from.unlock()
366
338
            rmtree(cache_root)
367
339
 
368
340
 
375
347
 
376
348
    To retrieve the branch as of a particular revision, supply the --revision
377
349
    parameter, as in "branch foo/bar -r 5".
378
 
 
379
 
    --basis is to speed up branching from remote branches.  When specified, it
380
 
    copies all the file-contents, inventory and revision data from the basis
381
 
    branch before copying anything from the remote branch.
382
350
    """
383
351
    takes_args = ['from_location', 'to_location?']
384
 
    takes_options = ['revision', 'basis']
 
352
    takes_options = ['revision']
385
353
    aliases = ['get', 'clone']
386
354
 
387
 
    def run(self, from_location, to_location=None, revision=None, basis=None):
388
 
        from bzrlib.clone import copy_branch
 
355
    def run(self, from_location, to_location=None, revision=None):
 
356
        from bzrlib.branch import copy_branch, find_cached_branch
389
357
        import tempfile
390
358
        import errno
391
359
        from shutil import rmtree
392
360
        cache_root = tempfile.mkdtemp()
393
 
        if revision is None:
394
 
            revision = [None]
395
 
        elif len(revision) > 1:
396
 
            raise BzrCommandError(
397
 
                'bzr branch --revision takes exactly 1 revision value')
398
 
        try:
399
 
            br_from = Branch.open(from_location)
400
 
        except OSError, e:
401
 
            if e.errno == errno.ENOENT:
402
 
                raise BzrCommandError('Source location "%s" does not'
403
 
                                      ' exist.' % to_location)
404
 
            else:
405
 
                raise
406
 
        br_from.lock_read()
407
 
        try:
408
 
            br_from.setup_caching(cache_root)
409
 
            if basis is not None:
410
 
                basis_branch = Branch.open_containing(basis)
411
 
            else:
412
 
                basis_branch = None
413
 
            if len(revision) == 1 and revision[0] is not None:
414
 
                revision_id = revision[0].in_history(br_from)[1]
415
 
            else:
416
 
                revision_id = None
 
361
        try:
 
362
            if revision is None:
 
363
                revision = [None]
 
364
            elif len(revision) > 1:
 
365
                raise BzrCommandError(
 
366
                    'bzr branch --revision takes exactly 1 revision value')
 
367
            try:
 
368
                br_from = find_cached_branch(from_location, cache_root)
 
369
            except OSError, e:
 
370
                if e.errno == errno.ENOENT:
 
371
                    raise BzrCommandError('Source location "%s" does not'
 
372
                                          ' exist.' % to_location)
 
373
                else:
 
374
                    raise
417
375
            if to_location is None:
418
376
                to_location = os.path.basename(from_location.rstrip("/\\"))
419
377
            try:
428
386
                else:
429
387
                    raise
430
388
            try:
431
 
                copy_branch(br_from, to_location, revision_id, basis_branch)
 
389
                copy_branch(br_from, to_location, revision[0])
432
390
            except bzrlib.errors.NoSuchRevision:
433
391
                rmtree(to_location)
434
392
                msg = "The branch %s has no revision %d." % (from_location, revision[0])
435
393
                raise BzrCommandError(msg)
436
 
            except bzrlib.errors.UnlistableBranch:
437
 
                msg = "The branch %s cannot be used as a --basis"
438
394
        finally:
439
 
            br_from.unlock()
440
395
            rmtree(cache_root)
441
396
 
442
397
 
443
398
class cmd_renames(Command):
444
399
    """Show list of renamed files.
 
400
 
 
401
    TODO: Option to show renames between two historical versions.
 
402
 
 
403
    TODO: Only show renames under dir, rather than in the whole branch.
445
404
    """
446
 
    # TODO: Option to show renames between two historical versions.
447
 
 
448
 
    # TODO: Only show renames under dir, rather than in the whole branch.
449
405
    takes_args = ['dir?']
450
406
 
451
407
    def run(self, dir='.'):
452
 
        b = Branch.open_containing(dir)
 
408
        b = find_branch(dir)
453
409
        old_inv = b.basis_tree().inventory
454
410
        new_inv = b.read_working_inventory()
455
411
 
465
421
    
466
422
    def run(self, branch=None):
467
423
        import info
468
 
        b = Branch.open_containing(branch)
 
424
 
 
425
        b = find_branch(branch)
469
426
        info.show_info(b)
470
427
 
471
428
 
477
434
    """
478
435
    takes_args = ['file+']
479
436
    takes_options = ['verbose']
480
 
    aliases = ['rm']
481
437
    
482
438
    def run(self, file_list, verbose=False):
483
 
        b = Branch.open_containing(file_list[0])
 
439
        b = find_branch(file_list[0])
484
440
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
485
441
 
486
442
 
494
450
    hidden = True
495
451
    takes_args = ['filename']
496
452
    def run(self, filename):
497
 
        b = Branch.open_containing(filename)
 
453
        b = find_branch(filename)
498
454
        i = b.inventory.path2id(b.relpath(filename))
499
455
        if i == None:
500
456
            raise BzrError("%r is not a versioned file" % filename)
510
466
    hidden = True
511
467
    takes_args = ['filename']
512
468
    def run(self, filename):
513
 
        b = Branch.open_containing(filename)
 
469
        b = find_branch(filename)
514
470
        inv = b.inventory
515
471
        fid = inv.path2id(b.relpath(filename))
516
472
        if fid == None:
523
479
    """Display list of revision ids on this branch."""
524
480
    hidden = True
525
481
    def run(self):
526
 
        for patchid in Branch.open_containing('.').revision_history():
 
482
        for patchid in find_branch('.').revision_history():
527
483
            print patchid
528
484
 
529
485
 
530
 
class cmd_ancestry(Command):
531
 
    """List all revisions merged into this branch."""
532
 
    hidden = True
533
 
    def run(self):
534
 
        b = find_branch('.')
535
 
        for revision_id in b.get_ancestry(b.last_revision()):
536
 
            print revision_id
537
 
 
538
 
 
539
486
class cmd_directories(Command):
540
487
    """Display list of versioned directories in this branch."""
541
488
    def run(self):
542
 
        for name, ie in Branch.open_containing('.').read_working_inventory().directories():
 
489
        for name, ie in find_branch('.').read_working_inventory().directories():
543
490
            if name == '':
544
491
                print '.'
545
492
            else:
560
507
        bzr commit -m 'imported project'
561
508
    """
562
509
    def run(self):
563
 
        Branch.initialize('.')
 
510
        from bzrlib.branch import Branch
 
511
        Branch('.', init=True)
564
512
 
565
513
 
566
514
class cmd_diff(Command):
569
517
    If files are listed, only the changes in those files are listed.
570
518
    Otherwise, all changes for the tree are listed.
571
519
 
 
520
    TODO: Allow diff across branches.
 
521
 
 
522
    TODO: Option to use external diff command; could be GNU diff, wdiff,
 
523
          or a graphical diff.
 
524
 
 
525
    TODO: Python difflib is not exactly the same as unidiff; should
 
526
          either fix it up or prefer to use an external diff.
 
527
 
 
528
    TODO: If a directory is given, diff everything under that.
 
529
 
 
530
    TODO: Selected-file diff is inefficient and doesn't show you
 
531
          deleted files.
 
532
 
 
533
    TODO: This probably handles non-Unix newlines poorly.
 
534
 
572
535
    examples:
573
536
        bzr diff
574
537
        bzr diff -r1
575
 
        bzr diff -r1..2
 
538
        bzr diff -r1:2
576
539
    """
577
 
    # TODO: Allow diff across branches.
578
 
    # TODO: Option to use external diff command; could be GNU diff, wdiff,
579
 
    #       or a graphical diff.
580
 
 
581
 
    # TODO: Python difflib is not exactly the same as unidiff; should
582
 
    #       either fix it up or prefer to use an external diff.
583
 
 
584
 
    # TODO: If a directory is given, diff everything under that.
585
 
 
586
 
    # TODO: Selected-file diff is inefficient and doesn't show you
587
 
    #       deleted files.
588
 
 
589
 
    # TODO: This probably handles non-Unix newlines poorly.
590
540
    
591
541
    takes_args = ['file*']
592
542
    takes_options = ['revision', 'diff-options']
596
546
        from bzrlib.diff import show_diff
597
547
 
598
548
        if file_list:
599
 
            b = Branch.open_containing(file_list[0])
 
549
            b = find_branch(file_list[0])
600
550
            file_list = [b.relpath(f) for f in file_list]
601
551
            if file_list == ['']:
602
552
                # just pointing to top-of-tree
603
553
                file_list = None
604
554
        else:
605
 
            b = Branch.open_containing('.')
 
555
            b = find_branch('.')
606
556
 
607
557
        if revision is not None:
608
558
            if len(revision) == 1:
623
573
 
624
574
class cmd_deleted(Command):
625
575
    """List files deleted in the working tree.
 
576
 
 
577
    TODO: Show files deleted since a previous revision, or between two revisions.
626
578
    """
627
 
    # TODO: Show files deleted since a previous revision, or
628
 
    # between two revisions.
629
 
    # TODO: Much more efficient way to do this: read in new
630
 
    # directories with readdir, rather than stating each one.  Same
631
 
    # level of effort but possibly much less IO.  (Or possibly not,
632
 
    # if the directories are very large...)
633
579
    def run(self, show_ids=False):
634
 
        b = Branch.open_containing('.')
 
580
        b = find_branch('.')
635
581
        old = b.basis_tree()
636
582
        new = b.working_tree()
 
583
 
 
584
        ## TODO: Much more efficient way to do this: read in new
 
585
        ## directories with readdir, rather than stating each one.  Same
 
586
        ## level of effort but possibly much less IO.  (Or possibly not,
 
587
        ## if the directories are very large...)
 
588
 
637
589
        for path, ie in old.inventory.iter_entries():
638
590
            if not new.has_id(ie.file_id):
639
591
                if show_ids:
648
600
    def run(self):
649
601
        from bzrlib.delta import compare_trees
650
602
 
651
 
        b = Branch.open_containing('.')
 
603
        b = find_branch('.')
652
604
        td = compare_trees(b.basis_tree(), b.working_tree())
653
605
 
654
 
        for path, id, kind, text_modified, meta_modified in td.modified:
 
606
        for path, id, kind in td.modified:
655
607
            print path
656
608
 
657
609
 
660
612
    """List files added in working tree."""
661
613
    hidden = True
662
614
    def run(self):
663
 
        b = Branch.open_containing('.')
 
615
        b = find_branch('.')
664
616
        wt = b.working_tree()
665
617
        basis_inv = b.basis_tree().inventory
666
618
        inv = wt.inventory
682
634
    takes_args = ['filename?']
683
635
    def run(self, filename=None):
684
636
        """Print the branch root."""
685
 
        b = Branch.open_containing(filename)
686
 
        print b.base
 
637
        b = find_branch(filename)
 
638
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
687
639
 
688
640
 
689
641
class cmd_log(Command):
695
647
 
696
648
    --message allows you to give a regular expression, which will be evaluated
697
649
    so that only matching entries will be displayed.
 
650
 
 
651
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
 
652
  
698
653
    """
699
654
 
700
 
    # TODO: Make --revision support uuid: and hash: [future tag:] notation.
701
 
 
702
655
    takes_args = ['filename?']
703
656
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
704
657
                     'long', 'message', 'short',]
717
670
        direction = (forward and 'forward') or 'reverse'
718
671
        
719
672
        if filename:
720
 
            b = Branch.open_containing(filename)
 
673
            b = find_branch(filename)
721
674
            fp = b.relpath(filename)
722
675
            if fp:
723
676
                file_id = b.read_working_inventory().path2id(fp)
724
677
            else:
725
678
                file_id = None  # points to branch root
726
679
        else:
727
 
            b = Branch.open_containing('.')
 
680
            b = find_branch('.')
728
681
            file_id = None
729
682
 
730
683
        if revision is None:
731
684
            rev1 = None
732
685
            rev2 = None
733
686
        elif len(revision) == 1:
734
 
            rev1 = rev2 = revision[0].in_history(b).revno
 
687
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
735
688
        elif len(revision) == 2:
736
 
            rev1 = revision[0].in_history(b).revno
737
 
            rev2 = revision[1].in_history(b).revno
 
689
            rev1 = b.get_revision_info(revision[0])[0]
 
690
            rev2 = b.get_revision_info(revision[1])[0]
738
691
        else:
739
692
            raise BzrCommandError('bzr log --revision takes one or two values.')
740
693
 
776
729
    hidden = True
777
730
    takes_args = ["filename"]
778
731
    def run(self, filename):
779
 
        b = Branch.open_containing(filename)
 
732
        b = find_branch(filename)
780
733
        inv = b.read_working_inventory()
781
734
        file_id = inv.path2id(b.relpath(filename))
782
735
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
785
738
 
786
739
class cmd_ls(Command):
787
740
    """List files in a tree.
 
741
 
 
742
    TODO: Take a revision or remote path and list that tree instead.
788
743
    """
789
 
    # TODO: Take a revision or remote path and list that tree instead.
790
744
    hidden = True
791
745
    def run(self, revision=None, verbose=False):
792
 
        b = Branch.open_containing('.')
 
746
        b = find_branch('.')
793
747
        if revision == None:
794
748
            tree = b.working_tree()
795
749
        else:
796
 
            tree = b.revision_tree(revision.in_history(b).rev_id)
797
 
        for fp, fc, kind, fid, entry in tree.list_files():
 
750
            tree = b.revision_tree(b.lookup_revision(revision))
 
751
 
 
752
        for fp, fc, kind, fid in tree.list_files():
798
753
            if verbose:
799
 
                kindch = entry.kind_character()
 
754
                if kind == 'directory':
 
755
                    kindch = '/'
 
756
                elif kind == 'file':
 
757
                    kindch = ''
 
758
                else:
 
759
                    kindch = '???'
 
760
 
800
761
                print '%-8s %s%s' % (fc, fp, kindch)
801
762
            else:
802
763
                print fp
807
768
    """List unknown files."""
808
769
    def run(self):
809
770
        from bzrlib.osutils import quotefn
810
 
        for f in Branch.open_containing('.').unknowns():
 
771
        for f in find_branch('.').unknowns():
811
772
            print quotefn(f)
812
773
 
813
774
 
818
779
    To remove patterns from the ignore list, edit the .bzrignore file.
819
780
 
820
781
    If the pattern contains a slash, it is compared to the whole path
821
 
    from the branch root.  Otherwise, it is compared to only the last
822
 
    component of the path.  To match a file only in the root directory,
823
 
    prepend './'.
 
782
    from the branch root.  Otherwise, it is comapred to only the last
 
783
    component of the path.
824
784
 
825
785
    Ignore patterns are case-insensitive on case-insensitive systems.
826
786
 
830
790
        bzr ignore ./Makefile
831
791
        bzr ignore '*.class'
832
792
    """
833
 
    # TODO: Complain if the filename is absolute
834
793
    takes_args = ['name_pattern']
835
794
    
836
795
    def run(self, name_pattern):
837
796
        from bzrlib.atomicfile import AtomicFile
838
797
        import os.path
839
798
 
840
 
        b = Branch.open_containing('.')
 
799
        b = find_branch('.')
841
800
        ifn = b.abspath('.bzrignore')
842
801
 
843
802
        if os.path.exists(ifn):
877
836
 
878
837
    See also: bzr ignore"""
879
838
    def run(self):
880
 
        tree = Branch.open_containing('.').working_tree()
881
 
        for path, file_class, kind, file_id, entry in tree.list_files():
 
839
        tree = find_branch('.').working_tree()
 
840
        for path, file_class, kind, file_id in tree.list_files():
882
841
            if file_class != 'I':
883
842
                continue
884
843
            ## XXX: Slightly inefficient since this was already calculated
901
860
        except ValueError:
902
861
            raise BzrCommandError("not a valid revision-number: %r" % revno)
903
862
 
904
 
        print Branch.open_containing('.').get_rev_id(revno)
 
863
        print find_branch('.').lookup_revision(revno)
905
864
 
906
865
 
907
866
class cmd_export(Command):
920
879
    takes_options = ['revision', 'format', 'root']
921
880
    def run(self, dest, revision=None, format=None, root=None):
922
881
        import os.path
923
 
        b = Branch.open_containing('.')
 
882
        b = find_branch('.')
924
883
        if revision is None:
925
 
            rev_id = b.last_revision()
 
884
            rev_id = b.last_patch()
926
885
        else:
927
886
            if len(revision) != 1:
928
887
                raise BzrError('bzr export --revision takes exactly 1 argument')
929
 
            rev_id = revision[0].in_history(b).rev_id
 
888
            revno, rev_id = b.get_revision_info(revision[0])
930
889
        t = b.revision_tree(rev_id)
931
 
        arg_root, ext = os.path.splitext(os.path.basename(dest))
932
 
        if ext in ('.gz', '.bz2'):
933
 
            new_root, new_ext = os.path.splitext(arg_root)
934
 
            if new_ext == '.tar':
935
 
                arg_root = new_root
936
 
                ext = new_ext + ext
937
 
        if root is None:
938
 
            root = arg_root
 
890
        root, ext = os.path.splitext(dest)
939
891
        if not format:
940
892
            if ext in (".tar",):
941
893
                format = "tar"
942
 
            elif ext in (".tar.gz", ".tgz"):
 
894
            elif ext in (".gz", ".tgz"):
943
895
                format = "tgz"
944
 
            elif ext in (".tar.bz2", ".tbz2"):
 
896
            elif ext in (".bz2", ".tbz2"):
945
897
                format = "tbz2"
946
898
            else:
947
899
                format = "dir"
955
907
    takes_args = ['filename']
956
908
 
957
909
    def run(self, filename, revision=None):
958
 
        if revision is None:
 
910
        if revision == None:
959
911
            raise BzrCommandError("bzr cat requires a revision number")
960
912
        elif len(revision) != 1:
961
913
            raise BzrCommandError("bzr cat --revision takes exactly one number")
962
 
        b = Branch.open_containing('.')
963
 
        b.print_file(b.relpath(filename), revision[0].in_history(b).revno)
 
914
        b = find_branch('.')
 
915
        b.print_file(b.relpath(filename), revision[0])
964
916
 
965
917
 
966
918
class cmd_local_time_offset(Command):
983
935
    A selected-file commit may fail in some cases where the committed
984
936
    tree would be invalid, such as trying to commit a file in a
985
937
    newly-added directory that is not itself committed.
 
938
 
 
939
    TODO: Run hooks on tree to-be-committed, and after commit.
 
940
 
 
941
    TODO: Strict commit that fails if there are unknown or deleted files.
986
942
    """
987
 
    # TODO: Run hooks on tree to-be-committed, and after commit.
988
 
 
989
 
    # TODO: Strict commit that fails if there are unknown or deleted files.
990
 
    # TODO: Give better message for -s, --summary, used by tla people
991
 
 
992
 
    # XXX: verbose currently does nothing
993
 
 
994
943
    takes_args = ['selected*']
995
944
    takes_options = ['message', 'file', 'verbose', 'unchanged']
996
945
    aliases = ['ci', 'checkin']
997
946
 
 
947
    # TODO: Give better message for -s, --summary, used by tla people
 
948
    
998
949
    def run(self, message=None, file=None, verbose=True, selected_list=None,
999
950
            unchanged=False):
1000
 
        from bzrlib.errors import PointlessCommit, ConflictsInTree
 
951
        from bzrlib.errors import PointlessCommit
1001
952
        from bzrlib.msgeditor import edit_commit_message
1002
953
        from bzrlib.status import show_status
1003
954
        from cStringIO import StringIO
1004
955
 
1005
 
        b = Branch.open_containing('.')
 
956
        b = find_branch('.')
1006
957
        if selected_list:
1007
958
            selected_list = [b.relpath(s) for s in selected_list]
1008
 
 
1009
959
            
1010
 
        if message is None and not file:
 
960
        if not message and not file:
1011
961
            catcher = StringIO()
1012
962
            show_status(b, specific_files=selected_list,
1013
963
                        to_file=catcher)
1014
964
            message = edit_commit_message(catcher.getvalue())
1015
 
 
 
965
            
1016
966
            if message is None:
1017
967
                raise BzrCommandError("please specify a commit message"
1018
968
                                      " with either --message or --file")
1023
973
            import codecs
1024
974
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1025
975
 
1026
 
        if message == "":
1027
 
                raise BzrCommandError("empty commit message specified")
1028
 
            
1029
976
        try:
1030
 
            b.commit(message,
 
977
            b.commit(message, verbose=verbose,
1031
978
                     specific_files=selected_list,
1032
979
                     allow_pointless=unchanged)
1033
980
        except PointlessCommit:
1035
982
            # perhaps prepare the commit; get the message; then actually commit
1036
983
            raise BzrCommandError("no changes to commit",
1037
984
                                  ["use --unchanged to commit anyhow"])
1038
 
        except ConflictsInTree:
1039
 
            raise BzrCommandError("Conflicts detected in working tree.  "
1040
 
                'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1041
985
 
1042
986
 
1043
987
class cmd_check(Command):
1045
989
 
1046
990
    This command checks various invariants about the branch storage to
1047
991
    detect data corruption or bzr bugs.
 
992
 
 
993
    If given the --update flag, it will update some optional fields
 
994
    to help ensure data consistency.
1048
995
    """
1049
996
    takes_args = ['dir?']
1050
997
 
1051
998
    def run(self, dir='.'):
1052
999
        from bzrlib.check import check
1053
1000
 
1054
 
        check(Branch.open_containing(dir))
 
1001
        check(find_branch(dir))
1055
1002
 
1056
1003
 
1057
1004
class cmd_scan_cache(Command):
1079
1026
 
1080
1027
    The check command or bzr developers may sometimes advise you to run
1081
1028
    this command.
1082
 
 
1083
 
    This version of this command upgrades from the full-text storage
1084
 
    used by bzr 0.0.8 and earlier to the weave format (v5).
1085
1029
    """
1086
1030
    takes_args = ['dir?']
1087
1031
 
1088
1032
    def run(self, dir='.'):
1089
1033
        from bzrlib.upgrade import upgrade
1090
 
        upgrade(dir)
 
1034
        upgrade(find_branch(dir))
 
1035
 
1091
1036
 
1092
1037
 
1093
1038
class cmd_whoami(Command):
1096
1041
    
1097
1042
    def run(self, email=False):
1098
1043
        try:
1099
 
            b = bzrlib.branch.Branch.open_containing('.')
1100
 
        except NotBranchError:
 
1044
            b = bzrlib.branch.find_branch('.')
 
1045
        except:
1101
1046
            b = None
1102
1047
        
1103
1048
        if email:
1104
 
            print bzrlib.config.user_email(b)
 
1049
            print bzrlib.osutils.user_email(b)
1105
1050
        else:
1106
 
            print bzrlib.config.username(b)
 
1051
            print bzrlib.osutils.username(b)
1107
1052
 
1108
1053
 
1109
1054
class cmd_selftest(Command):
1110
 
    """Run internal test suite.
1111
 
    
1112
 
    This creates temporary test directories in the working directory,
1113
 
    but not existing data is affected.  These directories are deleted
1114
 
    if the tests pass, or left behind to help in debugging if they
1115
 
    fail.
1116
 
    
1117
 
    If arguments are given, they are regular expressions that say
1118
 
    which tests should run."""
1119
 
    # TODO: --list should give a list of all available tests
 
1055
    """Run internal test suite"""
1120
1056
    hidden = True
1121
 
    takes_args = ['testspecs*']
1122
 
    takes_options = ['verbose']
1123
 
    def run(self, testspecs_list=None, verbose=False):
 
1057
    takes_options = ['verbose', 'pattern']
 
1058
    def run(self, verbose=False, pattern=".*"):
1124
1059
        import bzrlib.ui
1125
1060
        from bzrlib.selftest import selftest
1126
1061
        # we don't want progress meters from the tests to go to the
1130
1065
        bzrlib.trace.info('running tests...')
1131
1066
        try:
1132
1067
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1133
 
            if testspecs_list is not None:
1134
 
                pattern = '|'.join(testspecs_list)
1135
 
            else:
1136
 
                pattern = ".*"
1137
 
            result = selftest(verbose=verbose, 
1138
 
                              pattern=pattern)
 
1068
            result = selftest(verbose=verbose, pattern=pattern)
1139
1069
            if result:
1140
1070
                bzrlib.trace.info('tests passed')
1141
1071
            else:
1173
1103
 
1174
1104
class cmd_find_merge_base(Command):
1175
1105
    """Find and print a base revision for merging two branches.
 
1106
 
 
1107
    TODO: Options to specify revisions on either side, as if
 
1108
          merging only part of the history.
1176
1109
    """
1177
 
    # TODO: Options to specify revisions on either side, as if
1178
 
    #       merging only part of the history.
1179
1110
    takes_args = ['branch', 'other']
1180
1111
    hidden = True
1181
1112
    
1182
1113
    def run(self, branch, other):
1183
1114
        from bzrlib.revision import common_ancestor, MultipleRevisionSources
1184
1115
        
1185
 
        branch1 = Branch.open_containing(branch)
1186
 
        branch2 = Branch.open_containing(other)
 
1116
        branch1 = find_branch(branch)
 
1117
        branch2 = find_branch(other)
1187
1118
 
1188
1119
        history_1 = branch1.revision_history()
1189
1120
        history_2 = branch2.revision_history()
1190
1121
 
1191
 
        last1 = branch1.last_revision()
1192
 
        last2 = branch2.last_revision()
 
1122
        last1 = branch1.last_patch()
 
1123
        last2 = branch2.last_patch()
1193
1124
 
1194
1125
        source = MultipleRevisionSources(branch1, branch2)
1195
1126
        
1213
1144
class cmd_merge(Command):
1214
1145
    """Perform a three-way merge.
1215
1146
    
1216
 
    The branch is the branch you will merge from.  By default, it will
1217
 
    merge the latest revision.  If you specify a revision, that
1218
 
    revision will be merged.  If you specify two revisions, the first
1219
 
    will be used as a BASE, and the second one as OTHER.  Revision
1220
 
    numbers are always relative to the specified branch.
1221
 
 
1222
 
    By default bzr will try to merge in all new work from the other
1223
 
    branch, automatically determining an appropriate base.  If this
1224
 
    fails, you may need to give an explicit base.
 
1147
    The branch is the branch you will merge from.  By default, it will merge
 
1148
    the latest revision.  If you specify a revision, that revision will be
 
1149
    merged.  If you specify two revisions, the first will be used as a BASE, 
 
1150
    and the second one as OTHER.  Revision numbers are always relative to the
 
1151
    specified branch.
1225
1152
    
1226
1153
    Examples:
1227
1154
 
1240
1167
    takes_args = ['branch?']
1241
1168
    takes_options = ['revision', 'force', 'merge-type']
1242
1169
 
1243
 
    def run(self, branch=None, revision=None, force=False, 
 
1170
    def run(self, branch='.', revision=None, force=False, 
1244
1171
            merge_type=None):
1245
1172
        from bzrlib.merge import merge
1246
1173
        from bzrlib.merge_core import ApplyMerge3
1247
1174
        if merge_type is None:
1248
1175
            merge_type = ApplyMerge3
1249
 
        if branch is None:
1250
 
            branch = Branch.open_containing('.').get_parent()
1251
 
            if branch is None:
1252
 
                raise BzrCommandError("No merge location known or specified.")
1253
 
            else:
1254
 
                print "Using saved location: %s" % branch 
 
1176
 
1255
1177
        if revision is None or len(revision) < 1:
1256
1178
            base = [None, None]
1257
 
            other = [branch, -1]
 
1179
            other = (branch, -1)
1258
1180
        else:
1259
1181
            if len(revision) == 1:
1260
 
                base = [None, None]
1261
 
                other = [branch, revision[0].in_history(branch).revno]
 
1182
                other = (branch, revision[0])
 
1183
                base = (None, None)
1262
1184
            else:
1263
1185
                assert len(revision) == 2
1264
1186
                if None in revision:
1265
1187
                    raise BzrCommandError(
1266
1188
                        "Merge doesn't permit that revision specifier.")
1267
 
                b = Branch.open(branch)
1268
 
 
1269
 
                base = [branch, revision[0].in_history(b).revno]
1270
 
                other = [branch, revision[1].in_history(b).revno]
1271
 
 
1272
 
        try:
1273
 
            merge(other, base, check_clean=(not force), merge_type=merge_type)
1274
 
        except bzrlib.errors.AmbiguousBase, e:
1275
 
            m = ("sorry, bzr can't determine the right merge base yet\n"
1276
 
                 "candidates are:\n  "
1277
 
                 + "\n  ".join(e.bases)
1278
 
                 + "\n"
1279
 
                 "please specify an explicit base with -r,\n"
1280
 
                 "and (if you want) report this to the bzr developers\n")
1281
 
            log_error(m)
 
1189
                base = (branch, revision[0])
 
1190
                other = (branch, revision[1])
 
1191
            
 
1192
        merge(other, base, check_clean=(not force), merge_type=merge_type)
1282
1193
 
1283
1194
 
1284
1195
class cmd_revert(Command):
1294
1205
 
1295
1206
    def run(self, revision=None, no_backup=False, file_list=None):
1296
1207
        from bzrlib.merge import merge
 
1208
        from bzrlib.branch import Branch
1297
1209
        from bzrlib.commands import parse_spec
1298
1210
 
1299
1211
        if file_list is not None:
1300
1212
            if len(file_list) == 0:
1301
1213
                raise BzrCommandError("No files specified")
1302
1214
        if revision is None:
1303
 
            revno = -1
 
1215
            revision = [-1]
1304
1216
        elif len(revision) != 1:
1305
1217
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1306
 
        else:
1307
 
            b = Branch.open_containing('.')
1308
 
            revno = revision[0].in_history(b).revno
1309
 
        merge(('.', revno), parse_spec('.'),
 
1218
        merge(('.', revision[0]), parse_spec('.'),
1310
1219
              check_clean=False,
1311
1220
              ignore_zero=True,
1312
1221
              backup_files=not no_backup,
1313
1222
              file_list=file_list)
1314
1223
        if not file_list:
1315
 
            Branch.open_containing('.').set_pending_merges([])
 
1224
            Branch('.').set_pending_merges([])
1316
1225
 
1317
1226
 
1318
1227
class cmd_assert_fail(Command):
1350
1259
        shellcomplete.shellcomplete(context)
1351
1260
 
1352
1261
 
1353
 
class cmd_fetch(Command):
1354
 
    """Copy in history from another branch but don't merge it.
1355
 
 
1356
 
    This is an internal method used for pull and merge."""
1357
 
    hidden = True
1358
 
    takes_args = ['from_branch', 'to_branch']
1359
 
    def run(self, from_branch, to_branch):
1360
 
        from bzrlib.fetch import Fetcher
1361
 
        from bzrlib.branch import Branch
1362
 
        from_b = Branch(from_branch)
1363
 
        to_b = Branch(to_branch)
1364
 
        Fetcher(to_b, from_b)
1365
 
        
1366
 
 
1367
 
 
1368
1262
class cmd_missing(Command):
1369
1263
    """What is missing in this branch relative to other branch.
1370
1264
    """
1371
 
    # TODO: rewrite this in terms of ancestry so that it shows only
1372
 
    # unmerged things
1373
 
    
1374
1265
    takes_args = ['remote?']
1375
1266
    aliases = ['mis', 'miss']
1376
1267
    # We don't have to add quiet to the list, because 
1384
1275
        if verbose and quiet:
1385
1276
            raise BzrCommandError('Cannot pass both quiet and verbose')
1386
1277
 
1387
 
        b = Branch.open_containing('.')
 
1278
        b = find_branch('.')
1388
1279
        parent = b.get_parent()
1389
1280
        if remote is None:
1390
1281
            if parent is None:
1394
1285
                    print "Using last location: %s" % parent
1395
1286
                remote = parent
1396
1287
        elif parent is None:
1397
 
            # We only update parent if it did not exist, missing
1398
 
            # should not change the parent
1399
 
            b.set_parent(remote)
1400
 
        br_remote = Branch.open_containing(remote)
 
1288
            # We only update x-pull if it did not exist, missing should not change the parent
 
1289
            b.controlfile('x-pull', 'wb').write(remote + '\n')
 
1290
        br_remote = find_branch(remote)
 
1291
 
1401
1292
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1402
1293
 
1403
1294
 
 
1295
 
1404
1296
class cmd_plugins(Command):
1405
1297
    """List plugins"""
1406
1298
    hidden = True
1420
1312
                print '\t', d.split('\n')[0]
1421
1313
 
1422
1314
 
1423
 
class cmd_testament(Command):
1424
 
    """Show testament (signing-form) of a revision."""
1425
 
    takes_options = ['revision', 'long']
1426
 
    takes_args = ['branch?']
1427
 
    def run(self, branch='.', revision=None, long=False):
1428
 
        from bzrlib.testament import Testament
1429
 
        b = Branch.open_containing(branch)
1430
 
        b.lock_read()
1431
 
        try:
1432
 
            if revision is None:
1433
 
                rev_id = b.last_revision()
1434
 
            else:
1435
 
                rev_id = revision[0].in_history(b).rev_id
1436
 
            t = Testament.from_revision(b, rev_id)
1437
 
            if long:
1438
 
                sys.stdout.writelines(t.as_text_lines())
1439
 
            else:
1440
 
                sys.stdout.write(t.as_short_text())
1441
 
        finally:
1442
 
            b.unlock()
1443
 
 
1444
 
 
1445
 
class cmd_annotate(Command):
1446
 
    """Show the origin of each line in a file.
1447
 
 
1448
 
    This prints out the given file with an annotation on the 
1449
 
    left side indicating which revision, author and date introduced the 
1450
 
    change.
1451
 
    """
1452
 
    # TODO: annotate directories; showing when each file was last changed
1453
 
    # TODO: annotate a previous version of a file
1454
 
    aliases = ['blame', 'praise']
1455
 
    takes_args = ['filename']
1456
 
 
1457
 
    def run(self, filename):
1458
 
        from bzrlib.annotate import annotate_file
1459
 
        b = Branch.open_containing(filename)
1460
 
        b.lock_read()
1461
 
        try:
1462
 
            rp = b.relpath(filename)
1463
 
            tree = b.revision_tree(b.last_revision())
1464
 
            file_id = tree.inventory.path2id(rp)
1465
 
            file_version = tree.inventory[file_id].revision
1466
 
            annotate_file(b, file_version, file_id, sys.stdout)
1467
 
        finally:
1468
 
            b.unlock()
1469
 
 
1470
 
# these get imported and then picked up by the scan for cmd_*
1471
 
# TODO: Some more consistent way to split command definitions across files;
1472
 
# we do need to load at least some information about them to know of 
1473
 
# aliases.
1474
 
from bzrlib.conflicts import cmd_resolve, cmd_conflicts