/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: John Arbash Meinel
  • Date: 2006-04-28 17:18:42 UTC
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060428171842-04f65c89de82599e
A couple more fixes to make sure memory:/// works correctly.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005, 2006 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
"""builtin bzr commands"""
 
18
 
 
19
 
 
20
import errno
 
21
import os
 
22
import codecs
 
23
from shutil import rmtree
 
24
import sys
 
25
 
 
26
import bzrlib
 
27
import bzrlib.branch
 
28
from bzrlib.branch import Branch
 
29
import bzrlib.bzrdir as bzrdir
 
30
from bzrlib.commands import Command, display_command
 
31
from bzrlib.revision import common_ancestor
 
32
import bzrlib.errors as errors
 
33
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError, 
 
34
                           NotBranchError, DivergedBranches, NotConflicted,
 
35
                           NoSuchFile, NoWorkingTree, FileInWrongBranch,
 
36
                           NotVersionedError)
 
37
from bzrlib.log import show_one_log
 
38
from bzrlib.merge import Merge3Merger
 
39
from bzrlib.option import Option
 
40
import bzrlib.osutils
 
41
from bzrlib.progress import DummyProgress, ProgressPhase
 
42
from bzrlib.revisionspec import RevisionSpec
 
43
import bzrlib.trace
 
44
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
 
45
from bzrlib.transport.local import LocalTransport
 
46
import bzrlib.ui
 
47
from bzrlib.workingtree import WorkingTree
 
48
 
 
49
 
 
50
def tree_files(file_list, default_branch=u'.'):
 
51
    try:
 
52
        return internal_tree_files(file_list, default_branch)
 
53
    except FileInWrongBranch, e:
 
54
        raise BzrCommandError("%s is not in the same branch as %s" %
 
55
                             (e.path, file_list[0]))
 
56
 
 
57
 
 
58
# XXX: Bad function name; should possibly also be a class method of
 
59
# WorkingTree rather than a function.
 
60
def internal_tree_files(file_list, default_branch=u'.'):
 
61
    """Convert command-line paths to a WorkingTree and relative paths.
 
62
 
 
63
    This is typically used for command-line processors that take one or
 
64
    more filenames, and infer the workingtree that contains them.
 
65
 
 
66
    The filenames given are not required to exist.
 
67
 
 
68
    :param file_list: Filenames to convert.  
 
69
 
 
70
    :param default_branch: Fallback tree path to use if file_list is empty or None.
 
71
 
 
72
    :return: workingtree, [relative_paths]
 
73
    """
 
74
    if file_list is None or len(file_list) == 0:
 
75
        return WorkingTree.open_containing(default_branch)[0], file_list
 
76
    tree = WorkingTree.open_containing(file_list[0])[0]
 
77
    new_list = []
 
78
    for filename in file_list:
 
79
        try:
 
80
            new_list.append(tree.relpath(filename))
 
81
        except errors.PathNotChild:
 
82
            raise FileInWrongBranch(tree.branch, filename)
 
83
    return tree, new_list
 
84
 
 
85
 
 
86
def get_format_type(typestring):
 
87
    """Parse and return a format specifier."""
 
88
    if typestring == "weave":
 
89
        return bzrdir.BzrDirFormat6()
 
90
    if typestring == "metadir":
 
91
        return bzrdir.BzrDirMetaFormat1()
 
92
    if typestring == "knit":
 
93
        format = bzrdir.BzrDirMetaFormat1()
 
94
        format.repository_format = bzrlib.repository.RepositoryFormatKnit1()
 
95
        return format
 
96
    msg = "No known bzr-dir format %s. Supported types are: weave, metadir\n" %\
 
97
        (typestring)
 
98
    raise BzrCommandError(msg)
 
99
 
 
100
 
 
101
# TODO: Make sure no commands unconditionally use the working directory as a
 
102
# branch.  If a filename argument is used, the first of them should be used to
 
103
# specify the branch.  (Perhaps this can be factored out into some kind of
 
104
# Argument class, representing a file in a branch, where the first occurrence
 
105
# opens the branch?)
 
106
 
 
107
class cmd_status(Command):
 
108
    """Display status summary.
 
109
 
 
110
    This reports on versioned and unknown files, reporting them
 
111
    grouped by state.  Possible states are:
 
112
 
 
113
    added
 
114
        Versioned in the working copy but not in the previous revision.
 
115
 
 
116
    removed
 
117
        Versioned in the previous revision but removed or deleted
 
118
        in the working copy.
 
119
 
 
120
    renamed
 
121
        Path of this file changed from the previous revision;
 
122
        the text may also have changed.  This includes files whose
 
123
        parent directory was renamed.
 
124
 
 
125
    modified
 
126
        Text has changed since the previous revision.
 
127
 
 
128
    unchanged
 
129
        Nothing about this file has changed since the previous revision.
 
130
        Only shown with --all.
 
131
 
 
132
    unknown
 
133
        Not versioned and not matching an ignore pattern.
 
134
 
 
135
    To see ignored files use 'bzr ignored'.  For details in the
 
136
    changes to file texts, use 'bzr diff'.
 
137
 
 
138
    If no arguments are specified, the status of the entire working
 
139
    directory is shown.  Otherwise, only the status of the specified
 
140
    files or directories is reported.  If a directory is given, status
 
141
    is reported for everything inside that directory.
 
142
 
 
143
    If a revision argument is given, the status is calculated against
 
144
    that revision, or between two revisions if two are provided.
 
145
    """
 
146
    
 
147
    # TODO: --no-recurse, --recurse options
 
148
    
 
149
    takes_args = ['file*']
 
150
    takes_options = ['all', 'show-ids', 'revision']
 
151
    aliases = ['st', 'stat']
 
152
 
 
153
    encoding_type = 'replace'
 
154
    
 
155
    @display_command
 
156
    def run(self, all=False, show_ids=False, file_list=None, revision=None):
 
157
        from bzrlib.status import show_tree_status
 
158
 
 
159
        tree, file_list = tree_files(file_list)
 
160
            
 
161
        show_tree_status(tree, show_unchanged=all, show_ids=show_ids,
 
162
                         specific_files=file_list, revision=revision,
 
163
                         to_file=self.outf)
 
164
 
 
165
 
 
166
class cmd_cat_revision(Command):
 
167
    """Write out metadata for a revision.
 
168
    
 
169
    The revision to print can either be specified by a specific
 
170
    revision identifier, or you can use --revision.
 
171
    """
 
172
 
 
173
    hidden = True
 
174
    takes_args = ['revision_id?']
 
175
    takes_options = ['revision']
 
176
    
 
177
    @display_command
 
178
    def run(self, revision_id=None, revision=None):
 
179
 
 
180
        if revision_id is not None and revision is not None:
 
181
            raise BzrCommandError('You can only supply one of revision_id or --revision')
 
182
        if revision_id is None and revision is None:
 
183
            raise BzrCommandError('You must supply either --revision or a revision_id')
 
184
        b = WorkingTree.open_containing(u'.')[0].branch
 
185
 
 
186
        # TODO: jam 20060112 should cat-revision always output utf-8?
 
187
        if revision_id is not None:
 
188
            self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
 
189
        elif revision is not None:
 
190
            for rev in revision:
 
191
                if rev is None:
 
192
                    raise BzrCommandError('You cannot specify a NULL revision.')
 
193
                revno, rev_id = rev.in_history(b)
 
194
                self.outf.write(b.repository.get_revision_xml(rev_id).decode('utf-8'))
 
195
    
 
196
 
 
197
class cmd_revno(Command):
 
198
    """Show current revision number.
 
199
 
 
200
    This is equal to the number of revisions on this branch.
 
201
    """
 
202
 
 
203
    takes_args = ['location?']
 
204
 
 
205
    @display_command
 
206
    def run(self, location=u'.'):
 
207
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
 
208
        self.outf.write('\n')
 
209
 
 
210
 
 
211
class cmd_revision_info(Command):
 
212
    """Show revision number and revision id for a given revision identifier.
 
213
    """
 
214
    hidden = True
 
215
    takes_args = ['revision_info*']
 
216
    takes_options = ['revision']
 
217
 
 
218
    @display_command
 
219
    def run(self, revision=None, revision_info_list=[]):
 
220
 
 
221
        revs = []
 
222
        if revision is not None:
 
223
            revs.extend(revision)
 
224
        if revision_info_list is not None:
 
225
            for rev in revision_info_list:
 
226
                revs.append(RevisionSpec(rev))
 
227
        if len(revs) == 0:
 
228
            raise BzrCommandError('You must supply a revision identifier')
 
229
 
 
230
        b = WorkingTree.open_containing(u'.')[0].branch
 
231
 
 
232
        for rev in revs:
 
233
            revinfo = rev.in_history(b)
 
234
            if revinfo.revno is None:
 
235
                print '     %s' % revinfo.rev_id
 
236
            else:
 
237
                print '%4d %s' % (revinfo.revno, revinfo.rev_id)
 
238
 
 
239
    
 
240
class cmd_add(Command):
 
241
    """Add specified files or directories.
 
242
 
 
243
    In non-recursive mode, all the named items are added, regardless
 
244
    of whether they were previously ignored.  A warning is given if
 
245
    any of the named files are already versioned.
 
246
 
 
247
    In recursive mode (the default), files are treated the same way
 
248
    but the behaviour for directories is different.  Directories that
 
249
    are already versioned do not give a warning.  All directories,
 
250
    whether already versioned or not, are searched for files or
 
251
    subdirectories that are neither versioned or ignored, and these
 
252
    are added.  This search proceeds recursively into versioned
 
253
    directories.  If no names are given '.' is assumed.
 
254
 
 
255
    Therefore simply saying 'bzr add' will version all files that
 
256
    are currently unknown.
 
257
 
 
258
    Adding a file whose parent directory is not versioned will
 
259
    implicitly add the parent, and so on up to the root. This means
 
260
    you should never need to explictly add a directory, they'll just
 
261
    get added when you add a file in the directory.
 
262
 
 
263
    --dry-run will show which files would be added, but not actually 
 
264
    add them.
 
265
    """
 
266
    takes_args = ['file*']
 
267
    takes_options = ['no-recurse', 'dry-run', 'verbose']
 
268
    encoding_type = 'replace'
 
269
 
 
270
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
 
271
        import bzrlib.add
 
272
 
 
273
        action = bzrlib.add.AddAction(to_file=self.outf,
 
274
            should_add=(not dry_run), should_print=(not is_quiet()))
 
275
 
 
276
        added, ignored = bzrlib.add.smart_add(file_list, not no_recurse, 
 
277
                                              action=action)
 
278
        if len(ignored) > 0:
 
279
            for glob in sorted(ignored.keys()):
 
280
                match_len = len(ignored[glob])
 
281
                if verbose:
 
282
                    for path in ignored[glob]:
 
283
                        self.outf.write("ignored %s matching \"%s\"\n" 
 
284
                                        % (path, glob))
 
285
                else:
 
286
                    self.outf.write("ignored %d file(s) matching \"%s\"\n"
 
287
                                    % (match_len, glob))
 
288
            self.outf.write("If you wish to add some of these files,"
 
289
                            " please add them by name.\n")
 
290
 
 
291
 
 
292
class cmd_mkdir(Command):
 
293
    """Create a new versioned directory.
 
294
 
 
295
    This is equivalent to creating the directory and then adding it.
 
296
    """
 
297
    takes_args = ['dir+']
 
298
    encoding_type = 'replace'
 
299
 
 
300
    def run(self, dir_list):
 
301
        for d in dir_list:
 
302
            os.mkdir(d)
 
303
            wt, dd = WorkingTree.open_containing(d)
 
304
            wt.add([dd])
 
305
            print >>self.outf, 'added', d
 
306
 
 
307
 
 
308
class cmd_relpath(Command):
 
309
    """Show path of a file relative to root"""
 
310
    takes_args = ['filename']
 
311
    hidden = True
 
312
    
 
313
    @display_command
 
314
    def run(self, filename):
 
315
        # TODO: jam 20050106 Can relpath return a munged path if
 
316
        #       sys.stdout encoding cannot represent it?
 
317
        tree, relpath = WorkingTree.open_containing(filename)
 
318
        self.outf.write(relpath)
 
319
        self.outf.write('\n')
 
320
 
 
321
 
 
322
class cmd_inventory(Command):
 
323
    """Show inventory of the current working copy or a revision.
 
324
 
 
325
    It is possible to limit the output to a particular entry
 
326
    type using the --kind option.  For example; --kind file.
 
327
    """
 
328
    takes_options = ['revision', 'show-ids', 'kind']
 
329
    
 
330
    @display_command
 
331
    def run(self, revision=None, show_ids=False, kind=None):
 
332
        if kind and kind not in ['file', 'directory', 'symlink']:
 
333
            raise BzrCommandError('invalid kind specified')
 
334
        tree = WorkingTree.open_containing(u'.')[0]
 
335
        if revision is None:
 
336
            inv = tree.read_working_inventory()
 
337
        else:
 
338
            if len(revision) > 1:
 
339
                raise BzrCommandError('bzr inventory --revision takes'
 
340
                    ' exactly one revision identifier')
 
341
            inv = tree.branch.repository.get_revision_inventory(
 
342
                revision[0].in_history(tree.branch).rev_id)
 
343
 
 
344
        for path, entry in inv.entries():
 
345
            if kind and kind != entry.kind:
 
346
                continue
 
347
            if show_ids:
 
