~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

[merge] jam-integration 1495

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