/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: Aaron Bentley
  • Date: 2007-04-01 03:52:31 UTC
  • mto: This revision was merged to the branch mainline in revision 2389.
  • Revision ID: aaron.bentley@utoronto.ca-20070401035231-w87zy7hrpre4v81m
rename get_target_revision to install_revisions

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2004, 2005, 2006, 2007 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
import os
 
20
from StringIO import StringIO
 
21
 
 
22
from bzrlib.lazy_import import lazy_import
 
23
lazy_import(globals(), """
 
24
import codecs
 
25
import errno
 
26
import smtplib
 
27
import sys
 
28
import tempfile
 
29
import time
 
30
 
 
31
import bzrlib
 
32
from bzrlib import (
 
33
    branch,
 
34
    bundle,
 
35
    bzrdir,
 
36
    delta,
 
37
    config,
 
38
    errors,
 
39
    globbing,
 
40
    ignores,
 
41
    log,
 
42
    merge as _mod_merge,
 
43
    merge_directive,
 
44
    osutils,
 
45
    registry,
 
46
    repository,
 
47
    symbol_versioning,
 
48
    transport,
 
49
    tree as _mod_tree,
 
50
    ui,
 
51
    urlutils,
 
52
    )
 
53
from bzrlib.branch import Branch
 
54
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
 
55
from bzrlib.conflicts import ConflictList
 
56
from bzrlib.revision import common_ancestor
 
57
from bzrlib.revisionspec import RevisionSpec
 
58
from bzrlib.workingtree import WorkingTree
 
59
""")
 
60
 
 
61
from bzrlib.commands import Command, display_command
 
62
from bzrlib.option import Option, RegistryOption
 
63
from bzrlib.progress import DummyProgress, ProgressPhase
 
64
from bzrlib.trace import mutter, note, log_error, warning, is_quiet, info
 
65
 
 
66
 
 
67
def tree_files(file_list, default_branch=u'.'):
 
68
    try:
 
69
        return internal_tree_files(file_list, default_branch)
 
70
    except errors.FileInWrongBranch, e:
 
71
        raise errors.BzrCommandError("%s is not in the same branch as %s" %
 
72
                                     (e.path, file_list[0]))
 
73
 
 
74
 
 
75
# XXX: Bad function name; should possibly also be a class method of
 
76
# WorkingTree rather than a function.
 
77
def internal_tree_files(file_list, default_branch=u'.'):
 
78
    """Convert command-line paths to a WorkingTree and relative paths.
 
79
 
 
80
    This is typically used for command-line processors that take one or
 
81
    more filenames, and infer the workingtree that contains them.
 
82
 
 
83
    The filenames given are not required to exist.
 
84
 
 
85
    :param file_list: Filenames to convert.  
 
86
 
 
87
    :param default_branch: Fallback tree path to use if file_list is empty or
 
88
        None.
 
89
 
 
90
    :return: workingtree, [relative_paths]
 
91
    """
 
92
    if file_list is None or len(file_list) == 0:
 
93
        return WorkingTree.open_containing(default_branch)[0], file_list
 
94
    tree = WorkingTree.open_containing(osutils.realpath(file_list[0]))[0]
 
95
    new_list = []
 
96
    for filename in file_list:
 
97
        try:
 
98
            new_list.append(tree.relpath(osutils.dereference_path(filename)))
 
99
        except errors.PathNotChild:
 
100
            raise errors.FileInWrongBranch(tree.branch, filename)
 
101
    return tree, new_list
 
102
 
 
103
 
 
104
@symbol_versioning.deprecated_function(symbol_versioning.zero_fifteen)
 
105
def get_format_type(typestring):
 
106
    """Parse and return a format specifier."""
 
107
    # Have to use BzrDirMetaFormat1 directly, so that
 
108
    # RepositoryFormat.set_default_format works
 
109
    if typestring == "default":
 
110
        return bzrdir.BzrDirMetaFormat1()
 
111
    try:
 
112
        return bzrdir.format_registry.make_bzrdir(typestring)
 
113
    except KeyError:
 
114
        msg = 'Unknown bzr format "%s". See "bzr help formats".' % typestring
 
115
        raise errors.BzrCommandError(msg)
 
116
 
 
117
 
 
118
# TODO: Make sure no commands unconditionally use the working directory as a
 
119
# branch.  If a filename argument is used, the first of them should be used to
 
120
# specify the branch.  (Perhaps this can be factored out into some kind of
 
121
# Argument class, representing a file in a branch, where the first occurrence
 
122
# opens the branch?)
 
123
 
 
124
class cmd_status(Command):
 
125
    """Display status summary.
 
126
 
 
127
    This reports on versioned and unknown files, reporting them
 
128
    grouped by state.  Possible states are:
 
129
 
 
130
    added
 
131
        Versioned in the working copy but not in the previous revision.
 
132
 
 
133
    removed
 
134
        Versioned in the previous revision but removed or deleted
 
135
        in the working copy.
 
136
 
 
137
    renamed
 
138
        Path of this file changed from the previous revision;
 
139
        the text may also have changed.  This includes files whose
 
140
        parent directory was renamed.
 
141
 
 
142
    modified
 
143
        Text has changed since the previous revision.
 
144
 
 
145
    kind changed
 
146
        File kind has been changed (e.g. from file to directory).
 
147
 
 
148
    unknown
 
149
        Not versioned and not matching an ignore pattern.
 
150
 
 
151
    To see ignored files use 'bzr ignored'.  For details in the
 
152
    changes to file texts, use 'bzr diff'.
 
153
    
 
154
    --short gives a status flags for each item, similar to the SVN's status
 
155
    command.
 
156
 
 
157
    Column 1: versioning / renames
 
158
      + File versioned
 
159
      - File unversioned
 
160
      R File renamed
 
161
      ? File unknown
 
162
      C File has conflicts
 
163
      P Entry for a pending merge (not a file)
 
164
 
 
165
    Column 2: Contents
 
166
      N File created
 
167
      D File deleted
 
168
      K File kind changed
 
169
      M File modified
 
170
 
 
171
    Column 3: Execute
 
172
      * The execute bit was changed
 
173
 
 
174
    If no arguments are specified, the status of the entire working
 
175
    directory is shown.  Otherwise, only the status of the specified
 
176
    files or directories is reported.  If a directory is given, status
 
177
    is reported for everything inside that directory.
 
178
 
 
179
    If a revision argument is given, the status is calculated against
 
180
    that revision, or between two revisions if two are provided.
 
181
    """
 
182
    
 
183
    # TODO: --no-recurse, --recurse options
 
184
    
 
185
    takes_args = ['file*']
 
186
    takes_options = ['show-ids', 'revision', 'short',
 
187
                     Option('versioned', help='Only show versioned files')]
 
188
    aliases = ['st', 'stat']
 
189
 
 
190
    encoding_type = 'replace'
 
191
    
 
192
    @display_command
 
193
    def run(self, show_ids=False, file_list=None, revision=None, short=False,
 
194
            versioned=False):
 
195
        from bzrlib.status import show_tree_status
 
196
 
 
197
        tree, file_list = tree_files(file_list)
 
198
            
 
199
        show_tree_status(tree, show_ids=show_ids,
 
200
                         specific_files=file_list, revision=revision,
 
201
                         to_file=self.outf, short=short, versioned=versioned)
 
202
 
 
203
 
 
204
class cmd_cat_revision(Command):
 
205
    """Write out metadata for a revision.
 
206
    
 
207
    The revision to print can either be specified by a specific
 
208
    revision identifier, or you can use --revision.
 
209
    """
 
210
 
 
211
    hidden = True
 
212
    takes_args = ['revision_id?']
 
213
    takes_options = ['revision']
 
214
    # cat-revision is more for frontends so should be exact
 
215
    encoding = 'strict'
 
216
    
 
217
    @display_command
 
218
    def run(self, revision_id=None, revision=None):
 
219
 
 
220
        if revision_id is not None and revision is not None:
 
221
            raise errors.BzrCommandError('You can only supply one of'
 
222
                                         ' revision_id or --revision')
 
223
        if revision_id is None and revision is None:
 
224
            raise errors.BzrCommandError('You must supply either'
 
225
                                         ' --revision or a revision_id')
 
226
        b = WorkingTree.open_containing(u'.')[0].branch
 
227
 
 
228
        # TODO: jam 20060112 should cat-revision always output utf-8?
 
229
        if revision_id is not None:
 
230
            self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
 
231
        elif revision is not None:
 
232
            for rev in revision:
 
233
                if rev is None:
 
234
                    raise errors.BzrCommandError('You cannot specify a NULL'
 
235
                                                 ' revision.')
 
236
                revno, rev_id = rev.in_history(b)
 
237
                self.outf.write(b.repository.get_revision_xml(rev_id).decode('utf-8'))
 
238
    
 
239
 
 
240
class cmd_remove_tree(Command):
 
241
    """Remove the working tree from a given branch/checkout.
 
242
 
 
243
    Since a lightweight checkout is little more than a working tree
 
244
    this will refuse to run against one.
 
245
    """
 
246
 
 
247
    takes_args = ['location?']
 
248
 
 
249
    def run(self, location='.'):
 
250
        d = bzrdir.BzrDir.open(location)
 
251
        
 
252
        try:
 
253
            working = d.open_workingtree()
 
254
        except errors.NoWorkingTree:
 
255
            raise errors.BzrCommandError("No working tree to remove")
 
256
        except errors.NotLocalUrl:
 
257
            raise errors.BzrCommandError("You cannot remove the working tree of a "
 
258
                                         "remote path")
 
259
        
 
260
        working_path = working.bzrdir.root_transport.base
 
261
        branch_path = working.branch.bzrdir.root_transport.base
 
262
        if working_path != branch_path:
 
263
            raise errors.BzrCommandError("You cannot remove the working tree from "
 
264
                                         "a lightweight checkout")
 
265
        
 
266
        d.destroy_workingtree()
 
267
        
 
268
 
 
269
class cmd_revno(Command):
 
270
    """Show current revision number.
 
271
 
 
272
    This is equal to the number of revisions on this branch.
 
273
    """
 
274
 
 
275
    takes_args = ['location?']
 
276
 
 
277
    @display_command
 
278
    def run(self, location=u'.'):
 
279
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
 
280
        self.outf.write('\n')
 
281
 
 
282
 
 
283
class cmd_revision_info(Command):
 
284
    """Show revision number and revision id for a given revision identifier.
 
285
    """
 
286
    hidden = True
 
287
    takes_args = ['revision_info*']
 
288
    takes_options = ['revision']
 
289
 
 
290
    @display_command
 
291
    def run(self, revision=None, revision_info_list=[]):
 
292
 
 
293
        revs = []
 
294
        if revision is not None:
 
295
            revs.extend(revision)
 
296
        if revision_info_list is not None:
 
297
            for rev in revision_info_list:
 
298
                revs.append(RevisionSpec.from_string(rev))
 
299
        if len(revs) == 0:
 
300
            raise errors.BzrCommandError('You must supply a revision identifier')
 
301
 
 
302
        b = WorkingTree.open_containing(u'.')[0].branch
 
303
 
 
304
        for rev in revs:
 
305
            revinfo = rev.in_history(b)
 
306
            if revinfo.revno is None:
 
307
                print '     %s' % revinfo.rev_id
 
308
            else:
 
309
                print '%4d %s' % (revinfo.revno, revinfo.rev_id)
 
310
 
 
311
    
 
312
class cmd_add(Command):
 
313
    """Add specified files or directories.
 
314
 
 
315
    In non-recursive mode, all the named items are added, regardless
 
316
    of whether they were previously ignored.  A warning is given if
 
317
    any of the named files are already versioned.
 
318
 
 
319
    In recursive mode (the default), files are treated the same way
 
320
    but the behaviour for directories is different.  Directories that
 
321
    are already versioned do not give a warning.  All directories,
 
322
    whether already versioned or not, are searched for files or
 
323
    subdirectories that are neither versioned or ignored, and these
 
324
    are added.  This search proceeds recursively into versioned
 
325
    directories.  If no names are given '.' is assumed.
 
326
 
 
327
    Therefore simply saying 'bzr add' will version all files that
 
328
    are currently unknown.
 
329
 
 
330
    Adding a file whose parent directory is not versioned will
 
331
    implicitly add the parent, and so on up to the root. This means
 
332
    you should never need to explicitly add a directory, they'll just
 
333
    get added when you add a file in the directory.
 
334
 
 
335
    --dry-run will show which files would be added, but not actually 
 
336
    add them.
 
337
 
 
338
    --file-ids-from will try to use the file ids from the supplied path.
 
339
    It looks up ids trying to find a matching parent directory with the
 
340
    same filename, and then by pure path.
 
341
    """
 
342
    takes_args = ['file*']
 
343
    takes_options = ['no-recurse', 'dry-run', 'verbose',
 
344
                     Option('file-ids-from', type=unicode,
 
345
                            help='Lookup file ids from here')]
 
346
    encoding_type = 'replace'
 
347
 
 
348
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
 
349
            file_ids_from=None):
 
350
        import bzrlib.add
 
351
 
 
352
        base_tree = None
 
353
        if file_ids_from is not None:
 
354
            try:
 
355
                base_tree, base_path = WorkingTree.open_containing(
 
356
                                            file_ids_from)
 
357
            except errors.NoWorkingTree:
 
358
                base_branch, base_path = Branch.open_containing(
 
359
                                            file_ids_from)
 
360
                base_tree = base_branch.basis_tree()
 
361
 
 
362
            action = bzrlib.add.AddFromBaseAction(base_tree, base_path,
 
363
                          to_file=self.outf, should_print=(not is_quiet()))
 
364
        else:
 
365
            action = bzrlib.add.AddAction(to_file=self.outf,
 
366
                should_print=(not is_quiet()))
 
367
 
 
368
        if base_tree:
 
369
            base_tree.lock_read()
 
370
        try:
 
371
            added, ignored = bzrlib.add.smart_add(file_list, not no_recurse,
 
372
                action=action, save=not dry_run)
 
373
        finally:
 
374
            if base_tree is not None:
 
375
                base_tree.unlock()
 
376
        if len(ignored) > 0:
 
377
            if verbose:
 
378
                for glob in sorted(ignored.keys()):
 
379
                    for path in ignored[glob]:
 
380
                        self.outf.write("ignored %s matching \"%s\"\n" 
 
381
                                        % (path, glob))
 
382
            else:
 
383
                match_len = 0
 
384
                for glob, paths in ignored.items():
 
385
                    match_len += len(paths)
 
386
                self.outf.write("ignored %d file(s).\n" % match_len)
 
387
            self.outf.write("If you wish to add some of these files,"
 
388
                            " please add them by name.\n")
 
389
 
 
390
 
 
391
class cmd_mkdir(Command):
 
392
    """Create a new versioned directory.
 
393
 
 
394
    This is equivalent to creating the directory and then adding it.
 
395
    """
 
396
 
 
397
    takes_args = ['dir+']
 
398
    encoding_type = 'replace'
 
399
 
 
400
    def run(self, dir_list):
 
401
        for d in dir_list:
 
402
            os.mkdir(d)
 
403
            wt, dd = WorkingTree.open_containing(d)
 
404
            wt.add([dd])
 
405
            self.outf.write('added %s\n' % d)
 
406
 
 
407
 
 
408
class cmd_relpath(Command):
 
409
    """Show path of a file relative to root"""
 
410
 
 
411
    takes_args = ['filename']
 
412
    hidden = True
 
413
    
 
414
    @display_command
 
415
    def run(self, filename):
 
416
        # TODO: jam 20050106 Can relpath return a munged path if
 
417
        #       sys.stdout encoding cannot represent it?
 
418
        tree, relpath = WorkingTree.open_containing(filename)
 
419
        self.outf.write(relpath)
 
420
        self.outf.write('\n')
 
421
 
 
422
 
 
423
class cmd_inventory(Command):
 
424
    """Show inventory of the current working copy or a revision.
 
425
 
 
426
    It is possible to limit the output to a particular entry
 
427
    type using the --kind option.  For example: --kind file.
 
428
 
 
429
    It is also possible to restrict the list of files to a specific
 
430
    set. For example: bzr inventory --show-ids this/file
 
431
 
 
432
    See also: bzr ls
 
433
    """
 
434
 
 
435
    hidden = True
 
436
 
 
437
    takes_options = ['revision', 'show-ids', 'kind']
 
438
 
 
439
    takes_args = ['file*']
 
440
 
 
441
    @display_command
 
442
    def run(self, revision=None, show_ids=False, kind=None, file_list=None):
 
443
        if kind and kind not in ['file', 'directory', 'symlink']:
 
444
            raise errors.BzrCommandError('invalid kind specified')
 
445
 
 
446
        work_tree, file_list = tree_files(file_list)
 
447
        work_tree.lock_read()
 
448
        try:
 
449
            if revision is not None:
 
450
                if len(revision) > 1:
 
451
                    raise errors.BzrCommandError(
 
452
                        'bzr inventory --revision takes exactly one revision'
 
453
                        ' identifier')
 
454
                revision_id = revision[0].in_history(work_tree.branch).rev_id
 
455
                tree = work_tree.branch.repository.revision_tree(revision_id)
 
456
 
 
457
                extra_trees = [work_tree]
 
458
                tree.lock_read()
 
459
            else:
 
460
                tree = work_tree
 
461
                extra_trees = []
 
462
 
 
463
            if file_list is not None:
 
464
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
465
                                          require_versioned=True)
 
466
                # find_ids_across_trees may include some paths that don't
 
467
                # exist in 'tree'.
 
468
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
 
469
                                 for file_id in file_ids if file_id in tree)
 
470
            else:
 
471
                entries = tree.inventory.entries()
 
472
        finally:
 
473
            tree.unlock()
 
474
            if tree is not work_tree:
 
475
                work_tree.unlock()
 
476
 
 
477
        for path, entry in entries:
 
478
            if kind and kind != entry.kind:
 
479
                continue
 
480
            if show_ids:
 
481
                self.outf.write('%-50s %s\n' % (path, entry.file_id))
 
482
            else:
 
483
                self.outf.write(path)
 
484
                self.outf.write('\n')
 
485
 
 
486
 
 
487
class cmd_mv(Command):
 
488
    """Move or rename a file.
 
489
 
 
490
    usage:
 
491
        bzr mv OLDNAME NEWNAME
 
492
        bzr mv SOURCE... DESTINATION
 
493
 
 
494
    If the last argument is a versioned directory, all the other names
 
495
    are moved into it.  Otherwise, there must be exactly two arguments
 
496
    and the file is changed to a new name.
 
497
 
 
498
    If OLDNAME does not exist on the filesystem but is versioned and
 
499
    NEWNAME does exist on the filesystem but is not versioned, mv
 
500
    assumes that the file has been manually moved and only updates
 
501
    its internal inventory to reflect that change.
 
502
    The same is valid when moving many SOURCE files to a DESTINATION.
 
503
 
 
504
    Files cannot be moved between branches.
 
505
    """
 
506
 
 
507
    takes_args = ['names*']
 