348
                self.outf.write('%-50s %s\n' % (path, entry.file_id))
 
349
            else:
 
350
                self.outf.write(path)
 
351
                self.outf.write('\n')
 
352
 
 
353
 
 
354
class cmd_mv(Command):
 
355
    """Move or rename a file.
 
356
 
 
357
    usage:
 
358
        bzr mv OLDNAME NEWNAME
 
359
        bzr mv SOURCE... DESTINATION
 
360
 
 
361
    If the last argument is a versioned directory, all the other names
 
362
    are moved into it.  Otherwise, there must be exactly two arguments
 
363
    and the file is changed to a new name, which must not already exist.
 
364
 
 
365
    Files cannot be moved between branches.
 
366
    """
 
367
    takes_args = ['names*']
 
368
    aliases = ['move', 'rename']
 
369
 
 
370
    encoding_type = 'replace'
 
371
 
 
372
    def run(self, names_list):
 
373
        if len(names_list) < 2:
 
374
            raise BzrCommandError("missing file argument")
 
375
        tree, rel_names = tree_files(names_list)
 
376
        
 
377
        if os.path.isdir(names_list[-1]):
 
378
            # move into existing directory
 
379
            for pair in tree.move(rel_names[:-1], rel_names[-1]):
 
380
                self.outf.write("%s => %s\n" % pair)
 
381
        else:
 
382
            if len(names_list) != 2:
 
383
                raise BzrCommandError('to mv multiple files the destination '
 
384
                                      'must be a versioned directory')
 
385
            tree.rename_one(rel_names[0], rel_names[1])
 
386
            self.outf.write("%s => %s\n" % (rel_names[0], rel_names[1]))
 
387
            
 
388
    
 
389
class cmd_pull(Command):
 
390
    """Turn this branch into a mirror of another branch.
 
391
 
 
392
    This command only works on branches that have not diverged.  Branches are
 
393
    considered diverged if the destination branch's most recent commit is one
 
394
    that has not been merged (directly or indirectly) into the parent.
 
395
 
 
396
    If branches have diverged, you can use 'bzr merge' to integrate the changes
 
397
    from one into the other.  Once one branch has merged, the other should
 
398
    be able to pull it again.
 
399
 
 
400
    If branches have diverged, you can use 'bzr merge' to pull the text changes
 
401
    from one into the other.  Once one branch has merged, the other should
 
402
    be able to pull it again.
 
403
 
 
404
    If you want to forget your local changes and just update your branch to
 
405
    match the remote one, use pull --overwrite.
 
406
 
 
407
    If there is no default location set, the first pull will set it.  After
 
408
    that, you can omit the location to use the default.  To change the
 
409
    default, use --remember.
 
410
    """
 
411
    takes_options = ['remember', 'overwrite', 'revision', 'verbose']
 
412
    takes_args = ['location?']
 
413
    encoding_type = 'replace'
 
414
 
 
415
    def run(self, location=None, remember=False, overwrite=False, revision=None, verbose=False):
 
416
        # FIXME: too much stuff is in the command class
 
417
        try:
 
418
            tree_to = WorkingTree.open_containing(u'.')[0]
 
419
            branch_to = tree_to.branch
 
420
        except NoWorkingTree:
 
421
            tree_to = None
 
422
            branch_to = Branch.open_containing(u'.')[0] 
 
423
        stored_loc = branch_to.get_parent()
 
424
        if location is None:
 
425
            if stored_loc is None:
 
426
                raise BzrCommandError("No pull location known or specified.")
 
427
            else:
 
428
                self.outf.write("Using saved location: %s\n" 
 
429
                    % bzrlib.osutils.urlfordisplay(stored_loc))
 
430
                location = stored_loc
 
431
 
 
432
        branch_from = Branch.open(location)
 
433
 
 
434
        if branch_to.get_parent() is None or remember:
 
435
            branch_to.set_parent(branch_from.base)
 
436
 
 
437
 
 
438
        if revision is None:
 
439
            rev_id = None
 
440
        elif len(revision) == 1:
 
441
            rev_id = revision[0].in_history(branch_from).rev_id
 
442
        else:
 
443
            raise BzrCommandError('bzr pull --revision takes one value.')
 
444
 
 
445
        old_rh = branch_to.revision_history()
 
446
        if tree_to is not None:
 
447
            count = tree_to.pull(branch_from, overwrite, rev_id)
 
448
        else:
 
449
            count = branch_to.pull(branch_from, overwrite, rev_id)
 
450
        note('%d revision(s) pulled.' % (count,))
 
451
 
 
452
        if verbose:
 
453
            new_rh = branch_to.revision_history()
 
454
            if old_rh != new_rh:
 
455
                # Something changed
 
456
                from bzrlib.log import show_changed_revisions
 
457
                show_changed_revisions(branch_to, old_rh, new_rh,
 
458
                                       to_file=self.outf)
 
459
 
 
460
 
 
461
class cmd_push(Command):
 
462
    """Update a mirror of this branch.
 
463
    
 
464
    The target branch will not have its working tree populated because this
 
465
    is both expensive, and is not supported on remote file systems.
 
466
    
 
467
    Some smart servers or protocols *may* put the working tree in place in
 
468
    the future.
 
469
 
 
470
    This command only works on branches that have not diverged.  Branches are
 
471
    considered diverged if the destination branch's most recent commit is one
 
472
    that has not been merged (directly or indirectly) by the source branch.
 
473
 
 
474
    If branches have diverged, you can use 'bzr push --overwrite' to replace
 
475
    the other branch completely, discarding its unmerged changes.
 
476
    
 
477
    If you want to ensure you have the different changes in the other branch,
 
478
    do a merge (see bzr help merge) from the other branch, and commit that.
 
479
    After that you will be able to do a push without '--overwrite'.
 
480
 
 
481
    If there is no default push location set, the first push will set it.
 
482
    After that, you can omit the location to use the default.  To change the
 
483
    default, use --remember.
 
484
    """
 
485
    takes_options = ['remember', 'overwrite', 'verbose',
 
486
                     Option('create-prefix', 
 
487
                            help='Create the path leading up to the branch '
 
488
                                 'if it does not already exist')]
 
489
    takes_args = ['location?']
 
490
    encoding_type = 'replace'
 
491
 
 
492
    def run(self, location=None, remember=False, overwrite=False,
 
493
            create_prefix=False, verbose=False):
 
494
        # FIXME: Way too big!  Put this into a function called from the
 
495
        # command.
 
496
        from bzrlib.transport import get_transport
 
497
        
 
498
        tree_from = WorkingTree.open_containing(u'.')[0]
 
499
        br_from = tree_from.branch
 
500
        stored_loc = tree_from.branch.get_push_location()
 
501
 
 
502
        if location is None:
 
503
            if stored_loc is None:
 
504
                raise BzrCommandError("No push location known or specified.")
 
505
            else:
 
506
                self.outf.write("Using saved location: %s" 
 
507
                    % bzrlib.osutils.urlfordisplay(stored_loc))
 
508
                location = stored_loc
 
509
 
 
510
        transport = get_transport(location)
 
511
        location_url = transport.base
 
512
        if br_from.get_push_location() is None or remember:
 
513
            br_from.set_push_location(location_url)
 
514
        try:
 
515
            dir_to = bzrlib.bzrdir.BzrDir.open(location_url)
 
516
            br_to = dir_to.open_branch()
 
517
        except NotBranchError:
 
518
            # create a branch.
 
519
            transport = transport.clone('..')
 
520
            if not create_prefix:
 
521
                try:
 
522
                    relurl = transport.relpath(location_url)
 
523
                    mutter('creating directory %s => %s', location_url, relurl)
 
524
                    transport.mkdir(relurl)
 
525
                except NoSuchFile:
 
526
                    raise BzrCommandError("Parent directory of %s "
 
527
                                          "does not exist." % location)
 
528
            else:
 
529
                current = transport.base
 
530
                needed = [(transport, transport.relpath(location_url))]
 
531
                while needed:
 
532
                    try:
 
533
                        transport, relpath = needed[-1]
 
534
                        transport.mkdir(relpath)
 
535
                        needed.pop()
 
536
                    except NoSuchFile:
 
537
                        new_transport = transport.clone('..')
 
538
                        needed.append((new_transport,
 
539
                                       new_transport.relpath(transport.base)))
 
540
                        if new_transport.base == transport.base:
 
541
                            raise BzrCommandError("Could not create "
 
542
                                                  "path prefix.")
 
543
            dir_to = br_from.bzrdir.clone(location_url)
 
544
            br_to = dir_to.open_branch()
 
545
        old_rh = br_to.revision_history()
 
546
        try:
 
547
            try:
 
548
                tree_to = dir_to.open_workingtree()
 
549
            except errors.NotLocalUrl:
 
550
                # TODO: This should be updated for branches which don't have a
 
551
                # working tree, as opposed to ones where we just couldn't 
 
552
                # update the tree.
 
553
                warning('This transport does not update the working '
 
554
                        'tree of: %s' % (br_to.base,))
 
555
                count = br_to.pull(br_from, overwrite)
 
556
            except NoWorkingTree:
 
557
                count = br_to.pull(br_from, overwrite)
 
558
            else:
 
559
                count = tree_to.pull(br_from, overwrite)
 
560
        except DivergedBranches:
 
561
            raise BzrCommandError("These branches have diverged."
 
562
                                  "  Try a merge then push with overwrite.")
 
563
        note('%d revision(s) pushed.' % (count,))
 
564
 
 
565
        if verbose:
 
566
            new_rh = br_to.revision_history()
 
567
            if old_rh != new_rh:
 
568
                # Something changed
 
569
                from bzrlib.log import show_changed_revisions
 
570
                show_changed_revisions(br_to, old_rh, new_rh,
 
571
                                       to_file=self.outf)
 
572
 
 
573
 
 
574
class cmd_branch(Command):
 
575
    """Create a new copy of a branch.
 
576
 
 
577
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
 
578
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
 
579
 
 
580
    To retrieve the branch as of a particular revision, supply the --revision
 
581
    parameter, as in "branch foo/bar -r 5".
 
582
 
 
583
    --basis is to speed up branching from remote branches.  When specified, it
 
584
    copies all the file-contents, inventory and revision data from the basis
 
585
    branch before copying anything from the remote branch.
 
586
    """
 
587
    takes_args = ['from_location', 'to_location?']
 
588
    takes_options = ['revision', 'basis']
 
589
    aliases = ['get', 'clone']
 
590
 
 
591
    def run(self, from_location, to_location=None, revision=None, basis=None):
 
592
        from bzrlib.transport import get_transport
 
593
        if revision is None:
 
594
            revision = [None]
 
595
        elif len(revision) > 1:
 
596
            raise BzrCommandError(
 
597
                'bzr branch --revision takes exactly 1 revision value')
 
598
        try:
 
599
            br_from = Branch.open(from_location)
 
600
        except OSError, e:
 
601
            if e.errno == errno.ENOENT:
 
602
                raise BzrCommandError('Source location "%s" does not'
 
603
                                      ' exist.' % to_location)
 
604
            else:
 
605
                raise
 
606
        br_from.lock_read()
 
607
        try:
 
608
            if basis is not None:
 
609
                basis_dir = bzrdir.BzrDir.open_containing(basis)[0]
 
610
            else:
 
611
                basis_dir = None
 
612
            if len(revision) == 1 and revision[0] is not None:
 
613
                revision_id = revision[0].in_history(br_from)[1]
 
614
            else:
 
615
                # FIXME - wt.last_revision, fallback to branch, fall back to
 
616
                # None or perhaps NULL_REVISION to mean copy nothing
 
617
                # RBC 20060209
 
618
                revision_id = br_from.last_revision()
 
619
            if to_location is None:
 
620
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
621
                name = None
 
622
            else:
 
623
                name = os.path.basename(to_location) + '\n'
 
624
 
 
625
            to_transport = get_transport(to_location)
 
626
            try:
 
627
                to_transport.mkdir('.')
 
628
            except bzrlib.errors.FileExists:
 
629
                raise BzrCommandError('Target directory "%s" already'
 
630
                                      ' exists.' % to_location)
 
631
            except bzrlib.errors.NoSuchFile:
 
632
                raise BzrCommandError('Parent of "%s" does not exist.' %
 
633
                                      to_location)
 
634
            try:
 
635
                # preserve whatever source format we have.
 
636
                dir = br_from.bzrdir.sprout(to_transport.base,
 
637
                        revision_id, basis_dir)
 
638
                branch = dir.open_branch()
 
639
            except bzrlib.errors.NoSuchRevision:
 
640
                # TODO: jam 20060426 This only works on local paths
 
641
                #       and it would be nice if 'bzr branch' could
 
642
                #       work on a remote path
 
643
                rmtree(to_location)
 
644
                msg = "The branch %s has no revision %s." % (from_location, revision[0])
 
645
                raise BzrCommandError(msg)
 
646
            except bzrlib.errors.UnlistableBranch:
 
647
                rmtree(to_location)
 
648
                msg = "The branch %s cannot be used as a --basis" % (basis,)
 
649
                raise BzrCommandError(msg)
 
650
            if name:
 
651
                branch.control_files.put_utf8('branch-name', name)
 
652
            note('Branched %d revision(s).' % branch.revno())
 
653
        finally:
 
654
            br_from.unlock()
 
655
 
 
656
 
 
657
class cmd_checkout(Command):
 
