~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2005-09-05 05:35:25 UTC
  • mfrom: (974.1.55)
  • Revision ID: mbp@sourcefrog.net-20050905053525-2112bac069dbe331
- merge various bug fixes from aaron

aaron.bentley@utoronto.ca-20050905020131-a2d5b7711dd6cd98

Show diffs side-by-side

added added

removed removed

Lines of Context:
19
19
import os
20
20
 
21
21
import bzrlib
22
 
from bzrlib import BZRDIR
23
 
from bzrlib.commands import Command, display_command
24
 
from bzrlib.branch import Branch
25
 
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
26
 
from bzrlib.errors import DivergedBranches
27
 
from bzrlib.option import Option
28
 
from bzrlib.revisionspec import RevisionSpec
29
22
import bzrlib.trace
30
23
from bzrlib.trace import mutter, note, log_error, warning
31
 
from bzrlib.workingtree import WorkingTree
 
24
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError
 
25
from bzrlib.branch import find_branch
 
26
from bzrlib import BZRDIR
 
27
from bzrlib.commands import Command
32
28
 
33
29
 
34
30
class cmd_status(Command):
67
63
    files or directories is reported.  If a directory is given, status
68
64
    is reported for everything inside that directory.
69
65
 
70
 
    If a revision argument is given, the status is calculated against
71
 
    that revision, or between two revisions if two are provided.
 
66
    If a revision is specified, the changes since that revision are shown.
72
67
    """
73
 
    
74
 
    # XXX: FIXME: bzr status should accept a -r option to show changes
75
 
    # relative to a revision, or between revisions
76
 
 
77
 
    # TODO: --no-recurse, --recurse options
78
 
    
79
68
    takes_args = ['file*']
80
 
    takes_options = ['all', 'show-ids']
 
69
    takes_options = ['all', 'show-ids', 'revision']
81
70
    aliases = ['st', 'stat']
82
71
    
83
 
    @display_command
84
 
    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):
85
73
        if file_list:
86
 
            b, relpath = Branch.open_containing(file_list[0])
87
 
            if relpath == '' and len(file_list) == 1:
 
74
            b = find_branch(file_list[0])
 
75
            file_list = [b.relpath(x) for x in file_list]
 
76
            # special case: only one path was given and it's the root
 
77
            # of the branch
 
78
            if file_list == ['']:
88
79
                file_list = None
89
 
            else:
90
 
                # generate relative paths.
91
 
                # note that if this is a remote branch, we would want
92
 
                # relpath against the transport. RBC 20051018
93
 
                tree = WorkingTree(b.base, b)
94
 
                file_list = [tree.relpath(x) for x in file_list]
95
80
        else:
96
 
            b = Branch.open_containing('.')[0]
 
81
            b = find_branch('.')
97
82
            
98
83
        from bzrlib.status import show_status
99
84
        show_status(b, show_unchanged=all, show_ids=show_ids,
100
 
                    specific_files=file_list, revision=revision)
 
85
                    specific_files=file_list)
101
86
 
102
87
 
103
88
class cmd_cat_revision(Command):
104
 
    """Write out metadata for a revision.
105
 
    
106
 
    The revision to print can either be specified by a specific
107
 
    revision identifier, or you can use --revision.
108
 
    """
 
89
    """Write out metadata for a revision."""
109
90
 
110
91
    hidden = True
111
 
    takes_args = ['revision_id?']
112
 
    takes_options = ['revision']
 
92
    takes_args = ['revision_id']
113
93
    
114
 
    @display_command
115
 
    def run(self, revision_id=None, revision=None):
 
94
    def run(self, revision_id):
 
95
        from bzrlib.xml import pack_xml
 
96
        pack_xml(find_branch('.').get_revision(revision_id), sys.stdout)
116
97
 
117
 
        if revision_id is not None and revision is not None:
118
 
            raise BzrCommandError('You can only supply one of revision_id or --revision')
119
 
        if revision_id is None and revision is None:
120
 
            raise BzrCommandError('You must supply either --revision or a revision_id')
121
 
        b = Branch.open_containing('.')[0]
122
 
        if revision_id is not None:
123
 
            sys.stdout.write(b.get_revision_xml_file(revision_id).read())
124
 
        elif revision is not None:
125
 
            for rev in revision:
126
 
                if rev is None:
127
 
                    raise BzrCommandError('You cannot specify a NULL revision.')
128
 
                revno, rev_id = rev.in_history(b)
129
 
                sys.stdout.write(b.get_revision_xml_file(rev_id).read())
130
 
    
131
98
 
132
99
class cmd_revno(Command):
133
100
    """Show current revision number.
134
101
 
135
102
    This is equal to the number of revisions on this branch."""
136
 
    @display_command
137
103
    def run(self):
138
 
        print Branch.open_containing('.')[0].revno()
139
 
 
 
104
        print find_branch('.').revno()
140
105
 
141
106
class cmd_revision_info(Command):
142
107
    """Show revision number and revision id for a given revision identifier.
144
109
    hidden = True
145
110
    takes_args = ['revision_info*']
146
111
    takes_options = ['revision']
147
 
    @display_command
148
 
    def run(self, revision=None, revision_info_list=[]):
 
112
    def run(self, revision=None, revision_info_list=None):
 
113
        from bzrlib.branch import find_branch
149
114
 
150
115
        revs = []
151
116
        if revision is not None:
152
117
            revs.extend(revision)
153
118
        if revision_info_list is not None:
154
 
            for rev in revision_info_list:
155
 
                revs.append(RevisionSpec(rev))
 
119
            revs.extend(revision_info_list)
156
120
        if len(revs) == 0:
157
121
            raise BzrCommandError('You must supply a revision identifier')
158
122
 
159
 
        b = Branch.open_containing('.')[0]
 
123
        b = find_branch('.')
160
124
 
161
125
        for rev in revs:
162
 
            revinfo = rev.in_history(b)
163
 
            if revinfo.revno is None:
164
 
                print '     %s' % revinfo.rev_id
165
 
            else:
166
 
                print '%4d %s' % (revinfo.revno, revinfo.rev_id)
 
126
            print '%4d %s' % b.get_revision_info(rev)
167
127
 
168
128
    
169
129
class cmd_add(Command):
184
144
    Therefore simply saying 'bzr add' will version all files that
185
145
    are currently unknown.
186
146
 
187
 
    Adding a file whose parent directory is not versioned will
188
 
    implicitly add the parent, and so on up to the root. This means
189
 
    you should never need to explictly add a directory, they'll just
190
 
    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?