508
    takes_options = [Option("after", help="move only the bzr identifier"
 
509
        " of the file (file has already been moved). Use this flag if"
 
510
        " bzr is not able to detect this itself.")]
 
511
    aliases = ['move', 'rename']
 
512
    encoding_type = 'replace'
 
513
 
 
514
    def run(self, names_list, after=False):
 
515
        if names_list is None:
 
516
            names_list = []
 
517
 
 
518
        if len(names_list) < 2:
 
519
            raise errors.BzrCommandError("missing file argument")
 
520
        tree, rel_names = tree_files(names_list)
 
521
        
 
522
        if os.path.isdir(names_list[-1]):
 
523
            # move into existing directory
 
524
            for pair in tree.move(rel_names[:-1], rel_names[-1], after=after):
 
525
                self.outf.write("%s => %s\n" % pair)
 
526
        else:
 
527
            if len(names_list) != 2:
 
528
                raise errors.BzrCommandError('to mv multiple files the'
 
529
                                             ' destination must be a versioned'
 
530
                                             ' directory')
 
531
            tree.rename_one(rel_names[0], rel_names[1], after=after)
 
532
            self.outf.write("%s => %s\n" % (rel_names[0], rel_names[1]))
 
533
            
 
534
    
 
535
class cmd_pull(Command):
 
536
    """Turn this branch into a mirror of another branch.
 
537
 
 
538
    This command only works on branches that have not diverged.  Branches are
 
539
    considered diverged if the destination branch's most recent commit is one
 
540
    that has not been merged (directly or indirectly) into the parent.
 
541
 
 
542
    If branches have diverged, you can use 'bzr merge' to integrate the changes
 
543
    from one into the other.  Once one branch has merged, the other should
 
544
    be able to pull it again.
 
545
 
 
546
    If you want to forget your local changes and just update your branch to
 
547
    match the remote one, use pull --overwrite.
 
548
 
 
549
    If there is no default location set, the first pull will set it.  After
 
550
    that, you can omit the location to use the default.  To change the
 
551
    default, use --remember. The value will only be saved if the remote
 
552
    location can be accessed.
 
553
    """
 
554
 
 
555
    takes_options = ['remember', 'overwrite', 'revision', 'verbose',
 
556
        Option('directory',
 
557
            help='branch to pull into, '
 
558
                 'rather than the one containing the working directory',
 
559
            short_name='d',
 
560
            type=unicode,
 
561
            ),
 
562
        ]
 
563
    takes_args = ['location?']
 
564
    encoding_type = 'replace'
 
565
 
 
566
    def run(self, location=None, remember=False, overwrite=False,
 
567
            revision=None, verbose=False,
 
568
            directory=None):
 
569
        from bzrlib.tag import _merge_tags_if_possible
 
570
        # FIXME: too much stuff is in the command class
 
571
        rev_id = None
 
572
        mergeable = None
 
573
        if directory is None:
 
574
            directory = u'.'
 
575
        try:
 
576
            tree_to = WorkingTree.open_containing(directory)[0]
 
577
            branch_to = tree_to.branch
 
578
        except errors.NoWorkingTree:
 
579
            tree_to = None
 
580
            branch_to = Branch.open_containing(directory)[0]
 
581
 
 
582
        reader = None
 
583
        if location is not None:
 
584
            try:
 
585
                mergeable = bundle.read_mergeable_from_url(
 
586
                    location)
 
587
            except errors.NotABundle:
 
588
                pass # Continue on considering this url a Branch
 
589
 
 
590
        stored_loc = branch_to.get_parent()
 
591
        if location is None:
 
592
            if stored_loc is None:
 
593
                raise errors.BzrCommandError("No pull location known or"
 
594
                                             " specified.")
 
595
            else:
 
596
                display_url = urlutils.unescape_for_display(stored_loc,
 
597
                        self.outf.encoding)
 
598
                self.outf.write("Using saved location: %s\n" % display_url)
 
599
                location = stored_loc
 
600
 
 
601
        if mergeable is not None:
 
602
            if revision is not None:
 
603
                raise errors.BzrCommandError(
 
604
                    'Cannot use -r with merge directives or bundles')
 
605
            rev_id = mergeable.install_revisions(branch_to.repository)
 
606
            branch_from = branch_to
 
607
        else:
 
608
            branch_from = Branch.open(location)
 
609
 
 
610
            if branch_to.get_parent() is None or remember:
 
611
                branch_to.set_parent(branch_from.base)
 
612
 
 
613
        if revision is not None:
 
614
            if len(revision) == 1:
 
615
                rev_id = revision[0].in_history(branch_from).rev_id
 
616
            else:
 
617
                raise errors.BzrCommandError(
 
618
                    'bzr pull --revision takes one value.')
 
619
 
 
620
        old_rh = branch_to.revision_history()
 
621
        if tree_to is not None:
 
622
            result = tree_to.pull(branch_from, overwrite, rev_id,
 
623
                delta._ChangeReporter(unversioned_filter=tree_to.is_ignored))
 
624
        else:
 
625
            result = branch_to.pull(branch_from, overwrite, rev_id)
 
626
 
 
627
        result.report(self.outf)
 
628
        if verbose:
 
629
            from bzrlib.log import show_changed_revisions
 
630
            new_rh = branch_to.revision_history()
 
631
            show_changed_revisions(branch_to, old_rh, new_rh,
 
632
                                   to_file=self.outf)
 
633
 
 
634
 
 
635
class cmd_push(Command):
 
636
    """Update a mirror of this branch.
 
637
    
 
638
    The target branch will not have its working tree populated because this
 
639
    is both expensive, and is not supported on remote file systems.
 
640
    
 
641
    Some smart servers or protocols *may* put the working tree in place in
 
642
    the future.
 
643
 
 
644
    This command only works on branches that have not diverged.  Branches are
 
645
    considered diverged if the destination branch's most recent commit is one
 
646
    that has not been merged (directly or indirectly) by the source branch.
 
647
 
 
648
    If branches have diverged, you can use 'bzr push --overwrite' to replace
 
649
    the other branch completely, discarding its unmerged changes.
 
650
    
 
651
    If you want to ensure you have the different changes in the other branch,
 
652
    do a merge (see bzr help merge) from the other branch, and commit that.
 
653
    After that you will be able to do a push without '--overwrite'.
 
654
 
 
655
    If there is no default push location set, the first push will set it.
 
656
    After that, you can omit the location to use the default.  To change the
 
657
    default, use --remember. The value will only be saved if the remote
 
658
    location can be accessed.
 
659
    """
 
660
 
 
661
    takes_options = ['remember', 'overwrite', 'verbose',
 
662
        Option('create-prefix',
 
663
               help='Create the path leading up to the branch '
 
664
                    'if it does not already exist'),
 
665
        Option('directory',
 
666
            help='branch to push from, '
 
667
                 'rather than the one containing the working directory',
 
668
            short_name='d',
 
669
            type=unicode,
 
670
            ),
 
671
        Option('use-existing-dir',
 
672
               help='By default push will fail if the target'
 
673
                    ' directory exists, but does not already'
 
674
                    ' have a control directory. This flag will'
 
675
                    ' allow push to proceed.'),
 
676
        ]
 
677
    takes_args = ['location?']
 
678
    encoding_type = 'replace'
 
679
 
 
680
    def run(self, location=None, remember=False, overwrite=False,
 
681
            create_prefix=False, verbose=False,
 
682
            use_existing_dir=False,
 
683
            directory=None):
 
684
        # FIXME: Way too big!  Put this into a function called from the
 
685
        # command.
 
686
        if directory is None:
 
687
            directory = '.'
 
688
        br_from = Branch.open_containing(directory)[0]
 
689
        stored_loc = br_from.get_push_location()
 
690
        if location is None:
 
691
            if stored_loc is None:
 
692
                raise errors.BzrCommandError("No push location known or specified.")
 
693
            else:
 
694
                display_url = urlutils.unescape_for_display(stored_loc,
 
695
                        self.outf.encoding)
 
696
                self.outf.write("Using saved location: %s\n" % display_url)
 
697
                location = stored_loc
 
698
 
 
699
        to_transport = transport.get_transport(location)
 
700
        location_url = to_transport.base
 
701
 
 
702
        br_to = repository_to = dir_to = None
 
703
        try:
 
704
            dir_to = bzrdir.BzrDir.open_from_transport(to_transport)
 
705
        except errors.NotBranchError:
 
706
            pass # Didn't find anything
 
707
        else:
 
708
            # If we can open a branch, use its direct repository, otherwise see
 
709
            # if there is a repository without a branch.
 
710
            try:
 
711
                br_to = dir_to.open_branch()
 
712
            except errors.NotBranchError:
 
713
                # Didn't find a branch, can we find a repository?
 
714
                try:
 
715
                    repository_to = dir_to.find_repository()
 
716
                except errors.NoRepositoryPresent:
 
717
                    pass
 
718
            else:
 
719
                # Found a branch, so we must have found a repository
 
720
                repository_to = br_to.repository
 
721
        push_result = None
 
722
        old_rh = []
 
723
        if dir_to is None:
 
724
            # The destination doesn't exist; create it.
 
725
            # XXX: Refactor the create_prefix/no_create_prefix code into a
 
726
            #      common helper function
 
727
            try:
 
728
                to_transport.mkdir('.')
 
729
            except errors.FileExists:
 
730
                if not use_existing_dir:
 
731
                    raise errors.BzrCommandError("Target directory %s"
 
732
                         " already exists, but does not have a valid .bzr"
 
733
                         " directory. Supply --use-existing-dir to push"
 
734
                         " there anyway." % location)
 
735
            except errors.NoSuchFile:
 
736
                if not create_prefix:
 
737
                    raise errors.BzrCommandError("Parent directory of %s"
 
738
                        " does not exist."
 
739
                        "\nYou may supply --create-prefix to create all"
 
740
                        " leading parent directories."
 
741
                        % location)
 
742
 
 
743
                cur_transport = to_transport
 
744
                needed = [cur_transport]
 
745
                # Recurse upwards until we can create a directory successfully
 
746
                while True:
 
747
                    new_transport = cur_transport.clone('..')
 
748
                    if new_transport.base == cur_transport.base:
 
749
                        raise errors.BzrCommandError("Failed to create path"
 
750
                                                     " prefix for %s."
 
751
                                                     % location)
 
752
                    try:
 
753
                        new_transport.mkdir('.')
 
754
                    except errors.NoSuchFile:
 
755
                        needed.append(new_transport)
 
756
                        cur_transport = new_transport
 
757
                    else:
 
758
                        break
 
759
 
 
760
                # Now we only need to create child directories
 
761
                while needed:
 
762
                    cur_transport = needed.pop()
 
763
                    cur_transport.mkdir('.')
 
764
            
 
765
            # Now the target directory exists, but doesn't have a .bzr
 
766
            # directory. So we need to create it, along with any work to create
 
767
            # all of the dependent branches, etc.
 
768
            dir_to = br_from.bzrdir.clone(location_url,
 
769
                revision_id=br_from.last_revision())
 
770
            br_to = dir_to.open_branch()
 
771
            # TODO: Some more useful message about what was copied
 
772
            note('Created new branch.')
 
773
            # We successfully created the target, remember it
 
774
            if br_from.get_push_location() is None or remember:
 
775
                br_from.set_push_location(br_to.base)
 
776
        elif repository_to is None:
 
777
            # we have a bzrdir but no branch or repository
 
778
            # XXX: Figure out what to do other than complain.
 
779
            raise errors.BzrCommandError("At %s you have a valid .bzr control"
 
780
                " directory, but not a branch or repository. This is an"
 
781
                " unsupported configuration. Please move the target directory"
 
782
                " out of the way and try again."
 
783
                % location)
 
784
        elif br_to is None:
 
785
            # We have a repository but no branch, copy the revisions, and then
 
786
            # create a branch.
 
787
            last_revision_id = br_from.last_revision()
 
788
            repository_to.fetch(br_from.repository,
 
789
                                revision_id=last_revision_id)
 
790
            br_to = br_from.clone(dir_to, revision_id=last_revision_id)
 
791
            note('Created new branch.')
 
792
            if br_from.get_push_location() is None or remember:
 
793
                br_from.set_push_location(br_to.base)
 
794
        else: # We have a valid to branch
 
795
            # We were able to connect to the remote location, so remember it
 
796
            # we don't need to successfully push because of possible divergence.
 
797
            if br_from.get_push_location() is None or remember:
 
798
                br_from.set_push_location(br_to.base)
 
799
            old_rh = br_to.revision_history()
 
800
            try:
 
801
                try:
 
802
                    tree_to = dir_to.open_workingtree()
 
803
                except errors.NotLocalUrl:
 
804
                    warning('This transport does not update the working '
 
805
                            'tree of: %s' % (br_to.base,))
 
806
                    push_result = br_from.push(br_to, overwrite)
 
807
                except errors.NoWorkingTree:
 
808
                    push_result = br_from.push(br_to, overwrite)
 
809
                else:
 
810
                    tree_to.lock_write()
 
811
                    try:
 
812
                        push_result = br_from.push(tree_to.branch, overwrite)
 
813
                        tree_to.update()
 
814
                    finally:
 
815
                        tree_to.unlock()
 
816
            except errors.DivergedBranches:
 
817
                raise errors.BzrCommandError('These branches have diverged.'
 
818
                                        '  Try using "merge" and then "push".')
 
819
        if push_result is not None:
 
820
            push_result.report(self.outf)
 
821
        elif verbose:
 
822
            new_rh = br_to.revision_history()
 
823
            if old_rh != new_rh:
 
824
                # Something changed
 
825
                from bzrlib.log import show_changed_revisions
 
826
                show_changed_revisions(br_to, old_rh, new_rh,
 
827
                                       to_file=self.outf)
 
828
        else:
 
829
            # we probably did a clone rather than a push, so a message was
 
830
            # emitted above
 
831
            pass
 
832
 
 
833
 
 
834
class cmd_branch(Command):
 
835
    """Create a new copy of a branch.
 
836
 
 
837
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
 
838
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
 
839
 
 
840
    To retrieve the branch as of a particular revision, supply the --revision
 
841
    parameter, as in "branch foo/bar -r 5".
 
842
 
 
843
    --basis is to speed up branching from remote branches.  When specified, it
 
844
    copies all the file-contents, inventory and revision data from the basis
 
845
    branch before copying anything from the remote branch.
 
846
    """
 
847
    takes_args = ['from_location', 'to_location?']
 
848
    takes_options = ['revision', 'basis']
 
849
    aliases = ['get', 'clone']
 
850
 
 
851
    def run(self, from_location, to_location=None, revision=None, basis=None):
 
852
        from bzrlib.tag import _merge_tags_if_possible
 
853
        if revision is None:
 
854
            revision = [None]
 
855
        elif len(revision) > 1:
 
856
            raise errors.BzrCommandError(
 
857
                'bzr branch --revision takes exactly 1 revision value')
 
858
 
 
859
        br_from = Branch.open(from_location)
 
860
        br_from.lock_read()
 
861
        try:
 
862
            if basis is not None:
 
863
                basis_dir = bzrdir.BzrDir.open_containing(basis)[0]
 
864
            else:
 
865
                basis_dir = None
 
866
            if len(revision) == 1 and revision[0] is not None:
 
867
                revision_id = revision[0].in_history(br_from)[1]
 
868
            else:
 
869
                # FIXME - wt.last_revision, fallback to branch, fall back to
 
870
                # None or perhaps NULL_REVISION to mean copy nothing
 
871
                # RBC 20060209
 
872
                revision_id = br_from.last_revision()
 
873
            if to_location is None:
 
874
                to_location = os.path.basename(from_location.rstrip("/\\"))
 
875
                name = None
 
876
            else:
 
877
                name = os.path.basename(to_location) + '\n'
 
878
 
 
879
            to_transport = transport.get_transport(to_location)
 
880
            try:
 
881
                to_transport.mkdir('.')
 
882
            except errors.FileExists:
 
883
                raise errors.BzrCommandError('Target directory "%s" already'
 
884
                                             ' exists.' % to_location)
 
885
            except errors.NoSuchFile:
 
886
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
 
887
                                             % to_location)
 
888
            try:
 
889
                # preserve whatever source format we have.
 
890
                dir = br_from.bzrdir.sprout(to_transport.base,
 
891
                        revision_id, basis_dir)
 
892
                branch = dir.open_branch()
 
893
            except errors.NoSuchRevision:
 
894
                to_transport.delete_tree('.')
 
895
                msg = "The branch %s has no revision %s." % (from_location, revision[0])
 
896
                raise errors.BzrCommandError(msg)
 
897
            except errors.UnlistableBranch:
 
898
                osutils.rmtree(to_location)
 
899
                msg = "The branch %s cannot be used as a --basis" % (basis,)
 
900
                raise errors.BzrCommandError(msg)
 
901
            if name:
 
902
                branch.control_files.put_utf8('branch-name', name)
 
903
            _merge_tags_if_possible(br_from, branch)
 
904
            note('Branched %d revision(s).' % branch.revno())
 
905
        finally:
 
906
            br_from.unlock()
 
907
 
 
908
 
 
909
class cmd_checkout(Command):
 
910
    """Create a new checkout of an existing branch.
 
911
 
 
912
    If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree for
 
913
    the branch found in '.'. This is useful if you have removed the working tree
 
914
    or if it was never created - i.e. if you pushed the branch to its current
 
915
    location using SFTP.
 
916
    
 
917
    If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION will
 
918
    be used.  In other words, "checkout ../foo/bar" will attempt to create ./bar.
 
919
 
 
920
    To retrieve the branch as of a particular revision, supply the --revision
 
921
    parameter, as in "checkout foo/bar -r 5". Note that this will be immediately
 
922
    out of date [so you cannot commit] but it may be useful (i.e. to examine old
 
923
    code.)
 
924
 
 
925
    --basis is to speed up checking out from remote branches.  When specified, it
 
926
    uses the inventory and file contents from the basis branch in preference to the
 
927
    branch being checked out.
 
928
 
 
929
    See "help checkouts" for more information on checkouts.
 
930
    """
 
931
    takes_args = ['branch_location?', 'to_location?']
 
932
    takes_options = ['revision', # , 'basis']
 
933
                     Option('lightweight',
 
934
                            help="perform a lightweight checkout. Lightweight "
 
935
                                 "checkouts depend on access to the branch for "
 
936
                                 "every operation. Normal checkouts can perform "
 
937
                                 "common operations like diff and status without "
 
938
                                 "such access, and also support local commits."
 
939
                            ),
 
940
                     ]
 
941
    aliases = ['co']
 
942
 
 
943
    def run(self, branch_location=None, to_location=None, revision=None, basis=None,
 
944
            lightweight=False):
 
945
        if revision is None:
 
946
            revision = [None]
 
947
        elif len(revision) > 1:
 
948
            raise errors.BzrCommandError(
 
949
                'bzr checkout --revision takes exactly 1 revision value')
 
950
        if branch_location is None:
 
951
            branch_location = osutils.getcwd()
 
952
            to_location = branch_location
 
953
        source = Branch.open(branch_location)
 
954
        if len(revision) == 1 and revision[0] is not None:
 
955
            revision_id = revision[0].in_history(source)[1]
 
