~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: abentley
  • Date: 2005-10-16 23:56:25 UTC
  • mto: (1185.25.1)
  • mto: This revision was merged to the branch mainline in revision 1460.
  • Revision ID: abentley@lappy-20051016235625-f2e4f5d4ff1e889c
Cleanups for pull

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