191
149
    """
192
150
    takes_args = ['file*']
193
 
    takes_options = ['no-recurse', 'quiet']
 
151
    takes_options = ['verbose', 'no-recurse']
194
152
    
195
 
    def run(self, file_list, no_recurse=False, quiet=False):
196
 
        from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
197
 
        if quiet:
198
 
            reporter = add_reporter_null
199
 
        else:
200
 
            reporter = add_reporter_print
201
 
        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
 
202
158
 
203
159
 
204
160
class cmd_mkdir(Command):
214
170
        for d in dir_list:
215
171
            os.mkdir(d)
216
172
            if not b:
217
 
                b = Branch.open_containing(d)[0]
 
173
                b = find_branch(d)
218
174
            b.add([d])
219
175
            print 'added', d
220
176
 
224
180
    takes_args = ['filename']
225
181
    hidden = True
226
182
    
227
 
    @display_command
228
183
    def run(self, filename):
229
 
        branch, relpath = Branch.open_containing(filename)
230
 
        print relpath
 
184
        print find_branch(filename).relpath(filename)
 
185
 
231
186
 
232
187
 
233
188
class cmd_inventory(Command):
234
189
    """Show inventory of the current working copy or a revision."""
235
190
    takes_options = ['revision', 'show-ids']
236
191
    
237
 
    @display_command
238
192
    def run(self, revision=None, show_ids=False):
239
 
        b = Branch.open_containing('.')[0]
240
 
        if revision is None:
 
193
        b = find_branch('.')
 
194
        if revision == None:
241
195
            inv = b.read_working_inventory()
242
196
        else:
243
197
            if len(revision) > 1:
244
198
                raise BzrCommandError('bzr inventory --revision takes'
245
199
                    ' exactly one revision identifier')
246
 
            inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
 
200
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
247
201
 
248
202
        for path, entry in inv.entries():
249
203
            if show_ids:
262
216
    """
263
217
    takes_args = ['source$', 'dest']
264
218
    def run(self, source_list, dest):
265
 
        b = Branch.open_containing('.')[0]
 
219
        b = find_branch('.')
266
220
 
267
221
        # TODO: glob expansion on windows?
268
 
        tree = WorkingTree(b.base, b)
269
 
        b.move([tree.relpath(s) for s in source_list], tree.relpath(dest))
 
222
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
270
223
 
271
224
 
272
225
class cmd_rename(Command):
280
233
 
281
234
    See also the 'move' command, which moves files into a different
282
235
    directory without changing their name.
283
 
    """
284
 
    # TODO: Some way to rename multiple files without invoking 
285
 
    # bzr for each one?"""
 
236
 
 
237
    TODO: Some way to rename multiple files without invoking bzr for each
 
238
    one?"""
286
239
    takes_args = ['from_name', 'to_name']
287
240
    
288
241
    def run(self, from_name, to_name):
289
 
        b = Branch.open_containing('.')[0]
290
 
        tree = WorkingTree(b.base, b)
291
 
        b.rename_one(tree.relpath(from_name), tree.relpath(to_name))
 
242
        b = find_branch('.')
 
243
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
 
244
 
292
245
 
293
246
 
294
247
class cmd_mv(Command):
308
261
    def run(self, names_list):
309
262
        if len(names_list) < 2:
310
263
            raise BzrCommandError("missing file argument")
311
 
        b = Branch.open_containing(names_list[0])[0]
312
 
        tree = WorkingTree(b.base, b)
313
 
        rel_names = [tree.relpath(x) for x in names_list]
 
264
        b = find_branch(names_list[0])
 
265
 
 
266
        rel_names = [b.relpath(x) for x in names_list]
314
267
        
315
268
        if os.path.isdir(names_list[-1]):
316
269
            # move into existing directory
320
273
            if len(names_list) != 2:
321
274
                raise BzrCommandError('to mv multiple files the destination '
322
275
                                      'must be a versioned directory')
323
 
            b.rename_one(rel_names[0], rel_names[1])
324
 
            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
325
278
            
326
279
    
327
280
 
340
293
    If branches have diverged, you can use 'bzr merge' to pull the text changes
341
294
    from one into the other.
342
295
    """
343
 
    takes_options = ['remember', 'clobber']
344
296
    takes_args = ['location?']
345
297
 
346
 
    def run(self, location=None, remember=False, clobber=False):
 
298
    def run(self, location=None):
347
299
        from bzrlib.merge import merge
348
300
        import tempfile
349
301
        from shutil import rmtree
350
302
        import errno
 
303
        from bzrlib.branch import pull_loc
351
304
        
352
 
        br_to = Branch.open_containing('.')[0]
353
 
        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
354
312
        if location is None:
355
313
            if stored_loc is None:
356
314
                raise BzrCommandError("No pull location known or specified.")
357
315
            else:
358
 
                print "Using saved location: %s" % stored_loc
 
316
                print "Using last location: %s" % stored_loc
359
317
                location = stored_loc
360
 
        br_from = Branch.open(location)
 
318
        cache_root = tempfile.mkdtemp()
 
319
        from bzrlib.branch import DivergedBranches
 
320
        br_from = find_branch(location)
 
321
        location = pull_loc(br_from)
 
322
        old_revno = br_to.revno()
361
323
        try:
362
 
            br_to.working_tree().pull(br_from, remember, clobber)
363
 
        except DivergedBranches:
364
 
            raise BzrCommandError("These branches have diverged."
365
 
                                  "  Try merge.")
 
324
            from branch import find_cached_branch, DivergedBranches
 
325
            br_from = find_cached_branch(location, cache_root)
 
326
            location = pull_loc(br_from)
 
327
            old_revno = br_to.revno()
 
328
            try:
 
329
                br_to.update_revisions(br_from)
 
330
            except DivergedBranches:
 
331
                raise BzrCommandError("These branches have diverged."
 
332
                    "  Try merge.")
 
333
                
 
334
            merge(('.', -1), ('.', old_revno), check_clean=False)
 
335
            if location != stored_loc:
 
336
                br_to.controlfile("x-pull", "wb").write(location + "\n")
 
337
        finally:
 
338
            rmtree(cache_root)
 
339
 
366
340
 
367
341
 
368
342
class cmd_branch(Command):
373
347
 
374
348
    To retrieve the branch as of a particular revision, supply the --revision
375
349
    parameter, as in "branch foo/bar -r 5".
376
 
 
377
 
    --basis is to speed up branching from remote branches.  When specified, it
378
 
    copies all the file-contents, inventory and revision data from the basis
379
 
    branch before copying anything from the remote branch.
