~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: John Arbash Meinel
  • Date: 2005-11-08 18:36:26 UTC
  • mto: This revision was merged to the branch mainline in revision 1727.
  • Revision ID: john@arbash-meinel.com-20051108183626-71f8414338043265
Updating unified_diff to take a factory, using the new diff algorithm in the code.

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.
62
101
    directory is shown.  Otherwise, only the status of the specified
63
102
    files or directories is reported.  If a directory is given, status
64
103
    is reported for everything inside that directory.
 
104
 
 
105
    If a revision argument is given, the status is calculated against
 
106
    that revision, or between two revisions if two are provided.
65
107
    """
 
108
    
66
109
    # XXX: FIXME: bzr status should accept a -r option to show changes
67
110
    # relative to a revision, or between revisions
68
111
 
 
112
    # TODO: --no-recurse, --recurse options
 
113
    
69
114
    takes_args = ['file*']
70
115
    takes_options = ['all', 'show-ids']
71
116
    aliases = ['st', 'stat']
72
117
    
73
 
    def run(self, all=False, show_ids=False, file_list=None):
74
 
        if file_list:
75
 
            b = find_branch(file_list[0])
76
 
            file_list = [b.relpath(x) for x in file_list]
77
 
            # special case: only one path was given and it's the root
78
 
            # of the branch
79
 
            if file_list == ['']:
80
 
                file_list = None
81
 
        else:
82
 
            b = find_branch('.')
 
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)
83
121
            
84
122
        from bzrlib.status import show_status
85
123
        show_status(b, show_unchanged=all, show_ids=show_ids,
86
 
                    specific_files=file_list)
 
124
                    specific_files=file_list, revision=revision)
87
125
 
88
126
 
89
127
class cmd_cat_revision(Command):
90
 
    """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
    """
91
133
 
92
134
    hidden = True
93
 
    takes_args = ['revision_id']
 
135
    takes_args = ['revision_id?']
 
136
    takes_options = ['revision']
94
137
    
95
 
    def run(self, revision_id):
96
 
        b = find_branch('.')
97
 
        sys.stdout.write(b.get_revision_xml_file(revision_id).read())
 
138
    @display_command
 
139
    def run(self, revision_id=None, revision=None):
98
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
    
99
155
 
100
156
class cmd_revno(Command):
101
157
    """Show current revision number.
102
158
 
103
159
    This is equal to the number of revisions on this branch."""
 
160
    @display_command
104
161
    def run(self):
105
 
        print find_branch('.').revno()
 
162
        print Branch.open_containing('.')[0].revno()
106
163
 
107
164
 
108
165
class cmd_revision_info(Command):
111
168
    hidden = True
112
169
    takes_args = ['revision_info*']
113
170
    takes_options = ['revision']
114
 
    def run(self, revision=None, revision_info_list=None):
115
 
        from bzrlib.branch import find_branch
 
171
    @display_command
 
172
    def run(self, revision=None, revision_info_list=[]):
116
173
 
117
174
        revs = []
118
175
        if revision is not None:
119
176
            revs.extend(revision)
120
177
        if revision_info_list is not None:
121
 
            revs.extend(revision_info_list)
 
178
            for rev in revision_info_list:
 
179
                revs.append(RevisionSpec(rev))
122
180
        if len(revs) == 0:
123
181
            raise BzrCommandError('You must supply a revision identifier')
124
182
 
125
 
        b = find_branch('.')
 
183
        b = Branch.open_containing('.')[0]
126
184
 
127
185
        for rev in revs:
128
 
            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)
129
191
 
130
192
    
131
193
class cmd_add(Command):
152
214
    get added when you add a file in the directory.
153
215
    """
154
216
    takes_args = ['file*']
155
 
    takes_options = ['verbose', 'no-recurse']
 
217
    takes_options = ['no-recurse', 'quiet']
156
218
    
157
 
    def run(self, file_list, verbose=False, no_recurse=False):
158
 
        # verbose currently has no effect
159
 
        from bzrlib.add import smart_add, add_reporter_print
160
 
        smart_add(file_list, not no_recurse, add_reporter_print)
161
 
 
 
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)
162
226
 
163
227
 
164
228
class cmd_mkdir(Command):
174
238
        for d in dir_list:
175
239
            os.mkdir(d)
176
240
            if not b:
177
 
                b = find_branch(d)
 
241
                b = Branch.open_containing(d)[0]
178
242
            b.add([d])
179
243
            print 'added', d
180
244
 
184
248
    takes_args = ['filename']
185
249
    hidden = True
186
250
    
 
251
    @display_command
187
252
    def run(self, filename):
188
 
        print find_branch(filename).relpath(filename)
189
 
 
 
253
        branch, relpath = Branch.open_containing(filename)
 
254
        print relpath
190
255
 
191
256
 
192
257
class cmd_inventory(Command):
193
258
    """Show inventory of the current working copy or a revision."""
194
259
    takes_options = ['revision', 'show-ids']
195
260
    
 
261
    @display_command
196
262
    def run(self, revision=None, show_ids=False):
197
 
        b = find_branch('.')