956
        else:
 
957
            revision_id = None
 
958
        if to_location is None:
 
959
            to_location = os.path.basename(branch_location.rstrip("/\\"))
 
960
        # if the source and to_location are the same, 
 
961
        # and there is no working tree,
 
962
        # then reconstitute a branch
 
963
        if (osutils.abspath(to_location) ==
 
964
            osutils.abspath(branch_location)):
 
965
            try:
 
966
                source.bzrdir.open_workingtree()
 
967
            except errors.NoWorkingTree:
 
968
                source.bzrdir.create_workingtree()
 
969
                return
 
970
        try:
 
971
            os.mkdir(to_location)
 
972
        except OSError, e:
 
973
            if e.errno == errno.EEXIST:
 
974
                raise errors.BzrCommandError('Target directory "%s" already'
 
975
                                             ' exists.' % to_location)
 
976
            if e.errno == errno.ENOENT:
 
977
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
 
978
                                             % to_location)
 
979
            else:
 
980
                raise
 
981
        source.create_checkout(to_location, revision_id, lightweight)
 
982
 
 
983
 
 
984
class cmd_renames(Command):
 
985
    """Show list of renamed files.
 
986
    """
 
987
    # TODO: Option to show renames between two historical versions.
 
988
 
 
989
    # TODO: Only show renames under dir, rather than in the whole branch.
 
990
    takes_args = ['dir?']
 
991
 
 
992
    @display_command
 
993
    def run(self, dir=u'.'):
 
994
        tree = WorkingTree.open_containing(dir)[0]
 
995
        tree.lock_read()
 
996
        try:
 
997
            new_inv = tree.inventory
 
998
            old_tree = tree.basis_tree()
 
999
            old_tree.lock_read()
 
1000
            try:
 
1001
                old_inv = old_tree.inventory
 
1002
                renames = list(_mod_tree.find_renames(old_inv, new_inv))
 
1003
                renames.sort()
 
1004
                for old_name, new_name in renames:
 
1005
                    self.outf.write("%s => %s\n" % (old_name, new_name))
 
1006
            finally:
 
1007
                old_tree.unlock()
 
1008
        finally:
 
1009
            tree.unlock()
 
1010
 
 
1011
 
 
1012
class cmd_update(Command):
 
1013
    """Update a tree to have the latest code committed to its branch.
 
1014
    
 
1015
    This will perform a merge into the working tree, and may generate
 
1016
    conflicts. If you have any local changes, you will still 
 
1017
    need to commit them after the update for the update to be complete.
 
1018
    
 
1019
    If you want to discard your local changes, you can just do a 
 
1020
    'bzr revert' instead of 'bzr commit' after the update.
 
1021
    """
 
1022
    takes_args = ['dir?']
 
1023
    aliases = ['up']
 
1024
 
 
1025
    def run(self, dir='.'):
 
1026
        tree = WorkingTree.open_containing(dir)[0]
 
1027
        master = tree.branch.get_master_branch()
 
1028
        if master is not None:
 
1029
            tree.lock_write()
 
1030
        else:
 
1031
            tree.lock_tree_write()
 
1032
        try:
 
1033
            existing_pending_merges = tree.get_parent_ids()[1:]
 
1034
            last_rev = tree.last_revision()
 
1035
            if last_rev == tree.branch.last_revision():
 
1036
                # may be up to date, check master too.
 
1037
                master = tree.branch.get_master_branch()
 
1038
                if master is None or last_rev == master.last_revision():
 
1039
                    revno = tree.branch.revision_id_to_revno(last_rev)
 
1040
                    note("Tree is up to date at revision %d." % (revno,))
 
1041
                    return 0
 
1042
            conflicts = tree.update()
 
1043
            revno = tree.branch.revision_id_to_revno(tree.last_revision())
 
1044
            note('Updated to revision %d.' % (revno,))
 
1045
            if tree.get_parent_ids()[1:] != existing_pending_merges:
 
1046
                note('Your local commits will now show as pending merges with '
 
1047
                     "'bzr status', and can be committed with 'bzr commit'.")
 
1048
            if conflicts != 0:
 
1049
                return 1
 
1050
            else:
 
1051
                return 0
 
1052
        finally:
 
1053
            tree.unlock()
 
1054
 
 
1055
 
 
1056
class cmd_info(Command):
 
1057
    """Show information about a working tree, branch or repository.
 
1058
 
 
1059
    This command will show all known locations and formats associated to the
 
1060
    tree, branch or repository.  Statistical information is included with
 
1061
    each report.
 
1062
 
 
1063
    Branches and working trees will also report any missing revisions.
 
1064
    """
 
1065
    takes_args = ['location?']
 
1066
    takes_options = ['verbose']
 
1067
 
 
1068
    @display_command
 
1069
    def run(self, location=None, verbose=False):
 
1070
        from bzrlib.info import show_bzrdir_info
 
1071
        show_bzrdir_info(bzrdir.BzrDir.open_containing(location)[0],
 
1072
                         verbose=verbose)
 
1073
 
 
1074
 
 
1075
class cmd_remove(Command):
 
1076
    """Make a file unversioned.
 
1077
 
 
1078
    This makes bzr stop tracking changes to a versioned file.  It does
 
1079
    not delete the working copy.
 
1080
 
 
1081
    You can specify one or more files, and/or --new.  If you specify --new,
 
1082
    only 'added' files will be removed.  If you specify both, then new files
 
1083
    in the specified directories will be removed.  If the directories are
 
1084
    also new, they will also be removed.
 
1085
    """
 
1086
    takes_args = ['file*']
 
1087
    takes_options = ['verbose', Option('new', help='remove newly-added files')]
 
1088
    aliases = ['rm']
 
1089
    encoding_type = 'replace'
 
1090
    
 
1091
    def run(self, file_list, verbose=False, new=False):
 
1092
        tree, file_list = tree_files(file_list)
 
1093
        if new is False:
 
1094
            if file_list is None:
 
1095
                raise errors.BzrCommandError('Specify one or more files to'
 
1096
                                             ' remove, or use --new.')
 
1097
        else:
 
1098
            added = tree.changes_from(tree.basis_tree(),
 
1099
                specific_files=file_list).added
 
1100
            file_list = sorted([f[0] for f in added], reverse=True)
 
1101
            if len(file_list) == 0:
 
1102
                raise errors.BzrCommandError('No matching files.')
 
1103
        tree.remove(file_list, verbose=verbose, to_file=self.outf)
 
1104
 
 
1105
 
 
1106
class cmd_file_id(Command):
 
1107
    """Print file_id of a particular file or directory.
 
1108
 
 
1109
    The file_id is assigned when the file is first added and remains the
 
1110
    same through all revisions where the file exists, even when it is
 
1111
    moved or renamed.
 
1112
    """
 
1113
 
 
1114
    hidden = True
 
1115
    takes_args = ['filename']
 
1116
 
 
1117
    @display_command
 
1118
    def run(self, filename):
 
1119
        tree, relpath = WorkingTree.open_containing(filename)
 
1120
        i = tree.path2id(relpath)
 
1121
        if i is None:
 
1122
            raise errors.NotVersionedError(filename)
 
1123
        else:
 
1124
            self.outf.write(i + '\n')
 
1125
 
 
1126
 
 
1127
class cmd_file_path(Command):
 
1128
    """Print path of file_ids to a file or directory.
 
1129
 
 
1130
    This prints one line for each directory down to the target,
 
1131
    starting at the branch root.
 
1132
    """
 
1133
 
 
1134
    hidden = True
 
1135
    takes_args = ['filename']
 
1136
 
 
1137
    @display_command
 
1138
    def run(self, filename):
 
1139
        tree, relpath = WorkingTree.open_containing(filename)
 
1140
        fid = tree.path2id(relpath)
 
1141
        if fid is None:
 
1142
            raise errors.NotVersionedError(filename)
 
1143
        segments = osutils.splitpath(relpath)
 
1144
        for pos in range(1, len(segments) + 1):
 
1145
            path = osutils.joinpath(segments[:pos])
 
1146
            self.outf.write("%s\n" % tree.path2id(path))
 
1147
 
 
1148
 
 
1149
class cmd_reconcile(Command):
 
1150
    """Reconcile bzr metadata in a branch.
 
1151
 
 
1152
    This can correct data mismatches that may have been caused by
 
1153
    previous ghost operations or bzr upgrades. You should only
 
1154
    need to run this command if 'bzr check' or a bzr developer 
 
1155
    advises you to run it.
 
1156
 
 
1157
    If a second branch is provided, cross-branch reconciliation is
 
1158
    also attempted, which will check that data like the tree root
 
1159
    id which was not present in very early bzr versions is represented
 
1160
    correctly in both branches.
 
1161
 
 
1162
    At the same time it is run it may recompress data resulting in 
 
1163
    a potential saving in disk space or performance gain.
 
1164
 
 
1165
    The branch *MUST* be on a listable system such as local disk or sftp.
 
1166
    """
 
1167
    takes_args = ['branch?']
 
1168
 
 
1169
    def run(self, branch="."):
 
1170
        from bzrlib.reconcile import reconcile
 
1171
        dir = bzrdir.BzrDir.open(branch)
 
1172
        reconcile(dir)
 
1173
 
 
1174
 
 
1175
class cmd_revision_history(Command):
 
1176
    """Display the list of revision ids on a branch."""
 
1177
    takes_args = ['location?']
 
1178
 
 
1179
    hidden = True
 
1180
 
 
1181
    @display_command
 
1182
    def run(self, location="."):
 
1183
        branch = Branch.open_containing(location)[0]
 
1184
        for revid in branch.revision_history():
 
1185
            self.outf.write(revid)
 
1186
            self.outf.write('\n')
 
1187
 
 
1188
 
 
1189
class cmd_ancestry(Command):
 
1190
    """List all revisions merged into this branch."""
 
1191
    takes_args = ['location?']
 
1192
 
 
1193
    hidden = True
 
1194
 
 
1195
    @display_command
 
1196
    def run(self, location="."):
 
1197
        try:
 
1198
            wt = WorkingTree.open_containing(location)[0]
 
1199
        except errors.NoWorkingTree:
 
1200
            b = Branch.open(location)
 
1201
            last_revision = b.last_revision()
 
1202
        else:
 
1203
            b = wt.branch
 
1204
            last_revision = wt.last_revision()
 
1205
 
 
1206
        revision_ids = b.repository.get_ancestry(last_revision)
 
1207
        assert revision_ids[0] is None
 
1208
        revision_ids.pop(0)
 
1209
        for revision_id in revision_ids:
 
1210
            self.outf.write(revision_id + '\n')
 
1211
 
 
1212
 
 
1213
class cmd_init(Command):
 
1214
    """Make a directory into a versioned branch.
 
1215
 
 
1216
    Use this to create an empty branch, or before importing an
 
1217
    existing project.
 
1218
 
 
1219
    If there is a repository in a parent directory of the location, then 
 
1220
    the history of the branch will be stored in the repository.  Otherwise
 
1221
    init creates a standalone branch which carries its own history in 
 
1222
    .bzr.
 
1223
 
 
1224
    If there is already a branch at the location but it has no working tree,
 
1225
    the tree can be populated with 'bzr checkout'.
 
1226
 
 
1227
    Recipe for importing a tree of files:
 
1228
        cd ~/project
 
1229
        bzr init
 
1230
        bzr add .
 
1231
        bzr status
 
1232
        bzr commit -m 'imported project'
 
1233
    """
 
1234
    takes_args = ['location?']
 
1235
    takes_options = [
 
1236
         RegistryOption('format',
 
1237
                help='Specify a format for this branch. '
 
1238
                'See "help formats".',
 
1239
                registry=bzrdir.format_registry,
 
1240
                converter=bzrdir.format_registry.make_bzrdir,
 
1241
                value_switches=True,
 
1242
                title="Branch Format",
 
1243
                ),
 
1244
         Option('append-revisions-only',
 
1245
                help='Never change revnos or the existing log.'
 
1246
                '  Append revisions to it only.')
 
1247
         ]
 
1248
    def run(self, location=None, format=None, append_revisions_only=False):
 
1249
        if format is None:
 
1250
            format = bzrdir.format_registry.make_bzrdir('default')
 
1251
        if location is None:
 
1252
            location = u'.'
 
1253
 
 
1254
        to_transport = transport.get_transport(location)
 
1255
 
 
1256
        # The path has to exist to initialize a
 
1257
        # branch inside of it.
 
1258
        # Just using os.mkdir, since I don't
 
1259
        # believe that we want to create a bunch of
 
1260
        # locations if the user supplies an extended path
 
1261
        # TODO: create-prefix
 
1262
        try:
 
1263
            to_transport.mkdir('.')
 
1264
        except errors.FileExists:
 
1265
            pass
 
1266
                    
 
1267
        try:
 
1268
            existing_bzrdir = bzrdir.BzrDir.open(location)
 
1269
        except errors.NotBranchError:
 
1270
            # really a NotBzrDir error...
 
1271
            branch = bzrdir.BzrDir.create_branch_convenience(to_transport.base,
 
1272
                                                             format=format)
 
1273
        else:
 
1274
            from bzrlib.transport.local import LocalTransport
 
1275
            if existing_bzrdir.has_branch():
 
1276
                if (isinstance(to_transport, LocalTransport)
 
1277
                    and not existing_bzrdir.has_workingtree()):
 
1278
                        raise errors.BranchExistsWithoutWorkingTree(location)
 
1279
                raise errors.AlreadyBranchError(location)
 
1280
            else:
 
1281
                branch = existing_bzrdir.create_branch()
 
1282
                existing_bzrdir.create_workingtree()
 
1283
        if append_revisions_only:
 
1284
            try:
 
1285
                branch.set_append_revisions_only(True)
 
1286
            except errors.UpgradeRequired:
 
1287
                raise errors.BzrCommandError('This branch format cannot be set'
 
1288
                    ' to append-revisions-only.  Try --experimental-branch6')
 
1289
 
 
1290
 
 
1291
class cmd_init_repository(Command):
 
1292
    """Create a shared repository to hold branches.
 
1293
 
 
1294
    New branches created under the repository directory will store their revisions
 
1295
    in the repository, not in the branch directory, if the branch format supports
 
1296
    shared storage.
 
1297
 
 
1298
    example:
 
1299
        bzr init-repo --no-trees repo
 
1300
        bzr init repo/trunk
 
1301
        bzr checkout --lightweight repo/trunk trunk-checkout
 
1302
        cd trunk-checkout
 
1303
        (add files here)
 
1304
    """
 
1305
 
 
1306
    takes_args = ["location"]
 
1307
    takes_options = [RegistryOption('format',
 
1308
                            help='Specify a format for this repository. See'
 
1309
                                 ' "bzr help formats" for details',
 
1310
                            registry=bzrdir.format_registry,
 
1311
                            converter=bzrdir.format_registry.make_bzrdir,
 
1312
                            value_switches=True, title='Repository format'),
 
1313
                     Option('no-trees',
 
1314
                             help='Branches in the repository will default to'
 
1315
                                  'not having a working tree'),
 
1316
                    ]
 
1317
    aliases = ["init-repo"]
 
1318
 
 
1319
    def run(self, location, format=None, no_trees=False):
 
1320
        if format is None:
 
1321
            format = bzrdir.format_registry.make_bzrdir('default')
 
1322
 
 
1323
        if location is None:
 
1324
            location = '.'
 
1325
 
 
1326
        to_transport = transport.get_transport(location)
 
1327
        try:
 
1328
            to_transport.mkdir('.')
 
1329
        except errors.FileExists:
 
1330
            pass
 
1331
 
 
1332
        newdir = format.initialize_on_transport(to_transport)
 
1333
        repo = newdir.create_repository(shared=True)
 
1334
        repo.set_make_working_trees(not no_trees)
 
1335
 
 
1336
 
 
1337
class cmd_diff(Command):
 
1338
    """Show differences in the working tree or between revisions.
 
1339
    
 
1340
    If files are listed, only the changes in those files are listed.
 
1341
    Otherwise, all changes for the tree are listed.
 
1342
 
 
1343
    "bzr diff -p1" is equivalent to "bzr diff --prefix old/:new/", and
 
1344
    produces patches suitable for "patch -p1".
 
1345
 
 
1346
    examples:
 
1347
        bzr diff
 
1348
            Shows the difference in the working tree versus the last commit
 
1349
        bzr diff -r1
 
1350
            Difference between the working tree and revision 1
 
1351
        bzr diff -r1..2
 
1352
            Difference between revision 2 and revision 1
 
1353
        bzr diff --prefix old/:new/
 
1354
            Same as 'bzr diff' but prefix paths with old/ and new/
 
1355
        bzr diff bzr.mine bzr.dev
 
1356
            Show the differences between the two working trees
 
1357
        bzr diff foo.c
 
1358
            Show just the differences for 'foo.c'
 
1359
    """
 
1360
    # TODO: Option to use external diff command; could be GNU diff, wdiff,
 
1361
    #       or a graphical diff.
 
1362
 
 
1363
    # TODO: Python difflib is not exactly the same as unidiff; should
 
1364
    #       either fix it up or prefer to use an external diff.
 
1365
 
 
1366
    # TODO: Selected-file diff is inefficient and doesn't show you
 
1367
    #       deleted files.
 
1368
 
 
1369
    # TODO: This probably handles non-Unix newlines poorly.
 
1370
 
 
1371
    takes_args = ['file*']
 
1372
    takes_options = ['revision', 'diff-options',
 
1373
        Option('prefix', type=str,
 
1374
               short_name='p',
 
1375
               help='Set prefixes to added to old and new filenames, as '
 
1376
                    'two values separated by a colon. (eg "old/:new/")'),
 
1377
        ]
 
1378
    aliases = ['di', 'dif']
 
1379
    encoding_type = 'exact'
 
1380
 
 
1381
    @display_command
 
1382
    def run(self, revision=None, file_list=None, diff_options=None,
 
1383
            prefix=None):
 
1384
        from bzrlib.diff import diff_cmd_helper, show_diff_trees
 
1385
 
 
1386
        if (prefix is None) or (prefix == '0'):
 
1387
            # diff -p0 format
 
1388
            old_label = ''
 
1389
            new_label = ''
 
1390
        elif prefix == '1':
 
1391
            old_label = 'old/'
 
1392
            new_label = 'new/'
 
1393
        elif ':' in prefix:
 
1394
            old_label, new_label = prefix.split(":")
 
1395
        else:
 
1396
            raise errors.BzrCommandError(
 
1397
                '--prefix expects two values separated by a colon'
 
1398
                ' (eg "old/:new/")')
 
1399
 
 
1400
        if revision and len(revision) > 2:
 
1401
            raise errors.BzrCommandError('bzr diff --revision takes exactly'
 
1402
                                         ' one or two revision specifiers')
 
1403
 
 
1404
        try:
 
1405
            tree1, file_list = internal_tree_files(file_list)
 
1406
            tree2 = None
 
1407
            b = None
 
1408
            b2 = None
 
1409
        except errors.FileInWrongBranch:
 
1410
            if len(file_list) != 2:
 
1411
                raise errors.BzrCommandError("Files are in different branches")
 
