~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

[merge] aaron, various fixes

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