198
 
        if revision == None:
199
 
            inv = b.read_working_inventory()
 
263
        b = Branch.open_containing('.')[0]
 
264
        if revision is None:
 
265
            inv = b.working_tree().read_working_inventory()
200
266
        else:
201
267
            if len(revision) > 1:
202
268
                raise BzrCommandError('bzr inventory --revision takes'
203
269
                    ' exactly one revision identifier')
204
 
            inv = b.get_revision_inventory(b.lookup_revision(revision[0]))
 
270
            inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
205
271
 
206
272
        for path, entry in inv.entries():
207
273
            if show_ids:
220
286
    """
221
287
    takes_args = ['source$', 'dest']
222
288
    def run(self, source_list, dest):
223
 
        b = find_branch('.')
 
289
        b, source_list = branch_files(source_list)
224
290
 
225
291
        # TODO: glob expansion on windows?
226
 
        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))
227
294
 
228
295
 
229
296
class cmd_rename(Command):
237
304
 
238
305
    See also the 'move' command, which moves files into a different
239
306
    directory without changing their name.
240
 
 
241
 
    TODO: Some way to rename multiple files without invoking bzr for each
242
 
    one?"""
 
307
    """
 
308
    # TODO: Some way to rename multiple files without invoking 
 
309
    # bzr for each one?"""
243
310
    takes_args = ['from_name', 'to_name']
244
311
    
245
312
    def run(self, from_name, to_name):
246
 
        b = find_branch('.')
247
 
        b.rename_one(b.relpath(from_name), b.relpath(to_name))
248
 
 
 
313
        b, (from_name, to_name) = branch_files((from_name, to_name))
 
314
        b.rename_one(from_name, to_name)
249
315
 
250
316
 
251
317
class cmd_mv(Command):
265
331
    def run(self, names_list):
266
332
        if len(names_list) < 2:
267
333
            raise BzrCommandError("missing file argument")
268
 
        b = find_branch(names_list[0])
269
 
 
270
 
        rel_names = [b.relpath(x) for x in names_list]
 
334
        b, rel_names = branch_files(names_list)
271
335
        
272
336
        if os.path.isdir(names_list[-1]):
273
337
            # move into existing directory
281
345
            print "%s => %s" % (rel_names[0], rel_names[1])
282
346
            
283
347
    
284
 
 
285
 
 
286
348
class cmd_pull(Command):
287
349
    """Pull any changes from another branch into the current one.
288
350
 
289
 
    If the location is omitted, the last-used location will be used.
290
 
    Both the revision history and the working directory will be
291
 
    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.
292
354
 
293
355
    This command only works on branches that have not diverged.  Branches are
294
356
    considered diverged if both branches have had commits without first
295
357
    pulling from the other.
296
358
 
297
359
    If branches have diverged, you can use 'bzr merge' to pull the text changes
298
 
    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.
299
365
    """
 
366
    takes_options = ['remember', 'overwrite']
300
367
    takes_args = ['location?']
301
368
 
302
 
    def run(self, location=None):
 
369
    def run(self, location=None, remember=False, overwrite=False):
303
370
        from bzrlib.merge import merge
304
 
        import tempfile
305
371
        from shutil import rmtree
306
372
        import errno
307
373
        
308
 
        br_to = find_branch('.')
 
374
        br_to = Branch.open_containing('.')[0]
309
375
        stored_loc = br_to.get_parent()
310
376
        if location is None:
311
377
            if stored_loc is None:
312
378
                raise BzrCommandError("No pull location known or specified.")
313
379
            else:
314
 
                print "Using last location: %s" % stored_loc
315
 
                location = stored_loc
316
 
        cache_root = tempfile.mkdtemp()
317
 
        from bzrlib.branch import DivergedBranches
318
 
        br_from = find_branch(location)
319
 
        location = br_from.base
320
 
        old_revno = br_to.revno()
321
 
        try:
322
 
            from branch import find_cached_branch, DivergedBranches
323
 
            br_from = find_cached_branch(location, cache_root)
324
 
            location = br_from.base
325
 
            old_revno = br_to.revno()
326
 
            try:
327
 
                br_to.update_revisions(br_from)
328
 
            except DivergedBranches:
329
 
                raise BzrCommandError("These branches have diverged."
330
 
                    "  Try merge.")
331
 
                
332
 
            merge(('.', -1), ('.', old_revno), check_clean=False)
333
 
            if location != stored_loc:
334
 
                br_to.set_parent(location)
335
 
        finally:
336
 
            rmtree(cache_root)
337
 
 
 
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)
338
471
 
339
472
 
340
473
class cmd_branch(Command):
345
478
 
346
479
    To retrieve the branch as of a particular revision, supply the --revision