1412
 
 
1413
            tree1, file1 = WorkingTree.open_containing(file_list[0])
 
1414
            tree2, file2 = WorkingTree.open_containing(file_list[1])
 
1415
            if file1 != "" or file2 != "":
 
1416
                # FIXME diff those two files. rbc 20051123
 
1417
                raise errors.BzrCommandError("Files are in different branches")
 
1418
            file_list = None
 
1419
        except errors.NotBranchError:
 
1420
            if (revision is not None and len(revision) == 2
 
1421
                and not revision[0].needs_branch()
 
1422
                and not revision[1].needs_branch()):
 
1423
                # If both revision specs include a branch, we can
 
1424
                # diff them without needing a local working tree
 
1425
                tree1, tree2 = None, None
 
1426
            else:
 
1427
                raise
 
1428
 
 
1429
        if tree2 is not None:
 
1430
            if revision is not None:
 
1431
                # FIXME: but there should be a clean way to diff between
 
1432
                # non-default versions of two trees, it's not hard to do
 
1433
                # internally...
 
1434
                raise errors.BzrCommandError(
 
1435
                        "Sorry, diffing arbitrary revisions across branches "
 
1436
                        "is not implemented yet")
 
1437
            return show_diff_trees(tree1, tree2, sys.stdout, 
 
1438
                                   specific_files=file_list,
 
1439
                                   external_diff_options=diff_options,
 
1440
                                   old_label=old_label, new_label=new_label)
 
1441
 
 
1442
        return diff_cmd_helper(tree1, file_list, diff_options,
 
1443
                               revision_specs=revision,
 
1444
                               old_label=old_label, new_label=new_label)
 
1445
 
 
1446
 
 
1447
class cmd_deleted(Command):
 
1448
    """List files deleted in the working tree.
 
1449
    """
 
1450
    # TODO: Show files deleted since a previous revision, or
 
1451
    # between two revisions.
 
1452
    # TODO: Much more efficient way to do this: read in new
 
1453
    # directories with readdir, rather than stating each one.  Same
 
1454
    # level of effort but possibly much less IO.  (Or possibly not,
 
1455
    # if the directories are very large...)
 
1456
    takes_options = ['show-ids']
 
1457
 
 
1458
    @display_command
 
1459
    def run(self, show_ids=False):
 
1460
        tree = WorkingTree.open_containing(u'.')[0]
 
1461
        tree.lock_read()
 
1462
        try:
 
1463
            old = tree.basis_tree()
 
1464
            old.lock_read()
 
1465
            try:
 
1466
                for path, ie in old.inventory.iter_entries():
 
1467
                    if not tree.has_id(ie.file_id):
 
1468
                        self.outf.write(path)
 
1469
                        if show_ids:
 
1470
                            self.outf.write(' ')
 
1471
                            self.outf.write(ie.file_id)
 
1472
                        self.outf.write('\n')
 
1473
            finally:
 
1474
                old.unlock()
 
1475
        finally:
 
1476
            tree.unlock()
 
1477
 
 
1478
 
 
1479
class cmd_modified(Command):
 
1480
    """List files modified in working tree.
 
1481
 
 
1482
    See also: "bzr status".
 
1483
    """
 
1484
 
 
1485
    hidden = True
 
1486
 
 
1487
    @display_command
 
1488
    def run(self):
 
1489
        tree = WorkingTree.open_containing(u'.')[0]
 
1490
        td = tree.changes_from(tree.basis_tree())
 
1491
        for path, id, kind, text_modified, meta_modified in td.modified:
 
1492
            self.outf.write(path + '\n')
 
1493
 
 
1494
 
 
1495
class cmd_added(Command):
 
1496
    """List files added in working tree.
 
1497
 
 
1498
    See also: "bzr status".
 
1499
    """
 
1500
 
 
1501
    hidden = True
 
1502
 
 
1503
    @display_command
 
1504
    def run(self):
 
1505
        wt = WorkingTree.open_containing(u'.')[0]
 
1506
        wt.lock_read()
 
1507
        try:
 
1508
            basis = wt.basis_tree()
 
1509
            basis.lock_read()
 
1510
            try:
 
1511
                basis_inv = basis.inventory
 
1512
                inv = wt.inventory
 
1513
                for file_id in inv:
 
1514
                    if file_id in basis_inv:
 
1515
                        continue
 
1516
                    if inv.is_root(file_id) and len(basis_inv) == 0:
 
1517
                        continue
 
1518
                    path = inv.id2path(file_id)
 
1519
                    if not os.access(osutils.abspath(path), os.F_OK):
 
1520
                        continue
 
1521
                    self.outf.write(path + '\n')
 
1522
            finally:
 
1523
                basis.unlock()
 
1524
        finally:
 
1525
            wt.unlock()
 
1526
 
 
1527
 
 
1528
class cmd_root(Command):
 
1529
    """Show the tree root directory.
 
1530
 
 
1531
    The root is the nearest enclosing directory with a .bzr control
 
1532
    directory."""
 
1533
    takes_args = ['filename?']
 
1534
    @display_command
 
1535
    def run(self, filename=None):
 
1536
        """Print the branch root."""
 
1537
        tree = WorkingTree.open_containing(filename)[0]
 
1538
        self.outf.write(tree.basedir + '\n')
 
1539
 
 
1540
 
 
1541
class cmd_log(Command):
 
1542
    """Show log of a branch, file, or directory.
 
1543
 
 
1544
    By default show the log of the branch containing the working directory.
 
1545
 
 
1546
    To request a range of logs, you can use the command -r begin..end
 
1547
    -r revision requests a specific revision, -r ..end or -r begin.. are
 
1548
    also valid.
 
1549
 
 
1550
    examples:
 
1551
        bzr log
 
1552
        bzr log foo.c
 
1553
        bzr log -r -10.. http://server/branch
 
1554
    """
 
1555
 
 
1556
    # TODO: Make --revision support uuid: and hash: [future tag:] notation.
 
1557
 
 
1558
    takes_args = ['location?']
 
1559
    takes_options = [Option('forward', 
 
1560
                            help='show from oldest to newest'),
 
1561
                     'timezone', 
 
1562
                     Option('verbose', 
 
1563
                             short_name='v',
 
1564
                             help='show files changed in each revision'),
 
1565
                     'show-ids', 'revision',
 
1566
                     'log-format',
 
1567
                     Option('message',
 
1568
                            short_name='m',
 
1569
                            help='show revisions whose message matches this regexp',
 
1570
                            type=str),
 
1571
                     ]
 
1572
    encoding_type = 'replace'
 
1573
 
 
1574
    @display_command
 
1575
    def run(self, location=None, timezone='original',
 
1576
            verbose=False,
 
1577
            show_ids=False,
 
1578
            forward=False,
 
1579
            revision=None,
 
1580
            log_format=None,
 
1581
            message=None):
 
1582
        from bzrlib.log import show_log
 
1583
        assert message is None or isinstance(message, basestring), \
 
1584
            "invalid message argument %r" % message
 
1585
        direction = (forward and 'forward') or 'reverse'
 
1586
        
 
1587
        # log everything
 
1588
        file_id = None
 
1589
        if location:
 
1590
            # find the file id to log:
 
1591
 
 
1592
            tree, b, fp = bzrdir.BzrDir.open_containing_tree_or_branch(
 
1593
                location)
 
1594
            if fp != '':
 
1595
                if tree is None:
 
1596
                    tree = b.basis_tree()
 
1597
                file_id = tree.path2id(fp)
 
1598
                if file_id is None:
 
1599
                    raise errors.BzrCommandError(
 
1600
                        "Path does not have any revision history: %s" %
 
1601
                        location)
 
1602
        else:
 
1603
            # local dir only
 
1604
            # FIXME ? log the current subdir only RBC 20060203 
 
1605
            if revision is not None \
 
1606
                    and len(revision) > 0 and revision[0].get_branch():
 
1607
                location = revision[0].get_branch()
 
1608
            else:
 
1609
                location = '.'
 
1610
            dir, relpath = bzrdir.BzrDir.open_containing(location)
 
1611
            b = dir.open_branch()
 
1612
 
 
1613
        b.lock_read()
 
1614
        try:
 
1615
            if revision is None:
 
1616
                rev1 = None
 
1617
                rev2 = None
 
1618
            elif len(revision) == 1:
 
1619
                rev1 = rev2 = revision[0].in_history(b).revno
 
1620
            elif len(revision) == 2:
 
1621
                if revision[1].get_branch() != revision[0].get_branch():
 
1622
                    # b is taken from revision[0].get_branch(), and
 
1623
                    # show_log will use its revision_history. Having
 
1624
                    # different branches will lead to weird behaviors.
 
1625
                    raise errors.BzrCommandError(
 
1626
                        "Log doesn't accept two revisions in different"
 
1627
                        " branches.")
 
1628
                if revision[0].spec is None:
 
1629
                    # missing begin-range means first revision
 
1630
                    rev1 = 1
 
1631
                else:
 
1632
                    rev1 = revision[0].in_history(b).revno
 
1633
 
 
1634
                if revision[1].spec is None:
 
1635
                    # missing end-range means last known revision
 
1636
                    rev2 = b.revno()
 
1637
                else:
 
1638
                    rev2 = revision[1].in_history(b).revno
 
1639
            else:
 
1640
                raise errors.BzrCommandError(
 
1641
                    'bzr log --revision takes one or two values.')
 
1642
 
 
1643
            # By this point, the revision numbers are converted to the +ve
 
1644
            # form if they were supplied in the -ve form, so we can do
 
1645
            # this comparison in relative safety
 
1646
            if rev1 > rev2:
 
1647
                (rev2, rev1) = (rev1, rev2)
 
1648
 
 
1649
            if log_format is None:
 
1650
                log_format = log.log_formatter_registry.get_default(b)
 
1651
 
 
1652
            lf = log_format(show_ids=show_ids, to_file=self.outf,
 
1653
                            show_timezone=timezone)
 
1654
 
 
1655
            show_log(b,
 
1656
                     lf,
 
1657
                     file_id,
 
1658
                     verbose=verbose,
 
1659
                     direction=direction,
 
1660
                     start_revision=rev1,
 
1661
                     end_revision=rev2,
 
1662
                     search=message)
 
1663
        finally:
 
1664
            b.unlock()
 
1665
 
 
1666
 
 
1667
def get_log_format(long=False, short=False, line=False, default='long'):
 
1668
    log_format = default
 
1669
    if long:
 
1670
        log_format = 'long'
 
1671
    if short:
 
1672
        log_format = 'short'
 
1673
    if line:
 
1674
        log_format = 'line'
 
1675
    return log_format
 
1676
 
 
1677
 
 
1678
class cmd_touching_revisions(Command):
 
1679
    """Return revision-ids which affected a particular file.
 
1680
 
 
1681
    A more user-friendly interface is "bzr log FILE".
 
1682
    """
 
1683
 
 
1684
    hidden = True
 
1685
    takes_args = ["filename"]
 
1686
 
 
1687
    @display_command
 
1688
    def run(self, filename):
 
1689
        tree, relpath = WorkingTree.open_containing(filename)
 
1690
        b = tree.branch
 
1691
        file_id = tree.path2id(relpath)
 
1692
        for revno, revision_id, what in log.find_touching_revisions(b, file_id):
 
1693
            self.outf.write("%6d %s\n" % (revno, what))
 
1694
 
 
1695
 
 
1696
class cmd_ls(Command):
 
1697
    """List files in a tree.
 
1698
    """
 
1699
 
 
1700
    takes_args = ['path?']
 
1701
    # TODO: Take a revision or remote path and list that tree instead.
 
1702
    takes_options = ['verbose', 'revision',
 
1703
                     Option('non-recursive',
 
1704
                            help='don\'t recurse into sub-directories'),
 
1705
                     Option('from-root',
 
1706
                            help='Print all paths from the root of the branch.'),
 
1707
                     Option('unknown', help='Print unknown files'),
 
1708
                     Option('versioned', help='Print versioned files'),
 
1709
                     Option('ignored', help='Print ignored files'),
 
1710
 
 
1711
                     Option('null', help='Null separate the files'),
 
1712
                     'kind', 'show-ids'
 
1713
                    ]
 
1714
    @display_command
 
1715
    def run(self, revision=None, verbose=False, 
 
1716
            non_recursive=False, from_root=False,
 
1717
            unknown=False, versioned=False, ignored=False,
 
1718
            null=False, kind=None, show_ids=False, path=None):
 
1719
 
 
1720
        if kind and kind not in ('file', 'directory', 'symlink'):
 
1721
            raise errors.BzrCommandError('invalid kind specified')
 
1722
 
 
1723
        if verbose and null:
 
1724
            raise errors.BzrCommandError('Cannot set both --verbose and --null')
 
1725
        all = not (unknown or versioned or ignored)
 
1726
 
 
1727
        selection = {'I':ignored, '?':unknown, 'V':versioned}
 
1728
 
 
1729
        if path is None:
 
1730
            fs_path = '.'
 
1731
            prefix = ''
 
1732
        else:
 
1733
            if from_root:
 
1734
                raise errors.BzrCommandError('cannot specify both --from-root'
 
1735
                                             ' and PATH')
 
1736
            fs_path = path
 
1737
            prefix = path
 
1738
        tree, branch, relpath = bzrdir.BzrDir.open_containing_tree_or_branch(
 
1739
            fs_path)
 
1740
        if from_root:
 
1741
            relpath = u''
 
1742
        elif relpath:
 
1743
            relpath += '/'
 
1744
        if revision is not None:
 
1745
            tree = branch.repository.revision_tree(
 
1746
                revision[0].in_history(branch).rev_id)
 
1747
        elif tree is None:
 
1748
            tree = branch.basis_tree()
 
1749
 
 
1750
        tree.lock_read()
 
1751
        try:
 
1752
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False):
 
1753
                if fp.startswith(relpath):
 
1754
                    fp = osutils.pathjoin(prefix, fp[len(relpath):])
 
1755
                    if non_recursive and '/' in fp:
 
1756
                        continue
 
1757
                    if not all and not selection[fc]:
 
1758
                        continue
 
1759
                    if kind is not None and fkind != kind:
 
1760
                        continue
 
1761
                    if verbose:
 
1762
                        kindch = entry.kind_character()
 
1763
                        outstring = '%-8s %s%s' % (fc, fp, kindch)
 
1764
                        if show_ids and fid is not None:
 
1765
                            outstring = "%-50s %s" % (outstring, fid)
 
1766
                        self.outf.write(outstring + '\n')
 
1767
                    elif null:
 
1768
                        self.outf.write(fp + '\0')
 
1769
                        if show_ids:
 
1770
                            if fid is not None:
 
1771
                                self.outf.write(fid)
 
1772
                            self.outf.write('\0')
 
1773
                        self.outf.flush()
 
1774
                    else:
 
1775
                        if fid is not None:
 
1776
                            my_id = fid
 
1777
                        else:
 
1778
                            my_id = ''
 
1779
                        if show_ids:
 
1780
                            self.outf.write('%-50s %s\n' % (fp, my_id))
 
1781
                        else:
 
1782
                            self.outf.write(fp + '\n')
 
1783
        finally:
 
1784
            tree.unlock()
 
1785
 
 
1786
 
 
1787
class cmd_unknowns(Command):
 
1788
    """List unknown files.
 
1789
 
 
1790
    See also: "bzr ls --unknown".
 
1791
    """
 
1792
 
 
1793
    hidden = True
 
1794
 
 
1795
    @display_command
 
1796
    def run(self):
 
1797
        for f in WorkingTree.open_containing(u'.')[0].unknowns():
 
1798
            self.outf.write(osutils.quotefn(f) + '\n')
 
1799
 
 
1800
 
 
1801
class cmd_ignore(Command):
 
1802
    """Ignore specified files or patterns.
 
1803
 
 
1804
    To remove patterns from the ignore list, edit the .bzrignore file.
 
1805
 
 
1806
    Trailing slashes on patterns are ignored. 
 
1807
    If the pattern contains a slash or is a regular expression, it is compared 
 
1808
    to the whole path from the branch root.  Otherwise, it is compared to only
 
1809
    the last component of the path.  To match a file only in the root 
 
1810
    directory, prepend './'.
 
1811
 
 
1812
    Ignore patterns specifying absolute paths are not allowed.
 
1813
 
 
1814
    Ignore patterns may include globbing wildcards such as:
 
1815
      ? - Matches any single character except '/'
 
1816
      * - Matches 0 or more characters except '/'
 
1817
      /**/ - Matches 0 or more directories in a path
 
1818
      [a-z] - Matches a single character from within a group of characters
 
1819
 
 
1820
    Ignore patterns may also be Python regular expressions.  
 
1821
    Regular expression ignore patterns are identified by a 'RE:' prefix 
 
1822
    followed by the regular expression.  Regular expression ignore patterns
 
1823
    may not include named or numbered groups.
 
1824
 
 
1825
    Note: ignore patterns containing shell wildcards must be quoted from 
 
1826
    the shell on Unix.
 
1827
 
 
1828
    examples:
 
1829
        bzr ignore ./Makefile
 
1830
        bzr ignore '*.class'
 
1831
        bzr ignore 'lib/**/*.o'
 
1832
        bzr ignore 'RE:lib/.*\.o'
 
1833
    """
 
1834
    takes_args = ['name_pattern*']
 
1835
    takes_options = [
 
1836
                     Option('old-default-rules',
 
1837
                            help='Out the ignore rules bzr < 0.9 always used.')
 
1838
                     ]
 
1839
    
 
1840
    def run(self, name_pattern_list=None, old_default_rules=None):
 
1841
        from bzrlib.atomicfile import AtomicFile
 
1842
        if old_default_rules is not None:
 
1843
            # dump the rules and exit
 
1844
            for pattern in ignores.OLD_DEFAULTS:
 
1845
                print pattern
 
1846
            return
 
1847
        if not name_pattern_list:
 
1848
            raise errors.BzrCommandError("ignore requires at least one "
 
1849
                                  "NAME_PATTERN or --old-default-rules")
 
1850
        name_pattern_list = [globbing.normalize_pattern(p) 
 
1851
                             for p in name_pattern_list]
 
1852
        for name_pattern in name_pattern_list:
 
1853
            if (name_pattern[0] == '/' or 
 
1854
                (len(name_pattern) > 1 and name_pattern[1] == ':')):
 
1855
                raise errors.BzrCommandError(
 
1856
                    "NAME_PATTERN should not be an absolute path")
 
1857
        tree, relpath = WorkingTree.open_containing(u'.')
 
1858
        ifn = tree.abspath('.bzrignore')
 
1859
        if os.path.exists(ifn):
 
1860
            f = open(ifn, 'rt')
 
1861
            try:
 
1862
                igns = f.read().decode('utf-8')
 
1863
            finally:
 
1864
                f.close()
 
1865
        else:
 
1866
            igns = ''
 
1867
 
 
1868
        # TODO: If the file already uses crlf-style termination, maybe
 
1869
        # we should use that for the newly added lines?
 
1870
 
 
1871
        if igns and igns[-1] != '\n':
 
1872
            igns += '\n'
 
