~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

[merge] jam-integration

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