658
    """Create a new checkout of an existing branch.
 
659
 
 
660
    If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree for
 
661
    the branch found in '.'. This is useful if you have removed the working tree
 
662
    or if it was never created - i.e. if you pushed the branch to its current
 
663
    location using SFTP.
 
664
    
 
665
    If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION will
 
666
    be used.  In other words, "checkout ../foo/bar" will attempt to create ./bar.
 
667
 
 
668
    To retrieve the branch as of a particular revision, supply the --revision
 
669
    parameter, as in "checkout foo/bar -r 5". Note that this will be immediately
 
670
    out of date [so you cannot commit] but it may be useful (i.e. to examine old
 
671
    code.)
 
672
 
 
673
    --basis is to speed up checking out from remote branches.  When specified, it
 
674
    uses the inventory and file contents from the basis branch in preference to the
 
675
    branch being checked out. [Not implemented yet.]
 
676
    """
 
677
    takes_args = ['branch_location?', 'to_location?']
 
678
    takes_options = ['revision', # , 'basis']
 
679
                     Option('lightweight',
 
680
                            help="perform a lightweight checkout. Lightweight "
 
681
                                 "checkouts depend on access to the branch for "
 
682
                                 "every operation. Normal checkouts can perform "
 
683
                                 "common operations like diff and status without "
 
684
                                 "such access, and also support local commits."
 
685
                            ),
 
686
                     ]
 
687
 
 
688
    def run(self, branch_location=None, to_location=None, revision=None, basis=None,
 
689
            lightweight=False):
 
690
        if revision is None:
 
691
            revision = [None]
 
692
        elif len(revision) > 1:
 
693
            raise BzrCommandError(
 
694
                'bzr checkout --revision takes exactly 1 revision value')
 
695
        if branch_location is None:
 
696
            branch_location = bzrlib.osutils.getcwd()
 
697
            to_location = branch_location
 
698
        source = Branch.open(branch_location)
 
699
        if len(revision) == 1 and revision[0] is not None:
 
700
            revision_id = revision[0].in_history(source)[1]
 
701
        else:
 
702
            revision_id = None
 
703
        if to_location is None:
 
704
            to_location = os.path.basename(branch_location.rstrip("/\\"))
 
705
        # if the source and to_location are the same, 
 
706
        # and there is no working tree,
 
707
        # then reconstitute a branch
 
708
        if (bzrlib.osutils.abspath(to_location) == 
 
709
            bzrlib.osutils.abspath(branch_location)):
 
710
            try:
 
711
                source.bzrdir.open_workingtree()
 
712
            except errors.NoWorkingTree:
 
713
                source.bzrdir.create_workingtree()
 
714
                return
 
715
        try:
 
716
            os.mkdir(to_location)
 
717
        except OSError, e:
 
718
            if e.errno == errno.EEXIST:
 
719
                raise BzrCommandError('Target directory "%s" already'
 
720
                                      ' exists.' % to_location)
 
721
            if e.errno == errno.ENOENT:
 
722
                raise BzrCommandError('Parent of "%s" does not exist.' %
 
723
                                      to_location)
 
724
            else:
 
725
                raise
 
726
        old_format = bzrlib.bzrdir.BzrDirFormat.get_default_format()
 
727
        bzrlib.bzrdir.BzrDirFormat.set_default_format(bzrdir.BzrDirMetaFormat1())
 
728
        try:
 
729
            if lightweight:
 
730
                checkout = bzrdir.BzrDirMetaFormat1().initialize(to_location)
 
731
                bzrlib.branch.BranchReferenceFormat().initialize(checkout, source)
 
732
            else:
 
733
                checkout_branch =  bzrlib.bzrdir.BzrDir.create_branch_convenience(
 
734
                    to_location, force_new_tree=False)
 
735
                checkout = checkout_branch.bzrdir
 
736
                checkout_branch.bind(source)
 
737
                if revision_id is not None:
 
738
                    rh = checkout_branch.revision_history()
 
739
                    checkout_branch.set_revision_history(rh[:rh.index(revision_id) + 1])
 
740
            checkout.create_workingtree(revision_id)
 
741
        finally:
 
742
            bzrlib.bzrdir.BzrDirFormat.set_default_format(old_format)
 
743
 
 
744
 
 
745
class cmd_renames(Command):
 
746
    """Show list of renamed files.
 
747
    """
 
748
    # TODO: Option to show renames between two historical versions.
 
749
 
 
750
    # TODO: Only show renames under dir, rather than in the whole branch.
 
751
    takes_args = ['dir?']
 
752
 
 
753
    @display_command
 
754
    def run(self, dir=u'.'):
 
755
        tree = WorkingTree.open_containing(dir)[0]
 
756
        old_inv = tree.basis_tree().inventory
 
757
        new_inv = tree.read_working_inventory()
 
758
 
 
759
        renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
 
760
        renames.sort()
 
761
        for old_name, new_name in renames:
 
762
            self.outf.write("%s => %s\n" % (old_name, new_name))
 
763
 
 
764
 
 
765
class cmd_update(Command):
 
766
    """Update a tree to have the latest code committed to its branch.
 
767
    
 
768
    This will perform a merge into the working tree, and may generate
 
769
    conflicts. If you have any local changes, you will still 
 
770
    need to commit them after the update for the update to be complete.
 
771
    
 
772
    If you want to discard your local changes, you can just do a 
 
773
    'bzr revert' instead of 'bzr commit' after the update.
 
774
    """
 
775
    takes_args = ['dir?']
 
776
 
 
777
    def run(self, dir='.'):
 
778
        tree = WorkingTree.open_containing(dir)[0]
 
779
        tree.lock_write()
 
780
        try:
 
781
            if tree.last_revision() == tree.branch.last_revision():
 
782
                # may be up to date, check master too.
 
783
                master = tree.branch.get_master_branch()
 
784
                if master is None or master.last_revision == tree.last_revision():
 
785
                    note("Tree is up to date.")
 
786
                    return
 
787
            conflicts = tree.update()
 
788
            note('Updated to revision %d.' %
 
789
                 (tree.branch.revision_id_to_revno(tree.last_revision()),))
 
790
            if conflicts != 0:
 
791
                return 1
 
792
            else:
 
793
                return 0
 
794
        finally:
 
795
            tree.unlock()
 
796
 
 
797
 
 
798
class cmd_info(Command):
 
799
    """Show statistical information about a branch."""
 
800
    takes_args = ['branch?']
 
801
    takes_options = ['verbose']
 
802
    
 
803
    @display_command
 
804
    def run(self, branch=None, verbose=False):
 
805
        import bzrlib.info
 
806
        bzrlib.info.show_bzrdir_info(bzrdir.BzrDir.open_containing(branch)[0],
 
807
                                     verbose=verbose)
 
808
 
 
809
 
 
810
class cmd_remove(Command):
 
811
    """Make a file unversioned.
 
812
 
 
813
    This makes bzr stop tracking changes to a versioned file.  It does
 
814
    not delete the working copy.
 
815
    """
 
816
    takes_args = ['file+']
 
817
    takes_options = ['verbose']
 
818
    aliases = ['rm']
 
819
    
 
820
    def run(self, file_list, verbose=False):
 
821
        tree, file_list = tree_files(file_list)
 
822
        tree.remove(file_list, verbose=verbose)
 
823
 
 
824
 
 
825
class cmd_file_id(Command):
 
826
    """Print file_id of a particular file or directory.
 
827
 
 
828
    The file_id is assigned when the file is first added and remains the
 
829
    same through all revisions where the file exists, even when it is
 
830
    moved or renamed.
 
831
    """
 
832
    hidden = True
 
833
    takes_args = ['filename']
 
834
 
 
835
    @display_command
 
836
    def run(self, filename):
 
837
        tree, relpath = WorkingTree.open_containing(filename)
 
838
        i = tree.inventory.path2id(relpath)
 
839
        if i == None:
 
840
            raise BzrError("%r is not a versioned file" % filename)
 
841
        else:
 
842
            self.outf.write(i)
 
843
            self.outf.write('\n')
 
844
 
 
845
 
 
846
class cmd_file_path(Command):
 
847
    """Print path of file_ids to a file or directory.
 
848
 
 
849
    This prints one line for each directory down to the target,
 
850
    starting at the branch root.
 
851
    """
 
852
    hidden = True
 
853
    takes_args = ['filename']
 
854
 
 
855
    @display_command
 
856
    def run(self, filename):
 
857
        tree, relpath = WorkingTree.open_containing(filename)
 
858
        inv = tree.inventory
 
859
        fid = inv.path2id(relpath)
 
860
        if fid == None:
 
861
            raise BzrError("%r is not a versioned file" % filename)
 
862
        for fip in inv.get_idpath(fid):
 
863
            self.outf.write(fip)
 
864
            self.outf.write('\n')
 
865
 
 
866
 
 
867
class cmd_reconcile(Command):
 
868
    """Reconcile bzr metadata in a branch.
 
869
 
 
870
    This can correct data mismatches that may have been caused by
 
871
    previous ghost operations or bzr upgrades. You should only
 
872
    need to run this command if 'bzr check' or a bzr developer 
 
873
    advises you to run it.
 
874
 
 
875
    If a second branch is provided, cross-branch reconciliation is
 
876
    also attempted, which will check that data like the tree root
 
877
    id which was not present in very early bzr versions is represented
 
878
    correctly in both branches.
 
879
 
 
880
    At the same time it is run it may recompress data resulting in 
 
881
    a potential saving in disk space or performance gain.
 
882
 
 
883
    The branch *MUST* be on a listable system such as local disk or sftp.
 
884
    """
 
885
    takes_args = ['branch?']
 
886
 
 
887
    def run(self, branch="."):
 
888
        from bzrlib.reconcile import reconcile
 
889
        dir = bzrlib.bzrdir.BzrDir.open(branch)
 
890
        reconcile(dir)
 
891
 
 
892
 
 
893
class cmd_revision_history(Command):
 
894
    """Display list of revision ids on this branch."""
 
895
    hidden = True
 
896
 
 
897
    @display_command
 
898
    def run(self):
 
899
        branch = WorkingTree.open_containing(u'.')[0].branch
 
900
        for patchid in branch.revision_history():
 
901
            self.outf.write(patchid)
 
902
            self.outf.write('\n')
 
903
 
 
904
 
 
905
class cmd_ancestry(Command):
 
906
    """List all revisions merged into this branch."""
 
907
    hidden = True
 
908
 
 
909
    @display_command
 
910
    def run(self):
 
911
        tree = WorkingTree.open_containing(u'.')[0]
 
912
        b = tree.branch
 
913
        # FIXME. should be tree.last_revision
 
914
        for revision_id in b.repository.get_ancestry(b.last_revision()):
 
915
            if revision_id is None:
 
916
                continue
 
917
            self.outf.write(revision_id)
 
918
            self.outf.write('\n')
 
919
 
 
920
 
 
921
class cmd_init(Command):
 
922
    """Make a directory into a versioned branch.
 
923
 
 
924
    Use this to create an empty branch, or before importing an
 
925
    existing project.
 
926
 
 
927
    If there is a repository in a parent directory of the location, then 
 
928
    the history of the branch will be stored in the repository.  Otherwise
 
929
    init creates a standalone branch which carries its own history in 
 
930
    .bzr.
 
931
 
 
932
    If there is already a branch at the location but it has no working tree,
 
933
    the tree can be populated with 'bzr checkout'.
 
934
 
 
935
    Recipe for importing a tree of files:
 
936
        cd ~/project
 
937
        bzr init
 
938
        bzr add .
 
939
        bzr status
 
940
        bzr commit -m 'imported project'
 
941
    """
 
942
    takes_args = ['location?']
 
943
    takes_options = [
 
944
                     Option('format', 
 
945
                            help='Create a specific format rather than the'
 
946
                                 ' current default format. Currently this '
 
947
                                 ' option only accepts "metadir"',
 
948
                            type=get_format_type),
 
949
                     ]
 
950
    def run(self, location=None, format=None):
 
951
        from bzrlib.branch import Branch
 
952
        if location is None:
 
953
            location = u'.'
 
954
        else:
 
955
            # The path has to exist to initialize a
 
956
            # branch inside of it.
 
957
            # Just using os.mkdir, since I don't
 
958
            # believe that we want to create a bunch of
 
959
            # locations if the user supplies an extended path
 
960
            if not os.path.exists(location):
 
961
                os.mkdir(location)
 
962
        try:
 
963
            existing_bzrdir = bzrdir.BzrDir.open(location)
 
964
        except NotBranchError:
 
965
            # really a NotBzrDir error...
 
966
            bzrdir.BzrDir.create_branch_convenience(location, format=format)
 
967
        else:
 
968
            if existing_bzrdir.has_branch():
 
969
                if existing_bzrdir.has_workingtree():
 
970
                    raise errors.AlreadyBranchError(location)
 
971
                else:
 
972
                    raise errors.BranchExistsWithoutWorkingTree(location)
 
973
            else:
 
974
                existing_bzrdir.create_branch()
 
975
                existing_bzrdir.create_workingtree()
 
976
 
 
977
 
 
978
class cmd_init_repository(Command):
 
979
    """Create a shared repository to hold branches.
 
980
 
 
981
    New branches created under the repository directory will store their revisions
 
982
    in the repository, not in the branch directory, if the branch format supports
 
983
    shared storage.
 
984
 
 
985
    example:
 
986
        bzr init-repo repo
 
987
        bzr init repo/trunk
 
988
        bzr checkout --lightweight repo/trunk trunk-checkout
 
989
        cd trunk-checkout
 
990
        (add files here)
 
991
    """
 
