~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2006-01-30 06:23:50 UTC
  • mfrom: (1534.1.17 integration)
  • Revision ID: mbp@sourcefrog.net-20060130062350-d6f25277ddcdfd79
[merge] robert's integration of much recent work

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