~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

[merge] fix \t in commit messages

Show diffs side-by-side

added added

removed removed

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