992
    takes_args = ["location"] 
 
993
    takes_options = [Option('format', 
 
994
                            help='Use a specific format rather than the'
 
995
                            ' current default format. Currently this'
 
996
                            ' option accepts "weave", "metadir" and "knit"',
 
997
                            type=get_format_type),
 
998
                     Option('trees',
 
999
                             help='Allows branches in repository to have'
 
1000
                             ' a working tree')]
 
1001
    aliases = ["init-repo"]
 
1002
    def run(self, location, format=None, trees=False):
 
1003
        from bzrlib.bzrdir import BzrDirMetaFormat1
 
1004
        from bzrlib.transport import get_transport
 
1005
        if format is None:
 
1006
            format = BzrDirMetaFormat1()
 
1007
        transport = get_transport(location)
 
1008
        if not transport.has('.'):
 
1009
            transport.mkdir('')
 
1010
        newdir = format.initialize_on_transport(transport)
 
1011
        repo = newdir.create_repository(shared=True)
 
1012
        repo.set_make_working_trees(trees)
 
1013
 
 
1014
 
 
1015
class cmd_diff(Command):
 
1016
    """Show differences in working tree.
 
1017
    
 
1018
    If files are listed, only the changes in those files are listed.
 
1019
    Otherwise, all changes for the tree are listed.
 
1020
 
 
1021
    examples:
 
1022
        bzr diff
 
1023
        bzr diff -r1
 
1024
        bzr diff -r1..2
 
1025
    """
 
1026
    # TODO: Allow diff across branches.
 
1027
    # TODO: Option to use external diff command; could be GNU diff, wdiff,
 
1028
    #       or a graphical diff.
 
1029
 
 
1030
    # TODO: Python difflib is not exactly the same as unidiff; should
 
1031
    #       either fix it up or prefer to use an external diff.
 
1032
 
 
1033
    # TODO: If a directory is given, diff everything under that.
 
1034
 
 
1035
    # TODO: Selected-file diff is inefficient and doesn't show you
 
1036
    #       deleted files.
 
1037
 
 
1038
    # TODO: This probably handles non-Unix newlines poorly.
 
1039
    
 
1040
    takes_args = ['file*']
 
1041
    takes_options = ['revision', 'diff-options']
 
1042
    aliases = ['di', 'dif']
 
1043
    encoding_type = 'exact'
 
1044
 
 
1045
    @display_command
 
1046
    def run(self, revision=None, file_list=None, diff_options=None):
 
1047
        from bzrlib.diff import diff_cmd_helper, show_diff_trees
 
1048
        try:
 
1049
            tree1, file_list = internal_tree_files(file_list)
 
1050
            tree2 = None
 
1051
            b = None
 
1052
            b2 = None
 
1053
        except FileInWrongBranch:
 
1054
            if len(file_list) != 2:
 
1055
                raise BzrCommandError("Files are in different branches")
 
1056
 
 
1057
            tree1, file1 = WorkingTree.open_containing(file_list[0])
 
1058
            tree2, file2 = WorkingTree.open_containing(file_list[1])
 
1059
            if file1 != "" or file2 != "":
 
1060
                # FIXME diff those two files. rbc 20051123
 
1061
                raise BzrCommandError("Files are in different branches")
 
1062
            file_list = None
 
1063
        if revision is not None:
 
1064
            if tree2 is not None:
 
1065
                raise BzrCommandError("Can't specify -r with two branches")
 
1066
            if (len(revision) == 1) or (revision[1].spec is None):
 
1067
                return diff_cmd_helper(tree1, file_list, diff_options,
 
1068
                                       revision[0])
 
1069
            elif len(revision) == 2:
 
1070
                return diff_cmd_helper(tree1, file_list, diff_options,
 
1071
                                       revision[0], revision[1])
 
1072
            else:
 
1073
                raise BzrCommandError('bzr diff --revision takes exactly one or two revision identifiers')
 
1074
        else:
 
1075
            if tree2 is not None:
 
1076
                return show_diff_trees(tree1, tree2, sys.stdout, 
 
1077
                                       specific_files=file_list,
 
1078
                                       external_diff_options=diff_options)
 
1079
            else:
 
1080
                return diff_cmd_helper(tree1, file_list, diff_options)
 
1081
 
 
1082
 
 
1083
class cmd_deleted(Command):
 
1084
    """List files deleted in the working tree.
 
1085
    """
 
1086
    # TODO: Show files deleted since a previous revision, or
 
1087
    # between two revisions.
 
1088
    # TODO: Much more efficient way to do this: read in new
 
1089
    # directories with readdir, rather than stating each one.  Same
 
1090
    # level of effort but possibly much less IO.  (Or possibly not,
 
1091
    # if the directories are very large...)
 
1092
    takes_options = ['show-ids']
 
1093
 
 
1094
    @display_command
 
1095
    def run(self, show_ids=False):
 
1096
        tree = WorkingTree.open_containing(u'.')[0]
 
1097
        old = tree.basis_tree()
 
1098
        for path, ie in old.inventory.iter_entries():
 
1099
            if not tree.has_id(ie.file_id):
 
1100
                self.outf.write(path)
 
1101
                if show_ids:
 
1102
                    self.outf.write(' ')
 
1103
                    self.outf.write(ie.file_id)
 
1104
                self.outf.write('\n')
 
1105
 
 
1106
 
 
1107
class cmd_modified(Command):
 
1108
    """List files modified in working tree."""
 
1109
    hidden = True
 
1110
    @display_command
 
1111
    def run(self):
 
1112
        from bzrlib.delta import compare_trees
 
1113
 
 
1114
        tree = WorkingTree.open_containing(u'.')[0]
 
1115
        td = compare_trees(tree.basis_tree(), tree)
 
1116
 
 
1117
        for path, id, kind, text_modified, meta_modified in td.modified:
 
1118
            self.outf.write(path)
 
1119
            self.outf.write('\n')
 
1120
 
 
1121
 
 
1122
class cmd_added(Command):
 
1123
    """List files added in working tree."""
 
1124
    hidden = True
 
1125
    @display_command
 
1126
    def run(self):
 
1127
        wt = WorkingTree.open_containing(u'.')[0]
 
1128
        basis_inv = wt.basis_tree().inventory
 
1129
        inv = wt.inventory
 
1130
        for file_id in inv:
 
1131
            if file_id in basis_inv:
 
1132
                continue
 
1133
            path = inv.id2path(file_id)
 
1134
            if not os.access(bzrlib.osutils.abspath(path), os.F_OK):
 
1135
                continue
 
1136
            self.outf.write(path)
 
1137
            self.outf.write('\n')
 
1138
 
 
1139
 
 
1140
class cmd_root(Command):
 
1141
    """Show the tree root directory.
 
1142
 
 
1143
    The root is the nearest enclosing directory with a .bzr control
 
1144
    directory."""
 
1145
    takes_args = ['filename?']
 
1146
    @display_command
 
1147
    def run(self, filename=None):
 
1148
        """Print the branch root."""
 
1149
        tree = WorkingTree.open_containing(filename)[0]
 
1150
        self.outf.write(tree.basedir)
 
1151
        self.outf.write('\n')
 
1152
 
 
1153
 
 
1154
class cmd_log(Command):
 
1155
    """Show log of a branch, file, or directory.
 
1156
 
 
1157
    By default show the log of the branch containing the working directory.
 
1158
 
 
1159
    To request a range of logs, you can use the command -r begin..end
 
1160
    -r revision requests a specific revision, -r ..end or -r begin.. are
 
1161
    also valid.
 
1162
 
 
1163
    examples:
 
1164
        bzr log
 
1165
        bzr log foo.c
 
1166
        bzr log -r -10.. http://server/branch
 
1167
    """
 
1168
 
 
1169
    # TODO: Make --revision support uuid: and hash: [future tag:] notation.
 
1170
 
 
1171
    takes_args = ['location?']
 
1172
    takes_options = [Option('forward', 
 
1173
                            help='show from oldest to newest'),
 
1174
                     'timezone', 
 
1175
                     Option('verbose', 
 
1176
                             help='show files changed in each revision'),
 
1177
                     'show-ids', 'revision',
 
1178
                     'log-format',
 
1179
                     'line', 'long', 
 
1180
                     Option('message',
 
1181
                            help='show revisions whose message matches this regexp',
 
1182
                            type=str),
 
1183
                     'short',
 
1184
                     ]
 
1185
    encoding_type = 'replace'
 
1186
 
 
1187
    @display_command
 
1188
    def run(self, location=None, timezone='original',
 
1189
            verbose=False,
 
1190
            show_ids=False,
 
1191
            forward=False,
 
1192
            revision=None,
 
1193
            log_format=None,
 
1194
            message=None,
 
1195
            long=False,
 
1196
            short=False,
 
1197
            line=False):
 
1198
        from bzrlib.log import log_formatter, show_log
 
1199
        assert message is None or isinstance(message, basestring), \
 
1200
            "invalid message argument %r" % message
 
1201
        direction = (forward and 'forward') or 'reverse'
 
1202
        
 
1203
        # log everything
 
1204
        file_id = None
 
1205
        if location:
 
1206
            # find the file id to log:
 
1207
 
 
1208
            dir, fp = bzrdir.BzrDir.open_containing(location)
 
1209
            b = dir.open_branch()
 
1210
            if fp != '':
 
1211
                try:
 
1212
                    # might be a tree:
 
1213
                    inv = dir.open_workingtree().inventory
 
1214
                except (errors.NotBranchError, errors.NotLocalUrl):
 
1215
                    # either no tree, or is remote.
 
1216
                    inv = b.basis_tree().inventory
 
1217
                file_id = inv.path2id(fp)
 
1218
        else:
 
1219
            # local dir only
 
1220
            # FIXME ? log the current subdir only RBC 20060203 
 
1221
            dir, relpath = bzrdir.BzrDir.open_containing('.')
 
1222
            b = dir.open_branch()
 
1223
 
 
1224
        if revision is None:
 
1225
            rev1 = None
 
1226
            rev2 = None
 
1227
        elif len(revision) == 1:
 
1228
            rev1 = rev2 = revision[0].in_history(b).revno
 
1229
        elif len(revision) == 2:
 
1230
            if revision[0].spec is None:
 
1231
                # missing begin-range means first revision
 
1232
                rev1 = 1
 
1233
            else:
 
1234
                rev1 = revision[0].in_history(b).revno
 
1235
 
 
1236
            if revision[1].spec is None:
 
1237
                # missing end-range means last known revision
 
1238
                rev2 = b.revno()
 
1239
            else:
 
1240
                rev2 = revision[1].in_history(b).revno
 
1241
        else:
 
1242
            raise BzrCommandError('bzr log --revision takes one or two values.')
 
1243
 
 
1244
        # By this point, the revision numbers are converted to the +ve
 
1245
        # form if they were supplied in the -ve form, so we can do
 
1246
        # this comparison in relative safety
 
1247
        if rev1 > rev2:
 
1248
            (rev2, rev1) = (rev1, rev2)
 
1249
 
 
1250
        if (log_format == None):
 
1251
            default = bzrlib.config.BranchConfig(b).log_format()
 
1252
            log_format = get_log_format(long=long, short=short, line=line, default=default)
 
1253
        lf = log_formatter(log_format,
 
1254
                           show_ids=show_ids,
 
1255
                           to_file=self.outf,
 
1256
                           show_timezone=timezone)
 
1257
 
 
1258
        show_log(b,
 
1259
                 lf,
 
1260
                 file_id,
 
1261
                 verbose=verbose,
 
1262
                 direction=direction,
 
1263
                 start_revision=rev1,
 
1264
                 end_revision=rev2,
 
1265
                 search=message)
 
1266
 
 
1267
 
 
1268
def get_log_format(long=False, short=False, line=False, default='long'):
 
1269
    log_format = default
 
1270
    if long:
 
1271
        log_format = 'long'
 
1272
    if short:
 
1273
        log_format = 'short'
 
1274
    if line:
 
1275
        log_format = 'line'
 
1276
    return log_format
 
1277
 
 
1278
 
 
1279
class cmd_touching_revisions(Command):
 
1280
    """Return revision-ids which affected a particular file.
 
1281
 
 
1282
    A more user-friendly interface is "bzr log FILE"."""
 
1283
    hidden = True
 
1284
    takes_args = ["filename"]
 
1285
    encoding_type = 'replace'
 
1286
 
 
1287
    @display_command
 
1288
    def run(self, filename):
 
1289
        tree, relpath = WorkingTree.open_containing(filename)
 
1290
        b = tree.branch
 
1291
        inv = tree.read_working_inventory()
 
1292
        file_id = inv.path2id(relpath)
 
1293
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
 
1294
            self.outf.write("%6d %s\n" % (revno, what))
 
1295
 
 
1296
 
 
1297
class cmd_ls(Command):
 
1298
    """List files in a tree.
 
1299
    """
 
1300
    # TODO: Take a revision or remote path and list that tree instead.
 
1301
    hidden = True
 
1302
    takes_options = ['verbose', 'revision',
 
1303
                     Option('non-recursive',
 
1304
                            help='don\'t recurse into sub-directories'),
 
1305
                     Option('from-root',
 
1306
                            help='Print all paths from the root of the branch.'),
 
1307
                     Option('unknown', help='Print unknown files'),
 
1308
                     Option('versioned', help='Print versioned files'),
 
1309
                     Option('ignored', help='Print ignored files'),
 
1310
 
 
1311
                     Option('null', help='Null separate the files'),
 
1312
                    ]
 
1313
    @display_command
 
1314
    def run(self, revision=None, verbose=False, 
 
1315
            non_recursive=False, from_root=False,
 
1316
            unknown=False, versioned=False, ignored=False,
 
1317
            null=False):
 
1318
 
 
1319
        if verbose and null:
 
1320
            raise BzrCommandError('Cannot set both --verbose and --null')
 
1321
        all = not (unknown or versioned or ignored)
 
1322
 
 
1323
        selection = {'I':ignored, '?':unknown, 'V':versioned}
 
1324
 
 
1325
        tree, relpath = WorkingTree.open_containing(u'.')
 
1326
        if from_root:
 
1327
            relpath = u''
 
1328
        elif relpath:
 
1329
            relpath += '/'
 
1330
        if revision is not None:
 
1331
            tree = tree.branch.repository.revision_tree(
 
1332
                revision[0].in_history(tree.branch).rev_id)
 
1333
 
 
1334
        for fp, fc, kind, fid, entry in tree.list_files():
 
1335
            if fp.startswith(relpath):
 
1336
                fp = fp[len(relpath):]
 
1337
                if non_recursive and '/' in fp:
 
1338
                    continue
 
1339
                if not all and not selection[fc]:
 
1340
                    continue
 
1341
                if verbose:
 
1342
                    kindch = entry.kind_character()
 
1343
                    self.outf.write('%-8s %s%s\n' % (fc, fp, kindch))
 
1344
                elif null:
 
1345
                    self.outf.write(fp)
 
1346
                    self.outf.write('\0')
 
1347
                    self.outf.flush()
 
1348
                else:
 
1349
                    self.outf.write(fp)
 
1350
                    self.outf.write('\n')
 
1351
 
 
1352
 
 
1353
class cmd_unknowns(Command):
 
1354
    """List unknown files."""
 
1355
    @display_command
 
1356
    def run(self):
 
1357
        from bzrlib.osutils import quotefn
 
1358
        for f in WorkingTree.open_containing(u'.')[0].unknowns():
 
1359
            self.outf.write(quotefn(f))
 
1360
            self.outf.write('\n')
 
1361
 
 
1362
 
 
1363
class cmd_ignore(Command):
 
1364
    """Ignore a command or pattern.
 
1365
 
 
1366
    To remove patterns from the ignore list, edit the .bzrignore file.
 
1367
 
 
1368
    If the pattern contains a slash, it is compared to the whole path
 
1369
    from the branch root.  Otherwise, it is compared to only the last
 
1370
    component of the path.  To match a file only in the root directory,
 
1371
    prepend './'.
 
1372
 
 
1373
    Ignore patterns are case-insensitive on case-insensitive systems.
 
1374
 
 
1375
    Note: wildcards must be quoted from the shell on Unix.
 
1376
 
 
1377
    examples:
 
1378
        bzr ignore ./Makefile
 
1379
        bzr ignore '*.class'
 
1380
    """
 
1381
    # TODO: Complain if the filename is absolute
 
1382
    takes_args = ['name_pattern']
 
1383
    
 
1384
    def run(self, name_pattern):
 
1385
        from bzrlib.atomicfile import AtomicFile
 
1386
        import os.path
 
1387
 
 
1388
        tree, relpath = WorkingTree.open_containing(u'.')
 
1389
        ifn = tree.abspath('.bzrignore')
 
1390
 
 
1391
        if os.path.exists(ifn):
 
1392
            f = open(ifn, 'rt')
 
1393
            try:
 
1394
                igns = f.read().decode('utf-8')
 
1395
            finally:
 
1396
                f.close()
 
1397
        else:
 
1398
            igns = ''
 
1399
 
 
1400
        # TODO: If the file already uses crlf-style termination, maybe
 
1401
        # we should use that for the newly added lines?
 
1402
 
 
1403
        if igns and igns[-1] != '\n':
 
1404
            igns += '\n'
 
1405
        igns += name_pattern + '\n'
 
1406
 
 
1407
        try:
 
1408
            f = AtomicFile(ifn, 'wt')
 
1409
            f.write(igns.encode('utf-8'))
 
1410
            f.commit()
 
1411
        finally:
 
1412
            f.close()
 
1413
 
 
1414
        inv = tree.inventory
 
1415
        if inv.path2id('.bzrignore'):
 
1416
            mutter('.bzrignore is already versioned')
 
1417
        else:
 
1418
            mutter('need to make new .bzrignore file versioned')
 
1419
            tree.add(['.bzrignore'])
 
1420
 
 
1421
 
 
1422
class cmd_ignored(Command):
 
1423
    """List ignored files and the patterns that matched them.
 
1424
 
 
1425
    See also: bzr ignore"""
 
1426
    @display_command
 
1427
    def run(self):
 
1428
        tree = WorkingTree.open_containing(u'.')[0]
 
1429
        for path, file_class, kind, file_id, entry in tree.list_files():
 
1430
            if file_class != 'I':
 
1431
                continue
 
1432
            ## XXX: Slightly inefficient since this was already calculated
 
1433
            pat = tree.is_ignored(path)
 
1434
            print '%-50s %s' % (path, pat)
 
1435
 
 
1436
 
 
1437
class cmd_lookup_revision(Command):
 
1438
    """Lookup the revision-id from a revision-number
 
1439
 
 
1440
    example:
 
1441
        bzr lookup-revision 33
 
1442
    """
 
1443
    hidden = True
 
1444
    takes_args = ['revno']
 
1445
    
 
1446
    @display_command
 
1447
    def run(self, revno):
 
1448
        try:
 
1449
            revno = int(revno)
 
1450
        except ValueError:
 
1451
            raise BzrCommandError("not a valid revision-number: %r" % revno)
 
1452
 
 
1453
        print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
 
1454
 
 
1455
 
 
1456
class cmd_export(Command):
 
1457
    """Export past revision to destination directory.
 
1458
 
 
1459
    If no revision is specified this exports the last committed revision.
 
1460
 
 
1461
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
 
1462
    given, try to find the format with the extension. If no extension
 
1463
    is found exports to a directory (equivalent to --format=dir).
 
1464
 
 
1465
    Root may be the top directory for tar, tgz and tbz2 formats. If none
 
1466
    is given, the top directory will be the root name of the file.
 
1467
 
 
1468
    Note: export of tree with non-ascii filenames to zip is not supported.
 
1469
 
 
1470
     Supported formats       Autodetected by extension
 
1471
     -----------------       -------------------------
 
1472
         dir                            -
 
1473
         tar                          .tar
 
1474
         tbz2                    .tar.bz2, .tbz2
 
1475
         tgz                      .tar.gz, .tgz
 
1476
         zip                          .zip
 
1477
    """
 
1478
    takes_args = ['dest']
 
1479
    takes_options = ['revision', 'format', 'root']
 
1480
    def run(self, dest, revision=None, format=None, root=None):
 
1481
        import os.path
 
1482
        from bzrlib.export import export
 
1483
        tree = WorkingTree.open_containing(u'.')[0]
 
1484
        b = tree.branch
 
1485
        if revision is None:
 
1486
            # should be tree.last_revision  FIXME
 
1487
            rev_id = b.last_revision()
 
1488
        else:
 
1489
            if len(revision) != 1:
 
1490
                raise BzrError('bzr export --revision takes exactly 1 argument')
 
1491
            rev_id = revision[0].in_history(b).rev_id
 
1492
        t = b.repository.revision_tree(rev_id)
 
1493
        try:
 
1494
            export(t, dest, format, root)
 
1495
        except errors.NoSuchExportFormat, e:
 
1496
            raise BzrCommandError('Unsupported export format: %s' % e.format)
 
1497
 
 
1498
 
 
1499
class cmd_cat(Command):
 
1500
    """Write a file's text from a previous revision."""
 
1501
 
 
1502
    takes_options = ['revision']
 
1503
    takes_args = ['filename']
 
1504
 
 
1505
    @display_command
 
1506
    def run(self, filename, revision=None):
 
1507
        if revision is not None and len(revision) != 1:
 
1508
            raise BzrCommandError("bzr cat --revision takes exactly one number")
 
1509
        tree = None
 
1510
        try:
 
1511
            tree, relpath = WorkingTree.open_containing(filename)
 
1512
            b = tree.branch
 
1513
        except NotBranchError:
 
1514
            pass
 
1515
 
 
1516
        if tree is None:
 
1517
            b, relpath = Branch.open_containing(filename)
 
1518
        if revision is None:
 
1519
            revision_id = b.last_revision()
 
1520
        else:
 
1521
            revision_id = revision[0].in_history(b).rev_id
 
1522
        b.print_file(relpath, revision_id)
 
1523
 
 
1524
 
 
1525
class cmd_local_time_offset(Command):
 
1526
    """Show the offset in seconds from GMT to local time."""
 
1527
    hidden = True    
 
1528
    @display_command
 
1529
    def run(self):
 
1530
        print bzrlib.osutils.local_time_offset()
 
1531
 
 
1532
 
 
1533
 
 
1534
class cmd_commit(Command):
 
1535
    """Commit changes into a new revision.
 
1536
    
 
1537
    If no arguments are given, the entire tree is committed.
 
1538
 
 
1539
    If selected files are specified, only changes to those files are
 
1540
    committed.  If a directory is specified then the directory and everything 
 
1541
    within it is committed.
 
1542
 
 
1543
    A selected-file commit may fail in some cases where the committed
 
1544
    tree would be invalid, such as trying to commit a file in a
 
1545
    newly-added directory that is not itself committed.
 
1546
    """
 
1547
    # TODO: Run hooks on tree to-be-committed, and after commit.
 
1548
 
 
1549
    # TODO: Strict commit that fails if there are deleted files.
 
1550
    #       (what does "deleted files" mean ??)
 
1551
 
 
1552
    # TODO: Give better message for -s, --summary, used by tla people
 
1553
 
 
1554
    # XXX: verbose currently does nothing
 
1555
 
 
1556
    takes_args = ['selected*']
 
1557
    takes_options = ['message', 'verbose', 
 
1558
                     Option('unchanged',
 
1559
                            help='commit even if nothing has changed'),
 
1560
                     Option('file', type=str, 
 
1561
                            argname='msgfile',
 
1562
                            help='file containing commit message'),
 
1563
                     Option('strict',
 
1564
                            help="refuse to commit if there are unknown "
 
1565
                            "files in the working tree."),
 
1566
                     Option('local',
 
1567
                            help="perform a local only commit in a bound "
 
1568
                                 "branch. Such commits are not pushed to "
 
1569
                                 "the master branch until a normal commit "
 
1570
                                 "is performed."
 
1571
                            ),
 
1572
                     ]
 
1573
    aliases = ['ci', 'checkin']
 
1574
 
 
1575
    def run(self, message=None, file=None, verbose=True, selected_list=None,
 
1576
            unchanged=False, strict=False, local=False):
 
1577
        from bzrlib.commit import (NullCommitReporter, ReportCommitToLog)
 
1578
        from bzrlib.errors import (PointlessCommit, ConflictsInTree,
 
1579
                StrictCommitFailed)
 
1580
        from bzrlib.msgeditor import edit_commit_message, \
 
1581
                make_commit_message_template
 
1582
        from tempfile import TemporaryFile
 
1583
 
 
1584
        # TODO: Need a blackbox test for invoking the external editor; may be
 
1585
        # slightly problematic to run this cross-platform.
 
1586
 
 
1587
        # TODO: do more checks that the commit will succeed before 
 
1588
        # spending the user's valuable time typing a commit message.
 
1589
        #
 
1590
        # TODO: if the commit *does* happen to fail, then save the commit 
 
1591
        # message to a temporary file where it can be recovered
 
1592
        tree, selected_list = tree_files(selected_list)
 
1593
        if local and not tree.branch.get_bound_location():
 
1594
            raise errors.LocalRequiresBoundBranch()
 
1595
        if message is None and not file:
 
1596
            template = make_commit_message_template(tree, selected_list)
 
1597
            message = edit_commit_message(template)
 
1598
            if message is None:
 
1599
                raise BzrCommandError("please specify a commit message"
 
1600
                                      " with either --message or --file")
 
1601
        elif message and file:
 
1602
            raise BzrCommandError("please specify either --message or --file")
 
1603
        
 
1604
        if file:
 
1605
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
 
1606
 
 
1607
        if message == "":
 
1608
                raise BzrCommandError("empty commit message specified")
 
1609
        
 
1610
        if verbose:
 
1611
            reporter = ReportCommitToLog()
 
1612
        else:
 
1613
            reporter = NullCommitReporter()
 
1614
        
 
1615
        try:
 
1616
            tree.commit(message, specific_files=selected_list,
 
1617
                        allow_pointless=unchanged, strict=strict, local=local,
 
1618
                        reporter=reporter)
 
1619
        except PointlessCommit:
 
1620
            # FIXME: This should really happen before the file is read in;
 
1621
            # perhaps prepare the commit; get the message; then actually commit
 
1622
            raise BzrCommandError("no changes to commit",
 
1623
                                  ["use --unchanged to commit anyhow"])
 
1624
        except ConflictsInTree:
 
1625
            raise BzrCommandError("Conflicts detected in working tree.  "
 
1626
                'Use "bzr conflicts" to list, "bzr resolve FILE" to resolve.')
 
1627
        except StrictCommitFailed:
 
1628
            raise BzrCommandError("Commit refused because there are unknown "
 
1629
                                  "files in the working tree.")
 
1630
        except errors.BoundBranchOutOfDate, e:
 
1631
            raise BzrCommandError(str(e)
 
1632
                                  + ' Either unbind, update, or'
 
1633
                                    ' pass --local to commit.')
 
1634
 
 
1635
 
 
1636
class cmd_check(Command):
 
1637
    """Validate consistency of branch history.
 
1638
 
 
1639
    This command checks various invariants about the branch storage to
 
1640
    detect data corruption or bzr bugs.
 
1641
    """
 
1642
    takes_args = ['branch?']
 
1643
    takes_options = ['verbose']
 
1644
 
 
1645
    def run(self, branch=None, verbose=False):
 
1646
        from bzrlib.check import check
 
1647
        if branch is None:
 
1648
            tree = WorkingTree.open_containing()[0]
 
1649
            branch = tree.branch
 
1650
        else:
 
1651
            branch = Branch.open(branch)
 
1652
        check(branch, verbose)
 
1653
 
 
1654
 
 
1655
class cmd_scan_cache(Command):
 
1656
    hidden = True
 
1657
    def run(self):
 
1658
        from bzrlib.hashcache import HashCache
 
1659
 
 
1660
        c = HashCache(u'.')
 
1661
        c.read()
 
1662
        c.scan()
 
1663
            
 
1664
        print '%6d stats' % c.stat_count
 
1665
        print '%6d in hashcache' % len(c._cache)
 
1666
        print '%6d files removed from cache' % c.removed_count
 
1667
        print '%6d hashes updated' % c.update_count
 
1668
        print '%6d files changed too recently to cache' % c.danger_count
 
1669
 
 
1670
        if c.needs_write:
 
1671
            c.write()
 
1672
 
 
1673
 
 
1674
class cmd_upgrade(Command):
 
1675
    """Upgrade branch storage to current format.
 
1676
 
 
1677
    The check command or bzr developers may sometimes advise you to run
 
1678
    this command. When the default format has changed you may also be warned
 
1679
    during other operations to upgrade.
 
1680
    """
 
1681
    takes_args = ['url?']
 
1682
    takes_options = [
 
1683
                     Option('format', 
 
1684
                            help='Upgrade to a specific format rather than the'
 
1685
                                 ' current default format. Currently this'
 
1686
                                 ' option accepts "weave", "metadir" and'
 
1687
                                 ' "knit".',
 
1688
                            type=get_format_type),
 
1689
                    ]
 
1690
 
 
1691
 
 
1692
    def run(self, url='.', format=None):
 
1693
        from bzrlib.upgrade import upgrade
 
1694
        upgrade(url, format)
 
1695
 
 
1696
 
 
1697
class cmd_whoami(Command):
 
1698
    """Show bzr user id."""
 
1699
    takes_options = ['email']
 
1700
    
 
1701
    @display_command
 
1702
    def run(self, email=False):
 
1703
        try:
 
1704
            b = WorkingTree.open_containing(u'.')[0].branch
 
1705
            config = bzrlib.config.BranchConfig(b)
 
1706
        except NotBranchError:
 
1707
            config = bzrlib.config.GlobalConfig()
 
1708
        
 
1709
        if email:
 
1710
            print config.user_email()
 
1711
        else:
 
1712
            print config.username()
 
1713
 
 
1714
 
 
1715
class cmd_nick(Command):
 
1716
    """Print or set the branch nickname.  
 
1717
 
 
1718
    If unset, the tree root directory name is used as the nickname
 
1719
    To print the current nickname, execute with no argument.  
 
1720
    """
 
1721
    takes_args = ['nickname?']
 
1722
    def run(self, nickname=None):
 
1723
        branch = Branch.open_containing(u'.')[0]
 
1724
        if nickname is None:
 
1725
            self.printme(branch)
 
1726
        else:
 
1727
            branch.nick = nickname
 
1728
 
 
1729
    @display_command
 
1730
    def printme(self, branch):
 
1731
        print branch.nick 
 
1732
 
 
1733
 
 
1734
class cmd_selftest(Command):
 
1735
    """Run internal test suite.
 
1736
    
 
1737
    This creates temporary test directories in the working directory,
 
1738
    but not existing data is affected.  These directories are deleted
 
1739
    if the tests pass, or left behind to help in debugging if they
 
1740
    fail and --keep-output is specified.
 
1741
    
 
1742
    If arguments are given, they are regular expressions that say
 
1743
    which tests should run.
 
1744
 
 
1745
    If the global option '--no-plugins' is given, plugins are not loaded
 
1746
    before running the selftests.  This has two effects: features provided or
 
1747
    modified by plugins will not be tested, and tests provided by plugins will
 
1748
    not be run.
 
1749
 
 
1750
    examples:
 
1751
        bzr selftest ignore
 
1752
        bzr --no-plugins selftest -v
 
1753
    """
 
1754
    # TODO: --list should give a list of all available tests
 
1755
 
 
1756
    # NB: this is used from the class without creating an instance, which is
 
1757
    # why it does not have a self parameter.
 
1758
    def get_transport_type(typestring):
 
1759
        """Parse and return a transport specifier."""
 
1760
        if typestring == "sftp":
 
1761
            from bzrlib.transport.sftp import SFTPAbsoluteServer
 
1762
            return SFTPAbsoluteServer
 
1763
        if typestring == "memory":
 
1764
            from bzrlib.transport.memory import MemoryServer
 
1765
            return MemoryServer
 
1766
        if typestring == "fakenfs":
 
1767
            from bzrlib.transport.fakenfs import FakeNFSServer
 
1768
            return FakeNFSServer
 
1769
        msg = "No known transport type %s. Supported types are: sftp\n" %\
 
1770
            (typestring)
 
1771
        raise BzrCommandError(msg)
 
1772
 
 
1773
    hidden = True
 
1774
    takes_args = ['testspecs*']
 
1775
    takes_options = ['verbose',
 
1776
                     Option('one', help='stop when one test fails'),
 
1777
                     Option('keep-output', 
 
1778
                            help='keep output directories when tests fail'),
 
1779
                     Option('transport', 
 
1780
                            help='Use a different transport by default '
 
1781
                                 'throughout the test suite.',
 
1782
                            type=get_transport_type),
 
1783
                    ]
 
1784
 
 
1785
    def run(self, testspecs_list=None, verbose=False, one=False,
 
1786
            keep_output=False, transport=None):
 
1787
        import bzrlib.ui
 
1788
        from bzrlib.tests import selftest
 
1789
        # we don't want progress meters from the tests to go to the
 
1790
        # real output; and we don't want log messages cluttering up
 
1791
        # the real logs.
 
1792
        save_ui = bzrlib.ui.ui_factory
 
1793
        bzrlib.trace.info('running tests...')
 
1794
        try:
 
1795
            bzrlib.ui.ui_factory = bzrlib.ui.SilentUIFactory()
 
1796
            if testspecs_list is not None:
 
1797
                pattern = '|'.join(testspecs_list)
 
1798
            else:
 
1799
                pattern = ".*"
 
1800
            result = selftest(verbose=verbose, 
 
1801
                              pattern=pattern,
 
1802
                              stop_on_failure=one, 
 
1803
                              keep_output=keep_output,
 
1804
                              transport=transport)
 
1805
            if result:
 
1806
                bzrlib.trace.info('tests passed')
 
1807
            else:
 
1808
                bzrlib.trace.info('tests failed')
 
1809
            return int(not result)
 
1810
        finally:
 
1811
            bzrlib.ui.ui_factory = save_ui
 
1812
 
 
1813
 
 
1814
def _get_bzr_branch():
 
1815
    """If bzr is run from a branch, return Branch or None"""
 
1816
    import bzrlib.errors
 
1817
    from bzrlib.branch import Branch
 
1818
    from bzrlib.osutils import abspath
 
1819
    from os.path import dirname
 
1820
    
 
1821
    try:
 
1822
        branch = Branch.open(dirname(abspath(dirname(__file__))))
 
1823
        return branch
 
1824
    except bzrlib.errors.BzrError:
 
1825
        return None
 
1826
    
 
1827
 
 
1828
def show_version():
 
1829
    print "bzr (bazaar-ng) %s" % bzrlib.__version__
 
1830
    # is bzrlib itself in a branch?
 
1831
    branch = _get_bzr_branch()
 
1832
    if branch:
 
1833
        rh = branch.revision_history()
 
1834
        revno = len(rh)
 
1835
        print "  bzr checkout, revision %d" % (revno,)
 
1836
        print "  nick: %s" % (branch.nick,)
 
1837
        if rh:
 
1838
            print "  revid: %s" % (rh[-1],)
 
1839
    print bzrlib.__copyright__
 
1840
    print "http://bazaar-ng.org/"
 
1841
    print
 
1842
    print "bzr comes with ABSOLUTELY NO WARRANTY.  bzr is free software, and"
 
1843
    print "you may use, modify and redistribute it under the terms of the GNU"
 
1844
    print "General Public License version 2 or later."
 
1845
 
 
1846
 
 
1847
class cmd_version(Command):
 
1848
    """Show version of bzr."""
 
1849
    @display_command
 
1850
    def run(self):
 
1851
        show_version()
 
1852
 
 
1853
class cmd_rocks(Command):
 
1854
    """Statement of optimism."""
 
1855
    hidden = True
 
1856
    @display_command
 
1857
    def run(self):
 
1858
        print "it sure does!"
 
1859
 
 
1860
 
 
1861
class cmd_find_merge_base(Command):
 
1862
    """Find and print a base revision for merging two branches.
 
1863
    """
 
1864
    # TODO: Options to specify revisions on either side, as if
 
1865
    #       merging only part of the history.
 
1866
    takes_args = ['branch', 'other']
 
1867
    hidden = True
 
1868
    
 
1869
    @display_command
 
1870
    def run(self, branch, other):
 
1871
        from bzrlib.revision import common_ancestor, MultipleRevisionSources
 
1872
        
 
1873
        branch1 = Branch.open_containing(branch)[0]
 
1874
        branch2 = Branch.open_containing(other)[0]
 
1875
 
 
1876
        history_1 = branch1.revision_history()
 
1877
        history_2 = branch2.revision_history()
 
1878
 
 
1879
        last1 = branch1.last_revision()
 
1880
        last2 = branch2.last_revision()
 
1881
 
 
1882
        source = MultipleRevisionSources(branch1.repository, 
 
1883
                                         branch2.repository)
 
1884
        
 
1885
        base_rev_id = common_ancestor(last1, last2, source)
 
1886
 
 
1887
        print 'merge base is revision %s' % base_rev_id
 
1888
        
 
1889
        return
 
1890
 
 
1891
        if base_revno is None:
 
1892
            raise bzrlib.errors.UnrelatedBranches()
 
1893
 
 
1894
        print ' r%-6d in %s' % (base_revno, branch)
 
1895
 
 
1896
        other_revno = branch2.revision_id_to_revno(base_revid)
 
1897
        
 
1898
        print ' r%-6d in %s' % (other_revno, other)
 
1899
 
 
1900
 
 
1901
 
 
1902
class cmd_merge(Command):
 
1903
    """Perform a three-way merge.
 
1904
    
 
1905
    The branch is the branch you will merge from.  By default, it will
 
1906
    merge the latest revision.  If you specify a revision, that
 
1907
    revision will be merged.  If you specify two revisions, the first
 
1908
    will be used as a BASE, and the second one as OTHER.  Revision
 
1909
    numbers are always relative to the specified branch.
 
1910
 
 
1911
    By default, bzr will try to merge in all new work from the other
 
1912
    branch, automatically determining an appropriate base.  If this
 
1913
    fails, you may need to give an explicit base.
 
1914
    
 
1915
    Merge will do its best to combine the changes in two branches, but there
 
1916
    are some kinds of problems only a human can fix.  When it encounters those,
 
1917
    it will mark a conflict.  A conflict means that you need to fix something,
 
1918
    before you should commit.
 
1919
 
 
1920
    Use bzr resolve when you have fixed a problem.  See also bzr conflicts.
 
1921
 
 
1922
    If there is no default branch set, the first merge will set it. After
 
1923
    that, you can omit the branch to use the default.  To change the
 
1924
    default, use --remember.
 
1925
 
 
1926
    Examples:
 
1927
 
 
1928
    To merge the latest revision from bzr.dev
 
1929
    bzr merge ../bzr.dev
 
1930
 
 
1931
    To merge changes up to and including revision 82 from bzr.dev
 
1932
    bzr merge -r 82 ../bzr.dev
 
1933
 
 
1934
    To merge the changes introduced by 82, without previous changes:
 
1935
    bzr merge -r 81..82 ../bzr.dev
 
1936
    
 
1937
    merge refuses to run if there are any uncommitted changes, unless
 
1938
    --force is given.
 
1939
    """
 
1940
    takes_args = ['branch?']
 
1941
    takes_options = ['revision', 'force', 'merge-type', 'reprocess', 'remember',
 
1942
                     Option('show-base', help="Show base revision text in "
 
1943
                            "conflicts")]
 
1944
 
 
1945
    def run(self, branch=None, revision=None, force=False, merge_type=None,
 
1946
            show_base=False, reprocess=False, remember=False):
 
1947
        if merge_type is None:
 
1948
            merge_type = Merge3Merger
 
1949
 
 
1950
        tree = WorkingTree.open_containing(u'.')[0]
 
1951
        stored_loc = tree.branch.get_parent()
 
1952
        if branch is None:
 