1873
        for name_pattern in name_pattern_list:
 
1874
            igns += name_pattern + '\n'
 
1875
 
 
1876
        f = AtomicFile(ifn, 'wb')
 
1877
        try:
 
1878
            f.write(igns.encode('utf-8'))
 
1879
            f.commit()
 
1880
        finally:
 
1881
            f.close()
 
1882
 
 
1883
        if not tree.path2id('.bzrignore'):
 
1884
            tree.add(['.bzrignore'])
 
1885
 
 
1886
 
 
1887
class cmd_ignored(Command):
 
1888
    """List ignored files and the patterns that matched them.
 
1889
 
 
1890
    See also: bzr ignore"""
 
1891
    @display_command
 
1892
    def run(self):
 
1893
        tree = WorkingTree.open_containing(u'.')[0]
 
1894
        tree.lock_read()
 
1895
        try:
 
1896
            for path, file_class, kind, file_id, entry in tree.list_files():
 
1897
                if file_class != 'I':
 
1898
                    continue
 
1899
                ## XXX: Slightly inefficient since this was already calculated
 
1900
                pat = tree.is_ignored(path)
 
1901
                print '%-50s %s' % (path, pat)
 
1902
        finally:
 
1903
            tree.unlock()
 
1904
 
 
1905
 
 
1906
class cmd_lookup_revision(Command):
 
1907
    """Lookup the revision-id from a revision-number
 
1908
 
 
1909
    example:
 
1910
        bzr lookup-revision 33
 
1911
    """
 
1912
    hidden = True
 
1913
    takes_args = ['revno']
 
1914
    
 
1915
    @display_command
 
1916
    def run(self, revno):
 
1917
        try:
 
1918
            revno = int(revno)
 
1919
        except ValueError:
 
1920
            raise errors.BzrCommandError("not a valid revision-number: %r" % revno)
 
1921
 
 
1922
        print WorkingTree.open_containing(u'.')[0].branch.get_rev_id(revno)
 
1923
 
 
1924
 
 
1925
class cmd_export(Command):
 
1926
    """Export past revision to destination directory.
 
1927
 
 
1928
    If no revision is specified this exports the last committed revision.
 
1929
 
 
1930
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
 
1931
    given, try to find the format with the extension. If no extension
 
1932
    is found exports to a directory (equivalent to --format=dir).
 
1933
 
 
1934
    Root may be the top directory for tar, tgz and tbz2 formats. If none
 
1935
    is given, the top directory will be the root name of the file.
 
1936
 
 
1937
    If branch is omitted then the branch containing the CWD will be used.
 
1938
 
 
1939
    Note: export of tree with non-ascii filenames to zip is not supported.
 
1940
 
 
1941
     Supported formats       Autodetected by extension
 
1942
     -----------------       -------------------------
 
1943
         dir                            -
 
1944
         tar                          .tar
 
1945
         tbz2                    .tar.bz2, .tbz2
 
1946
         tgz                      .tar.gz, .tgz
 
1947
         zip                          .zip
 
1948
    """
 
1949
    takes_args = ['dest', 'branch?']
 
1950
    takes_options = ['revision', 'format', 'root']
 
1951
    def run(self, dest, branch=None, revision=None, format=None, root=None):
 
1952
        from bzrlib.export import export
 
1953
 
 
1954
        if branch is None:
 
1955
            tree = WorkingTree.open_containing(u'.')[0]
 
1956
            b = tree.branch
 
1957
        else:
 
1958
            b = Branch.open(branch)
 
1959
            
 
1960
        if revision is None:
 
1961
            # should be tree.last_revision  FIXME
 
1962
            rev_id = b.last_revision()
 
1963
        else:
 
1964
            if len(revision) != 1:
 
1965
                raise errors.BzrCommandError('bzr export --revision takes exactly 1 argument')
 
1966
            rev_id = revision[0].in_history(b).rev_id
 
1967
        t = b.repository.revision_tree(rev_id)
 
1968
        try:
 
1969
            export(t, dest, format, root)
 
1970
        except errors.NoSuchExportFormat, e:
 
1971
            raise errors.BzrCommandError('Unsupported export format: %s' % e.format)
 
1972
 
 
1973
 
 
1974
class cmd_cat(Command):
 
1975
    """Write a file's text from a previous revision."""
 
1976
 
 
1977
    takes_options = ['revision', 'name-from-revision']
 
1978
    takes_args = ['filename']
 
1979
    encoding_type = 'exact'
 
1980
 
 
1981
    @display_command
 
1982
    def run(self, filename, revision=None, name_from_revision=False):
 
1983
        if revision is not None and len(revision) != 1:
 
1984
            raise errors.BzrCommandError("bzr cat --revision takes exactly"
 
1985
                                        " one number")
 
1986
 
 
1987
        tree = None
 
1988
        try:
 
1989
            tree, b, relpath = \
 
1990
                    bzrdir.BzrDir.open_containing_tree_or_branch(filename)
 
1991
        except errors.NotBranchError:
 
1992
            pass
 
1993
 
 
1994
        if revision is not None and revision[0].get_branch() is not None:
 
1995
            b = Branch.open(revision[0].get_branch())
 
1996
        if tree is None:
 
1997
            tree = b.basis_tree()
 
1998
        if revision is None:
 
1999
            revision_id = b.last_revision()
 
2000
        else:
 
2001
            revision_id = revision[0].in_history(b).rev_id
 
2002
 
 
2003
        cur_file_id = tree.path2id(relpath)
 
2004
        rev_tree = b.repository.revision_tree(revision_id)
 
2005
        old_file_id = rev_tree.path2id(relpath)
 
2006
        
 
2007
        if name_from_revision:
 
2008
            if old_file_id is None:
 
2009
                raise errors.BzrCommandError("%r is not present in revision %s"
 
2010
                                                % (filename, revision_id))
 
2011
            else:
 
2012
                rev_tree.print_file(old_file_id)
 
2013
        elif cur_file_id is not None:
 
2014
            rev_tree.print_file(cur_file_id)
 
2015
        elif old_file_id is not None:
 
2016
            rev_tree.print_file(old_file_id)
 
2017
        else:
 
2018
            raise errors.BzrCommandError("%r is not present in revision %s" %
 
2019
                                         (filename, revision_id))
 
2020
 
 
2021
 
 
2022
class cmd_local_time_offset(Command):
 
2023
    """Show the offset in seconds from GMT to local time."""
 
2024
    hidden = True    
 
2025
    @display_command
 
2026
    def run(self):
 
2027
        print osutils.local_time_offset()
 
2028
 
 
2029
 
 
2030
 
 
2031
class cmd_commit(Command):
 
2032
    """Commit changes into a new revision.
 
2033
    
 
2034
    If no arguments are given, the entire tree is committed.
 
2035
 
 
2036
    If selected files are specified, only changes to those files are
 
2037
    committed.  If a directory is specified then the directory and everything 
 
2038
    within it is committed.
 
2039
 
 
2040
    A selected-file commit may fail in some cases where the committed
 
2041
    tree would be invalid, such as trying to commit a file in a
 
2042
    newly-added directory that is not itself committed.
 
2043
    """
 
2044
    # TODO: Run hooks on tree to-be-committed, and after commit.
 
2045
 
 
2046
    # TODO: Strict commit that fails if there are deleted files.
 
2047
    #       (what does "deleted files" mean ??)
 
2048
 
 
2049
    # TODO: Give better message for -s, --summary, used by tla people
 
2050
 
 
2051
    # XXX: verbose currently does nothing
 
2052
 
 
2053
    takes_args = ['selected*']
 
2054
    takes_options = ['message', 'verbose', 
 
2055
                     Option('unchanged',
 
2056
                            help='commit even if nothing has changed'),
 
2057
                     Option('file', type=str, 
 
2058
                            short_name='F',
 
2059
                            argname='msgfile',
 
2060
                            help='file containing commit message'),
 
2061
                     Option('strict',
 
2062
                            help="refuse to commit if there are unknown "
 
2063
                            "files in the working tree."),
 
2064
                     Option('local',
 
2065
                            help="perform a local only commit in a bound "
 
2066
                                 "branch. Such commits are not pushed to "
 
2067
                                 "the master branch until a normal commit "
 
2068
                                 "is performed."
 
2069
                            ),
 
2070
                     ]
 
2071
    aliases = ['ci', 'checkin']
 
2072
 
 
2073
    def run(self, message=None, file=None, verbose=True, selected_list=None,
 
2074
            unchanged=False, strict=False, local=False):
 
2075
        from bzrlib.commit import (NullCommitReporter, ReportCommitToLog)
 
2076
        from bzrlib.errors import (PointlessCommit, ConflictsInTree,
 
2077
                StrictCommitFailed)
 
2078
        from bzrlib.msgeditor import edit_commit_message, \
 
2079
                make_commit_message_template
 
2080
 
 
2081
        # TODO: Need a blackbox test for invoking the external editor; may be
 
2082
        # slightly problematic to run this cross-platform.
 
2083
 
 
2084
        # TODO: do more checks that the commit will succeed before 
 
2085
        # spending the user's valuable time typing a commit message.
 
2086
        tree, selected_list = tree_files(selected_list)
 
2087
        if selected_list == ['']:
 
2088
            # workaround - commit of root of tree should be exactly the same
 
2089
            # as just default commit in that tree, and succeed even though
 
2090
            # selected-file merge commit is not done yet
 
2091
            selected_list = []
 
2092
 
 
2093
        if local and not tree.branch.get_bound_location():
 
2094
            raise errors.LocalRequiresBoundBranch()
 
2095
 
 
2096
        def get_message(commit_obj):
 
2097
            """Callback to get commit message"""
 
2098
            my_message = message
 
2099
            if my_message is None and not file:
 
2100
                template = make_commit_message_template(tree, selected_list)
 
2101
                my_message = edit_commit_message(template)
 
2102
                if my_message is None:
 
2103
                    raise errors.BzrCommandError("please specify a commit"
 
2104
                        " message with either --message or --file")
 
2105
            elif my_message and file:
 
2106
                raise errors.BzrCommandError(
 
2107
                    "please specify either --message or --file")
 
2108
            if file:
 
2109
                my_message = codecs.open(file, 'rt', 
 
2110
                                         bzrlib.user_encoding).read()
 
2111
            if my_message == "":
 
2112
                raise errors.BzrCommandError("empty commit message specified")
 
2113
            return my_message
 
2114
        
 
2115
        if verbose:
 
2116
            reporter = ReportCommitToLog()
 
2117
        else:
 
2118
            reporter = NullCommitReporter()
 
2119
 
 
2120
        try:
 
2121
            tree.commit(message_callback=get_message,
 
2122
                        specific_files=selected_list,
 
2123
                        allow_pointless=unchanged, strict=strict, local=local,
 
2124
                        reporter=reporter)
 
2125
        except PointlessCommit:
 
2126
            # FIXME: This should really happen before the file is read in;
 
2127
            # perhaps prepare the commit; get the message; then actually commit
 
2128
            raise errors.BzrCommandError("no changes to commit."
 
2129
                              " use --unchanged to commit anyhow")
 
2130
        except ConflictsInTree:
 
2131
            raise errors.BzrCommandError('Conflicts detected in working '
 
2132
                'tree.  Use "bzr conflicts" to list, "bzr resolve FILE" to'
 
2133
                ' resolve.')
 
2134
        except StrictCommitFailed:
 
2135
            raise errors.BzrCommandError("Commit refused because there are"
 
2136
                              " unknown files in the working tree.")
 
2137
        except errors.BoundBranchOutOfDate, e:
 
2138
            raise errors.BzrCommandError(str(e) + "\n"
 
2139
            'To commit to master branch, run update and then commit.\n'
 
2140
            'You can also pass --local to commit to continue working '
 
2141
            'disconnected.')
 
2142
 
 
2143
 
 
2144
class cmd_check(Command):
 
2145
    """Validate consistency of branch history.
 
2146
 
 
2147
    This command checks various invariants about the branch storage to
 
2148
    detect data corruption or bzr bugs.
 
2149
    """
 
2150
    takes_args = ['branch?']
 
2151
    takes_options = ['verbose']
 
2152
 
 
2153
    def run(self, branch=None, verbose=False):
 
2154
        from bzrlib.check import check
 
2155
        if branch is None:
 
2156
            tree = WorkingTree.open_containing()[0]
 
2157
            branch = tree.branch
 
2158
        else:
 
2159
            branch = Branch.open(branch)
 
2160
        check(branch, verbose)
 
2161
 
 
2162
 
 
2163
class cmd_upgrade(Command):
 
2164
    """Upgrade branch storage to current format.
 
2165
 
 
2166
    The check command or bzr developers may sometimes advise you to run
 
2167
    this command. When the default format has changed you may also be warned
 
2168
    during other operations to upgrade.
 
2169
    """
 
2170
    takes_args = ['url?']
 
2171
    takes_options = [
 
2172
                    RegistryOption('format',
 
2173
                        help='Upgrade to a specific format.  See "bzr help'
 
2174
                             ' formats" for details',
 
2175
                        registry=bzrdir.format_registry,
 
2176
                        converter=bzrdir.format_registry.make_bzrdir,
 
2177
                        value_switches=True, title='Branch format'),
 
2178
                    ]
 
2179
 
 
2180
    def run(self, url='.', format=None):
 
2181
        from bzrlib.upgrade import upgrade
 
2182
        if format is None:
 
2183
            format = bzrdir.format_registry.make_bzrdir('default')
 
2184
        upgrade(url, format)
 
2185
 
 
2186
 
 
2187
class cmd_whoami(Command):
 
2188
    """Show or set bzr user id.
 
2189
    
 
2190
    examples:
 
2191
        bzr whoami --email
 
2192
        bzr whoami 'Frank Chu <fchu@example.com>'
 
2193
    """
 
2194
    takes_options = [ Option('email',
 
2195
                             help='display email address only'),
 
2196
                      Option('branch',
 
2197
                             help='set identity for the current branch instead of '
 
2198
                                  'globally'),
 
2199
                    ]
 
2200
    takes_args = ['name?']
 
2201
    encoding_type = 'replace'
 
2202
    
 
2203
    @display_command
 
2204
    def run(self, email=False, branch=False, name=None):
 
2205
        if name is None:
 
2206
            # use branch if we're inside one; otherwise global config
 
2207
            try:
 
2208
                c = Branch.open_containing('.')[0].get_config()
 
2209
            except errors.NotBranchError:
 
2210
                c = config.GlobalConfig()
 
2211
            if email:
 
2212
                self.outf.write(c.user_email() + '\n')
 
2213
            else:
 
2214
                self.outf.write(c.username() + '\n')
 
2215
            return
 
2216
 
 
2217
        # display a warning if an email address isn't included in the given name.
 
2218
        try:
 
2219
            config.extract_email_address(name)
 
2220
        except errors.NoEmailInUsername, e:
 
2221
            warning('"%s" does not seem to contain an email address.  '
 
2222
                    'This is allowed, but not recommended.', name)
 
2223
        
 
2224
        # use global config unless --branch given
 
2225
        if branch:
 
2226
            c = Branch.open_containing('.')[0].get_config()
 
2227
        else:
 
2228
            c = config.GlobalConfig()
 
2229
        c.set_user_option('email', name)
 
2230
 
 
2231
 
 
2232
class cmd_nick(Command):
 
2233
    """Print or set the branch nickname.  
 
2234
 
 
2235
    If unset, the tree root directory name is used as the nickname
 
2236
    To print the current nickname, execute with no argument.  
 
2237
    """
 
2238
    takes_args = ['nickname?']
 
2239
    def run(self, nickname=None):
 
2240
        branch = Branch.open_containing(u'.')[0]
 
2241
        if nickname is None:
 
2242
            self.printme(branch)
 
2243
        else:
 
2244
            branch.nick = nickname
 
2245
 
 
2246
    @display_command
 
2247
    def printme(self, branch):
 
2248
        print branch.nick 
 
2249
 
 
2250
 
 
2251
class cmd_selftest(Command):
 
2252
    """Run internal test suite.
 
2253
    
 
2254
    This creates temporary test directories in the working directory, but not
 
2255
    existing data is affected.  These directories are deleted if the tests
 
2256
    pass, or left behind to help in debugging if they fail and --keep-output
 
2257
    is specified.
 
2258
    
 
2259
    If arguments are given, they are regular expressions that say which tests
 
2260
    should run.  Tests matching any expression are run, and other tests are
 
2261
    not run.
 
2262
 
 
2263
    Alternatively if --first is given, matching tests are run first and then
 
2264
    all other tests are run.  This is useful if you have been working in a
 
2265
    particular area, but want to make sure nothing else was broken.
 
2266
 
 
2267
    If the global option '--no-plugins' is given, plugins are not loaded
 
2268
    before running the selftests.  This has two effects: features provided or
 
2269
    modified by plugins will not be tested, and tests provided by plugins will
 
2270
    not be run.
 
2271
 
 
2272
    examples::
 
2273
        bzr selftest ignore
 
2274
            run only tests relating to 'ignore'
 
2275
        bzr --no-plugins selftest -v
 
2276
            disable plugins and list tests as they're run
 
2277
 
 
2278
    For each test, that needs actual disk access, bzr create their own
 
2279
    subdirectory in the temporary testing directory (testXXXX.tmp).
 
2280
    By default the name of such subdirectory is based on the name of the test.
 
2281
    If option '--numbered-dirs' is given, bzr will use sequent numbers
 
2282
    of running tests to create such subdirectories. This is default behavior
 
2283
    on Windows because of path length limitation.
 
2284
    """
 
2285
    # TODO: --list should give a list of all available tests
 
2286
 
 
2287
    # NB: this is used from the class without creating an instance, which is
 
2288
    # why it does not have a self parameter.
 
2289
    def get_transport_type(typestring):
 
2290
        """Parse and return a transport specifier."""
 
2291
        if typestring == "sftp":
 
2292
            from bzrlib.transport.sftp import SFTPAbsoluteServer
 
2293
            return SFTPAbsoluteServer
 
2294
        if typestring == "memory":
 
2295
            from bzrlib.transport.memory import MemoryServer
 
2296
            return MemoryServer
 
2297
        if typestring == "fakenfs":
 
2298
            from bzrlib.transport.fakenfs import FakeNFSServer
 
2299
            return FakeNFSServer
 
2300
        msg = "No known transport type %s. Supported types are: sftp\n" %\
 
2301
            (typestring)
 
2302
        raise errors.BzrCommandError(msg)
 
2303
 
 
2304
    hidden = True
 
2305
    takes_args = ['testspecs*']
 
2306
    takes_options = ['verbose',
 
2307
                     Option('one', help='stop when one test fails'),
 
2308
                     Option('keep-output',
 
2309
                            help='keep output directories when tests fail'),
 
2310
                     Option('transport',
 
2311
                            help='Use a different transport by default '
 
2312
                                 'throughout the test suite.',
 
2313
                            type=get_transport_type),
 
2314
                     Option('benchmark', help='run the bzr benchmarks.'),
 
2315
                     Option('lsprof-timed',
 
2316
                            help='generate lsprof output for benchmarked'
 
2317
                                 ' sections of code.'),
 
2318
                     Option('cache-dir', type=str,
 
2319
                            help='a directory to cache intermediate'
 
2320
                                 ' benchmark steps'),
 
2321
                     Option('clean-output',
 
2322
                            help='clean temporary tests directories'
 
2323
                                 ' without running tests'),
 
2324
                     Option('first',
 
2325
                            help='run all tests, but run specified tests first'
 
2326
                            ),
 
2327
                     Option('numbered-dirs',
 
2328
                            help='use numbered dirs for TestCaseInTempDir'),
 
2329
                     ]
 