347
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.
348
485
    """
349
486
    takes_args = ['from_location', 'to_location?']
350
 
    takes_options = ['revision']
 
487
    takes_options = ['revision', 'basis']
351
488
    aliases = ['get', 'clone']
352
489
 
353
 
    def run(self, from_location, to_location=None, revision=None):
354
 
        from bzrlib.branch import copy_branch, find_cached_branch
355
 
        import tempfile
 
490
    def run(self, from_location, to_location=None, revision=None, basis=None):
 
491
        from bzrlib.clone import copy_branch
356
492
        import errno
357
493
        from shutil import rmtree
358
 
        cache_root = tempfile.mkdtemp()
359
 
        try:
360
 
            if revision is None:
361
 
                revision = [None]
362
 
            elif len(revision) > 1:
363
 
                raise BzrCommandError(
364
 
                    'bzr branch --revision takes exactly 1 revision value')
365
 
            try:
366
 
                br_from = find_cached_branch(from_location, cache_root)
367
 
            except OSError, e:
368
 
                if e.errno == errno.ENOENT:
369
 
                    raise BzrCommandError('Source location "%s" does not'
370
 
                                          ' exist.' % to_location)
371
 
                else:
372
 
                    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
373
517
            if to_location is None:
374
518
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
519
                name = None
 
520
            else:
 
521
                name = os.path.basename(to_location) + '\n'
375
522
            try:
376
523
                os.mkdir(to_location)
377
524
            except OSError, e:
384
531
                else:
385
532
                    raise
386
533
            try:
387
 
                copy_branch(br_from, to_location, revision[0])
 
534
                copy_branch(br_from, to_location, revision_id, basis_branch)
388
535
            except bzrlib.errors.NoSuchRevision:
389
536
                rmtree(to_location)
390
 
                msg = "The branch %s has no revision %d." % (from_location, revision[0])
391
 
                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)
392
547
        finally:
393
 
            rmtree(cache_root)
 
548
            br_from.unlock()
394
549
 
395
550
 
396
551
class cmd_renames(Command):
397
552
    """Show list of renamed files.
398
 
 
399
 
    TODO: Option to show renames between two historical versions.
400
 
 
401
 
    TODO: Only show renames under dir, rather than in the whole branch.
402
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.
403
557
    takes_args = ['dir?']
404
558
 
 
559
    @display_command
405
560
    def run(self, dir='.'):
406
 
        b = find_branch(dir)
 
561
        b = Branch.open_containing(dir)[0]
407
562
        old_inv = b.basis_tree().inventory
408
 
        new_inv = b.read_working_inventory()
 
563
        new_inv = b.working_tree().read_working_inventory()
409
564
 
410
565
        renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
411
566
        renames.sort()
417
572
    """Show statistical information about a branch."""
418
573
    takes_args = ['branch?']
419
574
    
 
575
    @display_command
420
576
    def run(self, branch=None):
421
577
        import info
422
 
 
423
 
        b = find_branch(branch)
 
578
        b = Branch.open_containing(branch)[0]
424
579
        info.show_info(b)
425
580
 
426
581
 
432
587
    """
433
588
    takes_args = ['file+']
434
589
    takes_options = ['verbose']
 
590
    aliases = ['rm']
435
591
    
436
592
    def run(self, file_list, verbose=False):
437
 
        b = find_branch(file_list[0])
438
 
        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)
439
596
 
440
597
 
441
598
class cmd_file_id(Command):
447
604
    """
448
605
    hidden = True
449
606
    takes_args = ['filename']
 
607
    @display_command
450
608
    def run(self, filename):
451
 
        b = find_branch(filename)
452
 
        i = b.inventory.path2id(b.relpath(filename))
 
609
        b, relpath = Branch.open_containing(filename)
 
610
        i = b.inventory.path2id(relpath)
453
611
        if i == None:
454
612
            raise BzrError("%r is not a versioned file" % filename)
455
613
        else:
463
621
    starting at the branch root."""
464
622
    hidden = True
465
623
    takes_args = ['filename']
 
624
    @display_command
466
625
    def run(self, filename):
467
 
        b = find_branch(filename)
 
626
        b, relpath = Branch.open_containing(filename)
468
627
        inv = b.inventory
469
 
        fid = inv.path2id(b.relpath(filename))
 
628
        fid = inv.path2id(relpath)
470
629
        if fid == None:
471
630
            raise BzrError("%r is not a versioned file" % filename)
472
631
        for fip in inv.get_idpath(fid):
476
635
class cmd_revision_history(Command):
477
636
    """Display list of revision ids on this branch."""
478
637
    hidden = True
 
638
    @display_command
479
639
    def run(self):
480
 
        for patchid in find_branch('.').revision_history():
 
640
        for patchid in Branch.open_containing('.')[0].revision_history():
481
641
            print patchid
482
642
 
483
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
 
484
654
class cmd_directories(Command):
485
655
    """Display list of versioned directories in this branch."""
 
656
    @display_command
486
657
    def run(self):
487
 
        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()):
488
660
            if name == '':
489
661
                print '.'
490
662
            else:
500
672
    Recipe for importing a tree of files:
501
673
        cd ~/project
502
674
        bzr init
503
 
        bzr add -v .
 
675
        bzr add .
504
676
        bzr status
505
677
        bzr commit -m 'imported project'
506
678
    """
507
 
    def run(self):
 
679
    takes_args = ['location?']
 
680
    def run(self, location=None):
508
681
        from bzrlib.branch import Branch
509
 
        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)
510
693
 
511
694
 
512
695
class cmd_diff(Command):
515
698
    If files are listed, only the changes in those files are listed.
516
699
    Otherwise, all changes for the tree are listed.
517
700
 
518
 
    TODO: Allow diff across branches.
519
 
 
520
 
    TODO: Option to use external diff command; could be GNU diff, wdiff,
521
 
          or a graphical diff.
522
 
 
523
 
    TODO: Python difflib is not exactly the same as unidiff; should
524
 
          either fix it up or prefer to use an external diff.
525
 
 
526
 
    TODO: If a directory is given, diff everything under that.
527
 
 
528
 
    TODO: Selected-file diff is inefficient and doesn't show you
529
 
          deleted files.
530
 
 
531
 
    TODO: This probably handles non-Unix newlines poorly.
532
 
 
533
701
    examples:
534
702
        bzr diff
535
703
        bzr diff -r1
536
704
        bzr diff -r1..2
537
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.
538
719
    
539
720
    takes_args = ['file*']
540
721
    takes_options = ['revision', 'diff-options']
541
722
    aliases = ['di', 'dif']
542
723
 
 
724
    @display_command
543
725
    def run(self, revision=None, file_list=None, diff_options=None):
544
726
        from bzrlib.diff import show_diff
545
 
 
546
 
        if file_list:
547
 
            b = find_branch(file_list[0])
548
 
            file_list = [b.relpath(f) for f in file_list]
549
 
            if file_list == ['']:
550
 
                # just pointing to top-of-tree
551
 
                file_list = None
552
 
        else:
553
 
            b = find_branch('.')
554
 
 
 
727
        
 
728
        b, file_list = branch_files(file_list)
555
729
        if revision is not None:
556
730
            if len(revision) == 1:
557
 
                show_diff(b, revision[0], specific_files=file_list,
558
 
                          external_diff_options=diff_options)
 
731
                return show_diff(b, revision[0], specific_files=file_list,
 
732
                                 external_diff_options=diff_options)
559
733
            elif len(revision) == 2:
560
 
                show_diff(b, revision[0], specific_files=file_list,
561
 
                          external_diff_options=diff_options,
562
 
                          revision2=revision[1])
 
734
                return show_diff(b, revision[0], specific_files=file_list,
 
735
                                 external_diff_options=diff_options,
 
736
                                 revision2=revision[1])
563
737
            else:
564
738
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
565
739
        else:
566
 
            show_diff(b, None, specific_files=file_list,
567
 
                      external_diff_options=diff_options)
568
 
 
569
 
        
 
740
            return show_diff(b, None, specific_files=file_list,
 
741
                             external_diff_options=diff_options)
570
742
 
571
743
 
572
744
class cmd_deleted(Command):
573
745
    """List files deleted in the working tree.
574
 
 
575
 
    TODO: Show files deleted since a previous revision, or between two revisions.
576
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
577
754
    def run(self, show_ids=False):
578
 
        b = find_branch('.')
 
755
        b = Branch.open_containing('.')[0]
579
756
        old = b.basis_tree()
580
757
        new = b.working_tree()
581
 
 
582
 
        ## TODO: Much more efficient way to do this: read in new
583
 
        ## directories with readdir, rather than stating each one.  Same
584
 
        ## level of effort but possibly much less IO.  (Or possibly not,
585
 
        ## if the directories are very large...)
586
 
 
587
758
        for path, ie in old.inventory.iter_entries():
588
759
            if not new.has_id(ie.file_id):
589
760
                if show_ids:
595
766
class cmd_modified(Command):
596
767
    """List files modified in working tree."""
597
768
    hidden = True
 
769
    @display_command
598
770
    def run(self):
599
771
        from bzrlib.delta import compare_trees
600
772
 
601
 
        b = find_branch('.')
 
773
        b = Branch.open_containing('.')[0]
602
774
        td = compare_trees(b.basis_tree(), b.working_tree())
603
775
 
604
 
        for path, id, kind in td.modified:
 
776
        for path, id, kind, text_modified, meta_modified in td.modified:
605
777
            print path
606
778
 
607
779
 
609
781
class cmd_added(Command):
610
782
    """List files added in working tree."""
611
783
    hidden = True
 
784
    @display_command
612
785
    def run(self):
613
 
        b = find_branch('.')
 
786
        b = Branch.open_containing('.')[0]
614
787
        wt = b.working_tree()
615
788
        basis_inv = b.basis_tree().inventory
616
789
        inv = wt.inventory
630
803
    The root is the nearest enclosing directory with a .bzr control
631
804
    directory."""
632
805
    takes_args = ['filename?']
 
806
    @display_command
633
807
    def run(self, filename=None):
634
808
        """Print the branch root."""
635
 
        b = find_branch(filename)
 
809
        b = Branch.open_containing(filename)[0]
636
810
        print b.base
637
811
 
638
812
 
642
816
    To request a range of logs, you can use the command -r begin:end
643
817
    -r revision requests a specific revision, -r :end or -r begin: are
644
818
    also valid.
645
 
 
646
 
    --message allows you to give a regular expression, which will be evaluated
647
 
    so that only matching entries will be displayed.
648
 
 
649
 
    TODO: Make --revision support uuid: and hash: [future tag:] notation.
650
 
  
651
819
    """
652
820
 
 
821
    # TODO: Make --revision support uuid: and hash: [future tag:] notation.
 
822
 
653
823
    takes_args = ['filename?']
654
 
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
655
 
                     'long', 'message', 'short',]
656
 
    
 
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
657
836
    def run(self, filename=None, timezone='original',
658
837
            verbose=False,
659
838
            show_ids=False,
661
840
            revision=None,
662
841
            message=None,
663
842
            long=False,
664
 
            short=False):
 
843
            short=False,
 
844
            line=False):
665
845
        from bzrlib.log import log_formatter, show_log
666
846
        import codecs
667
 
 
 
847
        assert message is None or isinstance(message, basestring), \
 
848
            "invalid message argument %r" % message
668
849
        direction = (forward and 'forward') or 'reverse'
669
850
        
670
851
        if filename:
671
 
            b = find_branch(filename)
672
 
            fp = b.relpath(filename)
673
 
            if fp:
674
 
                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)
675
859
            else:
676
860
                file_id = None  # points to branch root
677
861
        else:
678
 
            b = find_branch('.')
 
862
            b, relpath = Branch.open_containing('.')
679
863
            file_id = None
680
864
 
681
865
        if revision is None:
682
866
            rev1 = None
683
867
            rev2 = None
684
868
        elif len(revision) == 1:
685
 
            rev1 = rev2 = b.get_revision_info(revision[0])[0]
 
869
            rev1 = rev2 = revision[0].in_history(b).revno
686
870
        elif len(revision) == 2:
687
 
            rev1 = b.get_revision_info(revision[0])[0]
688
 
            rev2 = b.get_revision_info(revision[1])[0]
 
871
            rev1 = revision[0].in_history(b).revno
 
872
            rev2 = revision[1].in_history(b).revno
689
873
        else:
690
874
            raise BzrCommandError('bzr log --revision takes one or two values.')
691
875
 
700
884
        # in e.g. the default C locale.
701
885
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
702
886
 
703
 
        if not short:
704
 
            log_format = 'long'
705
 
        else:
 
887
        log_format = 'long'
 
888
        if short:
706
889
            log_format = 'short'
 
890
        if line:
 
891
            log_format = 'line'
707
892
        lf = log_formatter(log_format,
708
893
                           show_ids=show_ids,
709
894
                           to_file=outf,
726
911
    A more user-friendly interface is "bzr log FILE"."""
727
912
    hidden = True
728
913
    takes_args = ["filename"]
 
914
    @display_command
729
915
    def run(self, filename):
730
 
        b = find_branch(filename)
731
 
        inv = b.read_working_inventory()
732
 
        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)
733
919
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
734
920
            print "%6d %s" % (revno, what)
735
921
 
736
922
 
737
923
class cmd_ls(Command):
738
924
    """List files in a tree.
739
 
 
740
 
    TODO: Take a revision or remote path and list that tree instead.
741
925
    """
 
926
    # TODO: Take a revision or remote path and list that tree instead.
742
927
    hidden = True
743
 
    def run(self, revision=None, verbose=False):
744
 
        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 += '/'
745
956
        if revision == None:
746
957
            tree = b.working_tree()
747
958
        else:
748
 
            tree = b.revision_tree(b.lookup_revision(revision))
749
 
 
750
 
        for fp, fc, kind, fid in tree.list_files():
751
 
            if verbose:
752
 
                if kind == 'directory':
753
 
                    kindch = '/'
754
 
                elif kind == 'file':
755
 
                    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()
756
974
                else:
757
 
                    kindch = '???'
758
 
 
759
 
                print '%-8s %s%s' % (fc, fp, kindch)
760
 
            else:
761
 
                print fp
 
975
                    print fp
762
976
 
763
977
 
764
978
 
765
979
class cmd_unknowns(Command):
766
980
    """List unknown files."""
 
981
    @display_command
767
982
    def run(self):
768
983
        from bzrlib.osutils import quotefn
769
 
        for f in find_branch('.').unknowns():
 
984
        for f in Branch.open_containing('.')[0].unknowns():
770
985
            print quotefn(f)
771
986
 
772
987
 
777
992
    To remove patterns from the ignore list, edit the .bzrignore file.
778
993
 
779
994
    If the pattern contains a slash, it is compared to the whole path
780
 
    from the branch root.  Otherwise, it is comapred to only the last
781
 
    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 './'.
782
998
 
783
999
    Ignore patterns are case-insensitive on case-insensitive systems.
784
1000
 
788
1004
        bzr ignore ./Makefile
789
1005
        bzr ignore '*.class'
790
1006
    """
 
1007
    # TODO: Complain if the filename is absolute
791
1008
    takes_args = ['name_pattern']
792
1009
    
793
1010
    def run(self, name_pattern):
794
1011
        from bzrlib.atomicfile import AtomicFile
795
1012
        import os.path
796
1013
 
797
 
        b = find_branch('.')
 
1014
        b, relpath = Branch.open_containing('.')
798
1015
        ifn = b.abspath('.bzrignore')
799
1016
 
800
1017
        if os.path.exists(ifn):
833
1050
    """List ignored files and the patterns that matched them.
834
1051
 
835
1052
    See also: bzr ignore"""
 
1053
    @display_command
836
1054
    def run(self):
837
 
        tree = find_branch('.').working_tree()
838
 
        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():
839
1057
            if file_class != 'I':
840
1058
                continue
841
1059
            ## XXX: Slightly inefficient since this was already calculated
852
1070
    hidden = True
853
1071
    takes_args = ['revno']
854
1072
    
 
1073
    @display_command
855
1074
    def run(self, revno):
856
1075
        try:
857
1076
            revno = int(revno)
858
1077
        except ValueError:
859
1078
            raise BzrCommandError("not a valid revision-number: %r" % revno)
860
1079
 
861
 
        print find_branch('.').lookup_revision(revno)
 
1080
        print Branch.open_containing('.')[0].get_rev_id(revno)
862
1081
 
863
1082
 
864
1083
class cmd_export(Command):
877
1096
    takes_options = ['revision', 'format', 'root']
878
1097
    def run(self, dest, revision=None, format=None, root=None):
879
1098
        import os.path
880
 
        b = find_branch('.')
 
1099
        b = Branch.open_containing('.')[0]
881
1100
        if revision is None:
882
 
            rev_id = b.last_patch()
 
1101
            rev_id = b.last_revision()
883
1102
        else:
884
1103
            if len(revision) != 1:
885
1104
                raise BzrError('bzr export --revision takes exactly 1 argument')
886
 
            revno, rev_id = b.get_revision_info(revision[0])
 
1105
            rev_id = revision[0].in_history(b).rev_id
887
1106
        t = b.revision_tree(rev_id)
888
 
        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
889
1115
        if not format:
890
1116
            if ext in (".tar",):
891
1117
                format = "tar"
892
 
            elif ext in (".gz", ".tgz"):
 
1118
            elif ext in (".tar.gz", ".tgz"):
893
1119
                format = "tgz"
894
 
            elif ext in (".bz2", ".tbz2"):
 
1120
            elif ext in (".tar.bz2", ".tbz2"):
895
1121
                format = "tbz2"
896
1122
            else:
897
1123
                format = "dir"
904
1130
    takes_options = ['revision']
905
1131
    takes_args = ['filename']
906
1132
 
 
1133
    @display_command
907
1134
    def run(self, filename, revision=None):
908
 
        if revision == None:
 
1135
        if revision is None:
909
1136
            raise BzrCommandError("bzr cat requires a revision number")
910
1137
        elif len(revision) != 1:
911
1138
            raise BzrCommandError("bzr cat --revision takes exactly one number")
912
 
        b = find_branch('.')
913
 
        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)
914
1141
 
915
1142
 
916
1143
class cmd_local_time_offset(Command):
917
1144
    """Show the offset in seconds from GMT to local time."""
918
1145
    hidden = True    
 
1146
    @display_command
919
1147
    def run(self):
920
1148
        print bzrlib.osutils.local_time_offset()
921
1149
 
933
1161
    A selected-file commit may fail in some cases where the committed
934
1162
    tree would be invalid, such as trying to commit a file in a
935
1163
    newly-added directory that is not itself committed.
936
 
 
937
 
    TODO: Run hooks on tree to-be-committed, and after commit.
938
 
 
939
 
    TODO: Strict commit that fails if there are unknown or deleted files.
940
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
 
941
1174
    takes_args = ['selected*']
942
 
    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
                     ]
943
1185
    aliases = ['ci', 'checkin']
944
1186
 
945
 
    # TODO: Give better message for -s, --summary, used by tla people
946
 
    
947
1187
    def run(self, message=None, file=None, verbose=True, selected_list=None,
948
 
            unchanged=False):
949
 
        from bzrlib.errors import PointlessCommit
 
1188
            unchanged=False, strict=False):
 
1189
        from bzrlib.errors import (PointlessCommit, ConflictsInTree,
 
1190
                StrictCommitFailed)
950
1191
        from bzrlib.msgeditor import edit_commit_message
951
1192
        from bzrlib.status import show_status
952
1193
        from cStringIO import StringIO
953
1194
 
954
 
        b = find_branch('.')
955
 
        if selected_list:
956
 
            selected_list = [b.relpath(s) for s in selected_list]
957
 
            
958
 
        if not message and not file:
 
1195
        b, selected_list = branch_files(selected_list)
 
1196
        if message is None and not file:
959
1197
            catcher = StringIO()
960
1198
            show_status(b, specific_files=selected_list,
961
1199
                        to_file=catcher)
962
1200
            message = edit_commit_message(catcher.getvalue())
963
 
            
 
1201
 
964
1202
            if message is None:
965
1203
                raise BzrCommandError("please specify a commit message"
966
1204
                                      " with either --message or --file")
971
1209
            import codecs
972
1210
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
973
1211
 
 
1212
        if message == "":
 
1213
                raise BzrCommandError("empty commit message specified")
 
1214
            
974
1215
        try:
975
 
            b.commit(message, verbose=verbose,
976
 
                     specific_files=selected_list,
977
 
                     allow_pointless=unchanged)
 
1216
            b.commit(message, specific_files=selected_list,
 
1217
                     allow_pointless=unchanged, strict=strict)
978
1218
        except PointlessCommit:
979
1219
            # FIXME: This should really happen before the file is read in;
980
1220
            # perhaps prepare the commit; get the message; then actually commit
981
1221
            raise BzrCommandError("no changes to commit",
982
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.")
983
1229
 
984
1230
 
985
1231
class cmd_check(Command):
989
1235
    detect data corruption or bzr bugs.
990
1236
    """
