~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Robert Collins
  • Date: 2005-10-16 22:31:25 UTC
  • mto: This revision was merged to the branch mainline in revision 1458.
  • Revision ID: robertc@lifelesslap.robertcollins.net-20051016223125-26d4401cb94b7b82
Branch.relpath has been moved to WorkingTree.relpath.

WorkingTree no no longer takes an inventory, rather it takes an optional branch
parameter, and if None is given will open the branch at basedir implicitly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005 by Canonical Ltd
 
2
 
 
3
# This program is free software; you can redistribute it and/or modify
 
4
# it under the terms of the GNU General Public License as published by
 
5
# the Free Software Foundation; either version 2 of the License, or
 
6
# (at your option) any later version.
 
7
 
 
8
# This program is distributed in the hope that it will be useful,
 
9
# but WITHOUT ANY WARRANTY; without even the implied warranty of
 
10
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
11
# GNU General Public License for more details.
 
12
 
 
13
# You should have received a copy of the GNU General Public License
 
14
# along with this program; if not, write to the Free Software
 
15
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
16
 
 
17
 
 
18
import sys
 
19
import os
 
20
 
 
21
import bzrlib
 
22
import bzrlib.trace
 
23
from bzrlib.trace import mutter, note, log_error, warning
 
24
from bzrlib.errors import BzrError, BzrCheckError, BzrCommandError, NotBranchError
 
25
from bzrlib.errors import DivergedBranches
 
26
from bzrlib.branch import Branch
 
27
from bzrlib import BZRDIR
 
28
from bzrlib.commands import Command
 
29
from bzrlib.workingtree import WorkingTree
 
30
 
 
31
 
 
32
class cmd_status(Command):
 
33
    """Display status summary.
 
34
 
 
35
    This reports on versioned and unknown files, reporting them
 
36
    grouped by state.  Possible states are:
 
37
 
 
38
    added
 
39
        Versioned in the working copy but not in the previous revision.
 
40
 
 
41
    removed
 
42
        Versioned in the previous revision but removed or deleted
 
43
        in the working copy.
 
44
 
 
45
    renamed
 
46
        Path of this file changed from the previous revision;
 
47
        the text may also have changed.  This includes files whose
 
48
        parent directory was renamed.
 
49
 
 
50
    modified
 
51
        Text has changed since the previous revision.
 
52
 
 
53
    unchanged
 
54
        Nothing about this file has changed since the previous revision.
 
55
        Only shown with --all.
 
56
 
 
57
    unknown
 
58
        Not versioned and not matching an ignore pattern.
 
59
 
 
60
    To see ignored files use 'bzr ignored'.  For details in the
 
61
    changes to file texts, use 'bzr diff'.
 
62
 
 
63
    If no arguments are specified, the status of the entire working
 
64
    directory is shown.  Otherwise, only the status of the specified
 
65
    files or directories is reported.  If a directory is given, status
 
66
    is reported for everything inside that directory.
 
67
 
 
68
    If a revision argument is given, the status is calculated against
 
69
    that revision, or between two revisions if two are provided.
 
70
    """
 
71
    # XXX: FIXME: bzr status should accept a -r option to show changes
 
72
    # relative to a revision, or between revisions
 
73
 
 
74
    takes_args = ['file*']
 
75
    takes_options = ['all', 'show-ids']
 
76
    aliases = ['st', 'stat']
 
77
    
 
78
    def run(self, all=False, show_ids=False, file_list=None, revision=None):
 
79
        if file_list:
 
80
            b = Branch.open_containing(file_list[0])
 
81
            tree = WorkingTree(b.base, b)
 
82
            file_list = [tree.relpath(x) for x in file_list]
 
83
            # special case: only one path was given and it's the root
 
84
            # of the branch
 
85
            if file_list == ['']:
 
86
                file_list = None
 
87
        else:
 
88
            b = Branch.open_containing('.')
 
89
            
 
90
        from bzrlib.status import show_status
 
91
        show_status(b, show_unchanged=all, show_ids=show_ids,
 
92
                    specific_files=file_list, revision=revision)
 
93
 
 
94
 
 
95
class cmd_cat_revision(Command):
 
96
    """Write out metadata for a revision.
 
97
    
 
98
    The revision to print can either be specified by a specific
 
99
    revision identifier, or you can use --revision.
 
100
    """
 
101
 
 
102
    hidden = True
 
103
    takes_args = ['revision_id?']
 
104
    takes_options = ['revision']
 
105
    
 
106
    def run(self, revision_id=None, revision=None):
 
107
        from bzrlib.revisionspec import RevisionSpec
 
108
 
 
109
        if revision_id is not None and revision is not None:
 
110
            raise BzrCommandError('You can only supply one of revision_id or --revision')
 
111
        if revision_id is None and revision is None:
 
112
            raise BzrCommandError('You must supply either --revision or a revision_id')
 
113
        b = Branch.open_containing('.')
 
114
        if revision_id is not None:
 
115
            sys.stdout.write(b.get_revision_xml_file(revision_id).read())
 
116
        elif revision is not None:
 
117
            for rev in revision:
 
118
                if rev is None:
 
119
                    raise BzrCommandError('You cannot specify a NULL revision.')
 
120
                revno, rev_id = rev.in_history(b)
 
121
                sys.stdout.write(b.get_revision_xml_file(rev_id).read())
 
122
    
 
123
 
 
124
class cmd_revno(Command):
 
125
    """Show current revision number.
 
126
 
 
127
    This is equal to the number of revisions on this branch."""
 
128
    def run(self):
 
129
        print Branch.open_containing('.').revno()
 
130
 
 
131
 
 
132
class cmd_revision_info(Command):
 
133
    """Show revision number and revision id for a given revision identifier.
 
134
    """
 
135
    hidden = True
 
136
    takes_args = ['revision_info*']
 
137
    takes_options = ['revision']
 
138
    def run(self, revision=None, revision_info_list=[]):
 
139
        from bzrlib.revisionspec import RevisionSpec
 
140
 
 
141
        revs = []
 
142
        if revision is not None:
 
143
            revs.extend(revision)
 
144
        if revision_info_list is not None:
 
145
            for rev in revision_info_list:
 
146
                revs.append(RevisionSpec(rev))
 
147
        if len(revs) == 0:
 
148
            raise BzrCommandError('You must supply a revision identifier')
 
149
 
 
150
        b = Branch.open_containing('.')
 
151
 
 
152
        for rev in revs:
 
153
            revinfo = rev.in_history(b)
 
154
            if revinfo.revno is None:
 
155
                print '     %s' % revinfo.rev_id
 
156
            else:
 
157
                print '%4d %s' % (revinfo.revno, revinfo.rev_id)
 
158
 
 
159
    
 
160
class cmd_add(Command):
 
161
    """Add specified files or directories.
 
162
 
 
163
    In non-recursive mode, all the named items are added, regardless
 
164
    of whether they were previously ignored.  A warning is given if
 
165
    any of the named files are already versioned.
 
166
 
 
167
    In recursive mode (the default), files are treated the same way
 
168
    but the behaviour for directories is different.  Directories that
 
169
    are already versioned do not give a warning.  All directories,
 
170
    whether already versioned or not, are searched for files or
 
171
    subdirectories that are neither versioned or ignored, and these
 
172
    are added.  This search proceeds recursively into versioned
 
173
    directories.  If no names are given '.' is assumed.
 
174
 
 
175
    Therefore simply saying 'bzr add' will version all files that
 
176
    are currently unknown.
 
177
 
 
178
    Adding a file whose parent directory is not versioned will
 
179
    implicitly add the parent, and so on up to the root. This means
 
180
    you should never need to explictly add a directory, they'll just
 
181
    get added when you add a file in the directory.
 
182
    """
 