2330
    encoding_type = 'replace'
 
2331
 
 
2332
    def run(self, testspecs_list=None, verbose=None, one=False,
 
2333
            keep_output=False, transport=None, benchmark=None,
 
2334
            lsprof_timed=None, cache_dir=None, clean_output=False,
 
2335
            first=False, numbered_dirs=None):
 
2336
        import bzrlib.ui
 
2337
        from bzrlib.tests import selftest
 
2338
        import bzrlib.benchmarks as benchmarks
 
2339
        from bzrlib.benchmarks import tree_creator
 
2340
 
 
2341
        if clean_output:
 
2342
            from bzrlib.tests import clean_selftest_output
 
2343
            clean_selftest_output()
 
2344
            return 0
 
2345
 
 
2346
        if numbered_dirs is None and sys.platform == 'win32':
 
2347
            numbered_dirs = True
 
2348
 
 
2349
        if cache_dir is not None:
 
2350
            tree_creator.TreeCreator.CACHE_ROOT = osutils.abspath(cache_dir)
 
2351
        print '%10s: %s' % ('bzr', osutils.realpath(sys.argv[0]))
 
2352
        print '%10s: %s' % ('bzrlib', bzrlib.__path__[0])
 
2353
        print
 
2354
        if testspecs_list is not None:
 
2355
            pattern = '|'.join(testspecs_list)
 
2356
        else:
 
2357
            pattern = ".*"
 
2358
        if benchmark:
 
2359
            test_suite_factory = benchmarks.test_suite
 
2360
            if verbose is None:
 
2361
                verbose = True
 
2362
            # TODO: should possibly lock the history file...
 
2363
            benchfile = open(".perf_history", "at", buffering=1)
 
2364
        else:
 
2365
            test_suite_factory = None
 
2366
            if verbose is None:
 
2367
                verbose = False
 
2368
            benchfile = None
 
2369
        try:
 
2370
            result = selftest(verbose=verbose, 
 
2371
                              pattern=pattern,
 
2372
                              stop_on_failure=one, 
 
2373
                              keep_output=keep_output,
 
2374
                              transport=transport,
 
2375
                              test_suite_factory=test_suite_factory,
 
2376
                              lsprof_timed=lsprof_timed,
 
2377
                              bench_history=benchfile,
 
2378
                              matching_tests_first=first,
 
2379
                              numbered_dirs=numbered_dirs,
 
2380
                              )
 
2381
        finally:
 
2382
            if benchfile is not None:
 
2383
                benchfile.close()
 
2384
        if result:
 
2385
            info('tests passed')
 
2386
        else:
 
2387
            info('tests failed')
 
2388
        return int(not result)
 
2389
 
 
2390
 
 
2391
class cmd_version(Command):
 
2392
    """Show version of bzr."""
 
2393
 
 
2394
    @display_command
 
2395
    def run(self):
 
2396
        from bzrlib.version import show_version
 
2397
        show_version()
 
2398
 
 
2399
 
 
2400
class cmd_rocks(Command):
 
2401
    """Statement of optimism."""
 
2402
 
 
2403
    hidden = True
 
2404
 
 
2405
    @display_command
 
2406
    def run(self):
 
2407
        print "It sure does!"
 
2408
 
 
2409
 
 
2410
class cmd_find_merge_base(Command):
 
2411
    """Find and print a base revision for merging two branches."""
 
2412
    # TODO: Options to specify revisions on either side, as if
 
2413
    #       merging only part of the history.
 
2414
    takes_args = ['branch', 'other']
 
2415
    hidden = True
 
2416
    
 
2417
    @display_command
 
2418
    def run(self, branch, other):
 
2419
        from bzrlib.revision import MultipleRevisionSources
 
2420
        
 
2421
        branch1 = Branch.open_containing(branch)[0]
 
2422
        branch2 = Branch.open_containing(other)[0]
 
2423
 
 
2424
        last1 = branch1.last_revision()
 
2425
        last2 = branch2.last_revision()
 
2426
 
 
2427
        source = MultipleRevisionSources(branch1.repository, 
 
2428
                                         branch2.repository)
 
2429
        
 
2430
        base_rev_id = common_ancestor(last1, last2, source)
 
2431
 
 
2432
        print 'merge base is revision %s' % base_rev_id
 
2433
 
 
2434
 
 
2435
class cmd_merge(Command):
 
2436
    """Perform a three-way merge.
 
2437
    
 
2438
    The branch is the branch you will merge from.  By default, it will merge
 
2439
    the latest revision.  If you specify a revision, that revision will be
 
2440
    merged.  If you specify two revisions, the first will be used as a BASE,
 
2441
    and the second one as OTHER.  Revision numbers are always relative to the
 
2442
    specified branch.
 
2443
 
 
2444
    By default, bzr will try to merge in all new work from the other
 
2445
    branch, automatically determining an appropriate base.  If this
 
2446
    fails, you may need to give an explicit base.
 
2447
    
 
2448
    Merge will do its best to combine the changes in two branches, but there
 
2449
    are some kinds of problems only a human can fix.  When it encounters those,
 
2450
    it will mark a conflict.  A conflict means that you need to fix something,
 
2451
    before you should commit.
 
2452
 
 
2453
    Use bzr resolve when you have fixed a problem.  See also bzr conflicts.
 
2454
 
 
2455
    If there is no default branch set, the first merge will set it. After
 
2456
    that, you can omit the branch to use the default.  To change the
 
2457
    default, use --remember. The value will only be saved if the remote
 
2458
    location can be accessed.
 
2459
 
 
2460
    The results of the merge are placed into the destination working
 
2461
    directory, where they can be reviewed (with bzr diff), tested, and then
 
2462
    committed to record the result of the merge.
 
2463
 
 
2464
    Examples:
 
2465
 
 
2466
    To merge the latest revision from bzr.dev
 
2467
    bzr merge ../bzr.dev
 
2468
 
 
2469
    To merge changes up to and including revision 82 from bzr.dev
 
2470
    bzr merge -r 82 ../bzr.dev
 
2471
 
 
2472
    To merge the changes introduced by 82, without previous changes:
 
2473
    bzr merge -r 81..82 ../bzr.dev
 
2474
    
 
2475
    merge refuses to run if there are any uncommitted changes, unless
 
2476
    --force is given.
 
2477
 
 
2478
    The following merge types are available:
 
2479
    """
 
2480
    takes_args = ['branch?']
 
2481
    takes_options = ['revision', 'force', 'merge-type', 'reprocess', 'remember',
 
2482
        Option('show-base', help="Show base revision text in "
 
2483
               "conflicts"),
 
2484
        Option('uncommitted', help='Apply uncommitted changes'
 
2485
               ' from a working copy, instead of branch changes'),
 
2486
        Option('pull', help='If the destination is already'
 
2487
                ' completely merged into the source, pull from the'
 
2488
                ' source rather than merging. When this happens,'
 
2489
                ' you do not need to commit the result.'),
 
2490
        Option('directory',
 
2491
            help='Branch to merge into, '
 
2492
                 'rather than the one containing the working directory',
 
2493
            short_name='d',
 
2494
            type=unicode,
 
2495
            ),
 
2496
    ]
 
2497
 
 
2498
    def run(self, branch=None, revision=None, force=False, merge_type=None,
 
2499
            show_base=False, reprocess=False, remember=False,
 
2500
            uncommitted=False, pull=False,
 
2501
            directory=None,
 
2502
            ):
 
2503
        from bzrlib.tag import _merge_tags_if_possible
 
2504
        other_rev_id = None
 
2505
        if merge_type is None:
 
2506
            merge_type = _mod_merge.Merge3Merger
 
2507
 
 
2508
        if directory is None: directory = u'.'
 
2509
        # XXX: jam 20070225 WorkingTree should be locked before you extract its
 
2510
        #      inventory. Because merge is a mutating operation, it really
 
2511
        #      should be a lock_write() for the whole cmd_merge operation.
 
2512
        #      However, cmd_merge open's its own tree in _merge_helper, which
 
2513
        #      means if we lock here, the later lock_write() will always block.
 
2514
        #      Either the merge helper code should be updated to take a tree,
 
2515
        #      (What about tree.merge_from_branch?)
 
2516
        tree = WorkingTree.open_containing(directory)[0]
 
2517
        change_reporter = delta._ChangeReporter(
 
2518
            unversioned_filter=tree.is_ignored)
 
2519
 
 
2520
        if branch is not None:
 
2521
            try:
 
2522
                mergeable = bundle.read_mergeable_from_url(
 
2523
                    branch)
 
2524
            except errors.NotABundle:
 
2525
                pass # Continue on considering this url a Branch
 
2526
            else:
 
2527
                if revision is not None:
 
2528
                    raise errors.BzrCommandError(
 
2529
                        'Cannot use -r with merge directives or bundles')
 
2530
                other_rev_id = mergeable.install_revisions(
 
2531
                    tree.branch.repository)
 
2532
                revision = [RevisionSpec.from_string(
 
2533
                    'revid:' + other_rev_id)]
 
2534
 
 
2535
        if revision is None \
 
2536
                or len(revision) < 1 or revision[0].needs_branch():
 
2537
            branch = self._get_remembered_parent(tree, branch, 'Merging from')
 
2538
 
 
2539
        if revision is None or len(revision) < 1:
 
2540
            if uncommitted:
 
2541
                base = [branch, -1]
 
2542
                other = [branch, None]
 
2543
            else:
 
2544
                base = [None, None]
 
2545
                other = [branch, -1]
 
2546
            other_branch, path = Branch.open_containing(branch)
 
2547
        else:
 
2548
            if uncommitted:
 
2549
                raise errors.BzrCommandError('Cannot use --uncommitted and'
 
2550
                                             ' --revision at the same time.')
 
2551
            branch = revision[0].get_branch() or branch
 
2552
            if len(revision) == 1:
 
2553
                base = [None, None]
 
2554
                if other_rev_id is not None:
 
2555
                    other_branch = None
 
2556
                    path = ""
 
2557
                    other = None
 
2558
                else:
 
2559
                    other_branch, path = Branch.open_containing(branch)
 
2560
                    revno = revision[0].in_history(other_branch).revno
 
2561
                    other = [branch, revno]
 
2562
            else:
 
2563
                assert len(revision) == 2
 
2564
                if None in revision:
 
2565
                    raise errors.BzrCommandError(
 
2566
                        "Merge doesn't permit empty revision specifier.")
 
2567
                base_branch, path = Branch.open_containing(branch)
 
2568
                branch1 = revision[1].get_branch() or branch
 
2569
                other_branch, path1 = Branch.open_containing(branch1)
 
2570
                if revision[0].get_branch() is not None:
 
2571
                    # then path was obtained from it, and is None.
 
2572
                    path = path1
 
2573
 
 
2574
                base = [branch, revision[0].in_history(base_branch).revno]
 
2575
                other = [branch1, revision[1].in_history(other_branch).revno]
 
2576
 
 
2577
        if ((tree.branch.get_parent() is None or remember) and
 
2578
            other_branch is not None):
 
2579
            tree.branch.set_parent(other_branch.base)
 
2580
 
 
2581
        # pull tags now... it's a bit inconsistent to do it ahead of copying
 
2582
        # the history but that's done inside the merge code
 
2583
        if other_branch is not None:
 
2584
            _merge_tags_if_possible(other_branch, tree.branch)
 
2585
 
 
2586
        if path != "":
 
2587
            interesting_files = [path]
 
2588
        else:
 
2589
            interesting_files = None
 
2590
        pb = ui.ui_factory.nested_progress_bar()
 
2591
        try:
 
2592
            try:
 
2593
                conflict_count = _merge_helper(
 
2594
                    other, base, other_rev_id=other_rev_id,
 
2595
                    check_clean=(not force),
 
2596
                    merge_type=merge_type,
 
2597
                    reprocess=reprocess,
 
2598
                    show_base=show_base,
 
2599
                    pull=pull,
 
2600
                    this_dir=directory,
 
2601
                    pb=pb, file_list=interesting_files,
 
2602
                    change_reporter=change_reporter)
 
2603
            finally:
 
2604
                pb.finished()
 
2605
            if conflict_count != 0:
 
2606
                return 1
 
2607
            else:
 
2608
                return 0
 
2609
        except errors.AmbiguousBase, e:
 
2610
            m = ("sorry, bzr can't determine the right merge base yet\n"
 
2611
                 "candidates are:\n  "
 
2612
                 + "\n  ".join(e.bases)
 
2613
                 + "\n"
 
2614
                 "please specify an explicit base with -r,\n"
 
2615
                 "and (if you want) report this to the bzr developers\n")
 
2616
            log_error(m)
 
2617
 
 
2618
    # TODO: move up to common parent; this isn't merge-specific anymore. 
 
2619
    def _get_remembered_parent(self, tree, supplied_location, verb_string):
 
2620
        """Use tree.branch's parent if none was supplied.
 
2621
 
 
2622
        Report if the remembered location was used.
 
2623
        """
 
2624
        if supplied_location is not None:
 
2625
            return supplied_location
 
2626
        stored_location = tree.branch.get_parent()
 
2627
        mutter("%s", stored_location)
 
2628
        if stored_location is None:
 
2629
            raise errors.BzrCommandError("No location specified or remembered")
 
2630
        display_url = urlutils.unescape_for_display(stored_location, self.outf.encoding)
 
2631
        self.outf.write("%s remembered location %s\n" % (verb_string, display_url))
 
2632
        return stored_location
 
2633
 
 
2634
 
 
2635
class cmd_remerge(Command):
 
2636
    """Redo a merge.
 
2637
 
 
2638
    Use this if you want to try a different merge technique while resolving
 
2639
    conflicts.  Some merge techniques are better than others, and remerge 
 
2640
    lets you try different ones on different files.
 
2641
 
 
2642
    The options for remerge have the same meaning and defaults as the ones for
 
2643
    merge.  The difference is that remerge can (only) be run when there is a
 
2644
    pending merge, and it lets you specify particular files.
 
2645
 
 
2646
    Examples:
 
2647
    $ bzr remerge --show-base
 
2648
        Re-do the merge of all conflicted files, and show the base text in
 
2649
        conflict regions, in addition to the usual THIS and OTHER texts.
 
2650
 
 
2651
    $ bzr remerge --merge-type weave --reprocess foobar
 
2652
        Re-do the merge of "foobar", using the weave merge algorithm, with
 
2653
        additional processing to reduce the size of conflict regions.
 
2654
    
 
2655
    The following merge types are available:"""
 
2656
    takes_args = ['file*']
 
2657
    takes_options = ['merge-type', 'reprocess',
 
2658
                     Option('show-base', help="Show base revision text in "
 
2659
                            "conflicts")]
 
2660
 
 
2661
    def run(self, file_list=None, merge_type=None, show_base=False,
 
2662
            reprocess=False):
 
2663
        if merge_type is None:
 
2664
            merge_type = _mod_merge.Merge3Merger
 
2665
        tree, file_list = tree_files(file_list)
 
2666
        tree.lock_write()
 
2667
        try:
 
2668
            parents = tree.get_parent_ids()
 
2669
            if len(parents) != 2:
 
2670
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
2671
                                             " merges.  Not cherrypicking or"
 
2672
                                             " multi-merges.")
 
2673
            repository = tree.branch.repository
 
2674
            base_revision = common_ancestor(parents[0],
 
2675
                                            parents[1], repository)
 
2676
            base_tree = repository.revision_tree(base_revision)
 
2677
            other_tree = repository.revision_tree(parents[1])
 
2678
            interesting_ids = None
 
2679
            new_conflicts = []
 
2680
            conflicts = tree.conflicts()
 
2681
            if file_list is not None:
 
2682
                interesting_ids = set()
 
2683
                for filename in file_list:
 
2684
                    file_id = tree.path2id(filename)
 
2685
                    if file_id is None:
 
2686
                        raise errors.NotVersionedError(filename)
 
2687
                    interesting_ids.add(file_id)
 
2688
                    if tree.kind(file_id) != "directory":
 
2689
                        continue
 
2690
                    
 
2691
                    for name, ie in tree.inventory.iter_entries(file_id):
 
2692
                        interesting_ids.add(ie.file_id)
 
2693
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
2694
            else:
 
2695
                # Remerge only supports resolving contents conflicts
 
2696
                allowed_conflicts = ('text conflict', 'contents conflict')
 
2697
                restore_files = [c.path for c in conflicts
 
2698
                                 if c.typestring in allowed_conflicts]
 
2699
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
2700
            tree.set_conflicts(ConflictList(new_conflicts))
 
2701
            if file_list is not None:
 
2702
                restore_files = file_list
 
2703
            for filename in restore_files:
 
2704
                try:
 
2705
                    restore(tree.abspath(filename))
 
2706
                except errors.NotConflicted:
 
2707
                    pass
 
2708
            conflicts = _mod_merge.merge_inner(
 
2709
                                      tree.branch, other_tree, base_tree,
 
2710
                                      this_tree=tree,
 
2711
                                      interesting_ids=interesting_ids,
 
2712
                                      other_rev_id=parents[1],
 
2713
                                      merge_type=merge_type,
 
2714
                                      show_base=show_base,
 
2715
                                      reprocess=reprocess)
 
2716
        finally:
 
2717
            tree.unlock()
 
2718
        if conflicts > 0:
 
2719
            return 1
 
2720
        else:
 
2721
            return 0
 
2722
 
 
2723
 
 
2724
class cmd_revert(Command):
 
2725
    """Revert files to a previous revision.
 
2726
 
 
2727
    Giving a list of files will revert only those files.  Otherwise, all files
 
2728
    will be reverted.  If the revision is not specified with '--revision', the
 
2729
    last committed revision is used.
 
2730
 
 
2731
    To remove only some changes, without reverting to a prior version, use
 
2732
    merge instead.  For example, "merge . --r-2..-3" will remove the changes
 
2733
    introduced by -2, without affecting the changes introduced by -1.  Or
 
2734
    to remove certain changes on a hunk-by-hunk basis, see the Shelf plugin.
 
2735
    
 
2736
    By default, any files that have been manually changed will be backed up
 
2737
    first.  (Files changed only by merge are not backed up.)  Backup files have
 
2738
    '.~#~' appended to their name, where # is a number.
 
2739
 
 
2740
    When you provide files, you can use their current pathname or the pathname
 
2741
    from the target revision.  So you can use revert to "undelete" a file by
 
2742
    name.  If you name a directory, all the contents of that directory will be
 
2743
    reverted.
 
2744
    """
 
2745
    takes_options = ['revision', 'no-backup']
 
2746
    takes_args = ['file*']
 
