~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Robert Collins
  • Date: 2005-10-11 03:19:29 UTC
  • Revision ID: robertc@robertcollins.net-20051011031929-2d523107133c43be
further tuning of pull, do not do a local merge or fetch at all, if the remote branch is no newer than we are

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
16
16
 
17
 
"""builtin bzr commands"""
18
 
 
19
 
# DO NOT change this to cStringIO - it results in control files 
20
 
# written as UCS4
21
 
# FIXIT! (Only deal with byte streams OR unicode at any one layer.)
22
 
# RBC 20051018
23
 
 
24
 
from StringIO import StringIO
 
17
 
25
18
import sys
26
19
import os
27
20
 
28
21
import bzrlib
 
22
import bzrlib.trace
 
23
from bzrlib.trace import mutter, note, log_error, warning
 
24
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
 
25
from bzrlib.branch import Branch
29
26
from bzrlib import BZRDIR
30
 
from bzrlib.commands import Command, display_command
31
 
from bzrlib.branch import Branch
32
 
from bzrlib.revision import common_ancestor
33
 
import bzrlib.errors as errors
34
 
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError, 
35
 
                           NotBranchError, DivergedBranches, NotConflicted,
36
 
                           NoSuchFile, NoWorkingTree, FileInWrongBranch)
37
 
from bzrlib.option import Option
38
 
from bzrlib.revisionspec import RevisionSpec
39
 
import bzrlib.trace
40
 
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
41
 
from bzrlib.workingtree import WorkingTree
42
 
from bzrlib.log import show_one_log
43
 
 
44
 
 
45
 
def tree_files(file_list, default_branch=u'.'):
46
 
    try:
47
 
        return internal_tree_files(file_list, default_branch)
48
 
    except FileInWrongBranch, e:
49
 
        raise BzrCommandError("%s is not in the same branch as %s" %
50
 
                             (e.path, file_list[0]))
51
 
 
52
 
def internal_tree_files(file_list, default_branch=u'.'):
53
 
    """\
54
 
    Return a branch and list of branch-relative paths.
55
 
    If supplied file_list is empty or None, the branch default will be used,
56
 
    and returned file_list will match the original.
57
 
    """
58
 
    if file_list is None or len(file_list) == 0:
59
 
        return WorkingTree.open_containing(default_branch)[0], file_list
60
 
    tree = WorkingTree.open_containing(file_list[0])[0]
61
 
    new_list = []
62
 
    for filename in file_list:
63
 
        try:
64
 
            new_list.append(tree.relpath(filename))
65
 
        except errors.PathNotChild:
66
 
            raise FileInWrongBranch(tree.branch, filename)
67
 
    return tree, new_list
68
 
 
69
 
 
70
 
# TODO: Make sure no commands unconditionally use the working directory as a
71
 
# branch.  If a filename argument is used, the first of them should be used to
72
 
# specify the branch.  (Perhaps this can be factored out into some kind of
73
 
# Argument class, representing a file in a branch, where the first occurrence
74
 
# opens the branch?)
 
27
from bzrlib.commands import Command
 
28
 
75
29
 
76
30
class cmd_status(Command):
77
31
    """Display status summary.
112
66
    If a revision argument is given, the status is calculated against
113
67
    that revision, or between two revisions if two are provided.
114
68
    """
115
 
    
116
 
    # TODO: --no-recurse, --recurse options
117
 
    
 
69
    # XXX: FIXME: bzr status should accept a -r option to show changes
 
70
    # relative to a revision, or between revisions
 
71
 
118
72
    takes_args = ['file*']
119
 
    takes_options = ['all', 'show-ids', 'revision']
 
73
    takes_options = ['all', 'show-ids']
120
74
    aliases = ['st', 'stat']
121
75
    
122
 
    @display_command
123
76
    def run(self, all=False, show_ids=False, file_list=None, revision=None):
124
 
        tree, file_list = tree_files(file_list)
 
77
        if file_list:
 
78
            b = Branch.open_containing(file_list[0])
 
79
            file_list = [b.relpath(x) for x in file_list]
 
80
            # special case: only one path was given and it's the root
 
81
            # of the branch
 
82
            if file_list == ['']:
 
83
                file_list = None
 
84
        else:
 
85
            b = Branch.open_containing('.')
125
86
            
126
87
        from bzrlib.status import show_status