183
    takes_args = ['file*']
 
184
    takes_options = ['no-recurse', 'quiet']
 
185
    
 
186
    def run(self, file_list, no_recurse=False, quiet=False):
 
187
        from bzrlib.add import smart_add, add_reporter_print, add_reporter_null
 
188
        if quiet:
 
189
            reporter = add_reporter_null
 
190
        else:
 
191
            reporter = add_reporter_print
 
192
        smart_add(file_list, not no_recurse, reporter)
 
193
 
 
194
 
 
195
class cmd_mkdir(Command):
 
196
    """Create a new versioned directory.
 
197
 
 
198
    This is equivalent to creating the directory and then adding it.
 
199
    """
 
200
    takes_args = ['dir+']
 
201
 
 
202
    def run(self, dir_list):
 
203
        b = None
 
204
        
 
205
        for d in dir_list:
 
206
            os.mkdir(d)
 
207
            if not b:
 
208
                b = Branch.open_containing(d)
 
209
            b.add([d])
 
210
            print 'added', d
 
211
 
 
212
 
 
213
class cmd_relpath(Command):
 
214
    """Show path of a file relative to root"""
 
215
    takes_args = ['filename']
 
216
    hidden = True
 
217
    
 
218
    def run(self, filename):
 
219
        branch = Branch.open_containing(filename)
 
220
        print WorkingTree(branch.base, branch).relpath(filename)
 
221
 
 
222
 
 
223
class cmd_inventory(Command):
 
224
    """Show inventory of the current working copy or a revision."""
 
225
    takes_options = ['revision', 'show-ids']
 
226
    
 
227
    def run(self, revision=None, show_ids=False):
 
228
        b = Branch.open_containing('.')
 
229
        if revision is None:
 
230
            inv = b.read_working_inventory()
 
231
        else:
 
232
            if len(revision) > 1:
 
233
                raise BzrCommandError('bzr inventory --revision takes'
 
234
                    ' exactly one revision identifier')
 
235
            inv = b.get_revision_inventory(revision[0].in_history(b).rev_id)
 
236
 
 
237
        for path, entry in inv.entries():
 
238
            if show_ids:
 
239
                print '%-50s %s' % (path, entry.file_id)
 
240
            else:
 
241
                print path
 
242
 
 
243
 
 
244
class cmd_move(Command):
 
245
    """Move files to a different directory.
 
246
 
 
247
    examples:
 
248
        bzr move *.txt doc
 
249
 
 
250
    The destination must be a versioned directory in the same branch.
 
251
    """
 
252
    takes_args = ['source$', 'dest']
 
253
    def run(self, source_list, dest):
 
254
        b = Branch.open_containing('.')
 
255
 
 
256
        # TODO: glob expansion on windows?
 
257
        tree = WorkingTree(b.base, b)
 
258
        b.move([tree.relpath(s) for s in source_list], tree.relpath(dest))
 
259
 
 
260
 
 
261
class cmd_rename(Command):
 
262
    """Change the name of an entry.
 
263
 
 
264
    examples:
 
265
      bzr rename frob.c frobber.c
 
266
      bzr rename src/frob.c lib/frob.c
 
267
 
 
268
    It is an error if the destination name exists.
 
269
 
 
270
    See also the 'move' command, which moves files into a different
 
271
    directory without changing their name.
 
272
    """
 
273
    # TODO: Some way to rename multiple files without invoking 
 
274
    # bzr for each one?"""
 
275
    takes_args = ['from_name', 'to_name']
 
276
    
 
277
    def run(self, from_name, to_name):
 
278
        b = Branch.open_containing('.')
 
279
        tree = WorkingTree(b.base, b)
 
280
        b.rename_one(tree.relpath(from_name), tree.relpath(to_name))
 
281
 
 
282
 
 
283
class cmd_mv(Command):
 
284
    """Move or rename a file.
 
285
 
 
286
    usage:
 
287
        bzr mv OLDNAME NEWNAME
 
288
        bzr mv SOURCE... DESTINATION
 
289
 
 
290
    If the last argument is a versioned directory, all the other names
 
291
    are moved into it.  Otherwise, there must be exactly two arguments
 
292
    and the file is changed to a new name, which must not already exist.
 
293
 
 
294
    Files cannot be moved between branches.
 
295
    """
 
296
    takes_args = ['names*']
 
297
    def run(self, names_list):
 
298
        if len(names_list) < 2:
 
299
            raise BzrCommandError("missing file argument")
 
300
        b = Branch.open_containing(names_list[0])
 
301
        tree = WorkingTree(b.base, b)
 
302
        rel_names = [tree.relpath(x) for x in names_list]
 
303
        
 
304
        if os.path.isdir(names_list[-1]):
 
305
            # move into existing directory
 
306
            for pair in b.move(rel_names[:-1], rel_names[-1]):
 
307
                print "%s => %s" % pair
 
308
        else:
 
309
            if len(names_list) != 2:
 
310
                raise BzrCommandError('to mv multiple files the destination '
 
311
                                      'must be a versioned directory')
 
312
            b.rename_one(rel_names[0], rel_names[1])
 
313
            print "%s => %s" % (rel_names[0], rel_names[1])
 
314
            
 
315
    
 
316
 
 
317
 
 
318
class cmd_pull(Command):
 
319
    """Pull any changes from another branch into the current one.
 
320
 
 
321
    If the location is omitted, the last-used location will be used.
 
322
    Both the revision history and the working directory will be
 
323
    updated.
 
324
 
 
325
    This command only works on branches that have not diverged.  Branches are
 
326
    considered diverged if both branches have had commits without first
 
327
    pulling from the other.
 
328
 
 
329
    If branches have diverged, you can use 'bzr merge' to pull the text changes
 
330
    from one into the other.
 
331
    """
 
332
    takes_options = ['remember']
 
333
    takes_args = ['location?']
 
334
 
 
335
    def run(self, location=None, remember=False):
 
336
        from bzrlib.merge import merge
 
337
        import tempfile
 
338
        from shutil import rmtree
 
339
        import errno
 
340
        
 
341
        br_to = Branch.open_containing('.')
 
342
        stored_loc = br_to.get_parent()
 
343
        if location is None:
 
344
            if stored_loc is None:
 
345
                raise BzrCommandError("No pull location known or specified.")
 
346
            else:
 
347
                print "Using saved location: %s" % stored_loc
 
348
                location = stored_loc
 
349
        cache_root = tempfile.mkdtemp()
 
350
        br_from = Branch.open(location)
 
351
        br_from.lock_read()
 
352
        try:
 
353
            br_from.setup_caching(cache_root)
 
354
            location = br_from.base
 
355
            old_revno = br_to.revno()
 
356
            old_revision_history = br_to.revision_history()
 
357
            try:
 
358
                br_to.update_revisions(br_from)
 
359
            except DivergedBranches:
 
360
                raise BzrCommandError("These branches have diverged."
 
361
                    "  Try merge.")
 
362
            new_revision_history = br_to.revision_history()
 
363
            if new_revision_history != old_revision_history:
 
364
                merge(('.', -1), ('.', old_revno), check_clean=False)
 
365
            if stored_loc is None or remember:
 
366
                br_to.set_parent(location)
 
367
        finally:
 
368
            br_from.unlock()
 
369
            rmtree(cache_root)
 
370
 
 
371
 
 
372
 
 
373
class cmd_branch(Command):
 
374
    """Create a new copy of a branch.
 
375
 
 
376
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
 
377
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
 
378
 
 
379
    To retrieve the branch as of a particular revision, supply the --revision
 
380
    parameter, as in "branch foo/bar -r 5".
 
381
 
 
382
    --basis is to speed up branching from remote branches.  When specified, it
 
383
    copies all the file-contents, inventory and revision data from the basis
 
384
    branch before copying anything from the remote branch.
 
385
    """
 
386
    takes_args = ['from_location', 'to_location?']
 
387
    takes_options = ['revision', 'basis']
 
388
    aliases = ['get', 'clone']
 
389
 
 
390
    def run(self, from_location, to_location=None, revision=None, basis=None):
 
391
        from bzrlib.clone import copy_branch
 
392
        import tempfile
 
393
        import errno
 
394
        from shutil import rmtree
 
395
        cache_root = tempfile.mkdtemp()
 
396
        if revision is None:
 
397
            revision = [None]
 
398
        elif len(revision) > 1:
 
399
            raise BzrCommandError(
 
400
                'bzr branch --revision takes exactly 1 revision value')
 
401
        try:
 
402
            br_from = Branch.open(from_location)
 
403
        except OSError, e:
 
404
            if e.errno == errno.ENOENT:
 
405
                raise BzrCommandError('Source location "%s" does not'
 
406
                                      ' exist.' % to_location)
 
407
            else:
 
408
                raise
 
409
        br_from.lock_read()
 
410
        try:
 
411
            br_from.setup_caching(cache_root)
 
412
            if basis is not None:
 
413
                basis_branch = Branch.open_containing(basis)
 
414
            else:
 
415
                basis_branch = None
 
416
            if len(revision) == 1 and revision[0] is not None:
 
417
                revision_id = revision[0].in_history(br_from)[1]
 
418
            else:
 
419
                revision_id = None
 
420
            if to_location is None:
 
421
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
422
            try:
 
423
                os.mkdir(to_location)
 
424
            except OSError, e:
 
425
                if e.errno == errno.EEXIST:
 
426
                    raise BzrCommandError('Target directory "%s" already'
 
427
                                          ' exists.' % to_location)
 
428
                if e.errno == errno.ENOENT:
 
429
                    raise BzrCommandError('Parent of "%s" does not exist.' %
 
430
                                          to_location)
 
431
                else:
 
432
                    raise
 
433
            try:
 
434
                copy_branch(br_from, to_location, revision_id, basis_branch)
 
435
            except bzrlib.errors.NoSuchRevision:
 
436
                rmtree(to_location)
 
437
                msg = "The branch %s has no revision %d." % (from_location, revision[0])
 
438
                raise BzrCommandError(msg)
 
439
            except bzrlib.errors.UnlistableBranch:
 
440
                msg = "The branch %s cannot be used as a --basis"
 
441
        finally:
 
442
            br_from.unlock()
 
443
            rmtree(cache_root)
 
444
 
 
445
 
 
446
class cmd_renames(Command):
 
447
    """Show list of renamed files.
 
448
    """
 
449
    # TODO: Option to show renames between two historical versions.
 
450
 
 
451
    # TODO: Only show renames under dir, rather than in the whole branch.
 
452
    takes_args = ['dir?']
 
453
 
 
454
    def run(self, dir='.'):
 
455
        b = Branch.open_containing(dir)
 
456
        old_inv = b.basis_tree().inventory
 
457
        new_inv = b.read_working_inventory()
 
458
 
 
459
        renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
 
460
        renames.sort()
 
461
        for old_name, new_name in renames:
 
462
            print "%s => %s" % (old_name, new_name)        
 
463
 
 
464
 
 
465
class cmd_info(Command):
 
466
    """Show statistical information about a branch."""
 
467
    takes_args = ['branch?']
 
468
    
 
469
    def run(self, branch=None):
 
470
        import info
 
471
        b = Branch.open_containing(branch)
 
472
        info.show_info(b)
 
473
 
 
474
 
 
475
class cmd_remove(Command):
 
476
    """Make a file unversioned.
 
477
 
 
478
    This makes bzr stop tracking changes to a versioned file.  It does
 
479
    not delete the working copy.
 
480
    """
 
481
    takes_args = ['file+']
 
482
    takes_options = ['verbose']
 
483
    aliases = ['rm']
 
484
    
 
485
    def run(self, file_list, verbose=False):
 
486
        b = Branch.open_containing(file_list[0])
 
487
        tree = WorkingTree(b.base, b)
 
488
        b.remove([tree.relpath(f) for f in file_list], verbose=verbose)
 
489
 
 
490
 
 
491
class cmd_file_id(Command):
 
492
    """Print file_id of a particular file or directory.
 
493
 
 
494
    The file_id is assigned when the file is first added and remains the
 
495
    same through all revisions where the file exists, even when it is
 
496
    moved or renamed.
 
497
    """
 
498
    hidden = True
 
499
    takes_args = ['filename']
 
500
    def run(self, filename):
 
501
        b = Branch.open_containing(filename)
 
502
        tree = WorkingTree(b.base, b)
 
503
        i = b.inventory.path2id(tree.relpath(filename))
 
504
        if i == None:
 
505
            raise BzrError("%r is not a versioned file" % filename)
 
506
        else:
 
507
            print i
 
508
 
 
509
 
 
510
class cmd_file_path(Command):
 
511
    """Print path of file_ids to a file or directory.
 
512
 
 
513
    This prints one line for each directory down to the target,
 
514
    starting at the branch root."""
 
515
    hidden = True
 
516
    takes_args = ['filename']
 
517
    def run(self, filename):
 
518
        b = Branch.open_containing(filename)
 
519
        inv = b.inventory
 
520
        tree = WorkingTree(b.base, b)
 
521
        fid = inv.path2id(tree.relpath(filename))
 
522
        if fid == None:
 
523
            raise BzrError("%r is not a versioned file" % filename)
 
524
        for fip in inv.get_idpath(fid):
 
525
            print fip
 
526
 
 
527
 
 
528
class cmd_revision_history(Command):
 
529
    """Display list of revision ids on this branch."""
 
530
    hidden = True
 
531
    def run(self):
 
532
        for patchid in Branch.open_containing('.').revision_history():
 
533
            print patchid
 
534
 
 
535
 
 
536
class cmd_ancestry(Command):
 
537
    """List all revisions merged into this branch."""
 
538
    hidden = True
 
539
    def run(self):
 
540
        b = find_branch('.')
 
541
        for revision_id in b.get_ancestry(b.last_revision()):
 
542
            print revision_id
 
543
 
 
544
 
 
545
class cmd_directories(Command):
 
546
    """Display list of versioned directories in this branch."""
 
547
    def run(self):
 
548
        for name, ie in Branch.open_containing('.').read_working_inventory().directories():
 
549
            if name == '':
 
550
                print '.'
 
551
            else:
 
552
                print name
 
553
 
 
554
 
 
555
class cmd_init(Command):
 