2747
    aliases = ['merge-revert']
 
2748
 
 
2749
    def run(self, revision=None, no_backup=False, file_list=None):
 
2750
        if file_list is not None:
 
2751
            if len(file_list) == 0:
 
2752
                raise errors.BzrCommandError("No files specified")
 
2753
        else:
 
2754
            file_list = []
 
2755
        
 
2756
        tree, file_list = tree_files(file_list)
 
2757
        if revision is None:
 
2758
            # FIXME should be tree.last_revision
 
2759
            rev_id = tree.last_revision()
 
2760
        elif len(revision) != 1:
 
2761
            raise errors.BzrCommandError('bzr revert --revision takes exactly 1 argument')
 
2762
        else:
 
2763
            rev_id = revision[0].in_history(tree.branch).rev_id
 
2764
        pb = ui.ui_factory.nested_progress_bar()
 
2765
        try:
 
2766
            tree.revert(file_list, 
 
2767
                        tree.branch.repository.revision_tree(rev_id),
 
2768
                        not no_backup, pb, report_changes=True)
 
2769
        finally:
 
2770
            pb.finished()
 
2771
 
 
2772
 
 
2773
class cmd_assert_fail(Command):
 
2774
    """Test reporting of assertion failures"""
 
2775
    # intended just for use in testing
 
2776
 
 
2777
    hidden = True
 
2778
 
 
2779
    def run(self):
 
2780
        raise AssertionError("always fails")
 
2781
 
 
2782
 
 
2783
class cmd_help(Command):
 
2784
    """Show help on a command or other topic.
 
2785
 
 
2786
    For a list of all available commands, say 'bzr help commands'.
 
2787
    """
 
2788
    takes_options = [Option('long', 'show help on all commands')]
 
2789
    takes_args = ['topic?']
 
2790
    aliases = ['?', '--help', '-?', '-h']
 
2791
    
 
2792
    @display_command
 
2793
    def run(self, topic=None, long=False):
 
2794
        import bzrlib.help
 
2795
        if topic is None and long:
 
2796
            topic = "commands"
 
2797
        bzrlib.help.help(topic)
 
2798
 
 
2799
 
 
2800
class cmd_shell_complete(Command):
 
2801
    """Show appropriate completions for context.
 
2802
 
 
2803
    For a list of all available commands, say 'bzr shell-complete'.
 
2804
    """
 
2805
    takes_args = ['context?']
 
2806
    aliases = ['s-c']
 
2807
    hidden = True
 
2808
    
 
2809
    @display_command
 
2810
    def run(self, context=None):
 
2811
        import shellcomplete
 
2812
        shellcomplete.shellcomplete(context)
 
2813
 
 
2814
 
 
2815
class cmd_fetch(Command):
 
2816
    """Copy in history from another branch but don't merge it.
 
2817
 
 
2818
    This is an internal method used for pull and merge.
 
2819
    """
 
2820
    hidden = True
 
2821
    takes_args = ['from_branch', 'to_branch']
 
2822
    def run(self, from_branch, to_branch):
 
2823
        from bzrlib.fetch import Fetcher
 
2824
        from_b = Branch.open(from_branch)
 
2825
        to_b = Branch.open(to_branch)
 
2826
        Fetcher(to_b, from_b)
 
2827
 
 
2828
 
 
2829
class cmd_missing(Command):
 
2830
    """Show unmerged/unpulled revisions between two branches.
 
2831
 
 
2832
    OTHER_BRANCH may be local or remote.
 
2833
    """
 
2834
    takes_args = ['other_branch?']
 
2835
    takes_options = [Option('reverse', 'Reverse the order of revisions'),
 
2836
                     Option('mine-only', 
 
2837
                            'Display changes in the local branch only'),
 
2838
                     Option('theirs-only', 
 
2839
                            'Display changes in the remote branch only'), 
 
2840
                     'log-format',
 
2841
                     'show-ids',
 
2842
                     'verbose'
 
2843
                     ]
 
2844
    encoding_type = 'replace'
 
2845
 
 
2846
    @display_command
 
2847
    def run(self, other_branch=None, reverse=False, mine_only=False,
 
2848
            theirs_only=False, log_format=None, long=False, short=False, line=False, 
 
2849
            show_ids=False, verbose=False):
 
2850
        from bzrlib.missing import find_unmerged, iter_log_data
 
2851
        from bzrlib.log import log_formatter
 
2852
        local_branch = Branch.open_containing(u".")[0]
 
2853
        parent = local_branch.get_parent()
 
2854
        if other_branch is None:
 
2855
            other_branch = parent
 
2856
            if other_branch is None:
 
2857
                raise errors.BzrCommandError("No peer location known or specified.")
 
2858
            display_url = urlutils.unescape_for_display(parent,
 
2859
                                                        self.outf.encoding)
 
2860
            print "Using last location: " + display_url
 
2861
 
 
2862
        remote_branch = Branch.open(other_branch)
 
2863
        if remote_branch.base == local_branch.base:
 
2864
            remote_branch = local_branch
 
2865
        local_branch.lock_read()
 
2866
        try:
 
2867
            remote_branch.lock_read()
 
2868
            try:
 
2869
                local_extra, remote_extra = find_unmerged(local_branch, remote_branch)
 
2870
                if (log_format is None):
 
2871
                    log_format = log.log_formatter_registry.get_default(
 
2872
                        local_branch)
 
2873
                lf = log_format(to_file=self.outf,
 
2874
                                show_ids=show_ids,
 
2875
                                show_timezone='original')
 
2876
                if reverse is False:
 
2877
                    local_extra.reverse()
 
2878
                    remote_extra.reverse()
 
2879
                if local_extra and not theirs_only:
 
2880
                    print "You have %d extra revision(s):" % len(local_extra)
 
2881
                    for data in iter_log_data(local_extra, local_branch.repository,
 
2882
                                              verbose):
 
2883
                        lf.show(*data)
 
2884
                    printed_local = True
 
2885
                else:
 
2886
                    printed_local = False
 
2887
                if remote_extra and not mine_only:
 
2888
                    if printed_local is True:
 
2889
                        print "\n\n"
 
2890
                    print "You are missing %d revision(s):" % len(remote_extra)
 
2891
                    for data in iter_log_data(remote_extra, remote_branch.repository, 
 
2892
                                              verbose):
 
2893
                        lf.show(*data)
 
2894
                if not remote_extra and not local_extra:
 
2895
                    status_code = 0
 
2896
                    print "Branches are up to date."
 
2897
                else:
 
2898
                    status_code = 1
 
2899
            finally:
 
2900
                remote_branch.unlock()
 
2901
        finally:
 
2902
            local_branch.unlock()
 
2903
        if not status_code and parent is None and other_branch is not None:
 
2904
            local_branch.lock_write()
 
2905
            try:
 
2906
                # handle race conditions - a parent might be set while we run.
 
2907
                if local_branch.get_parent() is None:
 
2908
                    local_branch.set_parent(remote_branch.base)
 
2909
            finally:
 
2910
                local_branch.unlock()
 
2911
        return status_code
 
2912
 
 
2913
 
 
2914
class cmd_plugins(Command):
 
2915
    """List plugins"""
 
2916
    hidden = True
 
2917
    @display_command
 
2918
    def run(self):
 
2919
        import bzrlib.plugin
 
2920
        from inspect import getdoc
 
2921
        for name, plugin in bzrlib.plugin.all_plugins().items():
 
2922
            if getattr(plugin, '__path__', None) is not None:
 
2923
                print plugin.__path__[0]
 
2924
            elif getattr(plugin, '__file__', None) is not None:
 
2925
                print plugin.__file__
 
2926
            else:
 
2927
                print repr(plugin)
 
2928
                
 
2929
            d = getdoc(plugin)
 
2930
            if d:
 
2931
                print '\t', d.split('\n')[0]
 
2932
 
 
2933
 
 
2934
class cmd_testament(Command):
 
2935
    """Show testament (signing-form) of a revision."""
 
2936
    takes_options = ['revision', 
 
2937
                     Option('long', help='Produce long-format testament'), 
 
2938
                     Option('strict', help='Produce a strict-format'
 
2939
                            ' testament')]
 
2940
    takes_args = ['branch?']
 
2941
    @display_command
 
2942
    def run(self, branch=u'.', revision=None, long=False, strict=False):
 
2943
        from bzrlib.testament import Testament, StrictTestament
 
2944
        if strict is True:
 
2945
            testament_class = StrictTestament
 
2946
        else:
 
2947
            testament_class = Testament
 
2948
        b = WorkingTree.open_containing(branch)[0].branch
 
2949
        b.lock_read()
 
2950
        try:
 
2951
            if revision is None:
 
2952
                rev_id = b.last_revision()
 
2953
            else:
 
2954
                rev_id = revision[0].in_history(b).rev_id
 
2955
            t = testament_class.from_revision(b.repository, rev_id)
 
2956
            if long:
 
2957
                sys.stdout.writelines(t.as_text_lines())
 
2958
            else:
 
2959
                sys.stdout.write(t.as_short_text())
 
2960
        finally:
 
2961
            b.unlock()
 
2962
 
 
2963
 
 
2964
class cmd_annotate(Command):
 
2965
    """Show the origin of each line in a file.
 
2966
 
 
2967
    This prints out the given file with an annotation on the left side
 
2968
    indicating which revision, author and date introduced the change.
 
2969
 
 
2970
    If the origin is the same for a run of consecutive lines, it is 
 
2971
    shown only at the top, unless the --all option is given.
 
2972
    """
 
2973
    # TODO: annotate directories; showing when each file was last changed
 
2974
    # TODO: if the working copy is modified, show annotations on that 
 
2975
    #       with new uncommitted lines marked
 
2976
    aliases = ['ann', 'blame', 'praise']
 
2977
    takes_args = ['filename']
 
2978
    takes_options = [Option('all', help='show annotations on all lines'),
 
2979
                     Option('long', help='show date in annotations'),
 
2980
                     'revision',
 
2981
                     'show-ids',
 
2982
                     ]
 
2983
 
 
2984
    @display_command
 
2985
    def run(self, filename, all=False, long=False, revision=None,
 
2986
            show_ids=False):
 
2987
        from bzrlib.annotate import annotate_file
 
2988
        tree, relpath = WorkingTree.open_containing(filename)
 
2989
        branch = tree.branch
 
2990
        branch.lock_read()
 
2991
        try:
 
2992
            if revision is None:
 
2993
                revision_id = branch.last_revision()
 
2994
            elif len(revision) != 1:
 
2995
                raise errors.BzrCommandError('bzr annotate --revision takes exactly 1 argument')
 
2996
            else:
 
2997
                revision_id = revision[0].in_history(branch).rev_id
 
2998
            file_id = tree.path2id(relpath)
 
2999
            tree = branch.repository.revision_tree(revision_id)
 
3000
            file_version = tree.inventory[file_id].revision
 
3001
            annotate_file(branch, file_version, file_id, long, all, sys.stdout,
 
3002
                          show_ids=show_ids)
 
3003
        finally:
 
3004
            branch.unlock()
 
3005
 
 
3006
 
 
3007
class cmd_re_sign(Command):
 
3008
    """Create a digital signature for an existing revision."""
 
3009
    # TODO be able to replace existing ones.
 
3010
 
 
3011
    hidden = True # is this right ?
 
3012
    takes_args = ['revision_id*']
 
3013
    takes_options = ['revision']
 
3014
    
 
3015
    def run(self, revision_id_list=None, revision=None):
 
3016
        import bzrlib.gpg as gpg
 
3017
        if revision_id_list is not None and revision is not None:
 
3018
            raise errors.BzrCommandError('You can only supply one of revision_id or --revision')
 
3019
        if revision_id_list is None and revision is None:
 
3020
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
 
3021
        b = WorkingTree.open_containing(u'.')[0].branch
 
3022
        gpg_strategy = gpg.GPGStrategy(b.get_config())
 
3023
        if revision_id_list is not None:
 
3024
            for revision_id in revision_id_list:
 
3025
                b.repository.sign_revision(revision_id, gpg_strategy)
 
3026
        elif revision is not None:
 
3027
            if len(revision) == 1:
 
3028
                revno, rev_id = revision[0].in_history(b)
 
3029
                b.repository.sign_revision(rev_id, gpg_strategy)
 
3030
            elif len(revision) == 2:
 
3031
                # are they both on rh- if so we can walk between them
 
3032
                # might be nice to have a range helper for arbitrary
 
3033
                # revision paths. hmm.
 
3034
                from_revno, from_revid = revision[0].in_history(b)
 
3035
                to_revno, to_revid = revision[1].in_history(b)
 
3036
                if to_revid is None:
 
3037
                    to_revno = b.revno()
 
3038
                if from_revno is None or to_revno is None:
 
3039
                    raise errors.BzrCommandError('Cannot sign a range of non-revision-history revisions')
 
3040
                for revno in range(from_revno, to_revno + 1):
 
3041
                    b.repository.sign_revision(b.get_rev_id(revno), 
 
3042
                                               gpg_strategy)
 
3043
            else:
 
3044
                raise errors.BzrCommandError('Please supply either one revision, or a range.')
 
3045
 
 
3046
 
 
3047
class cmd_bind(Command):
 
3048
    """Convert the current branch into a checkout of the supplied branch.
 
3049
 
 
3050
    Once converted into a checkout, commits must succeed on the master branch
 
3051
    before they will be applied to the local branch.
 
3052
 
 
3053
    See "help checkouts" for more information on checkouts.
 
3054
    """
 
3055
 
 
3056
    takes_args = ['location?']
 
3057
    takes_options = []
 
3058
 
 
3059
    def run(self, location=None):
 
3060
        b, relpath = Branch.open_containing(u'.')
 
3061
        if location is None:
 
3062
            try:
 
3063
                location = b.get_old_bound_location()
 
3064
            except errors.UpgradeRequired:
 
3065
                raise errors.BzrCommandError('No location supplied.  '
 
3066
                    'This format does not remember old locations.')
 
3067
            else:
 
3068
                if location is None:
 
3069
                    raise errors.BzrCommandError('No location supplied and no '
 
3070
                        'previous location known')
 
3071
        b_other = Branch.open(location)
 
3072
        try:
 
3073
            b.bind(b_other)
 
3074
        except errors.DivergedBranches:
 
3075
            raise errors.BzrCommandError('These branches have diverged.'
 
3076
                                         ' Try merging, and then bind again.')
 
3077
 
 
3078
 
 
3079
class cmd_unbind(Command):
 
3080
    """Convert the current checkout into a regular branch.
 
3081
 
 
3082
    After unbinding, the local branch is considered independent and subsequent
 
3083
    commits will be local only.
 
3084
 
 
3085
    See "help checkouts" for more information on checkouts.
 
3086
    """
 
3087
 
 
3088
    takes_args = []
 
3089
    takes_options = []
 
3090
 
 
3091
    def run(self):
 
3092
        b, relpath = Branch.open_containing(u'.')
 
3093
        if not b.unbind():
 
3094
            raise errors.BzrCommandError('Local branch is not bound')
 
3095
 
 
3096
 
 
3097
class cmd_uncommit(Command):
 
3098
    """Remove the last committed revision.
 
3099
 
 
3100
    --verbose will print out what is being removed.
 
3101
    --dry-run will go through all the motions, but not actually
 
3102
    remove anything.
 
3103
    
 
3104
    In the future, uncommit will create a revision bundle, which can then
 
3105
    be re-applied.
 
3106
    """
 
3107
 
 
3108
    # TODO: jam 20060108 Add an option to allow uncommit to remove
 
3109
    # unreferenced information in 'branch-as-repository' branches.
 
3110
    # TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
 
3111
    # information in shared branches as well.
 
3112
    takes_options = ['verbose', 'revision',
 
3113
                    Option('dry-run', help='Don\'t actually make changes'),
 
3114
                    Option('force', help='Say yes to all questions.')]
 
3115
    takes_args = ['location?']
 
3116
    aliases = []
 
3117
 
 
3118
    def run(self, location=None,
 
3119
            dry_run=False, verbose=False,
 
3120
            revision=None, force=False):
 
3121
        from bzrlib.log import log_formatter, show_log
 
3122
        import sys
 
3123
        from bzrlib.uncommit import uncommit
 
3124
 
 
3125
        if location is None:
 
3126
            location = u'.'
 
3127
        control, relpath = bzrdir.BzrDir.open_containing(location)
 
3128
        try:
 
3129
            tree = control.open_workingtree()
 
3130
            b = tree.branch
 
3131
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
3132
            tree = None
 
3133
            b = control.open_branch()
 
3134
 
 
3135
        rev_id = None
 
3136
        if revision is None:
 
3137
            revno = b.revno()
 
3138
        else:
 
3139
            # 'bzr uncommit -r 10' actually means uncommit
 
3140
            # so that the final tree is at revno 10.
 
3141
            # but bzrlib.uncommit.uncommit() actually uncommits
 
3142
            # the revisions that are supplied.
 
3143
            # So we need to offset it by one
 
3144
            revno = revision[0].in_history(b).revno+1
 
3145
 
 
3146
        if revno <= b.revno():
 
3147
            rev_id = b.get_rev_id(revno)
 
3148
        if rev_id is None:
 
3149
            self.outf.write('No revisions to uncommit.\n')
 
3150
            return 1
 
3151
 
 
3152
        lf = log_formatter('short',
 
3153
                           to_file=self.outf,
 
3154
                           show_timezone='original')
 
3155
 
 
3156
        show_log(b,
 
3157
                 lf,
 
3158
                 verbose=False,
 
3159
                 direction='forward',
 
3160
                 start_revision=revno,
 
3161
                 end_revision=b.revno())
 
3162
 
 
3163
        if dry_run:
 
3164
            print 'Dry-run, pretending to remove the above revisions.'
 
3165
            if not force:
 
3166
                val = raw_input('Press <enter> to continue')
 
3167
        else:
 
3168
            print 'The above revision(s) will be removed.'
 
3169
            if not force:
 
3170
                val = raw_input('Are you sure [y/N]? ')
 
3171
                if val.lower() not in ('y', 'yes'):
 
3172
                    print 'Canceled'
 
3173
                    return 0
 
3174
 
 
3175
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
 
3176
                revno=revno)
 
3177
 
 
3178
 
 
3179
class cmd_break_lock(Command):
 
3180
    """Break a dead lock on a repository, branch or working directory.
 
3181
 
 
3182
    CAUTION: Locks should only be broken when you are sure that the process
 
3183
    holding the lock has been stopped.
 
3184
 
 
3185
    You can get information on what locks are open via the 'bzr info' command.
 
3186
    
 
3187
    example:
 
3188
        bzr break-lock
 
3189
    """
 
3190
    takes_args = ['location?']
 
3191
 
 
3192
    def run(self, location=None, show=False):
 
3193
        if location is None:
 
3194
            location = u'.'
 
3195
        control, relpath = bzrdir.BzrDir.open_containing(location)
 
3196
        try:
 
3197
            control.break_lock()
 
3198
        except NotImplementedError:
 
3199
            pass
 
3200
        
 
3201
 
 
3202
class cmd_wait_until_signalled(Command):
 