127
 
        show_status(tree.branch, show_unchanged=all, show_ids=show_ids,
 
88
        show_status(b, show_unchanged=all, show_ids=show_ids,
128
89
                    specific_files=file_list, revision=revision)
129
90
 
130
91
 
139
100
    takes_args = ['revision_id?']
140
101
    takes_options = ['revision']
141
102
    
142
 
    @display_command
143
103
    def run(self, revision_id=None, revision=None):
 
104
        from bzrlib.revisionspec import RevisionSpec
144
105
 
145
106
        if revision_id is not None and revision is not None:
146
107
            raise BzrCommandError('You can only supply one of revision_id or --revision')
147
108
        if revision_id is None and revision is None:
148
109
            raise BzrCommandError('You must supply either --revision or a revision_id')
149
 
        b = WorkingTree.open_containing(u'.')[0].branch
 
110
        b = Branch.open_containing('.')
150
111
        if revision_id is not None:
151
 
            sys.stdout.write(b.get_revision_xml(revision_id))
 
112
            sys.stdout.write(b.get_revision_xml_file(revision_id).read())
152
113
        elif revision is not None:
153
114
            for rev in revision:
154
115
                if rev is None:
155
116
                    raise BzrCommandError('You cannot specify a NULL revision.')
156
117
                revno, rev_id = rev.in_history(b)
157
 
                sys.stdout.write(b.get_revision_xml(rev_id))
 
118
                sys.stdout.write(b.get_revision_xml_file(rev_id).read())
158
119
    
159
120
 
160
121
class cmd_revno(Command):
161
122
    """Show current revision number.
162
123
 
163
124
    This is equal to the number of revisions on this branch."""
164
 
    takes_args = ['location?']
165
 
    @display_command
166
 
    def run(self, location=u'.'):
167
 
        print Branch.open_containing(location)[0].revno()
 
125
    def run(self):
 
126
        print Branch.open_containing('.').revno()
168
127
 
169
128
 
170
129
class cmd_revision_info(Command):
173
132
    hidden = True
174
133
    takes_args = ['revision_info*']
175
134
    takes_options = ['revision']
176
 
    @display_command
177
135
    def run(self, revision=None, revision_info_list=[]):
 
136
        from bzrlib.revisionspec import RevisionSpec
178
137
 
179
138
        revs = []
180
139
        if revision is not None:
185
144
        if len(revs) == 0:
186
145
            raise BzrCommandError('You must supply a revision identifier')
187
146
 
188
 
        b = WorkingTree.open_containing(u'.')[0].branch
 
147
        b = Branch.open_containing('.')
189
148
 
190
149
        for rev in revs:
191
150
            revinfo = rev.in_history(b)
217
176
    implicitly add the parent, and so on up to the root. This means
218
177
    you should never need to explictly add a directory, they'll just
219
178
    get added when you add a file in the directory.
220
 
 
221
 
    --dry-run will show which files would be added, but not actually 
222
 
    add them.
223
179
    """
224
180
    takes_args = ['file*']
225
 
    takes_options = ['no-recurse', 'dry-run', 'verbose']
226
 
 
227
 
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
228
 
        import bzrlib.add
229
 
 
230
 
        if dry_run:
231
 
            if is_quiet():
232
 
                # This is pointless, but I'd rather not raise an error
233
 
                action = bzrlib.add.add_action_null
234
 
            else:
235
 
                action = bzrlib.add.add_action_print
236
 
        elif is_quiet():
237
 
            action = bzrlib.add.add_action_add
238
 
        else:
239
 
            action = bzrlib.add.add_action_add_and_print
240
 
 
241
 
        added, ignored = bzrlib.add.smart_add(file_list, not no_recurse, 
242
 
                                              action)
243
 
        if len(ignored) > 0:
244
 
            for glob in sorted(ignored.keys()):
245
 
                match_len = len(ignored[glob])
246
 
                if verbose:
247
 
                    for path in ignored[glob]:
248
 
                        print "ignored %s matching \"%s\"" % (path, glob)
249
 
                else:
250
 
                    print "ignored %d file(s) matching \"%s\"" % (match_len,
251
 
                                                              glob)
252
 
            print "If you wish to add some of these files, please add them"\
253
 
                " by name."
 
181
    takes_options = ['verbose', 'no-recurse']
 
182
    
 
183
    def run(self, file_list, verbose=False, no_recurse=False):
 
184
        # verbose currently has no effect
 
185
        from bzrlib.add import smart_add, add_reporter_print
 
186
        smart_add(file_list, not no_recurse, add_reporter_print)
 
187
 
254
188
 
255
189
 
256
190
class cmd_mkdir(Command):
261
195
    takes_args = ['dir+']
262
196
 
263
197
    def run(self, dir_list):
 
198
        b = None
 
199
        
264
200
        for d in dir_list:
265
201
            os.mkdir(d)
266
 
            wt, dd = WorkingTree.open_containing(d)
267
 
            wt.add([dd])
 
202
            if not b:
 
203
                b = Branch.open_containing(d)
 
204
            b.add([d])
268
205
            print 'added', d
269
206
 
270
207
 
273
210
    takes_args = ['filename']
274
211
    hidden = True
275
212
    
276
 
    @display_command
277
213
    def run(self, filename):
278
 
        tree, relpath = WorkingTree.open_containing(filename)
279
 
        print relpath
 
214
        print Branch.open_containing(filename).relpath(filename)
 
215
 
280
216
 
281
217
 
282
218
class cmd_inventory(Command):
283
 
    """Show inventory of the current working copy or a revision.
284
 
 
285
 
    It is possible to limit the output to a particular entry
286
 
    type using the --kind option.  For example; --kind file.
287
 
    """
288
 
    takes_options = ['revision', 'show-ids', 'kind']
 
219
    """Show inventory of the current working copy or a revision."""
 
220
    takes_options = ['revision', 'show-ids']
289
221
    
290
 
    @display_command
291
 
    def run(self, revision=None, show_ids=False, kind=None):
292
 
        if kind and kind not in ['file', 'directory', 'symlink']:
293
 
            raise BzrCommandError('invalid kind specified')
294
 
        tree = WorkingTree.open_containing(u'.')[0]
 
222
    def run(self, revision=None, show_ids=False):
 
223
        b = Branch.open_containing('.')
295
224
        if revision is None:
296
 
            inv = tree.read_working_inventory()
 
225
            inv = b.read_working_inventory()
297
226
        else:
298
227
            if len(revision) > 1:
299
228
                raise BzrCommandError('bzr inventory --revision takes'
300
229
                    ' exactly one revision identifier')
301
 
            inv = tree.branch.get_revision_inventory(
302
 
                revision[0].in_history(tree.branch).rev_id)
 
230
            inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
303
231
 
304
232
        for path, entry in inv.entries():
305
 
            if kind and kind != entry.kind:
306
 
                continue
307
233
            if show_ids:
308
234
                print '%-50s %s' % (path, entry.file_id)
309
235
            else:
320
246
    """
321
247
    takes_args = ['source$', 'dest']
322
248
    def run(self, source_list, dest):
323
 
        tree, source_list = tree_files(source_list)
 
249
        b = Branch.open_containing('.')
 
250
 
324
251
        # TODO: glob expansion on windows?
325
 
        tree.move(source_list, tree.relpath(dest))
 
252
        b.move([b.relpath(s) for s in source_list], b.relpath(dest))
326
253
 
327
254
 
328
255
class cmd_rename(Command):
336
263
 
337
264
    See also the 'move' command, which moves files into a different
338
265
    directory without changing their name.
339
 
    """
340
 
    # TODO: Some way to rename multiple files without invoking 
341
 
    # bzr for each one?"""
 
266
 
 
267
    TODO: Some way to rename multiple files without invoking bzr for each
 
268
    one?"""
342
269
    takes_args = ['from_name', 'to_name']
343
270
    
344
271
    def run(self, from_name, to_name):
345
 
        tree, (from_name, to_name) = tree_files((from_name, to_name))
346
 
        tree.rename_one(from_name, to_name)
 
272
        b = Branch.open_containing('.')
 
273
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
 
274
 
347
275
 
348
276
 
349
277
class cmd_mv(Command):
363
291
    def run(self, names_list):
364
292
        if len(names_list) < 2:
365
293
            raise BzrCommandError("missing file argument")
366
 
        tree, rel_names = tree_files(names_list)
 
294
        b = Branch.open_containing(names_list[0])
 
295
 
 
296
        rel_names = [b.relpath(x) for x in names_list]
367
297
        
368
298
        if os.path.isdir(names_list[-1]):
369
299
            # move into existing directory
370
 
            for pair in tree.move(rel_names[:-1], rel_names[-1]):
 
300
            for pair in b.move(rel_names[:-1], rel_names[-1]):
371
301
                print "%s => %s" % pair
372
302
        else:
373
303
            if len(names_list) != 2:
374
304
                raise BzrCommandError('to mv multiple files the destination '
375
305
                                      'must be a versioned directory')
376
 
            tree.rename_one(rel_names[0], rel_names[1])
 
306
            b.rename_one(rel_names[0], rel_names[1])
377
307
            print "%s => %s" % (rel_names[0], rel_names[1])
378
308
            
379
309
    
 
310
 
 
311
 
380
312
class cmd_pull(Command):
381
313
    """Pull any changes from another branch into the current one.
382
314
 
383
 
    If there is no default location set, the first pull will set it.  After
384
 
    that, you can omit the location to use the default.  To change the
385
 
    default, use --remember.
 
315
    If the location is omitted, the last-used location will be used.
 
316
    Both the revision history and the working directory will be
 
317
    updated.
386
318
 
387
319
    This command only works on branches that have not diverged.  Branches are
388
320
    considered diverged if both branches have had commits without first
389
321
    pulling from the other.
390
322
 
391
323
    If branches have diverged, you can use 'bzr merge' to pull the text changes
392
 
    from one into the other.  Once one branch has merged, the other should
393
 
    be able to pull it again.
394
 
 
395
 
    If you want to forget your local changes and just update your branch to
396
 
    match the remote one, use --overwrite.
 
324
    from one into the other.
397
325
    """
398
 
    takes_options = ['remember', 'overwrite', 'verbose']
 
326
    takes_options = ['remember']
399
327
    takes_args = ['location?']
400
328
 
401
 
    def run(self, location=None, remember=False, overwrite=False, verbose=False):
 
329
    def run(self, location=None, remember=False):
402
330
        from bzrlib.merge import merge
 
331
        import tempfile
403
332
        from shutil import rmtree
404
333
        import errno
405
 
        # FIXME: too much stuff is in the command class        
406
 
        tree_to = WorkingTree.open_containing(u'.')[0]
407
 
        stored_loc = tree_to.branch.get_parent()
 
334
        
 
335
        br_to = Branch.open_containing('.')
 
336
        stored_loc = br_to.get_parent()
408
337
        if location is None:
409
338
            if stored_loc is None:
410
339
                raise BzrCommandError("No pull location known or specified.")
411
340
            else:
412
341
                print "Using saved location: %s" % stored_loc
413
342
                location = stored_loc
414
 
 
415
 
        br_from = Branch.open(location)
416
 
        br_to = tree_to.branch
417
 
 
418
 
        old_rh = br_to.revision_history()
419
 
        count = tree_to.pull(br_from, overwrite)
420
 
 
421
 
        if br_to.get_parent() is None or remember:
422
 
            br_to.set_parent(location)
423
 
        note('%d revision(s) pulled.' % (count,))
424
 
 
425
 
        if verbose:
426
 
            new_rh = tree_to.branch.revision_history()
427
 
            if old_rh != new_rh:
428
 
                # Something changed
429
 
                from bzrlib.log import show_changed_revisions
430
 
                show_changed_revisions(tree_to.branch, old_rh, new_rh)
431
 
 
432
 
 
433
 
class cmd_push(Command):
434
 
    """Push this branch into another branch.
435
 
    
436
 
    The remote branch will not have its working tree populated because this
437
 
    is both expensive, and may not be supported on the remote file system.
438
 
    
439
 
    Some smart servers or protocols *may* put the working tree in place.
440
 
 
441
 
    If there is no default push location set, the first push will set it.
442
 
    After that, you can omit the location to use the default.  To change the
443
 
    default, use --remember.
444
 
 
445
 
    This command only works on branches that have not diverged.  Branches are
446
 
    considered diverged if the branch being pushed to is not an older version
447
 
    of this branch.
448
 
 
449
 
    If branches have diverged, you can use 'bzr push --overwrite' to replace
450
 
    the other branch completely.
451
 
    
452
 
    If you want to ensure you have the different changes in the other branch,
453
 
    do a merge (see bzr help merge) from the other branch, and commit that
454
 
    before doing a 'push --overwrite'.
455
 
    """
456
 
    takes_options = ['remember', 'overwrite', 
457
 
                     Option('create-prefix', 
458
 
                            help='Create the path leading up to the branch '
459
 
                                 'if it does not already exist')]
460
 
    takes_args = ['location?']
461
 
 
462
 
    def run(self, location=None, remember=False, overwrite=False,
463
 
            create_prefix=False, verbose=False):
464
 
        # FIXME: Way too big!  Put this into a function called from the
465
 
        # command.
466
 
        import errno
467
 
        from shutil import rmtree
468
 
        from bzrlib.transport import get_transport
469
 
        
470
 
        tree_from = WorkingTree.open_containing(u'.')[0]
471
 
        br_from = tree_from.branch
472
 
        stored_loc = tree_from.branch.get_push_location()
473
 
        if location is None:
474
 
            if stored_loc is None:
475
 
                raise BzrCommandError("No push location known or specified.")
476
 
            else:
477
 
                print "Using saved location: %s" % stored_loc
478
 
                location = stored_loc
479
 
        try:
480
 
            br_to = Branch.open(location)
481
 
        except NotBranchError:
482
 
            # create a branch.
483
 
            transport = get_transport(location).clone('..')
484
 
            if not create_prefix:
485
 
                try:
486
 
                    transport.mkdir(transport.relpath(location))
487
 
                except NoSuchFile:
488
 
                    raise BzrCommandError("Parent directory of %s "
489
 
                                          "does not exist." % location)
490
 
            else:
491
 
                current = transport.base
492
 
                needed = [(transport, transport.relpath(location))]
493
 
                while needed:
494
 
                    try:
495
 
                        transport, relpath = needed[-1]
496
 
                        transport.mkdir(relpath)
497
 
                        needed.pop()
498
 
                    except NoSuchFile:
499
 
                        new_transport = transport.clone('..')
500
 
                        needed.append((new_transport,
501
 
                                       new_transport.relpath(transport.base)))
502
 
                        if new_transport.base == transport.base:
503
 
                            raise BzrCommandError("Could not creeate "
504
 
                                                  "path prefix.")
505
 
            br_to = Branch.initialize(location)
506
 
        old_rh = br_to.revision_history()
507
 
        try:
 
343
        cache_root = tempfile.mkdtemp()
 
344
        try:
 
345
            from bzrlib.errors import DivergedBranches
 
346
            br_from = Branch.open(location)
 
347
            br_from.lock_read()
 
348
            br_from.setup_caching(cache_root)
 
349
            location = br_from.base
 
350
            old_revno = br_to.revno()
508
351
            try:
509
 
                tree_to = br_to.working_tree()
510
 
            except NoWorkingTree:
511
 
                # TODO: This should be updated for branches which don't have a
512
 
                # working tree, as opposed to ones where we just couldn't 
513
 
                # update the tree.
514
 
                warning('Unable to update the working tree of: %s' % (br_to.base,))
515
 
                count = br_to.pull(br_from, overwrite)
516
 
            else:
517
 
                count = tree_to.pull(br_from, overwrite)
518
 
        except DivergedBranches:
519
 
            raise BzrCommandError("These branches have diverged."
520
 
                                  "  Try a merge then push with overwrite.")
521
 
        if br_from.get_push_location() is None or remember:
522
 
            br_from.set_push_location(location)
523
 
        note('%d revision(s) pushed.' % (count,))
 
352
                br_to.update_revisions(br_from)
 
353
            except DivergedBranches:
 
354
                raise BzrCommandError("These branches have diverged."
 
355
                    "  Try merge.")
 
356
                
 
357
            merge(('.', -1), ('.', old_revno), check_clean=False)
 
358
            if stored_loc is None or remember:
 
359
                br_to.set_parent(location)
 
360
        finally:
 
361
            br_from.unlock()
 
362
            rmtree(cache_root)
524
363
 
525
 
        if verbose:
526
 
            new_rh = br_to.revision_history()
527
 
            if old_rh != new_rh:
528
 
                # Something changed
529
 
                from bzrlib.log import show_changed_revisions
530
 
                show_changed_revisions(br_to, old_rh, new_rh)
531
364
 
532
365
 
533
366
class cmd_branch(Command):
549
382
 
550
383
    def run(self, from_location, to_location=None, revision=None, basis=None):
551
384
        from bzrlib.clone import copy_branch
 
385
        import tempfile
552
386
        import errno
553
387
        from shutil import rmtree
554
 
        if revision is None:
555
 
            revision = [None]
556
 
        elif len(revision) > 1:
557
 
            raise BzrCommandError(
558
 
                'bzr branch --revision takes exactly 1 revision value')
559
 
        try:
560
 
            br_from = Branch.open(from_location)
561
 
        except OSError, e:
562
 
            if e.errno == errno.ENOENT:
563
 
                raise BzrCommandError('Source location "%s" does not'
564
 
                                      ' exist.' % to_location)
565
 
            else:
566
 
                raise
567
 
        br_from.lock_read()
568
 
        try:
 
388
        cache_root = tempfile.mkdtemp()
 
389
        try:
 
390
            if revision is None:
 
391
                revision = [None]
 
392
            elif len(revision) > 1:
 
393
                raise BzrCommandError(
 
394
                    'bzr branch --revision takes exactly 1 revision value')
 
395
            try:
 
396
                br_from = Branch.open(from_location)
 
397
            except OSError, e:
 
398
                if e.errno == errno.ENOENT:
 
399
                    raise BzrCommandError('Source location "%s" does not'
 
400
                                          ' exist.' % to_location)
 
401
                else:
 
402
                    raise
 
403
            br_from.setup_caching(cache_root)
569
404
            if basis is not None:
570
 
                basis_branch = WorkingTree.open_containing(basis)[0].branch
 
405
                basis_branch = Branch.open_containing(basis)
571
406
            else:
572
407
                basis_branch = None
573
408
            if len(revision) == 1 and revision[0] is not None:
576
411
                revision_id = None
577
412
            if to_location is None:
578
413
                to_location = os.path.basename(from_location.rstrip("/\\"))
579
 
                name = None
580
 
            else:
581
 
                name = os.path.basename(to_location) + '\n'
582
414
            try:
583
415
                os.mkdir(to_location)
584
416
            except OSError, e:
594
426
                copy_branch(br_from, to_location, revision_id, basis_branch)
595
427
            except bzrlib.errors.NoSuchRevision:
596
428
                rmtree(to_location)
597
 
                msg = "The branch %s has no revision %s." % (from_location, revision[0])
 
429
                msg = "The branch %s has no revision %d." % (from_location, revision[0])
598
430
                raise BzrCommandError(msg)
599
431
            except bzrlib.errors.UnlistableBranch:
600
 
                rmtree(to_location)
601
432
                msg = "The branch %s cannot be used as a --basis"
602
 
                raise BzrCommandError(msg)
603
 
            branch = Branch.open(to_location)
604
 
            if name:
605
 
                name = StringIO(name)
606
 
                branch.put_controlfile('branch-name', name)
607
 
            note('Branched %d revision(s).' % branch.revno())
608
433
        finally:
609
 
            br_from.unlock()
 
434
            rmtree(cache_root)
610
435
 
611
436
 
612
437
class cmd_renames(Command):
613
438
    """Show list of renamed files.
 
439
 
 
440
    TODO: Option to show renames between two historical versions.
 
441
 
 
442
    TODO: Only show renames under dir, rather than in the whole branch.
614
443
    """
615
 
    # TODO: Option to show renames between two historical versions.
616
 
 
617
 
    # TODO: Only show renames under dir, rather than in the whole branch.
618
444
    takes_args = ['dir?']
619
445
 
620
 
    @display_command
621
 
    def run(self, dir=u'.'):
622
 
        tree = WorkingTree.open_containing(dir)[0]
623
 
        old_inv = tree.branch.basis_tree().inventory
624
 
        new_inv = tree.read_working_inventory()
 
446
    def run(self, dir='.'):
 
447
        b = Branch.open_containing(dir)
 
448
        old_inv = b.basis_tree().inventory
 
449
        new_inv = b.read_working_inventory()
625
450
 
626
451
        renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
627
452
        renames.sort()
633
458
    """Show statistical information about a branch."""
634
459
    takes_args = ['branch?']
635
460
    
636
 
    @display_command
637
461
    def run(self, branch=None):
638
462
        import info
639
 
        b = WorkingTree.open_containing(branch)[0].branch
 
463
 
 
464
        b = Branch.open_containing(branch)
640
465
        info.show_info(b)
641
466
 
642
467
 
651
476
    aliases = ['rm']
652
477
    
653
478
    def run(self, file_list, verbose=False):
654
 
        tree, file_list = tree_files(file_list)
655
 
        tree.remove(file_list, verbose=verbose)
 
479
        b = Branch.open_containing(file_list[0])
 
480
        b.remove([b.relpath(f) for f in file_list], verbose=verbose)
656
481
 
657
482
 
658
483
class cmd_file_id(Command):
664
489
    """
665
490
    hidden = True
666
491
    takes_args = ['filename']
667
 
    @display_command
668
492
    def run(self, filename):
669
 
        tree, relpath = WorkingTree.open_containing(filename)
670
 
        i = tree.inventory.path2id(relpath)
 
493
        b = Branch.open_containing(filename)
 
494
        i = b.inventory.path2id(b.relpath(filename))
671
495
        if i == None:
672
496
            raise BzrError("%r is not a versioned file" % filename)
673
497
        else:
681
505
    starting at the branch root."""
682
506
    hidden = True
683
507
    takes_args = ['filename']
684
 
    @display_command
685
508
    def run(self, filename):
686
 
        tree, relpath = WorkingTree.open_containing(filename)
687
 
        inv = tree.inventory
688
 
        fid = inv.path2id(relpath)
 
509
        b = Branch.open_containing(filename)
 
510
        inv = b.inventory
 
511
        fid = inv.path2id(b.relpath(filename))
689
512
        if fid == None:
690
513
            raise BzrError("%r is not a versioned file" % filename)
691
514
        for fip in inv.get_idpath(fid):
695
518
class cmd_revision_history(Command):
696
519
    """Display list of revision ids on this branch."""
697
520
    hidden = True
698
 
    @display_command
699
521
    def run(self):
700
 
        branch = WorkingTree.open_containing(u'.')[0].branch
701
 
        for patchid in branch.revision_history():
 
522
        for patchid in Branch.open_containing('.').revision_history():
702
523
            print patchid
703
524
 
704
525
 
705
526
class cmd_ancestry(Command):
706
527
    """List all revisions merged into this branch."""
707
528
    hidden = True
708
 
    @display_command
709
529
    def run(self):
710
 
        tree = WorkingTree.open_containing(u'.')[0]
711
 
        b = tree.branch
712
 
        # FIXME. should be tree.last_revision
 
530
        b = find_branch('.')
713
531
        for revision_id in b.get_ancestry(b.last_revision()):
714
532
            print revision_id
715
533
 
716
534
 
 
535
class cmd_directories(Command):
 
536
    """Display list of versioned directories in this branch."""
 
537
    def run(self):
 
538
        for name, ie in Branch.open_containing('.').read_working_inventory().directories():
 
539
            if name == '':
 
540
                print '.'
 
541
            else:
 
542
                print name
 
543
 
 
544
 
717
545
class cmd_init(Command):
718
546
    """Make a directory into a versioned branch.
719
547
 
723
551
    Recipe for importing a tree of files:
724
552
        cd ~/project
725
553
        bzr init
726
 
        bzr add .
 
554
        bzr add -v .
727
555
        bzr status
728
556
        bzr commit -m 'imported project'
729
557
    """
730
 
    takes_args = ['location?']
731
 
    def run(self, location=None):
732
 
        from bzrlib.branch import Branch
733
 
        if location is None:
734
 
            location = u'.'
735
 
        else:
736
 
            # The path has to exist to initialize a
737
 
            # branch inside of it.
738
 
            # Just using os.mkdir, since I don't
739
 
            # believe that we want to create a bunch of
740
 
            # locations if the user supplies an extended path
741
 
            if not os.path.exists(location):
742
 
                os.mkdir(location)
743
 
        Branch.initialize(location)
 
558
    def run(self):
 
559
        Branch.initialize('.')
744
560
 
745
561
 
746
562
class cmd_diff(Command):
749
565
    If files are listed, only the changes in those files are listed.
750
566
    Otherwise, all changes for the tree are listed.
751
567
 
 
568
    TODO: Allow diff across branches.
 
569
 
 
570
    TODO: Option to use external diff command; could be GNU diff, wdiff,
 
571
          or a graphical diff.
 
572
 
 
573
    TODO: Python difflib is not exactly the same as unidiff; should
 
574
          either fix it up or prefer to use an external diff.
 
575
 
 
576
    TODO: If a directory is given, diff everything under that.
 
577
 
 
578
    TODO: Selected-file diff is inefficient and doesn't show you
 
579
          deleted files.
 
580
 
 
581
    TODO: This probably handles non-Unix newlines poorly.
 
582
 
752
583
    examples:
753
584
        bzr diff
754
585
        bzr diff -r1
755
586
        bzr diff -r1..2
756
587
    """
757
 
    # TODO: Allow diff across branches.
758
 
    # TODO: Option to use external diff command; could be GNU diff, wdiff,
759
 
    #       or a graphical diff.
760
 
 
761
 
    # TODO: Python difflib is not exactly the same as unidiff; should
762
 
    #       either fix it up or prefer to use an external diff.
763
 
 
764
 
    # TODO: If a directory is given, diff everything under that.
765
 
 
766
 
    # TODO: Selected-file diff is inefficient and doesn't show you
767
 
    #       deleted files.
768
 
 
769
 
    # TODO: This probably handles non-Unix newlines poorly.
770
588
    
771
589
    takes_args = ['file*']
772
590
    takes_options = ['revision', 'diff-options']
773
591
    aliases = ['di', 'dif']
774
592
 
775
 
    @display_command
776
593
    def run(self, revision=None, file_list=None, diff_options=None):
777
594
        from bzrlib.diff import show_diff
778
 
        try:
779
 
            tree, file_list = internal_tree_files(file_list)
780
 
            b = None
781
 
            b2 = None
782
 
        except FileInWrongBranch:
783
 
            if len(file_list) != 2:
784
 
                raise BzrCommandError("Files are in different branches")
785
 
 
786
 
            b, file1 = Branch.open_containing(file_list[0])
787
 
            b2, file2 = Branch.open_containing(file_list[1])
788
 
            if file1 != "" or file2 != "":
789
 
                # FIXME diff those two files. rbc 20051123
790
 
                raise BzrCommandError("Files are in different branches")
791
 
            file_list = None
 
595
 
 
596
        if file_list:
 
597
            b = Branch.open_containing(file_list[0])
 
598
            file_list = [b.relpath(f) for f in file_list]
 
599
            if file_list == ['']:
 
600
                # just pointing to top-of-tree
 
601
                file_list = None
 
602
        else:
 
603
            b = Branch.open_containing('.')
 
604
 
792
605
        if revision is not None:
793
 
            if b2 is not None:
794
 
                raise BzrCommandError("Can't specify -r with two branches")
795
606
            if len(revision) == 1:
796
 
                return show_diff(tree.branch, revision[0], specific_files=file_list,
797
 
                                 external_diff_options=diff_options)
 
607
                show_diff(b, revision[0], specific_files=file_list,
 
608
                          external_diff_options=diff_options)
798
609
            elif len(revision) == 2:
799
 
                return show_diff(tree.branch, revision[0], specific_files=file_list,
800
 
                                 external_diff_options=diff_options,
801
 
                                 revision2=revision[1])
 
610
                show_diff(b, revision[0], specific_files=file_list,
 
611
                          external_diff_options=diff_options,
 
612
                          revision2=revision[1])
802
613
            else:
803
614
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
804
615
        else:
805
 
            if b is not None:
806
 
                return show_diff(b, None, specific_files=file_list,
807
 
                                 external_diff_options=diff_options, b2=b2)
808
 
            else:
809
 
                return show_diff(tree.branch, None, specific_files=file_list,
810
 
                                 external_diff_options=diff_options)
 
616
            show_diff(b, None, specific_files=file_list,
 
617
                      external_diff_options=diff_options)
 
618
 
 
619
        
811
620
 
812
621
 
813
622
class cmd_deleted(Command):
814
623
    """List files deleted in the working tree.
 
624
 
 
625
    TODO: Show files deleted since a previous revision, or between two revisions.
815
626
    """
816
 
    # TODO: Show files deleted since a previous revision, or
817
 
    # between two revisions.
818
 
    # TODO: Much more efficient way to do this: read in new
819
 
    # directories with readdir, rather than stating each one.  Same
820
 
    # level of effort but possibly much less IO.  (Or possibly not,
821
 
    # if the directories are very large...)
822
 
    @display_command
823
627
    def run(self, show_ids=False):
824
 
        tree = WorkingTree.open_containing(u'.')[0]
825
 
        old = tree.branch.basis_tree()
 
628
        b = Branch.open_containing('.')
 
629
        old = b.basis_tree()
 
630
        new = b.working_tree()
 
631
 
 
632
        ## TODO: Much more efficient way to do this: read in new
 
633
        ## directories with readdir, rather than stating each one.  Same
 
634
        ## level of effort but possibly much less IO.  (Or possibly not,
 
635
        ## if the directories are very large...)
 
636
 
826
637
        for path, ie in old.inventory.iter_entries():
827
 
            if not tree.has_id(ie.file_id):
 
638
            if not new.has_id(ie.file_id):
828
639
                if show_ids:
829
640
                    print '%-50s %s' % (path, ie.file_id)
830
641
                else:
834
645
class cmd_modified(Command):
835
646
    """List files modified in working tree."""
836
647
    hidden = True
837
 
    @display_command
838
648
    def run(self):
839
649
        from bzrlib.delta import compare_trees
840
650
 
841
 
        tree = WorkingTree.open_containing(u'.')[0]
842
 
        td = compare_trees(tree.branch.basis_tree(), tree)
 
651
        b = Branch.open_containing('.')
 
652
        td = compare_trees(b.basis_tree(), b.working_tree())
843
653
 
844
654
        for path, id, kind, text_modified, meta_modified in td.modified:
845
655
            print path
849
659
class cmd_added(Command):
850
660
    """List files added in working tree."""
851
661
    hidden = True
852
 
    @display_command
853
662
    def run(self):
854
 
        wt = WorkingTree.open_containing(u'.')[0]
855
 
        basis_inv = wt.branch.basis_tree().inventory
 
663
        b = Branch.open_containing('.')
 
664
        wt = b.working_tree()
 
665
        basis_inv = b.basis_tree().inventory
856
666
        inv = wt.inventory
857
667
        for file_id in inv:
858
668
            if file_id in basis_inv:
870
680
    The root is the nearest enclosing directory with a .bzr control
871
681
    directory."""
872
682
    takes_args = ['filename?']
873
 
    @display_command
874
683
    def run(self, filename=None):
875
684
        """Print the branch root."""
876
 
        tree = WorkingTree.open_containing(filename)[0]
877
 
        print tree.basedir
 
685
        b = Branch.open_containing(filename)
 
686
        print b.base
878
687
 
879
688
 
880
689
class cmd_log(Command):
881
690
    """Show log of this branch.
882
691
 
883
 
    To request a range of logs, you can use the command -r begin..end
884
 
    -r revision requests a specific revision, -r ..end or -r begin.. are
 
692
    To request a range of logs, you can use the command -r begin:end
 
693
    -r revision requests a specific revision, -r :end or -r begin: are
885
694
    also valid.
 
695
 
 
696
    --message allows you to give a regular expression, which will be evaluated
 
697
    so that only matching entries will be displayed.
886
698
    """
887
699
 
888
700
    # TODO: Make --revision support uuid: and hash: [future tag:] notation.
889
701
 
890
702
    takes_args = ['filename?']
891
 
    takes_options = [Option('forward', 
892
 
                            help='show from oldest to newest'),
893
 
                     'timezone', 'verbose', 
894
 
                     'show-ids', 'revision',
895
 
                     'line', 'long', 
896
 
                     Option('message',
897
 
                            help='show revisions whose message matches this regexp',
898
 
                            type=str),
899
 
                     'short',
900
 
                     ]
901
 
    @display_command
 
703
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
 
704
                     'long', 'message', 'short',]
 
705
    
902
706
    def run(self, filename=None, timezone='original',
903
707
            verbose=False,
904
708
            show_ids=False,
906
710
            revision=None,
907
711
            message=None,
908
712
            long=False,
909
 
            short=False,
910
 
            line=False):
 
713
            short=False):
911
714
        from bzrlib.log import log_formatter, show_log
912
715
        import codecs
913
 
        assert message is None or isinstance(message, basestring), \
914
 
            "invalid message argument %r" % message
 
716
 
915
717
        direction = (forward and 'forward') or 'reverse'
916
718
        
917
719
        if filename:
918
 
            # might be a tree:
919
 
            tree = None
920
 
            try:
921
 
                tree, fp = WorkingTree.open_containing(filename)
922
 
                b = tree.branch
923
 
                if fp != '':
924
 
                    inv = tree.read_working_inventory()
925
 
            except NotBranchError:
926
 
                pass
927
 
            if tree is None:
928
 
                b, fp = Branch.open_containing(filename)
929
 
                if fp != '':
930
 
                    inv = b.get_inventory(b.last_revision())
931
 
            if fp != '':
932
 
                file_id = inv.path2id(fp)
 
720
            b = Branch.open_containing(filename)
 
721
            fp = b.relpath(filename)
 
722
            if fp:
 
723
                file_id = b.read_working_inventory().path2id(fp)
933
724
            else:
934
725
                file_id = None  # points to branch root
935
726
        else:
936
 
            tree, relpath = WorkingTree.open_containing(u'.')
937
 
            b = tree.branch
 
727
            b = Branch.open_containing('.')
938
728
            file_id = None
939
729
 
940
730
        if revision is None:
948
738
        else:
949
739
            raise BzrCommandError('bzr log --revision takes one or two values.')
950
740
 
951
 
        # By this point, the revision numbers are converted to the +ve
952
 
        # form if they were supplied in the -ve form, so we can do
953
 
        # this comparison in relative safety
954
 
        if rev1 > rev2:
955
 
            (rev2, rev1) = (rev1, rev2)
 
741
        if rev1 == 0:
 
742
            rev1 = None
 
743
        if rev2 == 0:
 
744
            rev2 = None
956
745
 
957
 
        mutter('encoding log as %r', bzrlib.user_encoding)
 
746
        mutter('encoding log as %r' % bzrlib.user_encoding)
958
747
 
959
748
        # use 'replace' so that we don't abort if trying to write out
960
749
        # in e.g. the default C locale.
961
750
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
962
751
 
963
 
        log_format = get_log_format(long=long, short=short, line=line)
 
752
        if not short:
 
753
            log_format = 'long'
 
754
        else:
 
755
            log_format = 'short'
964
756
        lf = log_formatter(log_format,
965
757
                           show_ids=show_ids,
966
758
                           to_file=outf,
975
767
                 end_revision=rev2,
976
768
                 search=message)
977
769
 
978
 
def get_log_format(long=False, short=False, line=False, default='long'):
979
 
    log_format = default
980
 
    if long:
981
 
        log_format = 'long'
982
 
    if short:
983
 
        log_format = 'short'
984
 
    if line:
985
 
        log_format = 'line'
986
 
    return log_format
987
770
 
988
771
 
989
772
class cmd_touching_revisions(Command):
992
775
    A more user-friendly interface is "bzr log FILE"."""
993
776
    hidden = True
994
777
    takes_args = ["filename"]
995
 
    @display_command
996
778
    def run(self, filename):
997
 
        tree, relpath = WorkingTree.open_containing(filename)
998
 
        b = tree.branch
999
 
        inv = tree.read_working_inventory()
1000
 
        file_id = inv.path2id(relpath)
 
779
        b = Branch.open_containing(filename)
 
780
        inv = b.read_working_inventory()
 
781
        file_id = inv.path2id(b.relpath(filename))
1001
782
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1002
783
            print "%6d %s" % (revno, what)
1003
784
 
1004
785
 
1005
786
class cmd_ls(Command):
1006
787
    """List files in a tree.
 
788
 
 
789
    TODO: Take a revision or remote path and list that tree instead.
1007
790
    """
1008
 
    # TODO: Take a revision or remote path and list that tree instead.
1009
791
    hidden = True
1010
 
    takes_options = ['verbose', 'revision',
1011
 
                     Option('non-recursive',
1012
 
                            help='don\'t recurse into sub-directories'),
1013
 
                     Option('from-root',
1014
 
                            help='Print all paths from the root of the branch.'),
1015
 
                     Option('unknown', help='Print unknown files'),
1016
 
                     Option('versioned', help='Print versioned files'),
1017
 
                     Option('ignored', help='Print ignored files'),
1018
 
 
1019
 
                     Option('null', help='Null separate the files'),
1020
 
                    ]
1021
 
    @display_command
1022
 
    def run(self, revision=None, verbose=False, 
1023
 
            non_recursive=False, from_root=False,
1024
 
            unknown=False, versioned=False, ignored=False,
1025
 
            null=False):
1026
 
 
1027
 
        if verbose and null:
1028
 
            raise BzrCommandError('Cannot set both --verbose and --null')
1029
 
        all = not (unknown or versioned or ignored)
1030
 
 
1031
 
        selection = {'I':ignored, '?':unknown, 'V':versioned}
1032
 
 
1033
 
        tree, relpath = WorkingTree.open_containing(u'.')
1034
 
        if from_root:
1035
 
            relpath = u''
1036
 
        elif relpath:
1037
 
            relpath += '/'
1038
 
        if revision is not None:
1039
 
            tree = tree.branch.revision_tree(
1040
 
                revision[0].in_history(tree.branch).rev_id)
 
792
    def run(self, revision=None, verbose=False):
 
793
        b = Branch.open_containing('.')
 
794
        if revision == None:
 
795
            tree = b.working_tree()
 
796
        else:
 
797
            tree = b.revision_tree(revision.in_history(b).rev_id)
1041
798
        for fp, fc, kind, fid, entry in tree.list_files():
1042
 
            if fp.startswith(relpath):
1043
 
                fp = fp[len(relpath):]
1044
 
                if non_recursive and '/' in fp:
1045
 
                    continue
1046
 
                if not all and not selection[fc]:
1047
 
                    continue
1048
 
                if verbose:
1049
 
                    kindch = entry.kind_character()
1050
 
                    print '%-8s %s%s' % (fc, fp, kindch)
1051
 
                elif null:
1052
 
                    sys.stdout.write(fp)
1053
 
                    sys.stdout.write('\0')
1054
 
                    sys.stdout.flush()
1055
 
                else:
1056
 
                    print fp
 
799
            if verbose:
 
800
                kindch = entry.kind_character()
 
801
                print '%-8s %s%s' % (fc, fp, kindch)
 
802
            else:
 
803
                print fp
 
804
 
1057
805
 
1058
806
 
1059
807
class cmd_unknowns(Command):
1060
808
    """List unknown files."""
1061
 
    @display_command
1062
809
    def run(self):
1063
810
        from bzrlib.osutils import quotefn
1064
 
        for f in WorkingTree.open_containing(u'.')[0].unknowns():
 
811
        for f in Branch.open_containing('.').unknowns():
1065
812
            print quotefn(f)
1066
813
 
1067
814
 
 
815
 
1068
816
class cmd_ignore(Command):
1069
817
    """Ignore a command or pattern.
1070
818
 
1071
819
    To remove patterns from the ignore list, edit the .bzrignore file.
1072
820
 
1073
821
    If the pattern contains a slash, it is compared to the whole path
1074
 
    from the branch root.  Otherwise, it is compared to only the last
1075
 
    component of the path.  To match a file only in the root directory,
1076
 
    prepend './'.
 
822
    from the branch root.  Otherwise, it is comapred to only the last
 
823
    component of the path.
1077
824
 
1078
825
    Ignore patterns are case-insensitive on case-insensitive systems.
1079
826
 
1083
830
        bzr ignore ./Makefile
1084
831
        bzr ignore '*.class'
1085
832
    """
1086
 
    # TODO: Complain if the filename is absolute
1087
833
    takes_args = ['name_pattern']
1088
834
    
1089
835
    def run(self, name_pattern):
1090
836
        from bzrlib.atomicfile import AtomicFile
1091
837
        import os.path
1092
838
 
1093
 
        tree, relpath = WorkingTree.open_containing(u'.')
1094
 
        ifn = tree.abspath('.bzrignore')
 
839
        b = Branch.open_containing('.')
 
840
        ifn = b.abspath('.bzrignore')
1095
841
 
1096
842
        if os.path.exists(ifn):
1097
843
            f = open(ifn, 'rt')
1116
862
        finally:
1117
863
            f.close()
1118
864
 
1119
 
        inv = tree.inventory
 
865
        inv = b.working_tree().inventory
1120
866
        if inv.path2id('.bzrignore'):
1121
867
            mutter('.bzrignore is already versioned')
1122
868
        else:
1123
869
            mutter('need to make new .bzrignore file versioned')
1124
 
            tree.add(['.bzrignore'])
 
870
            b.add(['.bzrignore'])
 
871
 
1125
872
 
1126
873
 
1127
874
class cmd_ignored(Command):
1128
875
    """List ignored files and the patterns that matched them.
1129
876
 
1130
877
    See also: bzr ignore"""
1131
 
    @display_command
1132
878
    def run(self):
1133
 
        tree = WorkingTree.open_containing(u'.')[0]
 
879
        tree = Branch.open_containing('.').working_tree()
1134
880
        for path, file_class, kind, file_id, entry in tree.list_files():
1135
881
            if file_class != 'I':
1136
882
                continue
1148
894
    hidden = True
1149
895
    takes_args = ['revno']
1150
896
    
1151
 
    @display_command
1152
897
    def run(self, revno):
1153
898
        try:
1154
899
            revno = int(revno)
1155
900
        except ValueError:
1156
901
            raise BzrCommandError("not a valid revision-number: %r" % revno)
1157
902
 
1158
 
        print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
 
903
        print Branch.open_containing('.').get_rev_id(revno)
1159
904
 
1160
905
 
1161
906
class cmd_export(Command):
1168
913
    is found exports to a directory (equivalent to --format=dir).
1169
914
 
1170
915
    Root may be the top directory for tar, tgz and tbz2 formats. If none
1171
 
    is given, the top directory will be the root name of the file.
1172
 
 
1173
 
    Note: export of tree with non-ascii filenames to zip is not supported.
1174
 
 
1175
 
    Supported formats       Autodetected by extension
1176
 
    -----------------       -------------------------
1177
 
         dir                            -
1178
 
         tar                          .tar
1179
 
         tbz2                    .tar.bz2, .tbz2
1180
 
         tgz                      .tar.gz, .tgz
1181
 
         zip                          .zip
1182
 
    """
 
916
    is given, the top directory will be the root name of the file."""
 
917
    # TODO: list known exporters
1183
918
    takes_args = ['dest']
1184
919
    takes_options = ['revision', 'format', 'root']
1185
920
    def run(self, dest, revision=None, format=None, root=None):
1186
921
        import os.path
1187
 
        from bzrlib.export import export
1188
 
        tree = WorkingTree.open_containing(u'.')[0]
1189
 
        b = tree.branch
 
922
        b = Branch.open_containing('.')
1190
923
        if revision is None:
1191
 
            # should be tree.last_revision  FIXME
1192
924
            rev_id = b.last_revision()
1193
925
        else:
1194
926
            if len(revision) != 1:
1195
927
                raise BzrError('bzr export --revision takes exactly 1 argument')
1196
928
            rev_id = revision[0].in_history(b).rev_id
1197
929
        t = b.revision_tree(rev_id)
1198
 
        try:
1199
 
            export(t, dest, format, root)
1200
 
        except errors.NoSuchExportFormat, e:
1201
 
            raise BzrCommandError('Unsupported export format: %s' % e.format)
 
930
        arg_root, ext = os.path.splitext(os.path.basename(dest))
 
931
        if ext in ('.gz', '.bz2'):
 
932
            new_root, new_ext = os.path.splitext(arg_root)
 
933
            if new_ext == '.tar':
 
934
                arg_root = new_root
 
935
                ext = new_ext + ext
 
936
        if root is None:
 
937
            root = arg_root
 
938
        if not format:
 
939
            if ext in (".tar",):
 
940
                format = "tar"
 
941
            elif ext in (".tar.gz", ".tgz"):
 
942
                format = "tgz"
 
943
            elif ext in (".tar.bz2", ".tbz2"):
 
944
                format = "tbz2"
 
945
            else:
 
946
                format = "dir"
 
947
        t.export(dest, format, root)
1202
948
 
1203
949
 
1204
950
class cmd_cat(Command):
1207
953
    takes_options = ['revision']
1208
954
    takes_args = ['filename']
1209
955
 
1210
 
    @display_command
1211
956
    def run(self, filename, revision=None):
1212
 
        if revision is not None and len(revision) != 1:
 
957
        if revision is None:
 
958
            raise BzrCommandError("bzr cat requires a revision number")
 
959
        elif len(revision) != 1:
1213
960
            raise BzrCommandError("bzr cat --revision takes exactly one number")
1214
 
        tree = None
1215
 
        try:
1216
 
            tree, relpath = WorkingTree.open_containing(filename)
1217
 
            b = tree.branch
1218
 
        except NotBranchError:
1219
 
            pass
1220
 
 
1221
 
        if tree is None:
1222
 
            b, relpath = Branch.open_containing(filename)
1223
 
        if revision is None:
1224
 
            revision_id = b.last_revision()
1225
 
        else:
1226
 
            revision_id = revision[0].in_history(b).rev_id
1227
 
        b.print_file(relpath, revision_id)
 
961
        b = Branch.open_containing('.')
 
962
        b.print_file(b.relpath(filename), revision[0].in_history(b).revno)
1228
963
 
1229
964
 
1230
965
class cmd_local_time_offset(Command):
1231
966
    """Show the offset in seconds from GMT to local time."""
1232
967
    hidden = True    
1233
 
    @display_command
1234
968
    def run(self):
1235
969
        print bzrlib.osutils.local_time_offset()
1236
970
 
1248
982
    A selected-file commit may fail in some cases where the committed
1249
983
    tree would be invalid, such as trying to commit a file in a
1250
984
    newly-added directory that is not itself committed.
 
985
 
 
986
    TODO: Run hooks on tree to-be-committed, and after commit.
 
987
 
 
988
    TODO: Strict commit that fails if there are unknown or deleted files.
1251
989
    """
1252
 
    # TODO: Run hooks on tree to-be-committed, and after commit.
1253
 
 
1254
 
    # TODO: Strict commit that fails if there are deleted files.
1255
 
    #       (what does "deleted files" mean ??)
 
990
    takes_args = ['selected*']
 
991
    takes_options = ['message', 'file', 'verbose', 'unchanged']
 
992
    aliases = ['ci', 'checkin']
1256
993
 
1257
994
    # TODO: Give better message for -s, --summary, used by tla people
1258
995
 
1259
996
    # XXX: verbose currently does nothing
1260
 
 
1261
 
    takes_args = ['selected*']
1262
 
    takes_options = ['message', 'verbose', 
1263
 
                     Option('unchanged',
1264
 
                            help='commit even if nothing has changed'),
1265
 
                     Option('file', type=str, 
1266
 
                            argname='msgfile',
1267
 
                            help='file containing commit message'),
1268
 
                     Option('strict',
1269
 
                            help="refuse to commit if there are unknown "
1270
 
                            "files in the working tree."),
1271
 
                     ]
1272
 
    aliases = ['ci', 'checkin']
1273
 
 
 
997
    
1274
998
    def run(self, message=None, file=None, verbose=True, selected_list=None,
1275
 
            unchanged=False, strict=False):
1276
 
        from bzrlib.errors import (PointlessCommit, ConflictsInTree,
1277
 
                StrictCommitFailed)
1278
 
        from bzrlib.msgeditor import edit_commit_message, \
1279
 
                make_commit_message_template
 
999
            unchanged=False):
 
1000
        from bzrlib.errors import PointlessCommit, ConflictsInTree
 
1001
        from bzrlib.msgeditor import edit_commit_message
1280
1002
        from bzrlib.status import show_status
1281
 
        from tempfile import TemporaryFile
1282
 
        import codecs
1283
 
 
1284
 
        # TODO: Need a blackbox test for invoking the external editor; may be
1285
 
        # slightly problematic to run this cross-platform.
1286
 
 
1287
 
        # TODO: do more checks that the commit will succeed before 
1288
 
        # spending the user's valuable time typing a commit message.
1289
 
        #
1290
 
        # TODO: if the commit *does* happen to fail, then save the commit 
1291
 
        # message to a temporary file where it can be recovered
1292
 
        tree, selected_list = tree_files(selected_list)
 
1003
        from cStringIO import StringIO
 
1004
 
 
1005
        b = Branch.open_containing('.')
 
1006
        if selected_list:
 
1007
            selected_list = [b.relpath(s) for s in selected_list]
 
1008
 
 
1009
            
1293
1010
        if message is None and not file:
1294
 
            template = make_commit_message_template(tree, selected_list)
1295
 
            message = edit_commit_message(template)
 
1011
            catcher = StringIO()
 
1012
            show_status(b, specific_files=selected_list,
 
1013
                        to_file=catcher)
 
1014
            message = edit_commit_message(catcher.getvalue())
 
1015
 
1296
1016
            if message is None:
1297
1017
                raise BzrCommandError("please specify a commit message"
1298
1018
                                      " with either --message or --file")
1307
1027
                raise BzrCommandError("empty commit message specified")
1308
1028
            
1309
1029
        try:
1310
 
            tree.commit(message, specific_files=selected_list,
1311
 
                        allow_pointless=unchanged, strict=strict)
 
1030
            b.commit(message,
 
1031
                     specific_files=selected_list,
 
1032
                     allow_pointless=unchanged)
1312
1033
        except PointlessCommit:
1313
1034
            # FIXME: This should really happen before the file is read in;
1314
1035
            # perhaps prepare the commit; get the message; then actually commit
1317
1038
        except ConflictsInTree:
1318
1039
            raise BzrCommandError("Conflicts detected in working tree.  "
1319
1040
                'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
1320
 
        except StrictCommitFailed:
1321
 
            raise BzrCommandError("Commit refused because there are unknown "
1322
 
                                  "files in the working tree.")
1323
 
        note('Committed revision %d.' % (tree.branch.revno(),))
1324
1041
 
1325
1042
 
1326
1043
class cmd_check(Command):
1329
1046
    This command checks various invariants about the branch storage to
1330
1047
    detect data corruption or bzr bugs.
1331
1048
    """
1332
 
    takes_args = ['branch?']
1333
 
    takes_options = ['verbose']
 
1049
    takes_args = ['dir?']
1334
1050
 
1335
 
    def run(self, branch=None, verbose=False):
 
1051
    def run(self, dir='.'):
1336
1052
        from bzrlib.check import check
1337
 
        if branch is None:
1338
 
            tree = WorkingTree.open_containing()[0]
1339
 
            branch = tree.branch
1340
 
        else:
1341
 
            branch = Branch.open(branch)
1342
 
        check(branch, verbose)
 
1053
 
 
1054
        check(Branch.open_containing(dir))
1343
1055
 
1344
1056
 
1345
1057
class cmd_scan_cache(Command):
1347
1059
    def run(self):
1348
1060
        from bzrlib.hashcache import HashCache
1349
1061
 
1350
 
        c = HashCache(u'.')
 
1062
        c = HashCache('.')
1351
1063
        c.read()
1352
1064
        c.scan()
1353
1065
            
1366
1078
    """Upgrade branch storage to current format.
1367
1079
 
1368
1080
    The check command or bzr developers may sometimes advise you to run
1369
 
    this command. When the default format has changed you may also be warned
1370
 
    during other operations to upgrade.
 
1081
    this command.
 
1082
 
 
1083
    This version of this command upgrades from the full-text storage
 
1084
    used by bzr 0.0.8 and earlier to the weave format (v5).
1371
1085
    """
1372
 
    takes_args = ['url?']
 
1086
    takes_args = ['dir?']
1373
1087
 
1374
 
    def run(self, url='.'):
 
1088
    def run(self, dir='.'):
1375
1089
        from bzrlib.upgrade import upgrade
1376
 
        upgrade(url)
 
1090
        upgrade(dir)
1377
1091
 
1378
1092
 
1379
1093
class cmd_whoami(Command):
1380
1094
    """Show bzr user id."""
1381
1095
    takes_options = ['email']
1382
1096
    
1383
 
    @display_command
1384
1097
    def run(self, email=False):
1385
1098
        try:
1386
 
            b = WorkingTree.open_containing(u'.')[0].branch
1387
 
            config = bzrlib.config.BranchConfig(b)
 
1099
            b = bzrlib.branch.Branch.open_containing('.')
1388
1100
        except NotBranchError:
1389
 
            config = bzrlib.config.GlobalConfig()
 
1101
            b = None
1390
1102
        
1391
1103
        if email:
1392
 
            print config.user_email()
1393
 
        else:
1394
 
            print config.username()
1395
 
 
1396
 
class cmd_nick(Command):
1397
 
    """\
1398
 
    Print or set the branch nickname.  
1399
 
    If unset, the tree root directory name is used as the nickname
1400
 
    To print the current nickname, execute with no argument.  
1401
 
    """
1402
 
    takes_args = ['nickname?']
1403
 
    def run(self, nickname=None):
1404
 
        branch = Branch.open_containing(u'.')[0]
1405
 
        if nickname is None:
1406
 
            self.printme(branch)
1407
 
        else:
1408
 
            branch.nick = nickname
1409
 
 
1410
 
    @display_command
1411
 
    def printme(self, branch):
1412
 
        print branch.nick 
 
1104
            print bzrlib.osutils.user_email(b)
 
1105
        else:
 
1106
            print bzrlib.osutils.username(b)
 
1107
 
1413
1108
 
1414
1109
class cmd_selftest(Command):
1415
1110
    """Run internal test suite.
1417
1112
    This creates temporary test directories in the working directory,
1418
1113
    but not existing data is affected.  These directories are deleted
1419
1114
    if the tests pass, or left behind to help in debugging if they
1420
 
    fail and --keep-output is specified.
 
1115
    fail.
1421
1116
    
1422
1117
    If arguments are given, they are regular expressions that say
1423
 
    which tests should run.
1424
 
    """
 
1118
    which tests should run."""
1425
1119
    # TODO: --list should give a list of all available tests
1426
1120
    hidden = True
1427
 
    takes_args = ['testspecs*']
1428
 
    takes_options = ['verbose', 
1429
 
                     Option('one', help='stop when one test fails'),
1430
 
                     Option('keep-output', 
1431
 
                            help='keep output directories when tests fail')
1432
 
                    ]
1433
 
 
1434
 
    def run(self, testspecs_list=None, verbose=False, one=False,
1435
 
            keep_output=False):
 
1121
    takes_args = ['testnames*']
 
1122
    takes_options = ['verbose', 'pattern']
 
1123
    def run(self, testnames_list=None, verbose=False, pattern=".*"):
1436
1124
        import bzrlib.ui
1437
 
        from bzrlib.tests import selftest
 
1125
        from bzrlib.selftest import selftest
1438
1126
        # we don't want progress meters from the tests to go to the
1439
1127
        # real output; and we don't want log messages cluttering up
1440
1128
        # the real logs.
1442
1130
        bzrlib.trace.info('running tests...')
1443
1131
        try:
1444
1132
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1445
 
            if testspecs_list is not None:
1446
 
                pattern = '|'.join(testspecs_list)
1447
 
            else:
1448
 
                pattern = ".*"
1449
1133
            result = selftest(verbose=verbose, 
1450
1134
                              pattern=pattern,
1451
 
                              stop_on_failure=one, 
1452
 
                              keep_output=keep_output)
 
1135
                              testnames=testnames_list)
1453
1136
            if result:
1454
1137
                bzrlib.trace.info('tests passed')
1455
1138
            else:
1459
1142
            bzrlib.ui.ui_factory = save_ui
1460
1143
 
1461
1144
 
1462
 
def _get_bzr_branch():
1463
 
    """If bzr is run from a branch, return Branch or None"""
1464
 
    import bzrlib.errors
1465
 
    from bzrlib.branch import Branch
1466
 
    from bzrlib.osutils import abspath
1467
 
    from os.path import dirname
1468
 
    
1469
 
    try:
1470
 
        branch = Branch.open(dirname(abspath(dirname(__file__))))
1471
 
        return branch
1472
 
    except bzrlib.errors.BzrError:
1473
 
        return None
1474
 
    
1475
 
 
1476
1145
def show_version():
1477
1146
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
1478
1147
    # is bzrlib itself in a branch?
1479
 
    branch = _get_bzr_branch()
1480
 
    if branch:
1481
 
        rh = branch.revision_history()
1482
 
        revno = len(rh)
1483
 
        print "  bzr checkout, revision %d" % (revno,)
1484
 
        print "  nick: %s" % (branch.nick,)
1485
 
        if rh:
1486
 
            print "  revid: %s" % (rh[-1],)
 
1148
    bzrrev = bzrlib.get_bzr_revision()
 
1149
    if bzrrev:
 
1150
        print "  (bzr checkout, revision %d {%s})" % bzrrev
1487
1151
    print bzrlib.__copyright__
1488
1152
    print "http://bazaar-ng.org/"
1489
1153
    print
1494
1158
 
1495
1159
class cmd_version(Command):
1496
1160
    """Show version of bzr."""
1497
 
    @display_command
1498
1161
    def run(self):
1499
1162
        show_version()
1500
1163
 
1501
1164
class cmd_rocks(Command):
1502
1165
    """Statement of optimism."""
1503
1166
    hidden = True
1504
 
    @display_command
1505
1167
    def run(self):
1506
1168
        print "it sure does!"
1507
1169
 
1508
1170
 
1509
1171
class cmd_find_merge_base(Command):
1510
1172
    """Find and print a base revision for merging two branches.
 
1173
 
 
1174
    TODO: Options to specify revisions on either side, as if
 
1175
          merging only part of the history.
1511
1176
    """
1512
 
    # TODO: Options to specify revisions on either side, as if
1513
 
    #       merging only part of the history.
1514
1177
    takes_args = ['branch', 'other']
1515
1178
    hidden = True
1516
1179
    
1517
 
    @display_command
1518
1180
    def run(self, branch, other):
1519
1181
        from bzrlib.revision import common_ancestor, MultipleRevisionSources
1520
1182
        
1521
 
        branch1 = Branch.open_containing(branch)[0]
1522
 
        branch2 = Branch.open_containing(other)[0]
 
1183
        branch1 = Branch.open_containing(branch)
 
1184
        branch2 = Branch.open_containing(other)
1523
1185
 
1524
1186
        history_1 = branch1.revision_history()
1525
1187
        history_2 = branch2.revision_history()
1574
1236
    --force is given.
1575
1237
    """
1576
1238
    takes_args = ['branch?']
1577
 
    takes_options = ['revision', 'force', 'merge-type', 'reprocess',
1578
 
                     Option('show-base', help="Show base revision text in "
1579
 
                            "conflicts")]
 
1239
    takes_options = ['revision', 'force', 'merge-type']
1580
1240
 
1581
 
    def run(self, branch=None, revision=None, force=False, merge_type=None,
1582
 
            show_base=False, reprocess=False):
 
1241
    def run(self, branch=None, revision=None, force=False, 
 
1242
            merge_type=None):
1583
1243
        from bzrlib.merge import merge
1584
1244
        from bzrlib.merge_core import ApplyMerge3
1585
1245
        if merge_type is None:
1586
1246
            merge_type = ApplyMerge3
1587
1247
        if branch is None:
1588
 
            branch = WorkingTree.open_containing(u'.')[0].branch.get_parent()
 
1248
            branch = Branch.open_containing('.').get_parent()
1589
1249
            if branch is None:
1590
1250
                raise BzrCommandError("No merge location known or specified.")
1591
1251
            else:
1596
1256
        else:
1597
1257
            if len(revision) == 1:
1598
1258
                base = [None, None]
1599
 
                other_branch = Branch.open_containing(branch)[0]
1600
 
                revno = revision[0].in_history(other_branch).revno
1601
 
                other = [branch, revno]
 
1259
                other = [branch, revision[0].in_history(branch).revno]
1602
1260
            else:
1603
1261
                assert len(revision) == 2
1604
1262
                if None in revision:
1605
1263
                    raise BzrCommandError(
1606
1264
                        "Merge doesn't permit that revision specifier.")
1607
 
                b = Branch.open_containing(branch)[0]
 
1265
                b = Branch.open(branch)
1608
1266
 
1609
1267
                base = [branch, revision[0].in_history(b).revno]
1610
1268
                other = [branch, revision[1].in_history(b).revno]
1611
1269
 
1612
1270
        try:
1613
 
            conflict_count = merge(other, base, check_clean=(not force),
1614
 
                                   merge_type=merge_type, reprocess=reprocess,
1615
 
                                   show_base=show_base)
1616
 
            if conflict_count != 0:
1617
 
                return 1
1618
 
            else:
1619
 
                return 0
 
1271
            merge(other, base, check_clean=(not force), merge_type=merge_type)
1620
1272
        except bzrlib.errors.AmbiguousBase, e:
1621
1273
            m = ("sorry, bzr can't determine the right merge base yet\n"
1622
1274
                 "candidates are:\n  "
1627
1279
            log_error(m)
1628
1280
 
1629
1281
 
1630
 
class cmd_remerge(Command):
1631
 
    """Redo a merge.
1632
 
    """
1633
 
    takes_args = ['file*']
1634
 
    takes_options = ['merge-type', 'reprocess',
1635
 
                     Option('show-base', help="Show base revision text in "
1636
 
                            "conflicts")]
1637
 
 
1638
 
    def run(self, file_list=None, merge_type=None, show_base=False,
1639
 
            reprocess=False):
1640
 
        from bzrlib.merge import merge_inner, transform_tree
1641
 
        from bzrlib.merge_core import ApplyMerge3
1642
 
        if merge_type is None:
1643
 
            merge_type = ApplyMerge3
1644
 
        tree, file_list = tree_files(file_list)
1645
 
        tree.lock_write()
1646
 
        try:
1647
 
            pending_merges = tree.pending_merges() 
1648
 
            if len(pending_merges) != 1:
1649
 
                raise BzrCommandError("Sorry, remerge only works after normal"
1650
 
                                      + " merges.  Not cherrypicking or"
1651
 
                                      + "multi-merges.")
1652
 
            base_revision = common_ancestor(tree.branch.last_revision(), 
1653
 
                                            pending_merges[0], tree.branch)
1654
 
            base_tree = tree.branch.revision_tree(base_revision)
1655
 
            other_tree = tree.branch.revision_tree(pending_merges[0])
1656
 
            interesting_ids = None
1657
 
            if file_list is not None:
1658
 
                interesting_ids = set()
1659
 
                for filename in file_list:
1660
 
                    file_id = tree.path2id(filename)
1661
 
                    interesting_ids.add(file_id)
1662
 
                    if tree.kind(file_id) != "directory":
1663
 
                        continue
1664
 
                    
1665
 
                    for name, ie in tree.inventory.iter_entries(file_id):
1666
 
                        interesting_ids.add(ie.file_id)
1667
 
            transform_tree(tree, tree.branch.basis_tree(), interesting_ids)
1668
 
            if file_list is None:
1669
 
                restore_files = list(tree.iter_conflicts())
1670
 
            else:
1671
 
                restore_files = file_list
1672
 
            for filename in restore_files:
1673
 
                try:
1674
 
                    restore(tree.abspath(filename))
1675
 
                except NotConflicted:
1676
 
                    pass
1677
 
            conflicts =  merge_inner(tree.branch, other_tree, base_tree, 
1678
 
                                     interesting_ids = interesting_ids, 
1679
 
                                     other_rev_id=pending_merges[0], 
1680
 
                                     merge_type=merge_type, 
1681
 
                                     show_base=show_base,
1682
 
                                     reprocess=reprocess)
1683
 
        finally:
1684
 
            tree.unlock()
1685
 
        if conflicts > 0:
1686
 
            return 1
1687
 
        else:
1688
 
            return 0
1689
 
 
1690
1282
class cmd_revert(Command):
1691
1283
    """Reverse all changes since the last commit.
1692
1284
 
1699
1291
    aliases = ['merge-revert']
1700
1292
 
1701
1293
    def run(self, revision=None, no_backup=False, file_list=None):
1702
 
        from bzrlib.merge import merge_inner
 
1294
        from bzrlib.merge import merge
1703
1295
        from bzrlib.commands import parse_spec
 
1296
 
1704
1297
        if file_list is not None:
1705
1298
            if len(file_list) == 0:
1706
1299
                raise BzrCommandError("No files specified")
1707
 
        else:
1708
 
            file_list = []
1709
1300
        if revision is None:
1710
1301
            revno = -1
1711
 
            tree = WorkingTree.open_containing(u'.')[0]
1712
 
            # FIXME should be tree.last_revision
1713
 
            rev_id = tree.branch.last_revision()
1714
1302
        elif len(revision) != 1:
1715
1303
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1716
1304
        else:
1717
 
            tree, file_list = tree_files(file_list)
1718
 
            rev_id = revision[0].in_history(tree.branch).rev_id
1719
 
        tree.revert(file_list, tree.branch.revision_tree(rev_id),
1720
 
                                not no_backup)
 
1305
            b = Branch.open_containing('.')
 
1306
            revno = revision[0].in_history(b).revno
 
1307
        merge(('.', revno), parse_spec('.'),
 
1308
              check_clean=False,
 
1309
              ignore_zero=True,
 
1310
              backup_files=not no_backup,
 
1311
              file_list=file_list)
 
1312
        if not file_list:
 
1313
            Branch.open_containing('.').set_pending_merges([])
1721
1314
 
1722
1315
 
1723
1316
class cmd_assert_fail(Command):
1735
1328
    takes_args = ['topic?']
1736
1329
    aliases = ['?']
1737
1330
    
1738
 
    @display_command
1739
1331
    def run(self, topic=None, long=False):
1740
1332
        import help
1741
1333
        if topic is None and long:
1751
1343
    aliases = ['s-c']
1752
1344
    hidden = True
1753
1345
    
1754
 
    @display_command
1755
1346
    def run(self, context=None):
1756
1347
        import shellcomplete
1757
1348
        shellcomplete.shellcomplete(context)
1766
1357
    def run(self, from_branch, to_branch):
1767
1358
        from bzrlib.fetch import Fetcher
1768
1359
        from bzrlib.branch import Branch
1769
 
        from_b = Branch.open(from_branch)
1770
 
        to_b = Branch.open(to_branch)
1771
 
        from_b.lock_read()
1772
 
        try:
1773
 
            to_b.lock_write()
1774
 
            try:
1775
 
                Fetcher(to_b, from_b)
1776
 
            finally:
1777
 
                to_b.unlock()
1778
 
        finally:
1779
 
            from_b.unlock()
 
1360
        from_b = Branch(from_branch)
 
1361
        to_b = Branch(to_branch)
 
1362
        Fetcher(to_b, from_b)
 
1363
        
1780
1364
 
1781
1365
 
1782
1366
class cmd_missing(Command):
1783
 
    """Show unmerged/unpulled revisions between two branches.
1784
 
 
1785
 
    OTHER_BRANCH may be local or remote."""
1786
 
    takes_args = ['other_branch?']
1787
 
    takes_options = [Option('reverse', 'Reverse the order of revisions'),
1788
 
                     Option('mine-only', 
1789
 
                            'Display changes in the local branch only'),
1790
 
                     Option('theirs-only', 
1791
 
                            'Display changes in the remote branch only'), 
1792
 
                     'line',
1793
 
                     'long', 
1794
 
                     'short',
1795
 
                     'show-ids',
1796
 
                     'verbose'
1797
 
                     ]
1798
 
 
1799
 
    def run(self, other_branch=None, reverse=False, mine_only=False,
1800
 
            theirs_only=False, long=True, short=False, line=False, 
1801
 
            show_ids=False, verbose=False):
1802
 
        from bzrlib.missing import find_unmerged, iter_log_data
1803
 
        from bzrlib.log import log_formatter
1804
 
        local_branch = bzrlib.branch.Branch.open_containing(u".")[0]
1805
 
        parent = local_branch.get_parent()
1806
 
        if other_branch is None:
1807
 
            other_branch = parent
1808
 
            if other_branch is None:
 
1367
    """What is missing in this branch relative to other branch.
 
1368
    """
 
1369
    # TODO: rewrite this in terms of ancestry so that it shows only
 
1370
    # unmerged things
 
1371
    
 
1372
    takes_args = ['remote?']
 
1373
    aliases = ['mis', 'miss']
 
1374
    # We don't have to add quiet to the list, because 
 
1375
    # unknown options are parsed as booleans
 
1376
    takes_options = ['verbose', 'quiet']
 
1377
 
 
1378
    def run(self, remote=None, verbose=False, quiet=False):
 
1379
        from bzrlib.errors import BzrCommandError
 
1380
        from bzrlib.missing import show_missing
 
1381
 
 
1382
        if verbose and quiet:
 
1383
            raise BzrCommandError('Cannot pass both quiet and verbose')
 
1384
 
 
1385
        b = Branch.open_containing('.')
 
1386
        parent = b.get_parent()
 
1387
        if remote is None:
 
1388
            if parent is None:
1809
1389
                raise BzrCommandError("No missing location known or specified.")
1810
 
            print "Using last location: " + local_branch.get_parent()
1811
 
        remote_branch = bzrlib.branch.Branch.open(other_branch)
1812
 
        local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
1813
 
        log_format = get_log_format(long=long, short=short, line=line)
1814
 
        lf = log_formatter(log_format, sys.stdout,
1815
 
                           show_ids=show_ids,
1816
 
                           show_timezone='original')
1817
 
        if reverse is False:
1818
 
            local_extra.reverse()
1819
 
            remote_extra.reverse()
1820
 
        if local_extra and not theirs_only:
1821
 
            print "You have %d extra revision(s):" % len(local_extra)
1822
 
            for data in iter_log_data(local_extra, local_branch, verbose):
1823
 
                lf.show(*data)
1824
 
            printed_local = True
1825
 
        else:
1826
 
            printed_local = False
1827
 
        if remote_extra and not mine_only:
1828
 
            if printed_local is True:
1829
 
                print "\n\n"
1830
 
            print "You are missing %d revision(s):" % len(remote_extra)
1831
 
            for data in iter_log_data(remote_extra, remote_branch, verbose):
1832
 
                lf.show(*data)
1833
 
        if not remote_extra and not local_extra:
1834
 
            status_code = 0
1835
 
            print "Branches are up to date."
1836
 
        else:
1837
 
            status_code = 1
1838
 
        if parent is None and other_branch is not None:
1839
 
            local_branch.set_parent(other_branch)
1840
 
        return status_code
 
1390
            else:
 
1391
                if not quiet:
 
1392
                    print "Using last location: %s" % parent
 
1393
                remote = parent
 
1394
        elif parent is None:
 
1395
            # We only update parent if it did not exist, missing
 
1396
            # should not change the parent
 
1397
            b.set_parent(remote)
 
1398
        br_remote = Branch.open_containing(remote)
 
1399
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1841
1400
 
1842
1401
 
1843
1402
class cmd_plugins(Command):
1844
1403
    """List plugins"""
1845
1404
    hidden = True
1846
 
    @display_command
1847
1405
    def run(self):
1848
1406
        import bzrlib.plugin
1849
1407
        from inspect import getdoc
1850
 
        for name, plugin in bzrlib.plugin.all_plugins().items():
 
1408
        for plugin in bzrlib.plugin.all_plugins:
1851
1409
            if hasattr(plugin, '__path__'):
1852
1410
                print plugin.__path__[0]
1853
1411
            elif hasattr(plugin, '__file__'):
1860
1418
                print '\t', d.split('\n')[0]
1861
1419
 
1862
1420
 
1863
 
class cmd_testament(Command):
1864
 
    """Show testament (signing-form) of a revision."""
1865
 
    takes_options = ['revision', 'long']
1866
 
    takes_args = ['branch?']
1867
 
    @display_command
1868
 
    def run(self, branch=u'.', revision=None, long=False):
1869
 
        from bzrlib.testament import Testament
1870
 
        b = WorkingTree.open_containing(branch)[0].branch
1871
 
        b.lock_read()
1872
 
        try:
1873
 
            if revision is None:
1874
 
                rev_id = b.last_revision()
1875
 
            else:
1876
 
                rev_id = revision[0].in_history(b).rev_id
1877
 
            t = Testament.from_revision(b, rev_id)
1878
 
            if long:
1879
 
                sys.stdout.writelines(t.as_text_lines())
1880
 
            else:
1881
 
                sys.stdout.write(t.as_short_text())
1882
 
        finally:
1883
 
            b.unlock()
1884
 
 
1885
 
 
1886
 
class cmd_annotate(Command):
1887
 
    """Show the origin of each line in a file.
1888
 
 
1889
 
    This prints out the given file with an annotation on the left side
1890
 
    indicating which revision, author and date introduced the change.
1891
 
 
1892
 
    If the origin is the same for a run of consecutive lines, it is 
1893
 
    shown only at the top, unless the --all option is given.
1894
 
    """
1895
 
    # TODO: annotate directories; showing when each file was last changed
1896
 
    # TODO: annotate a previous version of a file
1897
 
    # TODO: if the working copy is modified, show annotations on that 
1898
 
    #       with new uncommitted lines marked
1899
 
    aliases = ['blame', 'praise']
1900
 
    takes_args = ['filename']
1901
 
    takes_options = [Option('all', help='show annotations on all lines'),
1902
 
                     Option('long', help='show date in annotations'),
1903
 
                     ]
1904
 
 
1905
 
    @display_command
1906
 
    def run(self, filename, all=False, long=False):
1907
 
        from bzrlib.annotate import annotate_file
1908
 
        tree, relpath = WorkingTree.open_containing(filename)
1909
 
        branch = tree.branch
1910
 
        branch.lock_read()
1911
 
        try:
1912
 
            file_id = tree.inventory.path2id(relpath)
1913
 
            tree = branch.revision_tree(branch.last_revision())
1914
 
            file_version = tree.inventory[file_id].revision
1915
 
            annotate_file(branch, file_version, file_id, long, all, sys.stdout)
1916
 
        finally:
1917
 
            branch.unlock()
1918
 
 
1919
 
 
1920
 
class cmd_re_sign(Command):
1921
 
    """Create a digital signature for an existing revision."""
1922
 
    # TODO be able to replace existing ones.
1923
 
 
1924
 
    hidden = True # is this right ?
1925
 
    takes_args = ['revision_id?']
1926
 
    takes_options = ['revision']
1927
 
    
1928
 
    def run(self, revision_id=None, revision=None):
1929
 
        import bzrlib.config as config
1930
 
        import bzrlib.gpg as gpg
1931
 
        if revision_id is not None and revision is not None:
1932
 
            raise BzrCommandError('You can only supply one of revision_id or --revision')
1933
 
        if revision_id is None and revision is None:
1934
 
            raise BzrCommandError('You must supply either --revision or a revision_id')
1935
 
        b = WorkingTree.open_containing(u'.')[0].branch
1936
 
        gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
1937
 
        if revision_id is not None:
1938
 
            b.sign_revision(revision_id, gpg_strategy)
1939
 
        elif revision is not None:
1940
 
            if len(revision) == 1:
1941
 
                revno, rev_id = revision[0].in_history(b)
1942
 
                b.sign_revision(rev_id, gpg_strategy)
1943
 
            elif len(revision) == 2:
1944
 
                # are they both on rh- if so we can walk between them
1945
 
                # might be nice to have a range helper for arbitrary
1946
 
                # revision paths. hmm.
1947
 
                from_revno, from_revid = revision[0].in_history(b)
1948
 
                to_revno, to_revid = revision[1].in_history(b)
1949
 
                if to_revid is None:
1950
 
                    to_revno = b.revno()
1951
 
                if from_revno is None or to_revno is None:
1952
 
                    raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
1953
 
                for revno in range(from_revno, to_revno + 1):
1954
 
                    b.sign_revision(b.get_rev_id(revno), gpg_strategy)
1955
 
            else:
1956
 
                raise BzrCommandError('Please supply either one revision, or a range.')
1957
 
 
1958
 
 
1959
 
class cmd_uncommit(bzrlib.commands.Command):
1960
 
    """Remove the last committed revision.
1961
 
 
1962
 
    By supplying the --all flag, it will not only remove the entry 
1963
 
    from revision_history, but also remove all of the entries in the
1964
 
    stores.
1965
 
 
1966
 
    --verbose will print out what is being removed.
1967
 
    --dry-run will go through all the motions, but not actually
1968
 
    remove anything.
1969
 
    
1970
 
    In the future, uncommit will create a changeset, which can then
1971
 
    be re-applied.
1972
 
    """
1973
 
    takes_options = ['all', 'verbose', 'revision',
1974
 
                    Option('dry-run', help='Don\'t actually make changes'),
1975
 
                    Option('force', help='Say yes to all questions.')]
1976
 
    takes_args = ['location?']
1977
 
    aliases = []
1978
 
 
1979
 
    def run(self, location=None, all=False,
1980
 
            dry_run=False, verbose=False,
1981
 
            revision=None, force=False):
1982
 
        from bzrlib.branch import Branch
1983
 
        from bzrlib.log import log_formatter
1984
 
        import sys
1985
 
        from bzrlib.uncommit import uncommit
1986
 
 
1987
 
        if location is None:
1988
 
            location = u'.'
1989
 
        b, relpath = Branch.open_containing(location)
1990
 
 
1991
 
        if revision is None:
1992
 
            revno = b.revno()
1993
 
            rev_id = b.last_revision()
1994
 
        else:
1995
 
            revno, rev_id = revision[0].in_history(b)
1996
 
        if rev_id is None:
1997
 
            print 'No revisions to uncommit.'
1998
 
 
1999
 
        for r in range(revno, b.revno()+1):
2000
 
            rev_id = b.get_rev_id(r)
2001
 
            lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
2002
 
            lf.show(r, b.get_revision(rev_id), None)
2003
 
 
2004
 
        if dry_run:
2005
 
            print 'Dry-run, pretending to remove the above revisions.'
2006
 
            if not force:
2007
 
                val = raw_input('Press <enter> to continue')
2008
 
        else:
2009
 
            print 'The above revision(s) will be removed.'
2010
 
            if not force:
2011
 
                val = raw_input('Are you sure [y/N]? ')
2012
 
                if val.lower() not in ('y', 'yes'):
2013
 
                    print 'Canceled'
2014
 
                    return 0
2015
 
 
2016
 
        uncommit(b, remove_files=all,
2017
 
                dry_run=dry_run, verbose=verbose,
2018
 
                revno=revno)
2019
 
 
2020
 
 
2021
 
# these get imported and then picked up by the scan for cmd_*
2022
 
# TODO: Some more consistent way to split command definitions across files;
2023
 
# we do need to load at least some information about them to know of 
2024
 
# aliases.
2025
 
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore