~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2005-11-04 01:46:31 UTC
  • mto: (1185.33.49 bzr.dev)
  • mto: This revision was merged to the branch mainline in revision 1512.
  • Revision ID: mbp@sourcefrog.net-20051104014631-750e0ad4172c952c
Make biobench directly executable

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