556
    """Make a directory into a versioned branch.
 
557
 
 
558
    Use this to create an empty branch, or before importing an
 
559
    existing project.
 
560
 
 
561
    Recipe for importing a tree of files:
 
562
        cd ~/project
 
563
        bzr init
 
564
        bzr add -v .
 
565
        bzr status
 
566
        bzr commit -m 'imported project'
 
567
    """
 
568
    def run(self):
 
569
        Branch.initialize('.')
 
570
 
 
571
 
 
572
class cmd_diff(Command):
 
573
    """Show differences in working tree.
 
574
    
 
575
    If files are listed, only the changes in those files are listed.
 
576
    Otherwise, all changes for the tree are listed.
 
577
 
 
578
    examples:
 
579
        bzr diff
 
580
        bzr diff -r1
 
581
        bzr diff -r1..2
 
582
    """
 
583
    # TODO: Allow diff across branches.
 
584
    # TODO: Option to use external diff command; could be GNU diff, wdiff,
 
585
    #       or a graphical diff.
 
586
 
 
587
    # TODO: Python difflib is not exactly the same as unidiff; should
 
588
    #       either fix it up or prefer to use an external diff.
 
589
 
 
590
    # TODO: If a directory is given, diff everything under that.
 
591
 
 
592
    # TODO: Selected-file diff is inefficient and doesn't show you
 
593
    #       deleted files.
 
594
 
 
595
    # TODO: This probably handles non-Unix newlines poorly.
 
596
    
 
597
    takes_args = ['file*']
 
598
    takes_options = ['revision', 'diff-options']
 
599
    aliases = ['di', 'dif']
 
600
 
 
601
    def run(self, revision=None, file_list=None, diff_options=None):
 
602
        from bzrlib.diff import show_diff
 
603
 
 
604
        if file_list:
 
605
            b = Branch.open_containing(file_list[0])
 
606
            tree = WorkingTree(b.base, b)
 
607
            file_list = [tree.relpath(f) for f in file_list]
 
608
            if file_list == ['']:
 
609
                # just pointing to top-of-tree
 
610
                file_list = None
 
611
        else:
 
612
            b = Branch.open_containing('.')
 
613
 
 
614
        if revision is not None:
 
615
            if len(revision) == 1:
 
616
                show_diff(b, revision[0], specific_files=file_list,
 
617
                          external_diff_options=diff_options)
 
618
            elif len(revision) == 2:
 
619
                show_diff(b, revision[0], specific_files=file_list,
 
620
                          external_diff_options=diff_options,
 
621
                          revision2=revision[1])
 
622
            else:
 
623
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
 
624
        else:
 
625
            show_diff(b, None, specific_files=file_list,
 
626
                      external_diff_options=diff_options)
 
627
 
 
628
        
 
629
 
 
630
 
 
631
class cmd_deleted(Command):
 
632
    """List files deleted in the working tree.
 
633
    """
 
634
    # TODO: Show files deleted since a previous revision, or
 
635
    # between two revisions.
 
636
    # TODO: Much more efficient way to do this: read in new
 
637
    # directories with readdir, rather than stating each one.  Same
 
638
    # level of effort but possibly much less IO.  (Or possibly not,
 
639
    # if the directories are very large...)
 
640
    def run(self, show_ids=False):
 
641
        b = Branch.open_containing('.')
 
642
        old = b.basis_tree()
 
643
        new = b.working_tree()
 
644
        for path, ie in old.inventory.iter_entries():
 
645
            if not new.has_id(ie.file_id):
 
646
                if show_ids:
 
647
                    print '%-50s %s' % (path, ie.file_id)
 
648
                else:
 
649
                    print path
 
650
 
 
651
 
 
652
class cmd_modified(Command):
 
653
    """List files modified in working tree."""
 
654
    hidden = True
 
655
    def run(self):
 
656
        from bzrlib.delta import compare_trees
 
657
 
 
658
        b = Branch.open_containing('.')
 
659
        td = compare_trees(b.basis_tree(), b.working_tree())
 
660
 
 
661
        for path, id, kind, text_modified, meta_modified in td.modified:
 
662
            print path
 
663
 
 
664
 
 
665
 
 
666
class cmd_added(Command):
 
667
    """List files added in working tree."""
 
668
    hidden = True
 
669
    def run(self):
 
670
        b = Branch.open_containing('.')
 
671
        wt = b.working_tree()
 
672
        basis_inv = b.basis_tree().inventory
 
673
        inv = wt.inventory
 
674
        for file_id in inv:
 
675
            if file_id in basis_inv:
 
676
                continue
 
677
            path = inv.id2path(file_id)
 
678
            if not os.access(b.abspath(path), os.F_OK):
 
679
                continue
 
680
            print path
 
681
                
 
682
        
 
683
 
 
684
class cmd_root(Command):
 
685
    """Show the tree root directory.
 
686
 
 
687
    The root is the nearest enclosing directory with a .bzr control
 
688
    directory."""
 
689
    takes_args = ['filename?']
 
690
    def run(self, filename=None):
 
691
        """Print the branch root."""
 
692
        b = Branch.open_containing(filename)
 
693
        print b.base
 
694
 
 
695
 
 
696
class cmd_log(Command):
 
697
    """Show log of this branch.
 
698
 
 
699
    To request a range of logs, you can use the command -r begin:end
 
700
    -r revision requests a specific revision, -r :end or -r begin: are
 
701
    also valid.
 
702
 
 
703
    --message allows you to give a regular expression, which will be evaluated
 
704
    so that only matching entries will be displayed.
 
705
    """
 
706
 
 
707
    # TODO: Make --revision support uuid: and hash: [future tag:] notation.
 
708
 
 
709
    takes_args = ['filename?']
 
710
    takes_options = ['forward', 'timezone', 'verbose', 'show-ids', 'revision',
 
711
                     'long', 'message', 'short',]
 
712
    
 
713
    def run(self, filename=None, timezone='original',
 
714
            verbose=False,
 
715
            show_ids=False,
 
716
            forward=False,
 
717
            revision=None,
 
718
            message=None,
 
719
            long=False,
 
720
            short=False):
 
721
        from bzrlib.log import log_formatter, show_log
 
722
        import codecs
 
723
 
 
724
        direction = (forward and 'forward') or 'reverse'
 
725
        
 
726
        if filename:
 
727
            b = Branch.open_containing(filename)
 
728
            tree = WorkingTree(b.base, b)
 
729
            fp = tree.relpath(filename)
 
730
            if fp:
 
731
                file_id = b.read_working_inventory().path2id(fp)
 
732
            else:
 
733
                file_id = None  # points to branch root
 
734
        else:
 
735
            b = Branch.open_containing('.')
 
736
            file_id = None
 
737
 
 
738
        if revision is None:
 
739
            rev1 = None
 
740
            rev2 = None
 
741
        elif len(revision) == 1:
 
742
            rev1 = rev2 = revision[0].in_history(b).revno
 
743
        elif len(revision) == 2:
 
744
            rev1 = revision[0].in_history(b).revno
 
745
            rev2 = revision[1].in_history(b).revno
 
746
        else:
 
747
            raise BzrCommandError('bzr log --revision takes one or two values.')
 
748
 
 
749
        if rev1 == 0:
 
750
            rev1 = None
 
751
        if rev2 == 0:
 
752
            rev2 = None
 
753
 
 
754
        mutter('encoding log as %r' % bzrlib.user_encoding)
 
