/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

Merge from mbp.

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