380
350
    """
381
351
    takes_args = ['from_location', 'to_location?']
382
 
    takes_options = ['revision', 'basis']
 
352
    takes_options = ['revision']
383
353
    aliases = ['get', 'clone']
384
354
 
385
 
    def run(self, from_location, to_location=None, revision=None, basis=None):
386
 
        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
387
357
        import tempfile
388
358
        import errno
389
359
        from shutil import rmtree
390
360
        cache_root = tempfile.mkdtemp()
391
 
        if revision is None:
392
 
            revision = [None]
393
 
        elif len(revision) > 1:
394
 
            raise BzrCommandError(
395
 
                'bzr branch --revision takes exactly 1 revision value')
396
 
        try:
397
 
            br_from = Branch.open(from_location)
398
 
        except OSError, e:
399
 
            if e.errno == errno.ENOENT:
400
 
                raise BzrCommandError('Source location "%s" does not'
401
 
                                      ' exist.' % to_location)
402
 
            else:
403
 
                raise
404
 
        br_from.lock_read()
405
 
        try:
406
 
            br_from.setup_caching(cache_root)
407
 
            if basis is not None:
408
 
                basis_branch = Branch.open_containing(basis)[0]
409
 
            else:
410
 
                basis_branch = None
411
 
            if len(revision) == 1 and revision[0] is not None:
412
 
                revision_id = revision[0].in_history(br_from)[1]
413
 
            else:
414
 
                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
415
375
            if to_location is None:
416
376
                to_location = os.path.basename(from_location.rstrip("/\\"))
417
377
            try:
426
386
                else:
427
387
                    raise
428
388
            try:
429
 
                copy_branch(br_from, to_location, revision_id, basis_branch)
 
389
                copy_branch(br_from, to_location, revision[0])
430
390
            except bzrlib.errors.NoSuchRevision:
431
391
                rmtree(to_location)
432
 
                msg = "The branch %s has no revision %s." % (from_location, revision[0])
 
392
                msg = "The branch %s has no revision %d." % (from_location, revision[0])
433
393
                raise BzrCommandError(msg)
434
 
            except bzrlib.errors.UnlistableBranch:
435
 
                msg = "The branch %s cannot be used as a --basis"
436
394
        finally:
437
 
            br_from.unlock()
438
395
            rmtree(cache_root)
439
396
 
440
397
 
441
398
class cmd_renames(Command):
442
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.
443
404
    """
444
 
    # TODO: Option to show renames between two historical versions.
445
 
 
446
 
    # TODO: Only show renames under dir, rather than in the whole branch.
447
405
    takes_args = ['dir?']
448
406
 
449
 
    @display_command
450
407
    def run(self, dir='.'):
451
 
        b = Branch.open_containing(dir)[0]
 
408
        b = find_branch(dir)
452
409
        old_inv = b.basis_tree().inventory
453
410
        new_inv = b.read_working_inventory()
454
411
 
462
419
    """Show statistical information about a branch."""
463
420
    takes_args = ['branch?']
464
421
    
465
 
    @display_command
466
422
    def run(self, branch=None):
467
423
        import info
468
 
        b = Branch.open_containing(branch)[0]
 
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])[0]
484
 
        tree = WorkingTree(b.base, b)
485
 
        tree.remove([tree.relpath(f) for f in file_list], verbose=verbose)
 
439
        b = find_branch(file_list[0])
 
440
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
486
441
 
487
442
 
488
443
class cmd_file_id(Command):
494
449
    """
495
450
    hidden = True
496
451
    takes_args = ['filename']
497
 
    @display_command
498
452
    def run(self, filename):
499
 
        b, relpath = Branch.open_containing(filename)
500
 
        i = b.inventory.path2id(relpath)
 
453
        b = find_branch(filename)
 
454
        i = b.inventory.path2id(b.relpath(filename))
501
455
        if i == None:
502
456
            raise BzrError("%r is not a versioned file" % filename)
503
457
        else:
511
465
    starting at the branch root."""
512
466
    hidden = True
513
467
    takes_args = ['filename']
514
 
    @display_command
515
468
    def run(self, filename):
516
 
        b, relpath = Branch.open_containing(filename)
 
469
        b = find_branch(filename)
517
470
        inv = b.inventory
518
 
        fid = inv.path2id(relpath)
 
471
        fid = inv.path2id(b.relpath(filename))
519
472
        if fid == None:
520
473
            raise BzrError("%r is not a versioned file" % filename)
521
474
        for fip in inv.get_idpath(fid):
525
478
class cmd_revision_history(Command):
526
479
    """Display list of revision ids on this branch."""
527
480
    hidden = True
528
 
    @display_command
529
481
    def run(self):
530
 
        for patchid in Branch.open_containing('.')[0].revision_history():
 
482
        for patchid in find_branch('.').revision_history():
531
483
            print patchid
532
484
 
533
485
 
534
 
class cmd_ancestry(Command):
535
 
    """List all revisions merged into this branch."""
536
 
    hidden = True
537
 
    @display_command
538
 
    def run(self):
539
 
        b = Branch.open_containing('.')[0]
540
 
        for revision_id in b.get_ancestry(b.last_revision()):
541
 
            print revision_id
542
 
 
543
 
 
544
486
class cmd_directories(Command):
545
487
    """Display list of versioned directories in this branch."""
546
 
    @display_command
547
488
    def run(self):
548
 
        for name, ie in Branch.open_containing('.')[0].read_working_inventory().directories():
 
489
        for name, ie in find_branch('.').read_working_inventory().directories():
549
490
            if name == '':
550
491
                print '.'
551
492
            else:
566
507
        bzr commit -m 'imported project'
567
508
    """
568
509
    def run(self):
569
 
        Branch.initialize('.')
 
510
        from bzrlib.branch import Branch
 
511
        Branch('.', init=True)
570
512
 
571
513
 
572
514
class cmd_diff(Command):
575
517
    If files are listed, only the changes in those files are listed.
576
518
    Otherwise, all changes for the tree are listed.
577
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
 
578
535
    examples:
579
536
        bzr diff
580
537
        bzr diff -r1
581
 
        bzr diff -r1..2
 
538
        bzr diff -r1:2
582
539
    """
583
 
    # TODO: Allow diff across branches.
584
 
    # TODO: Option to use external diff command; could be GNU diff, wdiff,
585
 
    #       or a graphical diff.
586
 
 
587
 
    # TODO: Python difflib is not exactly the same as unidiff; should
588
 
    #       either fix it up or prefer to use an external diff.
589
 
 
590
 
    # TODO: If a directory is given, diff everything under that.
591
 
 
592
 
    # TODO: Selected-file diff is inefficient and doesn't show you
593
 
    #       deleted files.
594
 
 
595
 
    # TODO: This probably handles non-Unix newlines poorly.
596
540
    
