/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-06-17 00:49:34 UTC
  • mto: This revision was merged to the branch mainline in revision 1795.
  • Revision ID: john@arbash-meinel.com-20060617004934-7eb765919f2a4de5
Push should only save the location if it can actually connect (doesn't need to succeed)

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