991
1237
    takes_args = ['dir?']
 
1238
    takes_options = ['verbose']
992
1239
 
993
 
    def run(self, dir='.'):
 
1240
    def run(self, dir='.', verbose=False):
994
1241
        from bzrlib.check import check
995
 
 
996
 
        check(find_branch(dir))
 
1242
        check(Branch.open_containing(dir)[0], verbose)
997
1243
 
998
1244
 
999
1245
class cmd_scan_cache(Command):
1021
1267
 
1022
1268
    The check command or bzr developers may sometimes advise you to run
1023
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).
1024
1273
    """
1025
1274
    takes_args = ['dir?']
1026
1275
 
1027
1276
    def run(self, dir='.'):
1028
1277
        from bzrlib.upgrade import upgrade
1029
 
        upgrade(find_branch(dir))
1030
 
 
 
1278
        upgrade(dir)
1031
1279
 
1032
1280
 
1033
1281
class cmd_whoami(Command):
1034
1282
    """Show bzr user id."""
1035
1283
    takes_options = ['email']
1036
1284
    
 
1285
    @display_command
1037
1286
    def run(self, email=False):
1038
1287
        try:
1039
 
            b = bzrlib.branch.find_branch('.')
1040
 
        except:
1041
 
            b = None
 
1288
            b = bzrlib.branch.Branch.open_containing('.')[0]
 
1289
            config = bzrlib.config.BranchConfig(b)
 
1290
        except NotBranchError:
 
1291
            config = bzrlib.config.GlobalConfig()
1042
1292
        
1043
1293
        if email:
1044
 
            print bzrlib.osutils.user_email(b)
 
1294
            print config.user_email()
1045
1295
        else:
1046
 
            print bzrlib.osutils.username(b)
 
1296
            print config.username()
1047
1297
 
1048
1298
 
1049
1299
class cmd_selftest(Command):
1050
 
    """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
1051
1311
    hidden = True
1052
 
    takes_options = ['verbose', 'pattern']
1053
 
    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):
1054
1318
        import bzrlib.ui
1055
1319
        from bzrlib.selftest import selftest
1056
1320
        # we don't want progress meters from the tests to go to the
1060
1324
        bzrlib.trace.info('running tests...')
1061
1325
        try:
1062
1326
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
1063
 
            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)
1064
1334
            if result:
1065
1335
                bzrlib.trace.info('tests passed')
1066
1336
            else:
1086
1356
 
1087
1357
class cmd_version(Command):
1088
1358
    """Show version of bzr."""
 
1359
    @display_command
1089
1360
    def run(self):
1090
1361
        show_version()
1091
1362
 
1092
1363
class cmd_rocks(Command):
1093
1364
    """Statement of optimism."""
1094
1365
    hidden = True
 
1366
    @display_command
1095
1367
    def run(self):
1096
1368
        print "it sure does!"
1097
1369
 
1098
1370
 
1099
1371
class cmd_find_merge_base(Command):
1100
1372
    """Find and print a base revision for merging two branches.
1101
 
 
1102
 
    TODO: Options to specify revisions on either side, as if
1103
 
          merging only part of the history.
1104
1373
    """
 
1374
    # TODO: Options to specify revisions on either side, as if
 
1375
    #       merging only part of the history.
1105
1376
    takes_args = ['branch', 'other']
1106
1377
    hidden = True
1107
1378
    
 
1379
    @display_command
1108
1380
    def run(self, branch, other):
1109
1381
        from bzrlib.revision import common_ancestor, MultipleRevisionSources
1110
1382
        
1111
 
        branch1 = find_branch(branch)
1112
 
        branch2 = find_branch(other)
 
1383
        branch1 = Branch.open_containing(branch)[0]
 
1384
        branch2 = Branch.open_containing(other)[0]
1113
1385
 
1114
1386
        history_1 = branch1.revision_history()
1115
1387
        history_2 = branch2.revision_history()
1116
1388
 
1117
 
        last1 = branch1.last_patch()
1118
 
        last2 = branch2.last_patch()
 
1389
        last1 = branch1.last_revision()
 
1390
        last2 = branch2.last_revision()
1119
1391
 
1120
1392
        source = MultipleRevisionSources(branch1, branch2)
1121
1393
        
