~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

Exclude more files from dumb-rsync upload

Show diffs side-by-side

added added

removed removed

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