755
 
 
756
        # use 'replace' so that we don't abort if trying to write out
 
757
        # in e.g. the default C locale.
 
758
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
 
759
 
 
760
        if not short:
 
761
            log_format = 'long'
 
762
        else:
 
763
            log_format = 'short'
 
764
        lf = log_formatter(log_format,
 
765
                           show_ids=show_ids,
 
766
                           to_file=outf,
 
767
                           show_timezone=timezone)
 
768
 
 
769
        show_log(b,
 
770
                 lf,
 
771
                 file_id,
 
772
                 verbose=verbose,
 
773
                 direction=direction,
 
774
                 start_revision=rev1,
 
775
                 end_revision=rev2,
 
776
                 search=message)
 
777
 
 
778
 
 
779
 
 
780
class cmd_touching_revisions(Command):
 
781
    """Return revision-ids which affected a particular file.
 
782
 
 
783
    A more user-friendly interface is "bzr log FILE"."""
 
784
    hidden = True
 
785
    takes_args = ["filename"]
 
786
    def run(self, filename):
 
787
        b = Branch.open_containing(filename)
 
788
        inv = b.read_working_inventory()
 
789
        tree = WorkingTree(b.base, b)
 
790
        file_id = inv.path2id(tree.relpath(filename))
 
791
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
 
792
            print "%6d %s" % (revno, what)
 
793
 
 
794
 
 
795
class cmd_ls(Command):
 
796
    """List files in a tree.
 
797
    """
 
798
    # TODO: Take a revision or remote path and list that tree instead.
 
799
    hidden = True
 
800
    def run(self, revision=None, verbose=False):
 
801
        b = Branch.open_containing('.')
 
802
        if revision == None:
 
803
            tree = b.working_tree()
 
804
        else:
 
805
            tree = b.revision_tree(revision.in_history(b).rev_id)
 
806
        for fp, fc, kind, fid, entry in tree.list_files():
 
807
            if verbose:
 
808
                kindch = entry.kind_character()
 
809
                print '%-8s %s%s' % (fc, fp, kindch)
 
810
            else:
 
811
                print fp
 
812
 
 
813
 
 
814
 
 
815
class cmd_unknowns(Command):
 
816
    """List unknown files."""
 
817
    def run(self):
 
818
        from bzrlib.osutils import quotefn
 
819
        for f in Branch.open_containing('.').unknowns():
 
820
            print quotefn(f)
 
821
 
 
822
 
 
823
 
 
824
class cmd_ignore(Command):
 
825
    """Ignore a command or pattern.
 
826
 
 
827
    To remove patterns from the ignore list, edit the .bzrignore file.
 
828
 
 
829
    If the pattern contains a slash, it is compared to the whole path
 
830
    from the branch root.  Otherwise, it is compared to only the last
 
831
    component of the path.  To match a file only in the root directory,
 
832
    prepend './'.
 
833
 
 
834
    Ignore patterns are case-insensitive on case-insensitive systems.
 
835
 
 
836
    Note: wildcards must be quoted from the shell on Unix.
 
837
 
 
838
    examples:
 
839
        bzr ignore ./Makefile
 
840
        bzr ignore '*.class'
 
841
    """
 
842
    # TODO: Complain if the filename is absolute
 
843
    takes_args = ['name_pattern']
 
844
    
 
845
    def run(self, name_pattern):
 
846
        from bzrlib.atomicfile import AtomicFile
 
847
        import os.path
 
848
 
 
849
        b = Branch.open_containing('.')
 
850
        ifn = b.abspath('.bzrignore')
 
851
 
 
852
        if os.path.exists(ifn):
 
853
            f = open(ifn, 'rt')
 
854
            try:
 
855
                igns = f.read().decode('utf-8')
 
856
            finally:
 
857
                f.close()
 
858
        else:
 
859
            igns = ''
 
860
 
 
861
        # TODO: If the file already uses crlf-style termination, maybe
 
862
        # we should use that for the newly added lines?
 
863
 
 
864
        if igns and igns[-1] != '\n':
 
865
            igns += '\n'
 
866
        igns += name_pattern + '\n'
 
867
 
 
868
        try:
 
869
            f = AtomicFile(ifn, 'wt')
 
870
            f.write(igns.encode('utf-8'))
 
871
            f.commit()
 
872
        finally:
 
873
            f.close()
 
874
 
 
875
        inv = b.working_tree().inventory
 
876
        if inv.path2id('.bzrignore'):
 
877
            mutter('.bzrignore is already versioned')
 
878
        else:
 
879
            mutter('need to make new .bzrignore file versioned')
 
880
            b.add(['.bzrignore'])
 
881
 
 
882
 
 
883
 
 
884
class cmd_ignored(Command):
 
885
    """List ignored files and the patterns that matched them.
 
886
 
 
887
    See also: bzr ignore"""
 
888
    def run(self):
 
889
        tree = Branch.open_containing('.').working_tree()
 
890
        for path, file_class, kind, file_id, entry in tree.list_files():
 
891
            if file_class != 'I':
 
892
                continue
 
893
            ## XXX: Slightly inefficient since this was already calculated
 
894
            pat = tree.is_ignored(path)
 
895
            print '%-50s %s' % (path, pat)
 
896
 
 
897
 
 
898
class cmd_lookup_revision(Command):
 
899
    """Lookup the revision-id from a revision-number
 
900
 
 
901
    example:
 
902
        bzr lookup-revision 33
 
903
    """
 
904
    hidden = True
 
905
    takes_args = ['revno']
 
906
    
 
907
    def run(self, revno):
 
908
        try:
 
909
            revno = int(revno)
 
910
        except ValueError:
 
911
            raise BzrCommandError("not a valid revision-number: %r" % revno)
 
912
 
 
913
        print Branch.open_containing('.').get_rev_id(revno)
 
914
 
 
915
 
 
916
class cmd_export(Command):
 
917
    """Export past revision to destination directory.
 
918
 
 
919
    If no revision is specified this exports the last committed revision.
 
920
 
 
921
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
 
922
    given, try to find the format with the extension. If no extension
 
923
    is found exports to a directory (equivalent to --format=dir).
 
924
 
 
925
    Root may be the top directory for tar, tgz and tbz2 formats. If none
 
926
    is given, the top directory will be the root name of the file."""
 
927
    # TODO: list known exporters
 
928
    takes_args = ['dest']
 
929
    takes_options = ['revision', 'format', 'root']
 
930
    def run(self, dest, revision=None, format=None, root=None):
 
931
        import os.path
 
932
        b = Branch.open_containing('.')
 
933
        if revision is None:
 
934
            rev_id = b.last_revision()
 
935
        else:
 
936
            if len(revision) != 1:
 
937
                raise BzrError('bzr export --revision takes exactly 1 argument')
 
938
            rev_id = revision[0].in_history(b).rev_id
 
939
        t = b.revision_tree(rev_id)
 
940
        arg_root, ext = os.path.splitext(os.path.basename(dest))
 
941
        if ext in ('.gz', '.bz2'):
 
942
            new_root, new_ext = os.path.splitext(arg_root)
 
943
            if new_ext == '.tar':
 
944
                arg_root = new_root
 
945
                ext = new_ext + ext
 
946
        if root is None:
 
947
            root = arg_root
 
948
        if not format:
 
949
            if ext in (".tar",):
 
950
                format = "tar"
 
951
            elif ext in (".tar.gz", ".tgz"):
 
