/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Robert Collins
  • Date: 2005-10-20 02:52:44 UTC
  • Revision ID: robertc@robertcollins.net-20051020025244-fa1017d19a0ef618
post commit hook, first pass implementation

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