1164
1436
    --force is given.
1165
1437
    """
1166
1438
    takes_args = ['branch?']
1167
 
    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")]
1168
1442
 
1169
 
    def run(self, branch='.', revision=None, force=False, 
1170
 
            merge_type=None):
 
1443
    def run(self, branch=None, revision=None, force=False, merge_type=None,
 
1444
            show_base=False, reprocess=False):
1171
1445
        from bzrlib.merge import merge
1172
1446
        from bzrlib.merge_core import ApplyMerge3
1173
1447
        if merge_type is None:
1174
1448
            merge_type = ApplyMerge3
1175
 
 
 
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 
1176
1455
        if revision is None or len(revision) < 1:
1177
1456
            base = [None, None]
1178
1457
            other = [branch, -1]
1179
1458
        else:
1180
1459
            if len(revision) == 1:
1181
 
                other = [branch, revision[0]]
1182
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]
1183
1464
            else:
1184
1465
                assert len(revision) == 2
1185
1466
                if None in revision:
1186
1467
                    raise BzrCommandError(
1187
1468
                        "Merge doesn't permit that revision specifier.")
1188
 
                base = [branch, revision[0]]
1189
 
                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]
1190
1473
 
1191
1474
        try:
1192
 
            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
1193
1482
        except bzrlib.errors.AmbiguousBase, e:
1194
1483
            m = ("sorry, bzr can't determine the right merge base yet\n"
1195
1484
                 "candidates are:\n  "
1213
1502
 
1214
1503
    def run(self, revision=None, no_backup=False, file_list=None):
1215
1504
        from bzrlib.merge import merge
1216
 
        from bzrlib.branch import Branch
1217
1505
        from bzrlib.commands import parse_spec
1218
1506
 
1219
1507
        if file_list is not None:
1220
1508
            if len(file_list) == 0:
1221
1509
                raise BzrCommandError("No files specified")
1222
1510
        if revision is None:
1223
 
            revision = [-1]
 
1511
            revno = -1
1224
1512
        elif len(revision) != 1:
1225
1513
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
1226
 
        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('.'),
1227
1518
              check_clean=False,
1228
1519
              ignore_zero=True,
1229
1520
              backup_files=not no_backup,
1230
1521
              file_list=file_list)
1231
1522
        if not file_list:
1232
 
            Branch('.').set_pending_merges([])
 
1523
            Branch.open_containing('.')[0].set_pending_merges([])
1233
1524
 
1234
1525
 
1235
1526
class cmd_assert_fail(Command):
1247
1538
    takes_args = ['topic?']
1248
1539
    aliases = ['?']
1249
1540
    
 
1541
    @display_command
1250
1542
    def run(self, topic=None, long=False):
1251
1543
        import help
1252
1544
        if topic is None and long:
1262
1554
    aliases = ['s-c']
1263
1555
    hidden = True
1264
1556
    
 
1557
    @display_command
1265
1558
    def run(self, context=None):
1266
1559
        import shellcomplete
1267
1560
        shellcomplete.shellcomplete(context)
1268
1561
 
1269
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
 
1270
1585
class cmd_missing(Command):
1271
1586
    """What is missing in this branch relative to other branch.
1272
1587
    """
 
1588
    # TODO: rewrite this in terms of ancestry so that it shows only
 
1589
    # unmerged things
 
1590
    
1273
1591
    takes_args = ['remote?']
1274
1592
    aliases = ['mis', 'miss']
1275
1593
    # We don't have to add quiet to the list, because 
1276
1594
    # unknown options are parsed as booleans
1277
1595
    takes_options = ['verbose', 'quiet']
1278
1596
 
 
1597
    @display_command
1279
1598
    def run(self, remote=None, verbose=False, quiet=False):
1280
1599
        from bzrlib.errors import BzrCommandError
1281
1600
        from bzrlib.missing import show_missing
1283
1602
        if verbose and quiet:
1284
1603
            raise BzrCommandError('Cannot pass both quiet and verbose')
1285
1604
 
1286
 
        b = find_branch('.')
 
1605
        b = Branch.open_containing('.')[0]
1287
1606
        parent = b.get_parent()
1288
1607
        if remote is None:
1289
1608
            if parent is None:
1293
1612
                    print "Using last location: %s" % parent
1294
1613
                remote = parent
1295
1614
        elif parent is None:
1296
 
            # We only update parent if it did not exist, missing should not change the parent
 
1615
            # We only update parent if it did not exist, missing
 
1616
            # should not change the parent
1297
1617
            b.set_parent(remote)
1298
 
        br_remote = find_branch(remote)
1299
 
 
 
1618
        br_remote = Branch.open_containing(remote)[0]
1300
1619
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
1301
1620
 
1302
1621
 
1303
 
 
1304
1622
class cmd_plugins(Command):
1305
1623
    """List plugins"""
1306
1624
    hidden = True
 
1625
    @display_command
1307
1626
    def run(self):
1308
1627
        import bzrlib.plugin
1309
1628
        from inspect import getdoc
1320
1639
                print '\t', d.split('\n')[0]
1321
1640
 
1322
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