952
                format = "tgz"
 
953
            elif ext in (".tar.bz2", ".tbz2"):
 
954
                format = "tbz2"
 
955
            else:
 
956
                format = "dir"
 
957
        t.export(dest, format, root)
 
958
 
 
959
 
 
960
class cmd_cat(Command):
 
961
    """Write a file's text from a previous revision."""
 
962
 
 
963
    takes_options = ['revision']
 
964
    takes_args = ['filename']
 
965
 
 
966
    def run(self, filename, revision=None):
 
967
        if revision is None:
 
968
            raise BzrCommandError("bzr cat requires a revision number")
 
969
        elif len(revision) != 1:
 
970
            raise BzrCommandError("bzr cat --revision takes exactly one number")
 
971
        b = Branch.open_containing('.')
 
972
        tree = WorkingTree(b.base, b)
 
973
        b.print_file(tree.relpath(filename), revision[0].in_history(b).revno)
 
974
 
 
975
 
 
976
class cmd_local_time_offset(Command):
 
977
    """Show the offset in seconds from GMT to local time."""
 
978
    hidden = True    
 
979
    def run(self):
 
980
        print bzrlib.osutils.local_time_offset()
 
981
 
 
982
 
 
983
 
 
984
class cmd_commit(Command):
 
985
    """Commit changes into a new revision.
 
986
    
 
987
    If no arguments are given, the entire tree is committed.
 
988
 
 
989
    If selected files are specified, only changes to those files are
 
990
    committed.  If a directory is specified then the directory and everything 
 
991
    within it is committed.
 
992
 
 
993
    A selected-file commit may fail in some cases where the committed
 
994
    tree would be invalid, such as trying to commit a file in a
 
995
    newly-added directory that is not itself committed.
 
996
    """
 
997
    # TODO: Run hooks on tree to-be-committed, and after commit.
 
998
 
 
999
    # TODO: Strict commit that fails if there are unknown or deleted files.
 
1000
    # TODO: Give better message for -s, --summary, used by tla people
 
1001
 
 
1002
    # XXX: verbose currently does nothing
 
1003
 
 
1004
    takes_args = ['selected*']
 
1005
    takes_options = ['message', 'file', 'verbose', 'unchanged']
 
1006
    aliases = ['ci', 'checkin']
 
1007
 
 
1008
    def run(self, message=None, file=None, verbose=True, selected_list=None,
 
1009
            unchanged=False):
 
1010
        from bzrlib.errors import PointlessCommit, ConflictsInTree
 
1011
        from bzrlib.msgeditor import edit_commit_message
 
1012
        from bzrlib.status import show_status
 
1013
        from cStringIO import StringIO
 
1014
 
 
1015
        b = Branch.open_containing('.')
 
1016
        tree = WorkingTree(b.base, b)
 
1017
        if selected_list:
 
1018
            selected_list = [tree.relpath(s) for s in selected_list]
 
1019
            
 
1020
        if message is None and not file:
 
1021
            catcher = StringIO()
 
1022
            show_status(b, specific_files=selected_list,
 
1023
                        to_file=catcher)
 
1024
            message = edit_commit_message(catcher.getvalue())
 
1025
 
 
1026
            if message is None:
 
1027
                raise BzrCommandError("please specify a commit message"
 
1028
                                      " with either --message or --file")
 
1029
        elif message and file:
 
1030
            raise BzrCommandError("please specify either --message or --file")
 
1031
        
 
1032
        if file:
 
1033
            import codecs
 
1034
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
 
1035
 
 
1036
        if message == "":
 
1037
                raise BzrCommandError("empty commit message specified")
 
1038
            
 
1039
        try:
 
1040
            b.commit(message,
 
1041
                     specific_files=selected_list,
 
1042
                     allow_pointless=unchanged)
 
1043
        except PointlessCommit:
 
1044
            # FIXME: This should really happen before the file is read in;
 
1045
            # perhaps prepare the commit; get the message; then actually commit
 
1046
            raise BzrCommandError("no changes to commit",
 
1047
                                  ["use --unchanged to commit anyhow"])
 
1048
        except ConflictsInTree:
 