1953
            if stored_loc is None:
 
1954
                raise BzrCommandError("No merge branch known or specified.")
 
1955
            else:
 
1956
                print (u"Using saved branch: %s" 
 
1957
                    % bzrlib.osutils.urlfordisplay(stored_loc))
 
1958
                branch = stored_loc
 
1959
 
 
1960
        if revision is None or len(revision) < 1:
 
1961
            base = [None, None]
 
1962
            other = [branch, -1]
 
1963
            other_branch, path = Branch.open_containing(branch)
 
1964
        else:
 
1965
            if len(revision) == 1:
 
1966
                base = [None, None]
 
1967
                other_branch, path = Branch.open_containing(branch)
 
1968
                revno = revision[0].in_history(other_branch).revno
 
1969
                other = [branch, revno]
 
1970
            else:
 
1971
                assert len(revision) == 2
 
1972
                if None in revision:
 
1973
                    raise BzrCommandError(
 
1974
                        "Merge doesn't permit that revision specifier.")
 
1975
                other_branch, path = Branch.open_containing(branch)
 
1976
 
 
1977
                base = [branch, revision[0].in_history(other_branch).revno]
 
1978
                other = [branch, revision[1].in_history(other_branch).revno]
 
1979
 
 
1980
        if tree.branch.get_parent() is None or remember:
 
1981
            tree.branch.set_parent(other_branch.base)
 
1982
 
 
1983
        if path != "":
 
1984
            interesting_files = [path]
 
1985
        else:
 
1986
            interesting_files = None
 
1987
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
1988
        try:
 
1989
            try:
 
1990
                conflict_count = merge(other, base, check_clean=(not force),
 
1991
                                       merge_type=merge_type, 
 
1992
                                       reprocess=reprocess,
 
1993
                                       show_base=show_base, 
 
1994
                                       pb=pb, file_list=interesting_files)
 
1995
            finally:
 
1996
                pb.finished()
 
1997
            if conflict_count != 0:
 
1998
                return 1
 
1999
            else:
 
2000
                return 0
 
2001
        except bzrlib.errors.AmbiguousBase, e:
 
2002
            m = ("sorry, bzr can't determine the right merge base yet\n"
 
2003
                 "candidates are:\n  "
 
2004
                 + "\n  ".join(e.bases)
 
2005
                 + "\n"
 
2006
                 "please specify an explicit base with -r,\n"
 
2007
                 "and (if you want) report this to the bzr developers\n")
 
2008
            log_error(m)
 
2009
 
 
2010
 
 
2011
class cmd_remerge(Command):
 
2012
    """Redo a merge.
 
2013
    """
 
2014
    takes_args = ['file*']
 
2015
    takes_options = ['merge-type', 'reprocess',
 
2016
                     Option('show-base', help="Show base revision text in "
 
2017
                            "conflicts")]
 
2018
 
 
2019
    def run(self, file_list=None, merge_type=None, show_base=False,
 
2020
            reprocess=False):
 
2021
        from bzrlib.merge import merge_inner, transform_tree
 
2022
        if merge_type is None:
 
2023
            merge_type = Merge3Merger
 
2024
        tree, file_list = tree_files(file_list)
 
2025
        tree.lock_write()
 
2026
        try:
 
2027
            pending_merges = tree.pending_merges() 
 
2028
            if len(pending_merges) != 1:
 
2029
                raise BzrCommandError("Sorry, remerge only works after normal"
 
2030
                                      + " merges.  Not cherrypicking or"
 
2031
                                      + "multi-merges.")
 
2032
            repository = tree.branch.repository
 
2033
            base_revision = common_ancestor(tree.branch.last_revision(), 
 
2034
                                            pending_merges[0], repository)
 
2035
            base_tree = repository.revision_tree(base_revision)
 
2036
            other_tree = repository.revision_tree(pending_merges[0])
 
2037
            interesting_ids = None
 
2038
            if file_list is not None:
 
2039
                interesting_ids = set()
 
2040
                for filename in file_list:
 
2041
                    file_id = tree.path2id(filename)
 
2042
                    if file_id is None:
 
2043
                        raise NotVersionedError(filename)
 
2044
                    interesting_ids.add(file_id)
 
2045
                    if tree.kind(file_id) != "directory":
 
2046
                        continue
 
2047
                    
 
2048
                    for name, ie in tree.inventory.iter_entries(file_id):
 
2049
                        interesting_ids.add(ie.file_id)
 
2050
            transform_tree(tree, tree.basis_tree(), interesting_ids)
 
2051
            if file_list is None:
 
2052
                restore_files = list(tree.iter_conflicts())
 
2053
            else:
 
2054
                restore_files = file_list
 
2055
            for filename in restore_files:
 
2056
                try:
 
2057
                    restore(tree.abspath(filename))
 
2058
                except NotConflicted:
 
2059
                    pass
 
2060
            conflicts =  merge_inner(tree.branch, other_tree, base_tree,
 
2061
                                     this_tree=tree,
 
2062
                                     interesting_ids = interesting_ids, 
 
2063
                                     other_rev_id=pending_merges[0], 
 
2064
                                     merge_type=merge_type, 
 
2065
                                     show_base=show_base,
 
2066
                                     reprocess=reprocess)
 
2067
        finally:
 
2068
            tree.unlock()
 
2069
        if conflicts > 0:
 
2070
            return 1
 
2071
        else:
 
2072
            return 0
 
2073
 
 
2074
class cmd_revert(Command):
 
2075
    """Reverse all changes since the last commit.
 
2076
 
 
2077
    Only versioned files are affected.  Specify filenames to revert only 
 
2078
    those files.  By default, any files that are changed will be backed up
 
2079
    first.  Backup files have a '~' appended to their name.
 
2080
    """
 
2081
    takes_options = ['revision', 'no-backup']
 
2082
    takes_args = ['file*']
 
2083
    aliases = ['merge-revert']
 
2084
 
 
2085
    def run(self, revision=None, no_backup=False, file_list=None):
 
2086
        from bzrlib.commands import parse_spec
 
2087
        if file_list is not None:
 
2088
            if len(file_list) == 0:
 
2089
                raise BzrCommandError("No files specified")
 
2090
        else:
 
2091
            file_list = []
 
2092
        
 
2093
        tree, file_list = tree_files(file_list)
 
2094
        if revision is None:
 
2095
            # FIXME should be tree.last_revision
 
2096
            rev_id = tree.last_revision()
 
2097
        elif len(revision) != 1:
 
2098
            raise BzrCommandError('bzr revert --revision takes exactly 1 argument')
 
2099
        else:
 
2100
            rev_id = revision[0].in_history(tree.branch).rev_id
 
2101
        pb = bzrlib.ui.ui_factory.nested_progress_bar()
 
2102
        try:
 
2103
            tree.revert(file_list, 
 
2104
                        tree.branch.repository.revision_tree(rev_id),
 
2105
                        not no_backup, pb)
 
2106
        finally:
 
2107
            pb.finished()
 
2108
 
 
2109
 
 
2110
class cmd_assert_fail(Command):
 
2111
    """Test reporting of assertion failures"""
 
2112
    hidden = True
 
2113
    def run(self):
 
2114
        assert False, "always fails"
 
2115
 
 
2116
 
 
2117
class cmd_help(Command):
 
2118
    """Show help on a command or other topic.
 
2119
 
 
2120
    For a list of all available commands, say 'bzr help commands'."""
 
2121
    takes_options = [Option('long', 'show help on all commands')]
 
2122
    takes_args = ['topic?']
 
2123
    aliases = ['?', '--help', '-?', '-h']
 
2124
    
 
2125
    @display_command
 
2126
    def run(self, topic=None, long=False):
 
2127
        import help
 
2128
        if topic is None and long:
 
2129
            topic = "commands"
 
2130
        help.help(topic)
 
2131
 
 
2132
 
 
2133
class cmd_shell_complete(Command):
 
2134
    """Show appropriate completions for context.
 
2135
 
 
2136
    For a list of all available commands, say 'bzr shell-complete'."""
 
2137
    takes_args = ['context?']
 
2138
    aliases = ['s-c']
 
2139
    hidden = True
 
2140
    
 
2141
    @display_command
 
2142
    def run(self, context=None):
 
2143
        import shellcomplete
 
2144
        shellcomplete.shellcomplete(context)
 
2145
 
 
2146
 
 
2147
class cmd_fetch(Command):
 
2148
    """Copy in history from another branch but don't merge it.
 
2149
 
 
2150
    This is an internal method used for pull and merge."""
 
2151
    hidden = True
 
2152
    takes_args = ['from_branch', 'to_branch']
 
2153
    def run(self, from_branch, to_branch):
 
2154
        from bzrlib.fetch import Fetcher
 
2155
        from bzrlib.branch import Branch
 
2156
        from_b = Branch.open(from_branch)
 
2157
        to_b = Branch.open(to_branch)
 
2158
        Fetcher(to_b, from_b)
 
2159
 
 
2160
 
 
2161
class cmd_missing(Command):
 
2162
    """Show unmerged/unpulled revisions between two branches.
 
2163
 
 
2164
    OTHER_BRANCH may be local or remote."""
 
2165
    takes_args = ['other_branch?']
 
2166
    takes_options = [Option('reverse', 'Reverse the order of revisions'),
 
2167
                     Option('mine-only', 
 
2168
                            'Display changes in the local branch only'),
 
2169
                     Option('theirs-only', 
 
2170
                            'Display changes in the remote branch only'), 
 
2171
                     'log-format',
 
2172
                     'line',
 
2173
                     'long', 
 
2174
                     'short',
 
2175
                     'show-ids',
 
2176
                     'verbose'
 
2177
                     ]
 
2178
 
 
2179
    def run(self, other_branch=None, reverse=False, mine_only=False,
 
2180
            theirs_only=False, log_format=None, long=False, short=False, line=False, 
 
2181
            show_ids=False, verbose=False):
 
2182
        from bzrlib.missing import find_unmerged, iter_log_data
 
2183
        from bzrlib.log import log_formatter
 
2184
        local_branch = bzrlib.branch.Branch.open_containing(u".")[0]
 
2185
        parent = local_branch.get_parent()
 
2186
        if other_branch is None:
 
2187
            other_branch = parent
 
2188
            if other_branch is None:
 
2189
                raise BzrCommandError("No missing location known or specified.")
 
2190
            print "Using last location: " + local_branch.get_parent()
 
2191
        remote_branch = bzrlib.branch.Branch.open(other_branch)
 
2192
        if remote_branch.base == local_branch.base:
 
2193
            remote_branch = local_branch
 
2194
        local_branch.lock_read()
 
2195
        try:
 
2196
            remote_branch.lock_read()
 
2197
            try:
 
2198
                local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
 
2199
                if (log_format == None):
 
2200
                    default = bzrlib.config.BranchConfig(local_branch).log_format()
 
2201
                    log_format = get_log_format(long=long, short=short, line=line, default=default)
 
2202
                lf = log_formatter(log_format, sys.stdout,
 
2203
                                   show_ids=show_ids,
 
2204
                                   show_timezone='original')
 
2205
                if reverse is False:
 
2206
                    local_extra.reverse()
 
2207
                    remote_extra.reverse()
 
2208
                if local_extra and not theirs_only:
 
2209
                    print "You have %d extra revision(s):" % len(local_extra)
 
2210
                    for data in iter_log_data(local_extra, local_branch.repository,
 
2211
                                              verbose):
 
2212
                        lf.show(*data)
 
2213
                    printed_local = True
 
2214
                else:
 
2215
                    printed_local = False
 
2216
                if remote_extra and not mine_only:
 
2217
                    if printed_local is True:
 
2218
                        print "\n\n"
 
2219
                    print "You are missing %d revision(s):" % len(remote_extra)
 
2220
                    for data in iter_log_data(remote_extra, remote_branch.repository, 
 
2221
                                              verbose):
 
2222
                        lf.show(*data)
 
2223
                if not remote_extra and not local_extra:
 
2224
                    status_code = 0
 
2225
                    print "Branches are up to date."
 
2226
                else:
 
2227
                    status_code = 1
 
2228
            finally:
 
2229
                remote_branch.unlock()
 
2230
        finally:
 
2231
            local_branch.unlock()
 
2232
        if not status_code and parent is None and other_branch is not None:
 
2233
            local_branch.lock_write()
 
2234
            try:
 
2235
                # handle race conditions - a parent might be set while we run.
 
2236
                if local_branch.get_parent() is None:
 
2237
                    local_branch.set_parent(remote_branch.base)
 
2238
            finally:
 
2239
                local_branch.unlock()
 
2240
        return status_code
 
2241
 
 
2242
 
 
2243
class cmd_plugins(Command):
 
2244
    """List plugins"""
 
2245
    hidden = True
 
2246
    @display_command
 
2247
    def run(self):
 
2248
        import bzrlib.plugin
 
2249
        from inspect import getdoc
 
2250
        for name, plugin in bzrlib.plugin.all_plugins().items():
 
2251
            if hasattr(plugin, '__path__'):
 
2252
                print plugin.__path__[0]
 
2253
            elif hasattr(plugin, '__file__'):
 
2254
                print plugin.__file__
 
2255
            else:
 
2256
                print `plugin`
 
2257
                
 
2258
            d = getdoc(plugin)
 
2259
            if d:
 
2260
                print '\t', d.split('\n')[0]
 
2261
 
 
2262
 
 
2263
class cmd_testament(Command):
 
2264
    """Show testament (signing-form) of a revision."""
 