3203
    """Test helper for test_start_and_stop_bzr_subprocess_send_signal.
 
3204
 
 
3205
    This just prints a line to signal when it is ready, then blocks on stdin.
 
3206
    """
 
3207
 
 
3208
    hidden = True
 
3209
 
 
3210
    def run(self):
 
3211
        sys.stdout.write("running\n")
 
3212
        sys.stdout.flush()
 
3213
        sys.stdin.readline()
 
3214
 
 
3215
 
 
3216
class cmd_serve(Command):
 
3217
    """Run the bzr server."""
 
3218
 
 
3219
    aliases = ['server']
 
3220
 
 
3221
    takes_options = [
 
3222
        Option('inet',
 
3223
               help='serve on stdin/out for use from inetd or sshd'),
 
3224
        Option('port',
 
3225
               help='listen for connections on nominated port of the form '
 
3226
                    '[hostname:]portnumber. Passing 0 as the port number will '
 
3227
                    'result in a dynamically allocated port. Default port is '
 
3228
                    '4155.',
 
3229
               type=str),
 
3230
        Option('directory',
 
3231
               help='serve contents of directory',
 
3232
               type=unicode),
 
3233
        Option('allow-writes',
 
3234
               help='By default the server is a readonly server. Supplying '
 
3235
                    '--allow-writes enables write access to the contents of '
 
3236
                    'the served directory and below. '
 
3237
                ),
 
3238
        ]
 
3239
 
 
3240
    def run(self, port=None, inet=False, directory=None, allow_writes=False):
 
3241
        from bzrlib.transport import smart
 
3242
        from bzrlib.transport import get_transport
 
3243
        if directory is None:
 
3244
            directory = os.getcwd()
 
3245
        url = urlutils.local_path_to_url(directory)
 
3246
        if not allow_writes:
 
3247
            url = 'readonly+' + url
 
3248
        t = get_transport(url)
 
3249
        if inet:
 
3250
            server = smart.SmartServerPipeStreamMedium(sys.stdin, sys.stdout, t)
 
3251
        else:
 
3252
            if port is None:
 
3253
                port = smart.BZR_DEFAULT_PORT
 
3254
                host = '127.0.0.1'
 
3255
            else:
 
3256
                if ':' in port:
 
3257
                    host, port = port.split(':')
 
3258
                else:
 
3259
                    host = '127.0.0.1'
 
3260
                port = int(port)
 
3261
            server = smart.SmartTCPServer(t, host=host, port=port)
 
3262
            print 'listening on port: ', server.port
 
3263
            sys.stdout.flush()
 
3264
        server.serve()
 
3265
 
 
3266
class cmd_join(Command):
 
3267
    """Combine a subtree into its containing tree.
 
3268
    
 
3269
    This command is for experimental use only.  It requires the target tree
 
3270
    to be in dirstate-with-subtree format, which cannot be converted into
 
3271
    earlier formats.
 
3272
 
 
3273
    The TREE argument should be an independent tree, inside another tree, but
 
3274
    not part of it.  (Such trees can be produced by "bzr split", but also by
 
3275
    running "bzr branch" with the target inside a tree.)
 
3276
 
 
3277
    The result is a combined tree, with the subtree no longer an independant
 
3278
    part.  This is marked as a merge of the subtree into the containing tree,
 
3279
    and all history is preserved.
 
3280
 
 
3281
    If --reference is specified, the subtree retains its independence.  It can
 
3282
    be branched by itself, and can be part of multiple projects at the same
 
3283
    time.  But operations performed in the containing tree, such as commit
 
3284
    and merge, will recurse into the subtree.
 
3285
    """
 
3286
 
 
3287
    takes_args = ['tree']
 
3288
    takes_options = [Option('reference', 'join by reference')]
 
3289
    hidden = True
 
3290
 
 
3291
    def run(self, tree, reference=False):
 
3292
        sub_tree = WorkingTree.open(tree)
 
3293
        parent_dir = osutils.dirname(sub_tree.basedir)
 
3294
        containing_tree = WorkingTree.open_containing(parent_dir)[0]
 
3295
        repo = containing_tree.branch.repository
 
3296
        if not repo.supports_rich_root():
 
3297
            raise errors.BzrCommandError(
 
3298
                "Can't join trees because %s doesn't support rich root data.\n"
 
3299
                "You can use bzr upgrade on the repository."
 
3300
                % (repo,))
 
3301
        if reference:
 
3302
            try:
 
3303
                containing_tree.add_reference(sub_tree)
 
3304
            except errors.BadReferenceTarget, e:
 
3305
                # XXX: Would be better to just raise a nicely printable
 
3306
                # exception from the real origin.  Also below.  mbp 20070306
 
3307
                raise errors.BzrCommandError("Cannot join %s.  %s" %
 
3308
                                             (tree, e.reason))
 
3309
        else:
 
3310
            try:
 
3311
                containing_tree.subsume(sub_tree)
 
3312
            except errors.BadSubsumeSource, e:
 
3313
                raise errors.BzrCommandError("Cannot join %s.  %s" % 
 
3314
                                             (tree, e.reason))
 
3315
 
 
3316
 
 
3317
class cmd_split(Command):
 
3318
    """Split a tree into two trees.
 
3319
 
 
3320
    This command is for experimental use only.  It requires the target tree
 
3321
    to be in dirstate-with-subtree format, which cannot be converted into
 
3322
    earlier formats.
 
3323
 
 
3324
    The TREE argument should be a subdirectory of a working tree.  That
 
3325
    subdirectory will be converted into an independent tree, with its own
 
3326
    branch.  Commits in the top-level tree will not apply to the new subtree.
 
3327
    If you want that behavior, do "bzr join --reference TREE".
 
3328
 
 
3329
    To undo this operation, do "bzr join TREE".
 
3330
    """
 
3331
 
 
3332
    takes_args = ['tree']
 
3333
 
 
3334
    hidden = True
 
3335
 
 
3336
    def run(self, tree):
 
3337
        containing_tree, subdir = WorkingTree.open_containing(tree)
 
3338
        sub_id = containing_tree.path2id(subdir)
 
3339
        if sub_id is None:
 
3340
            raise errors.NotVersionedError(subdir)
 
3341
        try:
 
3342
            containing_tree.extract(sub_id)
 
3343
        except errors.RootNotRich:
 
3344
            raise errors.UpgradeRequired(containing_tree.branch.base)
 
3345
 
 
3346
 
 
3347
 
 
3348
class cmd_merge_directive(Command):
 
3349
    """Generate a merge directive for auto-merge tools.
 
3350
 
 
3351
    A directive requests a merge to be performed, and also provides all the
 
3352
    information necessary to do so.  This means it must either include a
 
3353
    revision bundle, or the location of a branch containing the desired
 
3354
    revision.
 
3355
 
 
3356
    A submit branch (the location to merge into) must be supplied the first
 
3357
    time the command is issued.  After it has been supplied once, it will
 
3358
    be remembered as the default.
 
3359
 
 
3360
    A public branch is optional if a revision bundle is supplied, but required
 
3361
    if --diff or --plain is specified.  It will be remembered as the default
 
3362
    after the first use.
 
3363
    """
 
3364
 
 
3365
    takes_args = ['submit_branch?', 'public_branch?']
 
3366
 
 
3367
    takes_options = [
 
3368
        RegistryOption.from_kwargs('patch-type',
 
3369
            'The type of patch to include in the directive',
 
3370
            title='Patch type', value_switches=True, enum_switch=False,
 
3371
            bundle='Bazaar revision bundle (default)',
 
3372
            diff='Normal unified diff',
 
3373
            plain='No patch, just directive'),
 
3374
        Option('sign', help='GPG-sign the directive'), 'revision',
 
3375
        Option('mail-to', type=str,
 
3376
            help='Instead of printing the directive, email to this address'),
 
3377
        Option('message', type=str, short_name='m',
 
3378
            help='Message to use when committing this merge')
 
3379
        ]
 
3380
 
 
3381
    def run(self, submit_branch=None, public_branch=None, patch_type='bundle',
 
3382
            sign=False, revision=None, mail_to=None, message=None):
 
3383
        if patch_type == 'plain':
 
3384
            patch_type = None
 
3385
        branch = Branch.open('.')
 
3386
        stored_submit_branch = branch.get_submit_branch()
 
3387
        if submit_branch is None:
 
3388
            submit_branch = stored_submit_branch
 
3389
        else:
 
3390
            if stored_submit_branch is None:
 
3391
                branch.set_submit_branch(submit_branch)
 
3392
        if submit_branch is None:
 
3393
            submit_branch = branch.get_parent()
 
3394
        if submit_branch is None:
 
3395
            raise errors.BzrCommandError('No submit branch specified or known')
 
3396
 
 
3397
        stored_public_branch = branch.get_public_branch()
 
3398
        if public_branch is None:
 
3399
            public_branch = stored_public_branch
 
3400
        elif stored_public_branch is None:
 
3401
            branch.set_public_branch(public_branch)
 
3402
        if patch_type != "bundle" and public_branch is None:
 
3403
            raise errors.BzrCommandError('No public branch specified or'
 
3404
                                         ' known')
 
3405
        if revision is not None:
 
3406
            if len(revision) != 1:
 
3407
                raise errors.BzrCommandError('bzr merge-directive takes '
 
3408
                    'exactly one revision identifier')
 
3409
            else:
 
3410
                revision_id = revision[0].in_history(branch).rev_id
 
3411
        else:
 
3412
            revision_id = branch.last_revision()
 
3413
        directive = merge_directive.MergeDirective.from_objects(
 
3414
            branch.repository, revision_id, time.time(),
 
3415
            osutils.local_time_offset(), submit_branch,
 
3416
            public_branch=public_branch, patch_type=patch_type,
 
3417
            message=message)
 
3418
        if mail_to is None:
 
3419
            if sign:
 
3420
                self.outf.write(directive.to_signed(branch))
 
3421
            else:
 
3422
                self.outf.writelines(directive.to_lines())
 
3423
        else:
 
3424
            message = directive.to_email(mail_to, branch, sign)
 
3425
            s = smtplib.SMTP()
 
3426
            server = branch.get_config().get_user_option('smtp_server')
 
3427
            if not server:
 
3428
                server = 'localhost'
 
3429
            s.connect(server)
 
3430
            s.sendmail(message['From'], message['To'], message.as_string())
 
3431
 
 
3432
 
 
3433
class cmd_tag(Command):
 
3434
    """Create a tag naming a revision.
 
3435
    
 
3436
    Tags give human-meaningful names to revisions.  Commands that take a -r
 
3437
    (--revision) option can be given -rtag:X, where X is any previously
 
3438
    created tag.
 
3439
 
 
3440
    Tags are stored in the branch.  Tags are copied from one branch to another
 
3441
    along when you branch, push, pull or merge.
 
3442
 
 
3443
    It is an error to give a tag name that already exists unless you pass 
 
3444
    --force, in which case the tag is moved to point to the new revision.
 
3445
    """
 
3446
 
 
3447
    takes_args = ['tag_name']
 
3448
    takes_options = [
 
3449
        Option('delete',
 
3450
            help='Delete this tag rather than placing it.',
 
3451
            ),
 
3452
        Option('directory',
 
3453
            help='Branch in which to place the tag.',
 
3454
            short_name='d',
 
3455
            type=unicode,
 
3456
            ),
 
3457
        Option('force',
 
3458
            help='Replace existing tags',
 
3459
            ),
 
3460
        'revision',
 
3461
        ]
 
3462
 
 
3463
    def run(self, tag_name,
 
3464
            delete=None,
 
3465
            directory='.',
 
3466
            force=None,
 
3467
            revision=None,
 
3468
            ):
 
3469
        branch, relpath = Branch.open_containing(directory)
 
3470
        branch.lock_write()
 
3471
        try:
 
3472
            if delete:
 
3473
                branch.tags.delete_tag(tag_name)
 
3474
                self.outf.write('Deleted tag %s.\n' % tag_name)
 
3475
            else:
 
3476
                if revision:
 
3477
                    if len(revision) != 1:
 
3478
                        raise errors.BzrCommandError(
 
3479
                            "Tags can only be placed on a single revision, "
 
3480
                            "not on a range")
 
3481
                    revision_id = revision[0].in_history(branch).rev_id
 
3482
                else:
 
3483
                    revision_id = branch.last_revision()
 
3484
                if (not force) and branch.tags.has_tag(tag_name):
 
3485
                    raise errors.TagAlreadyExists(tag_name)
 
3486
                branch.tags.set_tag(tag_name, revision_id)
 
3487
                self.outf.write('Created tag %s.\n' % tag_name)
 
3488
        finally:
 
3489
            branch.unlock()
 
3490
 
 
3491
 
 
3492
class cmd_tags(Command):
 
3493
    """List tags.
 
3494
 
 
3495
    This tag shows a table of tag names and the revisions they reference.
 
3496
    """
 
3497
 
 
3498
    takes_options = [
 
3499
        Option('directory',
 
3500
            help='Branch whose tags should be displayed',
 
3501
            short_name='d',
 
3502
            type=unicode,
 
3503
            ),
 
3504
    ]
 
3505
 
 
3506
    @display_command
 
3507
    def run(self,
 
3508
            directory='.',
 
3509
            ):
 
3510
        branch, relpath = Branch.open_containing(directory)
 
3511
        for tag_name, target in sorted(branch.tags.get_tag_dict().items()):
 
3512
            self.outf.write('%-20s %s\n' % (tag_name, target))
 
3513
 
 
3514
 
 
3515
# command-line interpretation helper for merge-related commands
 
3516
def _merge_helper(other_revision, base_revision,
 
3517
                  check_clean=True, ignore_zero=False,
 
3518
                  this_dir=None, backup_files=False,
 
3519
                  merge_type=None,
 
3520
                  file_list=None, show_base=False, reprocess=False,
 
3521
                  pull=False,
 
3522
                  pb=DummyProgress(),
 
3523
                  change_reporter=None,
 
3524
                  other_rev_id=None):
 
3525
    """Merge changes into a tree.
 
3526
 
 
3527
    base_revision
 
3528
        list(path, revno) Base for three-way merge.  
 
3529
        If [None, None] then a base will be automatically determined.
 
3530
    other_revision
 
3531
        list(path, revno) Other revision for three-way merge.
 
3532
    this_dir
 
3533
        Directory to merge changes into; '.' by default.
 
3534
    check_clean
 
3535
        If true, this_dir must have no uncommitted changes before the
 
3536
        merge begins.
 
3537
    ignore_zero - If true, suppress the "zero conflicts" message when 
 
3538
        there are no conflicts; should be set when doing something we expect
 
3539
        to complete perfectly.
 
3540
    file_list - If supplied, merge only changes to selected files.
 
3541
 
 
3542
    All available ancestors of other_revision and base_revision are
 
3543
    automatically pulled into the branch.
 
3544
 
 
3545
    The revno may be -1 to indicate the last revision on the branch, which is
 
3546
    the typical case.
 
3547
 
 
3548
    This function is intended for use from the command line; programmatic
 
3549
    clients might prefer to call merge.merge_inner(), which has less magic 
 
3550
    behavior.
 
3551
    """
 
3552
    # Loading it late, so that we don't always have to import bzrlib.merge
 
3553
    if merge_type is None:
 
3554
        merge_type = _mod_merge.Merge3Merger
 
3555
    if this_dir is None:
 
3556
        this_dir = u'.'
 
3557
    this_tree = WorkingTree.open_containing(this_dir)[0]
 
3558
    if show_base and not merge_type is _mod_merge.Merge3Merger:
 
3559
        raise errors.BzrCommandError("Show-base is not supported for this merge"
 
3560
                                     " type. %s" % merge_type)
 
3561
    if reprocess and not merge_type.supports_reprocess:
 
3562
        raise errors.BzrCommandError("Conflict reduction is not supported for merge"
 
3563
                                     " type %s." % merge_type)
 
3564
    if reprocess and show_base:
 
3565
        raise errors.BzrCommandError("Cannot do conflict reduction and show base.")
 
3566
    # TODO: jam 20070226 We should really lock these trees earlier. However, we
 
3567
    #       only want to take out a lock_tree_write() if we don't have to pull
 
3568
    #       any ancestry. But merge might fetch ancestry in the middle, in
 
3569
    #       which case we would need a lock_write().
 
3570
    #       Because we cannot upgrade locks, for now we live with the fact that
 
3571
    #       the tree will be locked multiple times during a merge. (Maybe
 
3572
    #       read-only some of the time, but it means things will get read
 
3573
    #       multiple times.)
 
3574
    try:
 
3575
        merger = _mod_merge.Merger(this_tree.branch, this_tree=this_tree,
 
3576
                                   pb=pb, change_reporter=change_reporter)
 
3577
        merger.pp = ProgressPhase("Merge phase", 5, pb)
 
3578
        merger.pp.next_phase()
 
3579
        merger.check_basis(check_clean)
 
3580
        if other_rev_id is not None:
 
3581
            merger.set_other_revision(other_rev_id, this_tree.branch)
 
3582
        else:
 
3583
            merger.set_other(other_revision)
 
3584
        merger.pp.next_phase()
 
3585
        merger.set_base(base_revision)
 
3586
        if merger.base_rev_id == merger.other_rev_id:
 
3587
            note('Nothing to do.')
 
3588
            return 0
 
3589
        if file_list is None:
 
3590
            if pull and merger.base_rev_id == merger.this_rev_id:
 
3591
                # FIXME: deduplicate with pull
 
3592
                result = merger.this_tree.pull(merger.this_branch,
 
3593
                        False, merger.other_rev_id)
 
3594
                if result.old_revid == result.new_revid:
 
3595
                    note('No revisions to pull.')
 
3596
                else:
 
3597
                    note('Now on revision %d.' % result.new_revno)
 
3598
                return 0
 
3599
        merger.backup_files = backup_files
 
3600
        merger.merge_type = merge_type 
 
3601
        merger.set_interesting_files(file_list)
 
3602
        merger.show_base = show_base 
 
3603
        merger.reprocess = reprocess
 
3604
        conflicts = merger.do_merge()
 
3605
        if file_list is None:
 
3606
            merger.set_pending()
 
3607
    finally:
 
3608
        pb.clear()
 
3609
    return conflicts
 
3610
 
 
3611
 
 
3612
# Compatibility
 
3613
merge = _merge_helper
 
3614
 
 
3615
 
 
3616
# these get imported and then picked up by the scan for cmd_*
 
3617
# TODO: Some more consistent way to split command definitions across files;
 
3618
# we do need to load at least some information about them to know of 
 
3619
# aliases.  ideally we would avoid loading the implementation until the
 
3620
# details were needed.
 
3621
from bzrlib.cmd_version_info import cmd_version_info
 
3622
from bzrlib.conflicts import cmd_resolve, cmd_conflicts, restore
 
3623
from bzrlib.bundle.commands import cmd_bundle_revisions
 
3624
from bzrlib.sign_my_commits import cmd_sign_my_commits
 
3625
from bzrlib.weave_commands import cmd_weave_list, cmd_weave_join, \
 
3626
        cmd_weave_plan_merge, cmd_weave_merge_text