597
541
    takes_args = ['file*']
598
542
    takes_options = ['revision', 'diff-options']
599
543
    aliases = ['di', 'dif']
600
544
 
601
 
    @display_command
602
545
    def run(self, revision=None, file_list=None, diff_options=None):
603
546
        from bzrlib.diff import show_diff
604
547
 
605
548
        if file_list:
606
 
            b = Branch.open_containing(file_list[0])[0]
607
 
            tree = WorkingTree(b.base, b)
608
 
            file_list = [tree.relpath(f) for f in file_list]
 
549
            b = find_branch(file_list[0])
 
550
            file_list = [b.relpath(f) for f in file_list]
609
551
            if file_list == ['']:
610
552
                # just pointing to top-of-tree
611
553
                file_list = None
612
554
        else:
613
 
            b = Branch.open_containing('.')[0]
 
555
            b = find_branch('.')
614
556
 
615
557
        if revision is not None:
616
558
            if len(revision) == 1:
631
573
 
632
574
class cmd_deleted(Command):
633
575
    """List files deleted in the working tree.
 
576
 
 
577
    TODO: Show files deleted since a previous revision, or between two revisions.
634
578
    """
635
 
    # TODO: Show files deleted since a previous revision, or
636
 
    # between two revisions.
637
 
    # TODO: Much more efficient way to do this: read in new
638
 
    # directories with readdir, rather than stating each one.  Same
639
 
    # level of effort but possibly much less IO.  (Or possibly not,
640
 
    # if the directories are very large...)
641
 
    @display_command
642
579
    def run(self, show_ids=False):
643
 
        b = Branch.open_containing('.')[0]
 
580
        b = find_branch('.')
644
581
        old = b.basis_tree()
645
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
 
646
589
        for path, ie in old.inventory.iter_entries():
647
590
            if not new.has_id(ie.file_id):
648
591
                if show_ids:
654
597
class cmd_modified(Command):
655
598
    """List files modified in working tree."""
656
599
    hidden = True
657
 
    @display_command
658
600
    def run(self):
659
601
        from bzrlib.delta import compare_trees
660
602
 
661
 
        b = Branch.open_containing('.')[0]
 
603
        b = find_branch('.')
662
604
        td = compare_trees(b.basis_tree(), b.working_tree())
663
605
 
664
 
        for path, id, kind, text_modified, meta_modified in td.modified:
 
606
        for path, id, kind in td.modified:
665
607
            print path
666
608
 
667
609
 
669
611
class cmd_added(Command):
670
612
    """List files added in working tree."""
671
613
    hidden = True
672
 
    @display_command
673
614
    def run(self):
674
 
        b = Branch.open_containing('.')[0]
 
615
        b = find_branch('.')
675
616
        wt = b.working_tree()
676
617
        basis_inv = b.basis_tree().inventory
677
618
        inv = wt.inventory
691
632
    The root is the nearest enclosing directory with a .bzr control
692
633
    directory."""
693
634
    takes_args = ['filename?']
694
 
    @display_command
695
635
    def run(self, filename=None):
696
636
        """Print the branch root."""
697
 
        b = Branch.open_containing(filename)[0]
698
 
        print b.base
 
637
        b = find_branch(filename)
 
638
        print getattr(b, 'base', None) or getattr(b, 'baseurl')
699
639
 
700
640
 
701
641
class cmd_log(Command):
704
644
    To request a range of logs, you can use the command -r begin:end
705
645
    -r revision requests a specific revision, -r :end or -r begin: are
706
646
    also valid.
 
647
 
 
648
    --message allows you to give a regular expression, which will be evaluated
 
649
    so that only matching entries will be displayed.
 
650
 
 
651
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
 
652
  
707
653
    """
708
654
 
709
 
    # TODO: Make --revision support uuid: and hash: [future tag:] notation.
710
 
 
711
655
    takes_args = ['filename?']
712
 
    takes_options = [Option('forward', 
713
 
                            help='show from oldest to newest'),
714
 
                     'timezone', 'verbose', 
715
 
                     'show-ids', 'revision',
716
 
                     Option('line', help='format with one line per revision'),
717
 
                     'long', 
718
 
                     Option('message',
719
 
                            help='show revisions whose message matches this regexp',
720
 
                            type=str),
721
 
                     Option('short', help='use moderately short format'),
722
 
                     ]
723
 
    @display_command
 
656
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
 
657
                     'long', 'message', 'short',]
 
658
    
724
659
    def run(self, filename=None, timezone='original',
725
660
            verbose=False,
726
661
            show_ids=False,
728
663
            revision=None,
729
664
            message=None,
730
665
            long=False,
731
 
            short=False,
732
 
            line=False):
 
666
            short=False):
733
667
        from bzrlib.log import log_formatter, show_log
734
668
        import codecs
735
 
        assert message is None or isinstance(message, basestring), \
736
 
            "invalid message argument %r" % message
 
669
 
737
670
        direction = (forward and 'forward') or 'reverse'
738
671
        
739
672
        if filename:
740
 
            b, fp = Branch.open_containing(filename)
741
 
            if fp != '':
 
673
            b = find_branch(filename)
 
674
            fp = b.relpath(filename)
 
675
            if fp:
742
676
                file_id = b.read_working_inventory().path2id(fp)
743
677
            else:
744
678
                file_id = None  # points to branch root
745
679
        else:
746
 
            b, relpath = Branch.open_containing('.')
 
680
            b = find_branch('.')
747
681
            file_id = None
748
682
 
749
683
        if revision is None:
750
684
            rev1 = None
751
685
            rev2 = None
752
686
        elif len(revision) == 1:
753
 
            rev1 = rev2 = revision[0].in_history(b).revno
 
687
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
754
688
        elif len(revision) == 2:
755
 
            rev1 = revision[0].in_history(b).revno
756
 
            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]
757
691
        else:
758
692
            raise BzrCommandError('bzr log --revision takes one or two values.')
759
693
 
768
702
        # in e.g. the default C locale.
769
703
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
770
704
 
771
 
        log_format = 'long'
772
 
        if short:
 
705
        if not short:
 
706
            log_format = 'long'
 
707
        else:
773
708
            log_format = 'short'
774
 
        if line:
775
 
            log_format = 'line'