1049
            raise BzrCommandError("Conflicts detected in working tree.  "
 
1050
                'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
 
1051
 
 
1052
 
 
1053
class cmd_check(Command):
 
1054
    """Validate consistency of branch history.
 
1055
 
 
1056
    This command checks various invariants about the branch storage to
 
1057
    detect data corruption or bzr bugs.
 
1058
    """
 
1059
    takes_args = ['dir?']
 
1060
    takes_options = ['verbose']
 
1061
 
 
1062
    def run(self, dir='.', verbose=False):
 
1063
        from bzrlib.check import check
 
1064
        check(Branch.open_containing(dir), verbose)
 
1065
 
 
1066
 
 
1067
class cmd_scan_cache(Command):
 
1068
    hidden = True
 
1069
    def run(self):
 
1070
        from bzrlib.hashcache import HashCache
 
1071
 
 
1072
        c = HashCache('.')
 
1073
        c.read()
 
1074
        c.scan()
 
1075
            
 
1076
        print '%6d stats' % c.stat_count
 
1077
        print '%6d in hashcache' % len(c._cache)
 
1078
        print '%6d files removed from cache' % c.removed_count
 
1079
        print '%6d hashes updated' % c.update_count
 
1080
        print '%6d files changed too recently to cache' % c.danger_count
 
1081
 
 
1082
        if c.needs_write:
 
1083
            c.write()
 
1084
            
 
1085
 
 
1086
 
 
1087
class cmd_upgrade(Command):
 
1088
    """Upgrade branch storage to current format.
 
1089
 
 
1090
    The check command or bzr developers may sometimes advise you to run
 
1091
    this command.
 
1092
 
 
1093
    This version of this command upgrades from the full-text storage
 
1094
    used by bzr 0.0.8 and earlier to the weave format (v5).
 
1095
    """
 
1096
    takes_args = ['dir?']
 
1097
 
 
1098
    def run(self, dir='.'):
 
1099
        from bzrlib.upgrade import upgrade
 
1100
        upgrade(dir)
 
1101
 
 
1102
 
 
1103
class cmd_whoami(Command):
 
1104
    """Show bzr user id."""
 
1105
    takes_options = ['email']
 
1106
    
 
1107
    def run(self, email=False):
 
1108
        try:
 
1109
            b = bzrlib.branch.Branch.open_containing('.')
 
1110
            config = bzrlib.config.BranchConfig(b)
 
1111
        except NotBranchError:
 
1112
            config = bzrlib.config.GlobalConfig()
 
1113
        
 
1114
        if email:
 
1115
            print config.user_email()
 
1116
        else:
 
1117
            print config.username()
 
1118
 
 
1119
 
 
1120
class cmd_selftest(Command):
 
1121
    """Run internal test suite.
 
1122
    
 
1123
    This creates temporary test directories in the working directory,
 
1124
    but not existing data is affected.  These directories are deleted
 
1125
    if the tests pass, or left behind to help in debugging if they
 
1126
    fail.
 
1127
    
 
1128
    If arguments are given, they are regular expressions that say
 
1129
    which tests should run."""
 
1130
    # TODO: --list should give a list of all available tests
 
1131
    hidden = True
 
1132
    takes_args = ['testspecs*']
 
1133
    takes_options = ['verbose']
 
1134
    def run(self, testspecs_list=None, verbose=False):
 
1135
        import bzrlib.ui
 
1136
        from bzrlib.selftest import selftest
 
1137
        # we don't want progress meters from the tests to go to the
 
1138
        # real output; and we don't want log messages cluttering up
 
1139
        # the real logs.
 
1140
        save_ui = bzrlib.ui.ui_factory
 
1141
        bzrlib.trace.info('running tests...')
 
1142
        try:
 
1143
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
 
1144
            if testspecs_list is not None:
 
1145
                pattern = '|'.join(testspecs_list)
 
1146
            else:
 
1147
                pattern = ".*"
 
1148
            result = selftest(verbose=verbose, 
 
1149
                              pattern=pattern)
 
1150
            if result:
 
1151
                bzrlib.trace.info('tests passed')
 
1152
            else:
 
1153
                bzrlib.trace.info('tests failed')
 
1154
            return int(not result)
 
1155
        finally:
 
1156
            bzrlib.ui.ui_factory = save_ui
 
1157
 
 
1158
 
 
1159
def show_version():
 
1160
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
 
1161
    # is bzrlib itself in a branch?
 
1162
    bzrrev = bzrlib.get_bzr_revision()
 
1163
    if bzrrev:
 
1164
        print "  (bzr checkout, revision %d {%s})" % bzrrev
 
1165
    print bzrlib.__copyright__
 
1166
    print "http://bazaar-ng.org/"
 
1167
    print
 
1168
    print "bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and"
 
1169
    print "you may use, modify and redistribute it under the terms of the GNU"
 
1170
    print "General Public License version 2 or later."
 
1171
 
 
1172
 
 
1173
class cmd_version(Command):
 
1174
    """Show version of bzr."""
 
1175
    def run(self):
 
1176
        show_version()
 
1177
 
 
1178
class cmd_rocks(Command):
 
1179
    """Statement of optimism."""
 
1180
    hidden = True
 
1181
    def run(self):
 
1182
        print "it sure does!"
 
1183
 
 
1184
 
 
1185
class cmd_find_merge_base(Command):
 
1186
    """Find and print a base revision for merging two branches.
 
1187
    """
 
1188
    # TODO: Options to specify revisions on either side, as if
 
1189
    #       merging only part of the history.
 
1190
    takes_args = ['branch', 'other']
 
1191
    hidden = True
 
1192
    
 
1193
    def run(self, branch, other):
 
1194
        from bzrlib.revision import common_ancestor, MultipleRevisionSources
 
1195
        
 
1196
        branch1 = Branch.open_containing(branch)
 
1197
        branch2 = Branch.open_containing(other)
 
1198
 
 
1199
        history_1 = branch1.revision_history()
 
1200
        history_2 = branch2.revision_history()
 
1201
 
 
1202
        last1 = branch1.last_revision()
 
1203
        last2 = branch2.last_revision()
 
1204
 
 
1205
        source = MultipleRevisionSources(branch1, branch2)
 
1206
        
 
1207
        base_rev_id = common_ancestor(last1, last2, source)
 
1208
 
 
1209
        print 'merge base is revision %s' % base_rev_id
 
1210
        
 
1211
        return
 
1212
 
 
1213
        if base_revno is None:
 
1214
            raise bzrlib.errors.UnrelatedBranches()
 
1215
 
 
1216
        print ' r%-6d in %s' % (base_revno, branch)
 
1217
 
 
1218
        other_revno = branch2.revision_id_to_revno(base_revid)
 
1219
        
 
1220
        print ' r%-6d in %s' % (other_revno, other)
 
1221
 
 
1222
 
 
1223
 
 
1224
class cmd_merge(Command):
 
1225
    """Perform a three-way merge.
 
1226
    
 
1227
    The branch is the branch you will merge from.  By default, it will
 
1228
    merge the latest revision.  If you specify a revision, that
 
1229
    revision will be merged.  If you specify two revisions, the first
 
1230
    will be used as a BASE, and the second one as OTHER.  Revision
 
1231
    numbers are always relative to the specified branch.
 
1232
 
 
1233
    By default bzr will try to merge in all new work from the other
 
1234
    branch, automatically determining an appropriate base.  If this
 
1235
    fails, you may need to give an explicit base.
 
1236
    
 
1237
    Examples:
 
1238
 
 
1239
    To merge the latest revision from bzr.dev
 
1240
    bzr merge ../bzr.dev
 
1241
 
 
1242
    To merge changes up to and including revision 82 from bzr.dev
 
1243
    bzr merge -r 82 ../bzr.dev
 
1244
 
 
1245
    To merge the changes introduced by 82, without previous changes:
 
1246
    bzr merge -r 81..82 ../bzr.dev
 
1247
    
 
1248
    merge refuses to run if there are any uncommitted changes, unless
 
1249
    --force is given.
 
1250
    """
 
1251
    takes_args = ['branch?']
 
1252
    takes_options = ['revision', 'force', 'merge-type']
 
1253
 
 
1254
    def run(self, branch=None, revision=None, force=False, 
 
1255
            merge_type=None):
 
1256
        from bzrlib.merge import merge
 
1257
        from bzrlib.merge_core import ApplyMerge3
 
1258
        if merge_type is None:
 
1259
            merge_type = ApplyMerge3
 
1260
        if branch is None:
 
1261
            branch = Branch.open_containing('.').get_parent()
 
1262
            if branch is None:
 
1263
                raise BzrCommandError("No merge location known or specified.")
 
1264
            else:
 
1265
                print "Using saved location: %s" % branch 
 
1266
        if revision is None or len(revision) < 1:
 
1267
            base = [None, None]
 
1268
            other = [branch, -1]
 
1269
        else:
 
1270
            if len(revision) == 1:
 
1271
                base = [None, None]
 
1272
                other = [branch, revision[0].in_history(branch).revno]
 
1273
            else:
 
1274
                assert len(revision) == 2
 
1275
                if None in revision:
 
1276
                    raise BzrCommandError(
 
1277
                        "Merge doesn't permit that revision specifier.")
 
1278
                b = Branch.open(branch)
 
1279
 
 
1280
                base = [branch, revision[0].in_history(b).revno]
 
1281
                other = [branch, revision[1].in_history(b).revno]
 
1282
 
 
1283
        try:
 
1284
            merge(other, base, check_clean=(not force), merge_type=merge_type)
 
1285
        except bzrlib.errors.AmbiguousBase, e:
 
1286
            m = ("sorry, bzr can't determine the right merge base yet\n"
 
1287
                 "candidates are:\n  "
 
1288
                 + "\n  ".join(e.bases)
 
1289
                 + "\n"
 
1290
                 "please specify an explicit base with -r,\n"
 
1291
                 "and (if you want) report this to the bzr developers\n")
 
1292
            log_error(m)
 
1293
 
 
1294
 
 
1295
class cmd_revert(Command):
 
1296
    """Reverse all changes since the last commit.
 
1297
 
 
1298
    Only versioned files are affected.  Specify filenames to revert only 
 
1299
    those files.  By default, any files that are changed will be backed up
 
1300
    first.  Backup files have a '~' appended to their name.
 
1301
    """
 
1302
    takes_options = ['revision', 'no-backup']
 
1303
    takes_args = ['file*']
 
1304
    aliases = ['merge-revert']
 
1305
 
 
1306
    def run(self, revision=None, no_backup=False, file_list=None):
 
1307
        from bzrlib.merge import merge
 
1308
        from bzrlib.commands import parse_spec
 
1309
 
 
1310
        if file_list is not None:
 
1311
            if len(file_list) == 0:
 
1312
                raise BzrCommandError("No files specified")
 
1313
        if revision is None:
 
1314
            revno = -1
 
1315
        elif len(revision) != 1:
 
1316
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
 
1317
        else:
 
1318
            b = Branch.open_containing('.')
 
1319
            revno = revision[0].in_history(b).revno
 
1320
        merge(('.', revno), parse_spec('.'),
 
1321
              check_clean=False,
 
1322
              ignore_zero=True,
 
1323
              backup_files=not no_backup,
 
1324
              file_list=file_list)
 
1325
        if not file_list:
 
1326
            Branch.open_containing('.').set_pending_merges([])
 
1327
 
 
1328
 
 
1329
class cmd_assert_fail(Command):
 
1330
    """Test reporting of assertion failures"""
 
1331
    hidden = True
 
1332
    def run(self):
 
1333
        assert False, "always fails"
 
1334
 
 
1335
 
 
1336
class cmd_help(Command):
 
1337
    """Show help on a command or other topic.
 
1338
 
 
1339
    For a list of all available commands, say 'bzr help commands'."""
 
1340
    takes_options = ['long']
 
1341
    takes_args = ['topic?']
 
1342
    aliases = ['?']
 
1343
    
 
1344
    def run(self, topic=None, long=False):
 
1345
        import help
 
1346
        if topic is None and long:
 
1347
            topic = "commands"
 
1348
        help.help(topic)
 
1349
 
 
1350
 
 
1351
class cmd_shell_complete(Command):
 
1352
    """Show appropriate completions for context.
 
1353
 
 
1354
    For a list of all available commands, say 'bzr shell-complete'."""
 
1355
    takes_args = ['context?']
 
1356
    aliases = ['s-c']
 
1357
    hidden = True
 
1358
    
 
1359
    def run(self, context=None):
 
1360
        import shellcomplete
 
1361
        shellcomplete.shellcomplete(context)
 
1362
 
 
1363
 
 
1364
class cmd_fetch(Command):
 
1365
    """Copy in history from another branch but don't merge it.
 
1366
 
 
1367
    This is an internal method used for pull and merge."""
 
1368
    hidden = True
 
1369
    takes_args = ['from_branch', 'to_branch']
 
1370
    def run(self, from_branch, to_branch):
 
1371
        from bzrlib.fetch import Fetcher
 
1372
        from bzrlib.branch import Branch
 
1373
        from_b = Branch(from_branch)
 
1374
        to_b = Branch(to_branch)
 
1375
        Fetcher(to_b, from_b)
 
1376
        
 
1377
 
 
1378
 
 
1379
class cmd_missing(Command):
 
1380
    """What is missing in this branch relative to other branch.
 
1381
    """
 
1382
    # TODO: rewrite this in terms of ancestry so that it shows only
 
1383
    # unmerged things
 
1384
    
 
1385
    takes_args = ['remote?']
 
1386
    aliases = ['mis', 'miss']
 
1387
    # We don't have to add quiet to the list, because 
 
1388
    # unknown options are parsed as booleans
 
1389
    takes_options = ['verbose', 'quiet']
 
1390
 
 
1391
    def run(self, remote=None, verbose=False, quiet=False):
 
1392
        from bzrlib.errors import BzrCommandError
 
1393
        from bzrlib.missing import show_missing
 
1394
 
 
1395
        if verbose and quiet:
 
1396
            raise BzrCommandError('Cannot pass both quiet and verbose')
 
1397
 
 
1398
        b = Branch.open_containing('.')
 
1399
        parent = b.get_parent()
 
1400
        if remote is None:
 
1401
            if parent is None:
 
1402
                raise BzrCommandError("No missing location known or specified.")
 
1403
            else:
 
1404
                if not quiet:
 
1405
                    print "Using last location: %s" % parent
 
1406
                remote = parent
 
1407
        elif parent is None:
 
1408
            # We only update parent if it did not exist, missing
 
1409
            # should not change the parent
 
1410
            b.set_parent(remote)
 
1411
        br_remote = Branch.open_containing(remote)
 
1412
        return show_missing(b, br_remote, verbose=verbose, quiet=quiet)
 
1413
 
 
1414
 
 
1415
class cmd_plugins(Command):
 
1416
    """List plugins"""
 
1417
    hidden = True
 
1418
    def run(self):
 
1419
        import bzrlib.plugin
 
1420
        from inspect import getdoc
 
1421
        for plugin in bzrlib.plugin.all_plugins:
 
1422
            if hasattr(plugin, '__path__'):
 
1423
                print plugin.__path__[0]
 
1424
            elif hasattr(plugin, '__file__'):
 
1425
                print plugin.__file__
 
1426
            else:
 
1427
                print `plugin`
 
1428
                
 
1429
            d = getdoc(plugin)
 
1430
            if d:
 
1431
                print '\t', d.split('\n')[0]
 
1432
 
 
1433
 
 
1434
class cmd_testament(Command):
 
1435
    """Show testament (signing-form) of a revision."""
 
1436
    takes_options = ['revision', 'long']
 
1437
    takes_args = ['branch?']
 
1438
    def run(self, branch='.', revision=None, long=False):
 
1439
        from bzrlib.testament import Testament
 
1440
        b = Branch.open_containing(branch)
 
1441
        b.lock_read()
 
1442
        try:
 
1443
            if revision is None:
 
1444
                rev_id = b.last_revision()
 
1445
            else:
 
1446
                rev_id = revision[0].in_history(b).rev_id
 
1447
            t = Testament.from_revision(b, rev_id)
 
1448
            if long:
 
1449
                sys.stdout.writelines(t.as_text_lines())
 
1450
            else:
 
1451
                sys.stdout.write(t.as_short_text())
 
1452
        finally:
 
1453
            b.unlock()
 
1454
 
 
1455
 
 
1456
class cmd_annotate(Command):
 
1457
    """Show the origin of each line in a file.
 
1458
 
 
1459
    This prints out the given file with an annotation on the 
 
1460
    left side indicating which revision, author and date introduced the 
 
1461
    change.
 
1462
    """
 
1463
    # TODO: annotate directories; showing when each file was last changed
 
1464
    # TODO: annotate a previous version of a file
 
1465
    aliases = ['blame', 'praise']
 
1466
    takes_args = ['filename']
 
1467
 
 
1468
    def run(self, filename):
 
1469
        from bzrlib.annotate import annotate_file
 
1470
        b = Branch.open_containing(filename)
 
1471
        b.lock_read()
 
1472
        try:
 
1473
            tree = WorkingTree(b.base, b)
 
1474
            rp = tree.relpath(filename)
 
1475
            tree = b.revision_tree(b.last_revision())
 
1476
            file_id = tree.inventory.path2id(rp)
 
1477
            file_version = tree.inventory[file_id].revision
 
1478
            annotate_file(b, file_version, file_id, sys.stdout)
 
1479
        finally:
 
1480
            b.unlock()
 
1481
 
 
1482
# these get imported and then picked up by the scan for cmd_*
 
1483
# TODO: Some more consistent way to split command definitions across files;
 
1484
# we do need to load at least some information about them to know of 
 
1485
# aliases.
 
1486
from bzrlib.conflicts import cmd_resolve, cmd_conflicts