2265
    takes_options = ['revision', 'long']
 
2266
    takes_args = ['branch?']
 
2267
    @display_command
 
2268
    def run(self, branch=u'.', revision=None, long=False):
 
2269
        from bzrlib.testament import Testament
 
2270
        b = WorkingTree.open_containing(branch)[0].branch
 
2271
        b.lock_read()
 
2272
        try:
 
2273
            if revision is None:
 
2274
                rev_id = b.last_revision()
 
2275
            else:
 
2276
                rev_id = revision[0].in_history(b).rev_id
 
2277
            t = Testament.from_revision(b.repository, rev_id)
 
2278
            if long:
 
2279
                sys.stdout.writelines(t.as_text_lines())
 
2280
            else:
 
2281
                sys.stdout.write(t.as_short_text())
 
2282
        finally:
 
2283
            b.unlock()
 
2284
 
 
2285
 
 
2286
class cmd_annotate(Command):
 
2287
    """Show the origin of each line in a file.
 
2288
 
 
2289
    This prints out the given file with an annotation on the left side
 
2290
    indicating which revision, author and date introduced the change.
 
2291
 
 
2292
    If the origin is the same for a run of consecutive lines, it is 
 
2293
    shown only at the top, unless the --all option is given.
 
2294
    """
 
2295
    # TODO: annotate directories; showing when each file was last changed
 
2296
    # TODO: annotate a previous version of a file
 
2297
    # TODO: if the working copy is modified, show annotations on that 
 
2298
    #       with new uncommitted lines marked
 
2299
    aliases = ['blame', 'praise']
 
2300
    takes_args = ['filename']
 
2301
    takes_options = [Option('all', help='show annotations on all lines'),
 
2302
                     Option('long', help='show date in annotations'),
 
2303
                     ]
 
2304
 
 
2305
    @display_command
 
2306
    def run(self, filename, all=False, long=False):
 
2307
        from bzrlib.annotate import annotate_file
 
2308
        tree, relpath = WorkingTree.open_containing(filename)
 
2309
        branch = tree.branch
 
2310
        branch.lock_read()
 
2311
        try:
 
2312
            file_id = tree.inventory.path2id(relpath)
 
2313
            tree = branch.repository.revision_tree(branch.last_revision())
 
2314
            file_version = tree.inventory[file_id].revision
 
2315
            annotate_file(branch, file_version, file_id, long, all, sys.stdout)
 
2316
        finally:
 
2317
            branch.unlock()
 
2318
 
 
2319
 
 
2320
class cmd_re_sign(Command):
 
2321
    """Create a digital signature for an existing revision."""
 
2322
    # TODO be able to replace existing ones.
 
2323
 
 
2324
    hidden = True # is this right ?
 
2325
    takes_args = ['revision_id*']
 
2326
    takes_options = ['revision']
 
2327
    
 
2328
    def run(self, revision_id_list=None, revision=None):
 
2329
        import bzrlib.config as config
 
2330
        import bzrlib.gpg as gpg
 
2331
        if revision_id_list is not None and revision is not None:
 
2332
            raise BzrCommandError('You can only supply one of revision_id or --revision')
 
2333
        if revision_id_list is None and revision is None:
 
2334
            raise BzrCommandError('You must supply either --revision or a revision_id')
 
2335
        b = WorkingTree.open_containing(u'.')[0].branch
 
2336
        gpg_strategy = gpg.GPGStrategy(config.BranchConfig(b))
 
2337
        if revision_id_list is not None:
 
2338
            for revision_id in revision_id_list:
 
2339
                b.repository.sign_revision(revision_id, gpg_strategy)
 
2340
        elif revision is not None:
 
2341
            if len(revision) == 1:
 
2342
                revno, rev_id = revision[0].in_history(b)
 
2343
                b.repository.sign_revision(rev_id, gpg_strategy)
 
2344
            elif len(revision) == 2:
 
2345
                # are they both on rh- if so we can walk between them
 
2346
                # might be nice to have a range helper for arbitrary
 
2347
                # revision paths. hmm.
 
2348
                from_revno, from_revid = revision[0].in_history(b)
 
2349
                to_revno, to_revid = revision[1].in_history(b)
 
2350
                if to_revid is None:
 
2351
                    to_revno = b.revno()
 
2352
                if from_revno is None or to_revno is None:
 
2353
                    raise BzrCommandError('Cannot sign a range of non-revision-history revisions')
 
2354
                for revno in range(from_revno, to_revno + 1):
 
2355
                    b.repository.sign_revision(b.get_rev_id(revno), 
 
2356
                                               gpg_strategy)
 
2357
            else:
 
2358
                raise BzrCommandError('Please supply either one revision, or a range.')
 
2359
 
 
2360
 
 
2361
class cmd_bind(Command):
 
2362
    """Bind the current branch to a master branch.
 
2363
 
 
2364
    After binding, commits must succeed on the master branch
 
2365
    before they are executed on the local one.
 
2366
    """
 
2367
 
 
2368
    takes_args = ['location']
 
2369
    takes_options = []
 
2370
 
 
2371
    def run(self, location=None):
 
2372
        b, relpath = Branch.open_containing(u'.')
 
2373
        b_other = Branch.open(location)
 
2374
        try:
 
2375
            b.bind(b_other)
 
2376
        except DivergedBranches:
 
2377
            raise BzrCommandError('These branches have diverged.'
 
2378
                                  ' Try merging, and then bind again.')
 
2379
 
 
2380
 
 
2381
class cmd_unbind(Command):
 
2382
    """Bind the current branch to its parent.
 
2383
 
 
2384
    After unbinding, the local branch is considered independent.
 
2385
    """
 
2386
 
 
2387
    takes_args = []
 
2388
    takes_options = []
 
2389
 
 
2390
    def run(self):
 
2391
        b, relpath = Branch.open_containing(u'.')
 
2392
        if not b.unbind():
 
2393
            raise BzrCommandError('Local branch is not bound')
 
2394
 
 
2395
 
 
2396
class cmd_uncommit(bzrlib.commands.Command):
 
2397
    """Remove the last committed revision.
 
2398
 
 
2399
    By supplying the --all flag, it will not only remove the entry 
 
2400
    from revision_history, but also remove all of the entries in the
 
2401
    stores.
 
2402
 
 
2403
    --verbose will print out what is being removed.
 
2404
    --dry-run will go through all the motions, but not actually
 
2405
    remove anything.
 
2406
    
 
2407
    In the future, uncommit will create a changeset, which can then
 
2408
    be re-applied.
 
2409
    """
 
2410
 
 
2411
    # TODO: jam 20060108 Add an option to allow uncommit to remove
 
2412
    # unreferenced information in 'branch-as-repostory' branches.
 
2413
    # TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
 
2414
    # information in shared branches as well.
 
2415
    takes_options = ['verbose', 'revision',
 
2416
                    Option('dry-run', help='Don\'t actually make changes'),
 
2417
                    Option('force', help='Say yes to all questions.')]
 
2418
    takes_args = ['location?']
 
2419
    aliases = []
 
2420
 
 
2421
    def run(self, location=None, 
 
2422
            dry_run=False, verbose=False,
 
2423
            revision=None, force=False):
 
2424
        from bzrlib.branch import Branch
 
2425
        from bzrlib.log import log_formatter
 
2426
        import sys
 
2427
        from bzrlib.uncommit import uncommit
 
2428
 
 
2429
        if location is None:
 
2430
            location = u'.'
 
2431
        control, relpath = bzrdir.BzrDir.open_containing(location)
 
2432
        try:
 
2433
            tree = control.open_workingtree()
 
2434
            b = tree.branch
 
2435
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2436
            tree = None
 
2437
            b = control.open_branch()
 
2438
 
 
2439
        if revision is None:
 
2440
            revno = b.revno()
 
2441
            rev_id = b.last_revision()
 
2442
        else:
 
2443
            revno, rev_id = revision[0].in_history(b)
 
2444
        if rev_id is None:
 
2445
            print 'No revisions to uncommit.'
 
2446
 
 
2447
        for r in range(revno, b.revno()+1):
 
2448
            rev_id = b.get_rev_id(r)
 
2449
            lf = log_formatter('short', to_file=sys.stdout,show_timezone='original')
 
2450
            lf.show(r, b.repository.get_revision(rev_id), None)
 
2451
 
 
2452
        if dry_run:
 
2453
            print 'Dry-run, pretending to remove the above revisions.'
 
2454
            if not force:
 
2455
                val = raw_input('Press <enter> to continue')
 
2456
        else:
 
2457
            print 'The above revision(s) will be removed.'
 
2458
            if not force:
 
2459
                val = raw_input('Are you sure [y/N]? ')
 
2460
                if val.lower() not in ('y', 'yes'):
 
2461
                    print 'Canceled'
 
2462
                    return 0
 
2463
 
 
2464
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
 
2465
                revno=revno)
 
2466
 
 
2467
 
 
2468
class cmd_break_lock(Command):
 
2469
    """Break a dead lock on a repository, branch or working directory.
 
2470
 
 
2471
    CAUTION: Locks should only be broken when you are sure that the process
 
2472
    holding the lock has been stopped.
 
2473
    
 
2474
    example:
 
2475
        bzr break-lock .
 
2476
    """
 
2477
    takes_args = ['location']
 
2478
    takes_options = [Option('show',
 
2479
                            help="just show information on the lock, " \
 
2480
                                 "don't break it"),
 
2481
                    ]
 
2482
    def run(self, location, show=False):
 
2483
        raise NotImplementedError("sorry, break-lock is not complete yet; "
 
2484
                "you can remove the 'held' directory manually to break the lock")
 
2485
 
 
2486
 
 
2487
# command-line interpretation helper for merge-related commands
 
2488
def merge(other_revision, base_revision,
 
2489
          check_clean=True, ignore_zero=False,
 
2490
          this_dir=None, backup_files=False, merge_type=Merge3Merger,
 
2491
          file_list=None, show_base=False, reprocess=False,
 
2492
          pb=DummyProgress()):
 
2493
    """Merge changes into a tree.
 
2494
 
 
2495
    base_revision
 
2496
        list(path, revno) Base for three-way merge.  
 
2497
        If [None, None] then a base will be automatically determined.
 
2498
    other_revision
 
2499
        list(path, revno) Other revision for three-way merge.
 
2500
    this_dir
 
2501
        Directory to merge changes into; '.' by default.
 
2502
    check_clean
 
2503
        If true, this_dir must have no uncommitted changes before the
 
2504
        merge begins.
 
2505
    ignore_zero - If true, suppress the "zero conflicts" message when 
 
2506
        there are no conflicts; should be set when doing something we expect
 
2507
        to complete perfectly.
 
2508
    file_list - If supplied, merge only changes to selected files.
 
2509
 
 
2510
    All available ancestors of other_revision and base_revision are
 
2511
    automatically pulled into the branch.
 
2512
 
 
2513
    The revno may be -1 to indicate the last revision on the branch, which is
 
2514
    the typical case.
 
2515
 
 
2516
    This function is intended for use from the command line; programmatic
 
2517
    clients might prefer to call merge.merge_inner(), which has less magic 
 
2518
    behavior.
 
2519
    """
 
2520
    from bzrlib.merge import Merger
 
2521
    if this_dir is None:
 
2522
        this_dir = u'.'
 
2523
    this_tree = WorkingTree.open_containing(this_dir)[0]
 
2524
    if show_base and not merge_type is Merge3Merger:
 
2525
        raise BzrCommandError("Show-base is not supported for this merge"
 
2526
                              " type. %s" % merge_type)
 
2527
    if reprocess and not merge_type.supports_reprocess:
 
2528
        raise BzrCommandError("Conflict reduction is not supported for merge"
 
2529
                              " type %s." % merge_type)
 
2530
    if reprocess and show_base:
 
2531
        raise BzrCommandError("Cannot do conflict reduction and show base.")
 
2532
    try:
 
2533
        merger = Merger(this_tree.branch, this_tree=this_tree, pb=pb)
 
2534
        merger.pp = ProgressPhase("Merge phase", 5, pb)
 
2535
        merger.pp.next_phase()
 
2536
        merger.check_basis(check_clean)
 
2537
        merger.set_other(other_revision)
 
2538
        merger.pp.next_phase()
 
2539
        merger.set_base(base_revision)
 
2540
        if merger.base_rev_id == merger.other_rev_id:
 
2541
            note('Nothing to do.')
 
2542
            return 0
 
2543
        merger.backup_files = backup_files
 
2544
        merger.merge_type = merge_type 
 
2545
        merger.set_interesting_files(file_list)
 
2546
        merger.show_base = show_base 
 
2547
        merger.reprocess = reprocess
 
2548
        conflicts = merger.do_merge()
 
2549
        if file_list is None:
 
2550
            merger.set_pending()
 
2551
    finally:
 
2552
        pb.clear()
 
2553
    return conflicts
 
2554
 
 
2555
 
 
2556
# these get imported and then picked up by the scan for cmd_*
 
2557
# TODO: Some more consistent way to split command definitions across files;
 
2558
# we do need to load at least some information about them to know of 
 
2559
# aliases.  ideally we would avoid loading the implementation until the
 
2560
# details were needed.
 
2561
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore
 
2562
from bzrlib.sign_my_commits import cmd_sign_my_commits
 
2563
from bzrlib.weave_commands import cmd_weave_list, cmd_weave_join, \
 
2564
        cmd_weave_plan_merge, cmd_weave_merge_text