776
709
        lf = log_formatter(log_format,
777
710
                           show_ids=show_ids,
778
711
                           to_file=outf,
795
728
    A more user-friendly interface is "bzr log FILE"."""
796
729
    hidden = True
797
730
    takes_args = ["filename"]
798
 
    @display_command
799
731
    def run(self, filename):
800
 
        b, relpath = Branch.open_containing(filename)[0]
 
732
        b = find_branch(filename)
801
733
        inv = b.read_working_inventory()
802
 
        file_id = inv.path2id(relpath)
 
734
        file_id = inv.path2id(b.relpath(filename))
803
735
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
804
736
            print "%6d %s" % (revno, what)
805
737
 
806
738
 
807
739
class cmd_ls(Command):
808
740
    """List files in a tree.
 
741
 
 
742
    TODO: Take a revision or remote path and list that tree instead.
809
743
    """
810
 
    # TODO: Take a revision or remote path and list that tree instead.
811
744
    hidden = True
812
 
    @display_command
813
745
    def run(self, revision=None, verbose=False):
814
 
        b, relpath = Branch.open_containing('.')[0]
 
746
        b = find_branch('.')
815
747
        if revision == None:
816
748
            tree = b.working_tree()
817
749
        else:
818
 
            tree = b.revision_tree(revision.in_history(b).rev_id)
819
 
        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():
820
753
            if verbose:
821
 
                kindch = entry.kind_character()
 
754
                if kind == 'directory':
 
755
                    kindch = '/'
 
756
                elif kind == 'file':
 
757
                    kindch = ''
 
758
                else:
 
759
                    kindch = '???'
 
760
 
822
761
                print '%-8s %s%s' % (fc, fp, kindch)
823
762
            else:
824
763
                print fp
827
766
 
828
767
class cmd_unknowns(Command):
829
768
    """List unknown files."""
830
 
    @display_command
831
769
    def run(self):
832
770
        from bzrlib.osutils import quotefn
833
 
        for f in Branch.open_containing('.')[0].unknowns():
 
771
        for f in find_branch('.').unknowns():
834
772
            print quotefn(f)
835
773
 
836
774
 
841
779
    To remove patterns from the ignore list, edit the .bzrignore file.
842
780
 
843
781
    If the pattern contains a slash, it is compared to the whole path
844
 
    from the branch root.  Otherwise, it is compared to only the last
845
 
    component of the path.  To match a file only in the root directory,
846
 
    prepend './'.
 
782
    from the branch root.  Otherwise, it is comapred to only the last
 
783
    component of the path.
847
784
 
848
785
    Ignore patterns are case-insensitive on case-insensitive systems.
849
786
 
853
790
        bzr ignore ./Makefile
854
791
        bzr ignore '*.class'
855
792
    """
856
 
    # TODO: Complain if the filename is absolute
857
793
    takes_args = ['name_pattern']
858
794
    
859
795
    def run(self, name_pattern):
860
796
        from bzrlib.atomicfile import AtomicFile
861
797
        import os.path
862
798
 
863
 
        b, relpath = Branch.open_containing('.')
 
799
        b = find_branch('.')
864
800
        ifn = b.abspath('.bzrignore')
865
801
 
866
802
        if os.path.exists(ifn):
899
835
    """List ignored files and the patterns that matched them.
900
836
 
901
837
    See also: bzr ignore"""
902
 
    @display_command
903
838
    def run(self):
904
 
        tree = Branch.open_containing('.')[0].working_tree()
905
 
        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():
906
841
            if file_class != 'I':
907
842
                continue
908
843
            ## XXX: Slightly inefficient since this was already calculated
919
854
    hidden = True
920
855
    takes_args = ['revno']
921
856
    
922
 
    @display_command
923
857
    def run(self, revno):
924
858
        try:
925
859
            revno = int(revno)
926
860
        except ValueError:
927
861
            raise BzrCommandError("not a valid revision-number: %r" % revno)
928
862
 
929
 
        print Branch.open_containing('.')[0].get_rev_id(revno)
 
863
        print find_branch('.').lookup_revision(revno)
930
864
 
931
865
 
932
866
class cmd_export(Command):
945
879
    takes_options = ['revision', 'format', 'root']
946
880
    def run(self, dest, revision=None, format=None, root=None):
947
881
        import os.path
948
 
        b = Branch.open_containing('.')[0]
 
882
        b = find_branch('.')
949
883
        if revision is None:
950
 
            rev_id = b.last_revision()
 
884
            rev_id = b.last_patch()
951
885
        else:
952
886
            if len(revision) != 1:
953
887
                raise BzrError('bzr export --revision takes exactly 1 argument')
954
 
            rev_id = revision[0].in_history(b).rev_id
 
888
            revno, rev_id = b.get_revision_info(revision[0])
955
889
        t = b.revision_tree(rev_id)
956
 
        arg_root, ext = os.path.splitext(os.path.basename(dest))
957
 
        if ext in ('.gz', '.bz2'):
958
 
            new_root, new_ext = os.path.splitext(arg_root)
959
 
            if new_ext == '.tar':
960
 
                arg_root = new_root
961
 
                ext = new_ext + ext
962
 
        if root is None:
963
 
            root = arg_root
 
890
        root, ext = os.path.splitext(dest)
964
891
        if not format:
965
892
            if ext in (".tar",):
966
893
                format = "tar"
967
 
            elif ext in (".tar.gz", ".tgz"):
 
894
            elif ext in (".gz", ".tgz"):
968
895
                format = "tgz"
969
 
            elif ext in (".tar.bz2", ".tbz2"):
 
896
            elif ext in (".bz2", ".tbz2"):
970
897
                format = "tbz2"
971
898
            else:
972
899
                format = "dir"
979
906
    takes_options = ['revision']
980
907
    takes_args = ['filename']
981
908
 
982
 
    @display_command
983
909
    def run(self, filename, revision=None):
984
 
        if revision is None:
 
910
        if revision == None:
985
911
            raise BzrCommandError("bzr cat requires a revision number")
986
912
        elif len(revision) != 1:
987
913
            raise BzrCommandError("bzr cat --revision takes exactly one number")
988
 
        b, relpath = Branch.open_containing(filename)
989
 
        b.print_file(relpath, revision[0].in_history(b).revno)
 
914
        b = find_branch('.')
 
915
        b.print_file(b.relpath(filename), revision[0])
990
916
 
991
917
 
992
918
class cmd_local_time_offset(Command):
993
919
    """Show the offset in seconds from GMT to local time."""
994
920
    hidden = True    
995
 
    @display_command
996
921
    def run(self):
997
922
        print bzrlib.osutils.local_time_offset()
998
923
 
1010
935
    A selected-file commit may fail in some cases where the committed
1011
936
    tree would be invalid, such as trying to commit a file in a
1012
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.
1013
942
    """
1014
 
    # TODO: Run hooks on tree to-be-committed, and after commit.
1015
 
 
1016
 
    # TODO: Strict commit that fails if there are deleted files.
1017
 
    #       (what does "deleted files" mean ??)
1018
 
 
1019
 
    # TODO: Give better message for -s, --summary, used by tla people
1020
 
 
1021
 
    # XXX: verbose currently does nothing
1022
 
 
1023
943
    takes_args = ['selected*']
1024
 
    takes_options = ['message', 'verbose', 
1025
 
                     Option('unchanged',
1026
 
                            help='commit even if nothing has changed'),
1027
 
                     Option('file', type=str, 
1028
 
                            argname='msgfile',
1029
 
                            help='file containing commit message'),
1030
 
                     Option('strict',
1031
 
                            help="refuse to commit if there are unknown "
1032
 
                            "files in the working tree."),
1033
 
                     ]
 
944
    takes_options = ['message', 'file', 'verbose', 'unchanged']
1034
945
    aliases = ['ci', 'checkin']
1035
946
 
 
947
    # TODO: Give better message for -s, --summary, used by tla people
 
948
    
1036
949
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1037
 
            unchanged=False, strict=False):
1038
 
        from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1039
 
                StrictCommitFailed)
 
950
            unchanged=False):
 
951
        from bzrlib.errors import PointlessCommit
1040
952
        from bzrlib.msgeditor import edit_commit_message
1041
953
        from bzrlib.status import show_status
1042
954
        from cStringIO import StringIO
1043
955
 
1044
 
        b = Branch.open_containing('.')[0]
1045
 
        tree = WorkingTree(b.base, b)
 
956
        b = find_branch('.')
1046
957
        if selected_list:
1047
 
            selected_list = [tree.relpath(s) for s in selected_list]
1048
 
        if message is None and not file:
 
958
            selected_list = [b.relpath(s) for s in selected_list]
 
959
            
 
960
        if not message and not file:
1049
961
            catcher = StringIO()
1050
962
            show_status(b, specific_files=selected_list,
1051
963
                        to_file=catcher)
1052
964
            message = edit_commit_message(catcher.getvalue())
1053
 
 
 
965
            
1054
966
            if message is None:
1055
967
                raise BzrCommandError("please specify a commit message"
1056
968
                                      " with either --message or --file")
1061
973
            import codecs
1062
974
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1063
975
 
1064
 
        if message == "":
1065
 
                raise BzrCommandError("empty commit message specified")
1066
 
            
1067
976
        try:
1068
 
            b.commit(message, specific_files=selected_list,
1069
 
                     allow_pointless=unchanged, strict=strict)
 
977
            b.commit(message, verbose=verbose,
 
978
                     specific_files=selected_list,
 
979
                     allow_pointless=unchanged)
1070
980
        except PointlessCommit:
1071
981
            # FIXME: This should really happen before the file is read in;
1072
982
            # perhaps prepare the commit; get the message; then actually commit
1073
983
            raise BzrCommandError("no changes to commit",
1074
984
                                  ["use --unchanged to commit anyhow"])
1075
 
        except ConflictsInTree:
1076
 
            raise BzrCommandError("Conflicts detected in working tree.  "
1077
 
                'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1078
 
        except StrictCommitFailed:
1079
 
            raise BzrCommandError("Commit refused because there are unknown "
1080
 
                                  "files in the working tree.")
1081
985
 
1082
986
 
1083
987
class cmd_check(Command):
1085
989
 
1086
990
    This command checks various invariants about the branch storage to
1087
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.
1088
995
    """
1089
996
    takes_args = ['dir?']
1090
 
    takes_options = ['verbose']
1091
997
 
1092
 
    def run(self, dir='.', verbose=False):
 
998
    def run(self, dir='.'):
1093
999
        from bzrlib.check import check
1094
 
        check(Branch.open_containing(dir)[0], verbose)
 
1000
 
 
1001
        check(find_branch(dir))
1095
1002
 
1096
1003
 
1097
1004
class cmd_scan_cache(Command):
1119
1026
 
1120
1027
    The check command or bzr developers may sometimes advise you to run
1121
1028
    this command.
1122
 
 
1123
 
    This version of this command upgrades from the full-text storage
1124
 
    used by bzr 0.0.8 and earlier to the weave format (v5).
1125
1029
    """
1126
1030
    takes_args = ['dir?']
1127
1031
 
1128
1032
    def run(self, dir='.'):
1129
1033
        from bzrlib.upgrade import upgrade
1130
 
        upgrade(dir)
 
1034
        upgrade(find_branch(dir))
 
1035
 
1131
1036
 
1132
1037
 
1133
1038
class cmd_whoami(Command):
1134
1039
    """Show bzr user id."""
1135
1040
    takes_options = ['email']
1136
1041
    
1137
 
    @display_command
1138
1042
    def run(self, email=False):
1139
1043
        try:
1140
 
            b = bzrlib.branch.Branch.open_containing('.')[0]
1141
 
            config = bzrlib.config.BranchConfig(b)
1142
 
        except NotBranchError:
1143
 
            config = bzrlib.config.GlobalConfig()
 
1044
            b = bzrlib.branch.find_branch('.')
 
1045
        except:
 
1046
            b = None
1144
1047
        
1145
1048
        if email:
1146
 
            print config.user_email()
 
1049
            print bzrlib.osutils.user_email(b)
1147
1050
        else:
1148
 
            print config.username()
 
1051
            print bzrlib.osutils.username(b)
1149
1052
 
1150
1053
 
1151
1054
class cmd_selftest(Command):
1152
 
    """Run internal test suite.
1153
 
    
1154
 
    This creates temporary test directories in the working directory,
1155
 
    but not existing data is affected.  These directories are deleted
1156
 
    if the tests pass, or left behind to help in debugging if they
1157
 
    fail.
1158
 
    
1159
 
    If arguments are given, they are regular expressions that say
1160
 
    which tests should run.
1161
 
    """
1162
 
    # TODO: --list should give a list of all available tests
 
1055
    """Run internal test suite"""
1163
1056
    hidden = True
1164
 
    takes_args = ['testspecs*']
1165
 
    takes_options = ['verbose', 
1166
 
                     Option('one', help='stop when one test fails'),
1167
 
                    ]
1168
 
 
1169
 
    def run(self, testspecs_list=None, verbose=False, one=False):
 
1057
    takes_options = ['verbose', 'pattern']
 
1058
    def run(self, verbose=False, pattern=".*"):
1170
1059
        import bzrlib.ui
1171
1060
        from bzrlib.selftest import selftest
1172
1061
        # we don't want progress meters from the tests to go to the
1176
1065
        bzrlib.trace.info('running tests...')
1177
1066
        try:
1178
1067
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1179
 
            if testspecs_list is not None:
1180
 
                pattern = '|'.join(testspecs_list)
1181
 
            else:
1182
 
                pattern = ".*"
1183
 
            result = selftest(verbose=verbose, 
1184
 
                              pattern=pattern,
1185
 
                              stop_on_failure=one)
 
1068
            result = selftest(verbose=verbose, pattern=pattern)
1186
1069
            if result:
1187
1070
                bzrlib.trace.info('tests passed')
1188
1071
            else:
1208
1091
 
1209
1092
class cmd_version(Command):
1210
1093
    """Show version of bzr."""
1211
 
    @display_command
1212
1094
    def run(self):
1213
1095
        show_version()
1214
1096
 
1215
1097
class cmd_rocks(Command):
1216
1098
    """Statement of optimism."""
1217
1099
    hidden = True
1218
 
    @display_command
1219
1100
    def run(self):
1220
1101
        print "it sure does!"
1221
1102
 
1222
1103
 
1223
1104
class cmd_find_merge_base(Command):
1224
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.
1225
1109
    """
1226
 
    # TODO: Options to specify revisions on either side, as if
1227
 
    #       merging only part of the history.
1228
1110
    takes_args = ['branch', 'other']
1229
1111
    hidden = True
1230
1112
    
1231
 
    @display_command
1232
1113
    def run(self, branch, other):
1233
1114
        from bzrlib.revision import common_ancestor, MultipleRevisionSources
1234
1115
        
1235
 
        branch1 = Branch.open_containing(branch)[0]
1236
 
        branch2 = Branch.open_containing(other)[0]
 
1116
        branch1 = find_branch(branch)
 
1117
        branch2 = find_branch(other)
1237
1118
 
1238
1119
        history_1 = branch1.revision_history()
1239
1120
        history_2 = branch2.revision_history()
1240
1121
 
1241
 
        last1 = branch1.last_revision()
1242
 
        last2 = branch2.last_revision()
 
1122
        last1 = branch1.last_patch()
 
1123
        last2 = branch2.last_patch()
1243
1124
 
1244
1125
        source = MultipleRevisionSources(branch1, branch2)
1245
1126
        
1288
1169
    --force is given.
1289
1170
    """
1290
1171
    takes_args = ['branch?']
1291
 
    takes_options = ['revision', 'force', 'merge-type', 
1292
 
                     Option('show-base', help="Show base revision text in "
1293
 
                            "conflicts")]
 
1172
    takes_options = ['revision', 'force', 'merge-type']
1294
1173
 
1295
 
    def run(self, branch=None, revision=None, force=False, merge_type=None,
1296
 
            show_base=False):
 
1174
    def run(self, branch='.', revision=None, force=False, 
 
1175
            merge_type=None):
1297
1176
        from bzrlib.merge import merge
1298
1177
        from bzrlib.merge_core import ApplyMerge3
1299
1178
        if merge_type is None:
1300
1179
            merge_type = ApplyMerge3
1301
 
        if branch is None:
1302
 
            branch = Branch.open_containing('.')[0].get_parent()
1303
 
            if branch is None:
1304
 
                raise BzrCommandError("No merge location known or specified.")
1305
 
            else:
1306
 
                print "Using saved location: %s" % branch 
 
1180
 
1307
1181
        if revision is None or len(revision) < 1:
1308
1182
            base = [None, None]
1309
1183
            other = [branch, -1]
1310
1184
        else:
1311
1185
            if len(revision) == 1:
 
1186
                other = [branch, revision[0]]
1312
1187
                base = [None, None]
1313
 
                other_branch = Branch.open_containing(branch)[0]
1314
 
                revno = revision[0].in_history(other_branch).revno
1315
 
                other = [branch, revno]
1316
1188
            else:
1317
1189
                assert len(revision) == 2
1318
1190
                if None in revision:
1319
1191
                    raise BzrCommandError(
1320
1192
                        "Merge doesn't permit that revision specifier.")
1321
 
                b = Branch.open_containing(branch)[0]
1322
 
 
1323
 
                base = [branch, revision[0].in_history(b).revno]
1324
 
                other = [branch, revision[1].in_history(b).revno]
 
1193
                base = [branch, revision[0]]
 
1194
                other = [branch, revision[1]]
1325
1195
 
1326
1196
        try:
1327
 
            merge(other, base, check_clean=(not force), merge_type=merge_type,
1328
 
                  show_base=show_base)
 
1197
            merge(other, base, check_clean=(not force), merge_type=merge_type)
1329
1198
        except bzrlib.errors.AmbiguousBase, e:
1330
1199
            m = ("sorry, bzr can't determine the right merge base yet\n"
1331
1200
                 "candidates are:\n  "
1349
1218
 
1350
1219
    def run(self, revision=None, no_backup=False, file_list=None):
1351
1220
        from bzrlib.merge import merge
 
1221
        from bzrlib.branch import Branch
1352
1222
        from bzrlib.commands import parse_spec
1353
1223
 
1354
1224
        if file_list is not None:
1355
1225
            if len(file_list) == 0:
1356
1226
                raise BzrCommandError("No files specified")
1357
1227
        if revision is None:
1358
 
            revno = -1
 
1228
            revision = [-1]
1359
1229
        elif len(revision) != 1:
1360
1230
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1361
 
        else:
1362
 
            b = Branch.open_containing('.')[0]
1363
 
            revno = revision[0].in_history(b).revno
1364
 
        merge(('.', revno), parse_spec('.'),
 
1231
        merge(('.', revision[0]), parse_spec('.'),
1365
1232
              check_clean=False,
1366
1233
              ignore_zero=True,
1367
1234
              backup_files=not no_backup,
1368
1235
              file_list=file_list)
1369
1236
        if not file_list:
1370
 
            Branch.open_containing('.')[0].set_pending_merges([])
 
1237
            Branch('.').set_pending_merges([])
1371
1238
 
1372
1239
 
1373
1240
class cmd_assert_fail(Command):
1385
1252
    takes_args = ['topic?']
1386
1253
    aliases = ['?']
1387
1254
    
1388
 
    @display_command
1389
1255
    def run(self, topic=None, long=False):
1390
1256
        import help
1391
1257
        if topic is None and long:
1401
1267
    aliases = ['s-c']
1402
1268
    hidden = True
1403
1269
    
1404
 
    @display_command
1405
1270
    def run(self, context=None):
1406
1271
        import shellcomplete
1407
1272
        shellcomplete.shellcomplete(context)
1408
1273
 
1409
1274
 
1410
 
class cmd_fetch(Command):
1411
 
    """Copy in history from another branch but don't merge it.
1412
 
 
1413
 
    This is an internal method used for pull and merge."""
1414
 
    hidden = True
1415
 
    takes_args = ['from_branch', 'to_branch']
1416
 
    def run(self, from_branch, to_branch):
1417
 
        from bzrlib.fetch import Fetcher
1418
 
        from bzrlib.branch import Branch
1419
 
        from_b = Branch(from_branch)
1420
 
        to_b = Branch(to_branch)
1421
 
        Fetcher(to_b, from_b)
1422
 
        
1423
 
 
1424
 
 
1425
1275
class cmd_missing(Command):
1426
1276
    """What is missing in this branch relative to other branch.
1427
1277
    """
1428
 
    # TODO: rewrite this in terms of ancestry so that it shows only
1429
 
    # unmerged things
1430
 
    
1431
1278
    takes_args = ['remote?']
1432
1279
    aliases = ['mis', 'miss']
1433
1280
    # We don't have to add quiet to the list, because 
1434
1281
    # unknown options are parsed as booleans
1435
1282
    takes_options = ['verbose', 'quiet']
1436
1283
 
1437
 
    @display_command
1438
1284
    def run(self, remote=None, verbose=False, quiet=False):
1439
1285
        from bzrlib.errors import BzrCommandError
1440
1286
        from bzrlib.missing import show_missing
1442
1288
        if verbose and quiet:
1443
1289
            raise BzrCommandError('Cannot pass both quiet and verbose')
1444
1290
 
1445
 
        b = Branch.open_containing('.')[0]
 
1291
        b = find_branch('.')
1446
1292
        parent = b.get_parent()
1447
1293
        if remote is None:
1448
1294
            if parent is None:
1452
1298
                    print "Using last location: %s" % parent
1453
1299
                remote = parent
1454
1300
        elif parent is None:
1455
 
            # We only update parent if it did not exist, missing
1456
 
            # should not change the parent
1457
 
            b.set_parent(remote)
1458
 
        br_remote = Branch.open_containing(remote)[0]
 
1301
            # We only update x-pull if it did not exist, missing should not change the parent
 
1302
            b.controlfile('x-pull', 'wb').write(remote + '\n')
 
1303
        br_remote = find_branch(remote)
 
1304
 
1459
1305
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1460
1306
 
1461
1307
 
 
1308
 
1462
1309
class cmd_plugins(Command):
1463
1310
    """List plugins"""
1464
1311
    hidden = True
1465
 
    @display_command
1466
1312
    def run(self):
1467
1313
        import bzrlib.plugin
1468
1314
        from inspect import getdoc
1479
1325
                print '\t', d.split('\n')[0]
1480
1326
 
1481
1327
 
1482
 
class cmd_testament(Command):
1483
 
    """Show testament (signing-form) of a revision."""
1484
 
    takes_options = ['revision', 'long']
1485
 
    takes_args = ['branch?']
1486
 
    @display_command
1487
 
    def run(self, branch='.', revision=None, long=False):
1488
 
        from bzrlib.testament import Testament
1489
 
        b = Branch.open_containing(branch)[0]
1490
 
        b.lock_read()
1491
 
        try:
1492
 
            if revision is None:
1493
 
                rev_id = b.last_revision()
1494
 
            else:
1495
 
                rev_id = revision[0].in_history(b).rev_id
1496
 
            t = Testament.from_revision(b, rev_id)
1497
 
            if long:
1498
 
                sys.stdout.writelines(t.as_text_lines())
1499
 
            else:
1500
 
                sys.stdout.write(t.as_short_text())
1501
 
        finally:
1502
 
            b.unlock()
1503
 
 
1504
 
 
1505
 
class cmd_annotate(Command):
1506
 
    """Show the origin of each line in a file.
1507
 
 
1508
 
    This prints out the given file with an annotation on the left side
1509
 
    indicating which revision, author and date introduced the change.
1510
 
 
1511
 
    If the origin is the same for a run of consecutive lines, it is 
1512
 
    shown only at the top, unless the --all option is given.
1513
 
    """
1514
 
    # TODO: annotate directories; showing when each file was last changed
1515
 
    # TODO: annotate a previous version of a file
1516
 
    # TODO: if the working copy is modified, show annotations on that 
1517
 
    #       with new uncommitted lines marked
1518
 
    aliases = ['blame', 'praise']
1519
 
    takes_args = ['filename']
1520
 
    takes_options = [Option('all', help='show annotations on all lines'),
1521
 
                     Option('long', help='show date in annotations'),
1522
 
                     ]
1523
 
 
1524
 
    @display_command
1525
 
    def run(self, filename, all=False, long=False):
1526
 
        from bzrlib.annotate import annotate_file
1527
 
        b, relpath = Branch.open_containing(filename)
1528
 
        b.lock_read()
1529
 
        try:
1530
 
            tree = WorkingTree(b.base, b)
1531
 
            tree = b.revision_tree(b.last_revision())
1532
 
            file_id = tree.inventory.path2id(relpath)
1533
 
            file_version = tree.inventory[file_id].revision
1534
 
            annotate_file(b, file_version, file_id, long, all, sys.stdout)
1535
 
        finally:
1536
 
            b.unlock()
1537
 
 
1538
 
 
1539
 
class cmd_re_sign(Command):
1540
 
    """Create a digital signature for an existing revision."""
1541
 
    # TODO be able to replace existing ones.
1542
 
 
1543
 
    hidden = True # is this right ?
1544
 
    takes_args = ['revision_id?']
1545
 
    takes_options = ['revision']
1546
 
    
1547
 
    def run(self, revision_id=None, revision=None):
1548
 
        import bzrlib.config as config
1549
 
        import bzrlib.gpg as gpg
1550
 
        if revision_id is not None and revision is not None:
1551
 
            raise BzrCommandError('You can only supply one of revision_id or --revision')
1552
 
        if revision_id is None and revision is None:
1553
 
            raise BzrCommandError('You must supply either --revision or a revision_id')
1554
 
        b = Branch.open_containing('.')[0]
1555
 
        gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1556
 
        if revision_id is not None:
1557
 
            b.sign_revision(revision_id, gpg_strategy)
1558
 
        elif revision is not None:
1559
 
            for rev in revision:
1560
 
                if rev is None:
1561
 
                    raise BzrCommandError('You cannot specify a NULL revision.')
1562
 
                revno, rev_id = rev.in_history(b)
1563
 
                b.sign_revision(rev_id, gpg_strategy)
1564
 
 
1565
 
 
1566
 
# these get imported and then picked up by the scan for cmd_*
1567
 
# TODO: Some more consistent way to split command definitions across files;
1568
 
# we do need to load at least some information about them to know of 
1569
 
# aliases.
1570
 
from bzrlib.conflicts import cmd_resolve, cmd_conflicts