/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 breezy/builtins.py

  • Committer: Jelmer Vernooij
  • Date: 2020-03-22 01:35:14 UTC
  • mfrom: (7490.7.6 work)
  • mto: This revision was merged to the branch mainline in revision 7499.
  • Revision ID: jelmer@jelmer.uk-20200322013514-7vw1ntwho04rcuj3
merge lp:brz/3.1.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# Copyright (C) 2005-2012 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 
16
 
 
17
"""builtin brz commands"""
 
18
 
 
19
import errno
 
20
import os
 
21
import sys
 
22
 
 
23
import breezy.bzr
 
24
import breezy.git
 
25
 
 
26
from . import (
 
27
    errors,
 
28
    )
 
29
 
 
30
from . import lazy_import
 
31
lazy_import.lazy_import(globals(), """
 
32
import time
 
33
 
 
34
import breezy
 
35
from breezy import (
 
36
    branch as _mod_branch,
 
37
    bugtracker,
 
38
    cache_utf8,
 
39
    controldir,
 
40
    directory_service,
 
41
    delta,
 
42
    config as _mod_config,
 
43
    globbing,
 
44
    gpg,
 
45
    hooks,
 
46
    lazy_regex,
 
47
    log,
 
48
    merge as _mod_merge,
 
49
    mergeable as _mod_mergeable,
 
50
    merge_directive,
 
51
    osutils,
 
52
    reconfigure,
 
53
    rename_map,
 
54
    revision as _mod_revision,
 
55
    symbol_versioning,
 
56
    timestamp,
 
57
    transport,
 
58
    tree as _mod_tree,
 
59
    ui,
 
60
    urlutils,
 
61
    views,
 
62
    )
 
63
from breezy.branch import Branch
 
64
from breezy.conflicts import ConflictList
 
65
from breezy.transport import memory
 
66
from breezy.smtp_connection import SMTPConnection
 
67
from breezy.workingtree import WorkingTree
 
68
from breezy.i18n import gettext, ngettext
 
69
""")
 
70
 
 
71
from .commands import (
 
72
    Command,
 
73
    builtin_command_registry,
 
74
    display_command,
 
75
    )
 
76
from .option import (
 
77
    ListOption,
 
78
    Option,
 
79
    RegistryOption,
 
80
    custom_help,
 
81
    _parse_revision_str,
 
82
    )
 
83
from .revisionspec import (
 
84
    RevisionSpec,
 
85
    RevisionInfo,
 
86
    )
 
87
from .trace import mutter, note, warning, is_quiet, get_verbosity_level
 
88
 
 
89
 
 
90
def _get_branch_location(control_dir, possible_transports=None):
 
91
    """Return location of branch for this control dir."""
 
92
    try:
 
93
        target = control_dir.get_branch_reference()
 
94
    except errors.NotBranchError:
 
95
        return control_dir.root_transport.base
 
96
    if target is not None:
 
97
        return target
 
98
    this_branch = control_dir.open_branch(
 
99
        possible_transports=possible_transports)
 
100
    # This may be a heavy checkout, where we want the master branch
 
101
    master_location = this_branch.get_bound_location()
 
102
    if master_location is not None:
 
103
        return master_location
 
104
    # If not, use a local sibling
 
105
    return this_branch.base
 
106
 
 
107
 
 
108
def _is_colocated(control_dir, possible_transports=None):
 
109
    """Check if the branch in control_dir is colocated.
 
110
 
 
111
    :param control_dir: Control directory
 
112
    :return: Tuple with boolean indicating whether the branch is colocated
 
113
        and the full URL to the actual branch
 
114
    """
 
115
    # This path is meant to be relative to the existing branch
 
116
    this_url = _get_branch_location(
 
117
        control_dir, possible_transports=possible_transports)
 
118
    # Perhaps the target control dir supports colocated branches?
 
119
    try:
 
120
        root = controldir.ControlDir.open(
 
121
            this_url, possible_transports=possible_transports)
 
122
    except errors.NotBranchError:
 
123
        return (False, this_url)
 
124
    else:
 
125
        try:
 
126
            control_dir.open_workingtree()
 
127
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
128
            return (False, this_url)
 
129
        else:
 
130
            return (
 
131
                root._format.colocated_branches and
 
132
                control_dir.control_url == root.control_url,
 
133
                this_url)
 
134
 
 
135
 
 
136
def lookup_new_sibling_branch(control_dir, location, possible_transports=None):
 
137
    """Lookup the location for a new sibling branch.
 
138
 
 
139
    :param control_dir: Control directory to find sibling branches from
 
140
    :param location: Name of the new branch
 
141
    :return: Full location to the new branch
 
142
    """
 
143
    location = directory_service.directories.dereference(location)
 
144
    if '/' not in location and '\\' not in location:
 
145
        (colocated, this_url) = _is_colocated(control_dir, possible_transports)
 
146
 
 
147
        if colocated:
 
148
            return urlutils.join_segment_parameters(
 
149
                this_url, {"branch": urlutils.escape(location)})
 
150
        else:
 
151
            return urlutils.join(this_url, '..', urlutils.escape(location))
 
152
    return location
 
153
 
 
154
 
 
155
def open_sibling_branch(control_dir, location, possible_transports=None):
 
156
    """Open a branch, possibly a sibling of another.
 
157
 
 
158
    :param control_dir: Control directory relative to which to lookup the
 
159
        location.
 
160
    :param location: Location to look up
 
161
    :return: branch to open
 
162
    """
 
163
    try:
 
164
        # Perhaps it's a colocated branch?
 
165
        return control_dir.open_branch(
 
166
            location, possible_transports=possible_transports)
 
167
    except (errors.NotBranchError, errors.NoColocatedBranchSupport):
 
168
        this_url = _get_branch_location(control_dir)
 
169
        return Branch.open(
 
170
            urlutils.join(
 
171
                this_url, '..', urlutils.escape(location)))
 
172
 
 
173
 
 
174
def open_nearby_branch(near=None, location=None, possible_transports=None):
 
175
    """Open a nearby branch.
 
176
 
 
177
    :param near: Optional location of container from which to open branch
 
178
    :param location: Location of the branch
 
179
    :return: Branch instance
 
180
    """
 
181
    if near is None:
 
182
        if location is None:
 
183
            location = "."
 
184
        try:
 
185
            return Branch.open(
 
186
                location, possible_transports=possible_transports)
 
187
        except errors.NotBranchError:
 
188
            near = "."
 
189
    cdir = controldir.ControlDir.open(
 
190
        near, possible_transports=possible_transports)
 
191
    return open_sibling_branch(
 
192
        cdir, location, possible_transports=possible_transports)
 
193
 
 
194
 
 
195
def iter_sibling_branches(control_dir, possible_transports=None):
 
196
    """Iterate over the siblings of a branch.
 
197
 
 
198
    :param control_dir: Control directory for which to look up the siblings
 
199
    :return: Iterator over tuples with branch name and branch object
 
200
    """
 
201
    try:
 
202
        reference = control_dir.get_branch_reference()
 
203
    except errors.NotBranchError:
 
204
        reference = None
 
205
    if reference is not None:
 
206
        try:
 
207
            ref_branch = Branch.open(
 
208
                reference, possible_transports=possible_transports)
 
209
        except errors.NotBranchError:
 
210
            ref_branch = None
 
211
    else:
 
212
        ref_branch = None
 
213
    if ref_branch is None or ref_branch.name:
 
214
        if ref_branch is not None:
 
215
            control_dir = ref_branch.controldir
 
216
        for name, branch in control_dir.get_branches().items():
 
217
            yield name, branch
 
218
    else:
 
219
        repo = ref_branch.controldir.find_repository()
 
220
        for branch in repo.find_branches(using=True):
 
221
            name = urlutils.relative_url(
 
222
                repo.user_url, branch.user_url).rstrip("/")
 
223
            yield name, branch
 
224
 
 
225
 
 
226
def tree_files_for_add(file_list):
 
227
    """
 
228
    Return a tree and list of absolute paths from a file list.
 
229
 
 
230
    Similar to tree_files, but add handles files a bit differently, so it a
 
231
    custom implementation.  In particular, MutableTreeTree.smart_add expects
 
232
    absolute paths, which it immediately converts to relative paths.
 
233
    """
 
234
    # FIXME Would be nice to just return the relative paths like
 
235
    # internal_tree_files does, but there are a large number of unit tests
 
236
    # that assume the current interface to mutabletree.smart_add
 
237
    if file_list:
 
238
        tree, relpath = WorkingTree.open_containing(file_list[0])
 
239
        if tree.supports_views():
 
240
            view_files = tree.views.lookup_view()
 
241
            if view_files:
 
242
                for filename in file_list:
 
243
                    if not osutils.is_inside_any(view_files, filename):
 
244
                        raise views.FileOutsideView(filename, view_files)
 
245
        file_list = file_list[:]
 
246
        file_list[0] = tree.abspath(relpath)
 
247
    else:
 
248
        tree = WorkingTree.open_containing(u'.')[0]
 
249
        if tree.supports_views():
 
250
            view_files = tree.views.lookup_view()
 
251
            if view_files:
 
252
                file_list = view_files
 
253
                view_str = views.view_display_str(view_files)
 
254
                note(gettext("Ignoring files outside view. View is %s"),
 
255
                     view_str)
 
256
    return tree, file_list
 
257
 
 
258
 
 
259
def _get_one_revision(command_name, revisions):
 
260
    if revisions is None:
 
261
        return None
 
262
    if len(revisions) != 1:
 
263
        raise errors.BzrCommandError(gettext(
 
264
            'brz %s --revision takes exactly one revision identifier') % (
 
265
                command_name,))
 
266
    return revisions[0]
 
267
 
 
268
 
 
269
def _get_one_revision_tree(command_name, revisions, branch=None, tree=None):
 
270
    """Get a revision tree. Not suitable for commands that change the tree.
 
271
 
 
272
    Specifically, the basis tree in dirstate trees is coupled to the dirstate
 
273
    and doing a commit/uncommit/pull will at best fail due to changing the
 
274
    basis revision data.
 
275
 
 
276
    If tree is passed in, it should be already locked, for lifetime management
 
277
    of the trees internal cached state.
 
278
    """
 
279
    if branch is None:
 
280
        branch = tree.branch
 
281
    if revisions is None:
 
282
        if tree is not None:
 
283
            rev_tree = tree.basis_tree()
 
284
        else:
 
285
            rev_tree = branch.basis_tree()
 
286
    else:
 
287
        revision = _get_one_revision(command_name, revisions)
 
288
        rev_tree = revision.as_tree(branch)
 
289
    return rev_tree
 
290
 
 
291
 
 
292
def _get_view_info_for_change_reporter(tree):
 
293
    """Get the view information from a tree for change reporting."""
 
294
    view_info = None
 
295
    try:
 
296
        current_view = tree.views.get_view_info()[0]
 
297
        if current_view is not None:
 
298
            view_info = (current_view, tree.views.lookup_view())
 
299
    except views.ViewsNotSupported:
 
300
        pass
 
301
    return view_info
 
302
 
 
303
 
 
304
def _open_directory_or_containing_tree_or_branch(filename, directory):
 
305
    """Open the tree or branch containing the specified file, unless
 
306
    the --directory option is used to specify a different branch."""
 
307
    if directory is not None:
 
308
        return (None, Branch.open(directory), filename)
 
309
    return controldir.ControlDir.open_containing_tree_or_branch(filename)
 
310
 
 
311
 
 
312
# TODO: Make sure no commands unconditionally use the working directory as a
 
313
# branch.  If a filename argument is used, the first of them should be used to
 
314
# specify the branch.  (Perhaps this can be factored out into some kind of
 
315
# Argument class, representing a file in a branch, where the first occurrence
 
316
# opens the branch?)
 
317
 
 
318
class cmd_status(Command):
 
319
    __doc__ = """Display status summary.
 
320
 
 
321
    This reports on versioned and unknown files, reporting them
 
322
    grouped by state.  Possible states are:
 
323
 
 
324
    added
 
325
        Versioned in the working copy but not in the previous revision.
 
326
 
 
327
    removed
 
328
        Versioned in the previous revision but removed or deleted
 
329
        in the working copy.
 
330
 
 
331
    renamed
 
332
        Path of this file changed from the previous revision;
 
333
        the text may also have changed.  This includes files whose
 
334
        parent directory was renamed.
 
335
 
 
336
    modified
 
337
        Text has changed since the previous revision.
 
338
 
 
339
    kind changed
 
340
        File kind has been changed (e.g. from file to directory).
 
341
 
 
342
    unknown
 
343
        Not versioned and not matching an ignore pattern.
 
344
 
 
345
    Additionally for directories, symlinks and files with a changed
 
346
    executable bit, Breezy indicates their type using a trailing
 
347
    character: '/', '@' or '*' respectively. These decorations can be
 
348
    disabled using the '--no-classify' option.
 
349
 
 
350
    To see ignored files use 'brz ignored'.  For details on the
 
351
    changes to file texts, use 'brz diff'.
 
352
 
 
353
    Note that --short or -S gives status flags for each item, similar
 
354
    to Subversion's status command. To get output similar to svn -q,
 
355
    use brz status -SV.
 
356
 
 
357
    If no arguments are specified, the status of the entire working
 
358
    directory is shown.  Otherwise, only the status of the specified
 
359
    files or directories is reported.  If a directory is given, status
 
360
    is reported for everything inside that directory.
 
361
 
 
362
    Before merges are committed, the pending merge tip revisions are
 
363
    shown. To see all pending merge revisions, use the -v option.
 
364
    To skip the display of pending merge information altogether, use
 
365
    the no-pending option or specify a file/directory.
 
366
 
 
367
    To compare the working directory to a specific revision, pass a
 
368
    single revision to the revision argument.
 
369
 
 
370
    To see which files have changed in a specific revision, or between
 
371
    two revisions, pass a revision range to the revision argument.
 
372
    This will produce the same results as calling 'brz diff --summarize'.
 
373
    """
 
374
 
 
375
    # TODO: --no-recurse/-N, --recurse options
 
376
 
 
377
    takes_args = ['file*']
 
378
    takes_options = ['show-ids', 'revision', 'change', 'verbose',
 
379
                     Option('short', help='Use short status indicators.',
 
380
                            short_name='S'),
 
381
                     Option('versioned', help='Only show versioned files.',
 
382
                            short_name='V'),
 
383
                     Option('no-pending', help='Don\'t show pending merges.'),
 
384
                     Option('no-classify',
 
385
                            help='Do not mark object type using indicator.'),
 
386
                     ]
 
387
    aliases = ['st', 'stat']
 
388
 
 
389
    encoding_type = 'replace'
 
390
    _see_also = ['diff', 'revert', 'status-flags']
 
391
 
 
392
    @display_command
 
393
    def run(self, show_ids=False, file_list=None, revision=None, short=False,
 
394
            versioned=False, no_pending=False, verbose=False,
 
395
            no_classify=False):
 
396
        from .status import show_tree_status
 
397
 
 
398
        if revision and len(revision) > 2:
 
399
            raise errors.BzrCommandError(
 
400
                gettext('brz status --revision takes exactly'
 
401
                        ' one or two revision specifiers'))
 
402
 
 
403
        tree, relfile_list = WorkingTree.open_containing_paths(file_list)
 
404
        # Avoid asking for specific files when that is not needed.
 
405
        if relfile_list == ['']:
 
406
            relfile_list = None
 
407
            # Don't disable pending merges for full trees other than '.'.
 
408
            if file_list == ['.']:
 
409
                no_pending = True
 
410
        # A specific path within a tree was given.
 
411
        elif relfile_list is not None:
 
412
            no_pending = True
 
413
        show_tree_status(tree, show_ids=show_ids,
 
414
                         specific_files=relfile_list, revision=revision,
 
415
                         to_file=self.outf, short=short, versioned=versioned,
 
416
                         show_pending=(not no_pending), verbose=verbose,
 
417
                         classify=not no_classify)
 
418
 
 
419
 
 
420
class cmd_cat_revision(Command):
 
421
    __doc__ = """Write out metadata for a revision.
 
422
 
 
423
    The revision to print can either be specified by a specific
 
424
    revision identifier, or you can use --revision.
 
425
    """
 
426
 
 
427
    hidden = True
 
428
    takes_args = ['revision_id?']
 
429
    takes_options = ['directory', 'revision']
 
430
    # cat-revision is more for frontends so should be exact
 
431
    encoding = 'strict'
 
432
 
 
433
    def print_revision(self, revisions, revid):
 
434
        stream = revisions.get_record_stream([(revid,)], 'unordered', True)
 
435
        record = next(stream)
 
436
        if record.storage_kind == 'absent':
 
437
            raise errors.NoSuchRevision(revisions, revid)
 
438
        revtext = record.get_bytes_as('fulltext')
 
439
        self.outf.write(revtext.decode('utf-8'))
 
440
 
 
441
    @display_command
 
442
    def run(self, revision_id=None, revision=None, directory=u'.'):
 
443
        if revision_id is not None and revision is not None:
 
444
            raise errors.BzrCommandError(gettext('You can only supply one of'
 
445
                                                 ' revision_id or --revision'))
 
446
        if revision_id is None and revision is None:
 
447
            raise errors.BzrCommandError(
 
448
                gettext('You must supply either --revision or a revision_id'))
 
449
 
 
450
        b = controldir.ControlDir.open_containing_tree_or_branch(directory)[1]
 
451
 
 
452
        revisions = getattr(b.repository, "revisions", None)
 
453
        if revisions is None:
 
454
            raise errors.BzrCommandError(
 
455
                gettext('Repository %r does not support '
 
456
                        'access to raw revision texts') % b.repository)
 
457
 
 
458
        with b.repository.lock_read():
 
459
            # TODO: jam 20060112 should cat-revision always output utf-8?
 
460
            if revision_id is not None:
 
461
                revision_id = cache_utf8.encode(revision_id)
 
462
                try:
 
463
                    self.print_revision(revisions, revision_id)
 
464
                except errors.NoSuchRevision:
 
465
                    msg = gettext(
 
466
                        "The repository {0} contains no revision {1}.").format(
 
467
                            b.repository.base, revision_id.decode('utf-8'))
 
468
                    raise errors.BzrCommandError(msg)
 
469
            elif revision is not None:
 
470
                for rev in revision:
 
471
                    if rev is None:
 
472
                        raise errors.BzrCommandError(
 
473
                            gettext('You cannot specify a NULL revision.'))
 
474
                    rev_id = rev.as_revision_id(b)
 
475
                    self.print_revision(revisions, rev_id)
 
476
 
 
477
 
 
478
class cmd_remove_tree(Command):
 
479
    __doc__ = """Remove the working tree from a given branch/checkout.
 
480
 
 
481
    Since a lightweight checkout is little more than a working tree
 
482
    this will refuse to run against one.
 
483
 
 
484
    To re-create the working tree, use "brz checkout".
 
485
    """
 
486
    _see_also = ['checkout', 'working-trees']
 
487
    takes_args = ['location*']
 
488
    takes_options = [
 
489
        Option('force',
 
490
               help='Remove the working tree even if it has '
 
491
                    'uncommitted or shelved changes.'),
 
492
        ]
 
493
 
 
494
    def run(self, location_list, force=False):
 
495
        if not location_list:
 
496
            location_list = ['.']
 
497
 
 
498
        for location in location_list:
 
499
            d = controldir.ControlDir.open(location)
 
500
 
 
501
            try:
 
502
                working = d.open_workingtree()
 
503
            except errors.NoWorkingTree:
 
504
                raise errors.BzrCommandError(
 
505
                    gettext("No working tree to remove"))
 
506
            except errors.NotLocalUrl:
 
507
                raise errors.BzrCommandError(
 
508
                    gettext("You cannot remove the working tree"
 
509
                            " of a remote path"))
 
510
            if not force:
 
511
                if (working.has_changes()):
 
512
                    raise errors.UncommittedChanges(working)
 
513
                if working.get_shelf_manager().last_shelf() is not None:
 
514
                    raise errors.ShelvedChanges(working)
 
515
 
 
516
            if working.user_url != working.branch.user_url:
 
517
                raise errors.BzrCommandError(
 
518
                    gettext("You cannot remove the working tree"
 
519
                            " from a lightweight checkout"))
 
520
 
 
521
            d.destroy_workingtree()
 
522
 
 
523
 
 
524
class cmd_repair_workingtree(Command):
 
525
    __doc__ = """Reset the working tree state file.
 
526
 
 
527
    This is not meant to be used normally, but more as a way to recover from
 
528
    filesystem corruption, etc. This rebuilds the working inventory back to a
 
529
    'known good' state. Any new modifications (adding a file, renaming, etc)
 
530
    will be lost, though modified files will still be detected as such.
 
531
 
 
532
    Most users will want something more like "brz revert" or "brz update"
 
533
    unless the state file has become corrupted.
 
534
 
 
535
    By default this attempts to recover the current state by looking at the
 
536
    headers of the state file. If the state file is too corrupted to even do
 
537
    that, you can supply --revision to force the state of the tree.
 
538
    """
 
539
 
 
540
    takes_options = [
 
541
        'revision', 'directory',
 
542
        Option('force',
 
543
               help='Reset the tree even if it doesn\'t appear to be'
 
544
                    ' corrupted.'),
 
545
    ]
 
546
    hidden = True
 
547
 
 
548
    def run(self, revision=None, directory='.', force=False):
 
549
        tree, _ = WorkingTree.open_containing(directory)
 
550
        self.enter_context(tree.lock_tree_write())
 
551
        if not force:
 
552
            try:
 
553
                tree.check_state()
 
554
            except errors.BzrError:
 
555
                pass  # There seems to be a real error here, so we'll reset
 
556
            else:
 
557
                # Refuse
 
558
                raise errors.BzrCommandError(gettext(
 
559
                    'The tree does not appear to be corrupt. You probably'
 
560
                    ' want "brz revert" instead. Use "--force" if you are'
 
561
                    ' sure you want to reset the working tree.'))
 
562
        if revision is None:
 
563
            revision_ids = None
 
564
        else:
 
565
            revision_ids = [r.as_revision_id(tree.branch) for r in revision]
 
566
        try:
 
567
            tree.reset_state(revision_ids)
 
568
        except errors.BzrError:
 
569
            if revision_ids is None:
 
570
                extra = gettext(', the header appears corrupt, try passing '
 
571
                                '-r -1 to set the state to the last commit')
 
572
            else:
 
573
                extra = ''
 
574
            raise errors.BzrCommandError(
 
575
                gettext('failed to reset the tree state{0}').format(extra))
 
576
 
 
577
 
 
578
class cmd_revno(Command):
 
579
    __doc__ = """Show current revision number.
 
580
 
 
581
    This is equal to the number of revisions on this branch.
 
582
    """
 
583
 
 
584
    _see_also = ['info']
 
585
    takes_args = ['location?']
 
586
    takes_options = [
 
587
        Option('tree', help='Show revno of working tree.'),
 
588
        'revision',
 
589
        ]
 
590
 
 
591
    @display_command
 
592
    def run(self, tree=False, location=u'.', revision=None):
 
593
        if revision is not None and tree:
 
594
            raise errors.BzrCommandError(
 
595
                gettext("--tree and --revision can not be used together"))
 
596
 
 
597
        if tree:
 
598
            try:
 
599
                wt = WorkingTree.open_containing(location)[0]
 
600
                self.enter_context(wt.lock_read())
 
601
            except (errors.NoWorkingTree, errors.NotLocalUrl):
 
602
                raise errors.NoWorkingTree(location)
 
603
            b = wt.branch
 
604
            revid = wt.last_revision()
 
605
        else:
 
606
            b = Branch.open_containing(location)[0]
 
607
            self.enter_context(b.lock_read())
 
608
            if revision:
 
609
                if len(revision) != 1:
 
610
                    raise errors.BzrCommandError(gettext(
 
611
                        "Revision numbers only make sense for single "
 
612
                        "revisions, not ranges"))
 
613
                revid = revision[0].as_revision_id(b)
 
614
            else:
 
615
                revid = b.last_revision()
 
616
        try:
 
617
            revno_t = b.revision_id_to_dotted_revno(revid)
 
618
        except (errors.NoSuchRevision, errors.GhostRevisionsHaveNoRevno):
 
619
            revno_t = ('???',)
 
620
        revno = ".".join(str(n) for n in revno_t)
 
621
        self.cleanup_now()
 
622
        self.outf.write(revno + '\n')
 
623
 
 
624
 
 
625
class cmd_revision_info(Command):
 
626
    __doc__ = """Show revision number and revision id for a given revision identifier.
 
627
    """
 
628
    hidden = True
 
629
    takes_args = ['revision_info*']
 
630
    takes_options = [
 
631
        'revision',
 
632
        custom_help('directory', help='Branch to examine, '
 
633
                    'rather than the one containing the working directory.'),
 
634
        Option('tree', help='Show revno of working tree.'),
 
635
        ]
 
636
 
 
637
    @display_command
 
638
    def run(self, revision=None, directory=u'.', tree=False,
 
639
            revision_info_list=[]):
 
640
 
 
641
        try:
 
642
            wt = WorkingTree.open_containing(directory)[0]
 
643
            b = wt.branch
 
644
            self.enter_context(wt.lock_read())
 
645
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
646
            wt = None
 
647
            b = Branch.open_containing(directory)[0]
 
648
            self.enter_context(b.lock_read())
 
649
        revision_ids = []
 
650
        if revision is not None:
 
651
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
652
        if revision_info_list is not None:
 
653
            for rev_str in revision_info_list:
 
654
                rev_spec = RevisionSpec.from_string(rev_str)
 
655
                revision_ids.append(rev_spec.as_revision_id(b))
 
656
        # No arguments supplied, default to the last revision
 
657
        if len(revision_ids) == 0:
 
658
            if tree:
 
659
                if wt is None:
 
660
                    raise errors.NoWorkingTree(directory)
 
661
                revision_ids.append(wt.last_revision())
 
662
            else:
 
663
                revision_ids.append(b.last_revision())
 
664
 
 
665
        revinfos = []
 
666
        maxlen = 0
 
667
        for revision_id in revision_ids:
 
668
            try:
 
669
                dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
670
                revno = '.'.join(str(i) for i in dotted_revno)
 
671
            except errors.NoSuchRevision:
 
672
                revno = '???'
 
673
            maxlen = max(maxlen, len(revno))
 
674
            revinfos.append((revno, revision_id))
 
675
 
 
676
        self.cleanup_now()
 
677
        for revno, revid in revinfos:
 
678
            self.outf.write(
 
679
                '%*s %s\n' % (maxlen, revno, revid.decode('utf-8')))
 
680
 
 
681
 
 
682
class cmd_add(Command):
 
683
    __doc__ = """Add specified files or directories.
 
684
 
 
685
    In non-recursive mode, all the named items are added, regardless
 
686
    of whether they were previously ignored.  A warning is given if
 
687
    any of the named files are already versioned.
 
688
 
 
689
    In recursive mode (the default), files are treated the same way
 
690
    but the behaviour for directories is different.  Directories that
 
691
    are already versioned do not give a warning.  All directories,
 
692
    whether already versioned or not, are searched for files or
 
693
    subdirectories that are neither versioned or ignored, and these
 
694
    are added.  This search proceeds recursively into versioned
 
695
    directories.  If no names are given '.' is assumed.
 
696
 
 
697
    A warning will be printed when nested trees are encountered,
 
698
    unless they are explicitly ignored.
 
699
 
 
700
    Therefore simply saying 'brz add' will version all files that
 
701
    are currently unknown.
 
702
 
 
703
    Adding a file whose parent directory is not versioned will
 
704
    implicitly add the parent, and so on up to the root. This means
 
705
    you should never need to explicitly add a directory, they'll just
 
706
    get added when you add a file in the directory.
 
707
 
 
708
    --dry-run will show which files would be added, but not actually
 
709
    add them.
 
710
 
 
711
    --file-ids-from will try to use the file ids from the supplied path.
 
712
    It looks up ids trying to find a matching parent directory with the
 
713
    same filename, and then by pure path. This option is rarely needed
 
714
    but can be useful when adding the same logical file into two
 
715
    branches that will be merged later (without showing the two different
 
716
    adds as a conflict). It is also useful when merging another project
 
717
    into a subdirectory of this one.
 
718
 
 
719
    Any files matching patterns in the ignore list will not be added
 
720
    unless they are explicitly mentioned.
 
721
 
 
722
    In recursive mode, files larger than the configuration option
 
723
    add.maximum_file_size will be skipped. Named items are never skipped due
 
724
    to file size.
 
725
    """
 
726
    takes_args = ['file*']
 
727
    takes_options = [
 
728
        Option('no-recurse',
 
729
               help="Don't recursively add the contents of directories.",
 
730
               short_name='N'),
 
731
        Option('dry-run',
 
732
               help="Show what would be done, but don't actually do "
 
733
                    "anything."),
 
734
        'verbose',
 
735
        Option('file-ids-from',
 
736
               type=str,
 
737
               help='Lookup file ids from this tree.'),
 
738
        ]
 
739
    encoding_type = 'replace'
 
740
    _see_also = ['remove', 'ignore']
 
741
 
 
742
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False,
 
743
            file_ids_from=None):
 
744
        import breezy.add
 
745
        tree, file_list = tree_files_for_add(file_list)
 
746
 
 
747
        if file_ids_from is not None and not tree.supports_setting_file_ids():
 
748
            warning(
 
749
                gettext('Ignoring --file-ids-from, since the tree does not '
 
750
                        'support setting file ids.'))
 
751
            file_ids_from = None
 
752
 
 
753
        base_tree = None
 
754
        if file_ids_from is not None:
 
755
            try:
 
756
                base_tree, base_path = WorkingTree.open_containing(
 
757
                    file_ids_from)
 
758
            except errors.NoWorkingTree:
 
759
                base_branch, base_path = Branch.open_containing(
 
760
                    file_ids_from)
 
761
                base_tree = base_branch.basis_tree()
 
762
 
 
763
            action = breezy.add.AddFromBaseAction(
 
764
                base_tree, base_path, to_file=self.outf,
 
765
                should_print=(not is_quiet()))
 
766
        else:
 
767
            action = breezy.add.AddWithSkipLargeAction(
 
768
                to_file=self.outf, should_print=(not is_quiet()))
 
769
 
 
770
        if base_tree:
 
771
            self.enter_context(base_tree.lock_read())
 
772
        added, ignored = tree.smart_add(
 
773
            file_list, not no_recurse, action=action, save=not dry_run)
 
774
        self.cleanup_now()
 
775
        if len(ignored) > 0:
 
776
            if verbose:
 
777
                for glob in sorted(ignored):
 
778
                    for path in ignored[glob]:
 
779
                        self.outf.write(
 
780
                            gettext("ignored {0} matching \"{1}\"\n").format(
 
781
                                path, glob))
 
782
 
 
783
 
 
784
class cmd_mkdir(Command):
 
785
    __doc__ = """Create a new versioned directory.
 
786
 
 
787
    This is equivalent to creating the directory and then adding it.
 
788
    """
 
789
 
 
790
    takes_args = ['dir+']
 
791
    takes_options = [
 
792
        Option(
 
793
            'parents',
 
794
            help='No error if existing, make parent directories as needed.',
 
795
            short_name='p'
 
796
            )
 
797
        ]
 
798
    encoding_type = 'replace'
 
799
 
 
800
    @classmethod
 
801
    def add_file_with_parents(cls, wt, relpath):
 
802
        if wt.is_versioned(relpath):
 
803
            return
 
804
        cls.add_file_with_parents(wt, osutils.dirname(relpath))
 
805
        wt.add([relpath])
 
806
 
 
807
    @classmethod
 
808
    def add_file_single(cls, wt, relpath):
 
809
        wt.add([relpath])
 
810
 
 
811
    def run(self, dir_list, parents=False):
 
812
        if parents:
 
813
            add_file = self.add_file_with_parents
 
814
        else:
 
815
            add_file = self.add_file_single
 
816
        for dir in dir_list:
 
817
            wt, relpath = WorkingTree.open_containing(dir)
 
818
            if parents:
 
819
                try:
 
820
                    os.makedirs(dir)
 
821
                except OSError as e:
 
822
                    if e.errno != errno.EEXIST:
 
823
                        raise
 
824
            else:
 
825
                os.mkdir(dir)
 
826
            add_file(wt, relpath)
 
827
            if not is_quiet():
 
828
                self.outf.write(gettext('added %s\n') % dir)
 
829
 
 
830
 
 
831
class cmd_relpath(Command):
 
832
    __doc__ = """Show path of a file relative to root"""
 
833
 
 
834
    takes_args = ['filename']
 
835
    hidden = True
 
836
 
 
837
    @display_command
 
838
    def run(self, filename):
 
839
        # TODO: jam 20050106 Can relpath return a munged path if
 
840
        #       sys.stdout encoding cannot represent it?
 
841
        tree, relpath = WorkingTree.open_containing(filename)
 
842
        self.outf.write(relpath)
 
843
        self.outf.write('\n')
 
844
 
 
845
 
 
846
class cmd_inventory(Command):
 
847
    __doc__ = """Show inventory of the current working copy or a revision.
 
848
 
 
849
    It is possible to limit the output to a particular entry
 
850
    type using the --kind option.  For example: --kind file.
 
851
 
 
852
    It is also possible to restrict the list of files to a specific
 
853
    set. For example: brz inventory --show-ids this/file
 
854
    """
 
855
 
 
856
    hidden = True
 
857
    _see_also = ['ls']
 
858
    takes_options = [
 
859
        'revision',
 
860
        'show-ids',
 
861
        Option('include-root',
 
862
               help='Include the entry for the root of the tree, if any.'),
 
863
        Option('kind',
 
864
               help='List entries of a particular kind: file, directory, '
 
865
                    'symlink.',
 
866
               type=str),
 
867
        ]
 
868
    takes_args = ['file*']
 
869
 
 
870
    @display_command
 
871
    def run(self, revision=None, show_ids=False, kind=None, include_root=False,
 
872
            file_list=None):
 
873
        if kind and kind not in ['file', 'directory', 'symlink']:
 
874
            raise errors.BzrCommandError(
 
875
                gettext('invalid kind %r specified') % (kind,))
 
876
 
 
877
        revision = _get_one_revision('inventory', revision)
 
878
        work_tree, file_list = WorkingTree.open_containing_paths(file_list)
 
879
        self.enter_context(work_tree.lock_read())
 
880
        if revision is not None:
 
881
            tree = revision.as_tree(work_tree.branch)
 
882
 
 
883
            extra_trees = [work_tree]
 
884
            self.enter_context(tree.lock_read())
 
885
        else:
 
886
            tree = work_tree
 
887
            extra_trees = []
 
888
 
 
889
        self.enter_context(tree.lock_read())
 
890
        if file_list is not None:
 
891
            paths = tree.find_related_paths_across_trees(
 
892
                file_list, extra_trees, require_versioned=True)
 
893
            # find_ids_across_trees may include some paths that don't
 
894
            # exist in 'tree'.
 
895
            entries = tree.iter_entries_by_dir(specific_files=paths)
 
896
        else:
 
897
            entries = tree.iter_entries_by_dir()
 
898
 
 
899
        for path, entry in sorted(entries):
 
900
            if kind and kind != entry.kind:
 
901
                continue
 
902
            if path == "" and not include_root:
 
903
                continue
 
904
            if show_ids:
 
905
                self.outf.write('%-50s %s\n' % (
 
906
                    path, entry.file_id.decode('utf-8')))
 
907
            else:
 
908
                self.outf.write(path)
 
909
                self.outf.write('\n')
 
910
 
 
911
 
 
912
class cmd_cp(Command):
 
913
    __doc__ = """Copy a file.
 
914
 
 
915
    :Usage:
 
916
        brz cp OLDNAME NEWNAME
 
917
 
 
918
        brz cp SOURCE... DESTINATION
 
919
 
 
920
    If the last argument is a versioned directory, all the other names
 
921
    are copied into it.  Otherwise, there must be exactly two arguments
 
922
    and the file is copied to a new name.
 
923
 
 
924
    Files cannot be copied between branches. Only files can be copied
 
925
    at the moment.
 
926
    """
 
927
 
 
928
    takes_args = ['names*']
 
929
    takes_options = []
 
930
    aliases = ['copy']
 
931
    encoding_type = 'replace'
 
932
 
 
933
    def run(self, names_list):
 
934
        if names_list is None:
 
935
            names_list = []
 
936
        if len(names_list) < 2:
 
937
            raise errors.BzrCommandError(gettext("missing file argument"))
 
938
        tree, rel_names = WorkingTree.open_containing_paths(
 
939
            names_list, canonicalize=False)
 
940
        for file_name in rel_names[0:-1]:
 
941
            if file_name == '':
 
942
                raise errors.BzrCommandError(
 
943
                    gettext("can not copy root of branch"))
 
944
        self.enter_context(tree.lock_tree_write())
 
945
        into_existing = osutils.isdir(names_list[-1])
 
946
        if not into_existing:
 
947
            try:
 
948
                (src, dst) = rel_names
 
949
            except IndexError:
 
950
                raise errors.BzrCommandError(
 
951
                    gettext('to copy multiple files the'
 
952
                            ' destination must be a versioned'
 
953
                            ' directory'))
 
954
            pairs = [(src, dst)]
 
955
        else:
 
956
            pairs = [
 
957
                (n, osutils.joinpath([rel_names[-1], osutils.basename(n)]))
 
958
                for n in rel_names[:-1]]
 
959
 
 
960
        for src, dst in pairs:
 
961
            try:
 
962
                src_kind = tree.stored_kind(src)
 
963
            except errors.NoSuchFile:
 
964
                raise errors.BzrCommandError(
 
965
                    gettext('Could not copy %s => %s: %s is not versioned.')
 
966
                    % (src, dst, src))
 
967
            if src_kind is None:
 
968
                raise errors.BzrCommandError(
 
969
                    gettext('Could not copy %s => %s . %s is not versioned\\.'
 
970
                            % (src, dst, src)))
 
971
            if src_kind == 'directory':
 
972
                raise errors.BzrCommandError(
 
973
                    gettext('Could not copy %s => %s . %s is a directory.'
 
974
                            % (src, dst, src)))
 
975
            dst_parent = osutils.split(dst)[0]
 
976
            if dst_parent != '':
 
977
                try:
 
978
                    dst_parent_kind = tree.stored_kind(dst_parent)
 
979
                except errors.NoSuchFile:
 
980
                    raise errors.BzrCommandError(
 
981
                        gettext('Could not copy %s => %s: %s is not versioned.')
 
982
                        % (src, dst, dst_parent))
 
983
                if dst_parent_kind != 'directory':
 
984
                    raise errors.BzrCommandError(
 
985
                        gettext('Could not copy to %s: %s is not a directory.')
 
986
                        % (dst_parent, dst_parent))
 
987
 
 
988
            tree.copy_one(src, dst)
 
989
 
 
990
 
 
991
class cmd_mv(Command):
 
992
    __doc__ = """Move or rename a file.
 
993
 
 
994
    :Usage:
 
995
        brz mv OLDNAME NEWNAME
 
996
 
 
997
        brz mv SOURCE... DESTINATION
 
998
 
 
999
    If the last argument is a versioned directory, all the other names
 
1000
    are moved into it.  Otherwise, there must be exactly two arguments
 
1001
    and the file is changed to a new name.
 
1002
 
 
1003
    If OLDNAME does not exist on the filesystem but is versioned and
 
1004
    NEWNAME does exist on the filesystem but is not versioned, mv
 
1005
    assumes that the file has been manually moved and only updates
 
1006
    its internal inventory to reflect that change.
 
1007
    The same is valid when moving many SOURCE files to a DESTINATION.
 
1008
 
 
1009
    Files cannot be moved between branches.
 
1010
    """
 
1011
 
 
1012
    takes_args = ['names*']
 
1013
    takes_options = [Option("after", help="Move only the brz identifier"
 
1014
                            " of the file, because the file has already been moved."),
 
1015
                     Option('auto', help='Automatically guess renames.'),
 
1016
                     Option(
 
1017
                         'dry-run', help='Avoid making changes when guessing renames.'),
 
1018
                     ]
 
1019
    aliases = ['move', 'rename']
 
1020
    encoding_type = 'replace'
 
1021
 
 
1022
    def run(self, names_list, after=False, auto=False, dry_run=False):
 
1023
        if auto:
 
1024
            return self.run_auto(names_list, after, dry_run)
 
1025
        elif dry_run:
 
1026
            raise errors.BzrCommandError(gettext('--dry-run requires --auto.'))
 
1027
        if names_list is None:
 
1028
            names_list = []
 
1029
        if len(names_list) < 2:
 
1030
            raise errors.BzrCommandError(gettext("missing file argument"))
 
1031
        tree, rel_names = WorkingTree.open_containing_paths(
 
1032
            names_list, canonicalize=False)
 
1033
        for file_name in rel_names[0:-1]:
 
1034
            if file_name == '':
 
1035
                raise errors.BzrCommandError(
 
1036
                    gettext("can not move root of branch"))
 
1037
        self.enter_context(tree.lock_tree_write())
 
1038
        self._run(tree, names_list, rel_names, after)
 
1039
 
 
1040
    def run_auto(self, names_list, after, dry_run):
 
1041
        if names_list is not None and len(names_list) > 1:
 
1042
            raise errors.BzrCommandError(
 
1043
                gettext('Only one path may be specified to --auto.'))
 
1044
        if after:
 
1045
            raise errors.BzrCommandError(
 
1046
                gettext('--after cannot be specified with --auto.'))
 
1047
        work_tree, file_list = WorkingTree.open_containing_paths(
 
1048
            names_list, default_directory='.')
 
1049
        self.enter_context(work_tree.lock_tree_write())
 
1050
        rename_map.RenameMap.guess_renames(
 
1051
            work_tree.basis_tree(), work_tree, dry_run)
 
1052
 
 
1053
    def _run(self, tree, names_list, rel_names, after):
 
1054
        into_existing = osutils.isdir(names_list[-1])
 
1055
        if into_existing and len(names_list) == 2:
 
1056
            # special cases:
 
1057
            # a. case-insensitive filesystem and change case of dir
 
1058
            # b. move directory after the fact (if the source used to be
 
1059
            #    a directory, but now doesn't exist in the working tree
 
1060
            #    and the target is an existing directory, just rename it)
 
1061
            if (not tree.case_sensitive
 
1062
                    and rel_names[0].lower() == rel_names[1].lower()):
 
1063
                into_existing = False
 
1064
            else:
 
1065
                # 'fix' the case of a potential 'from'
 
1066
                from_path = tree.get_canonical_path(rel_names[0])
 
1067
                if (not osutils.lexists(names_list[0]) and
 
1068
                    tree.is_versioned(from_path) and
 
1069
                        tree.stored_kind(from_path) == "directory"):
 
1070
                    into_existing = False
 
1071
        # move/rename
 
1072
        if into_existing:
 
1073
            # move into existing directory
 
1074
            # All entries reference existing inventory items, so fix them up
 
1075
            # for cicp file-systems.
 
1076
            rel_names = list(tree.get_canonical_paths(rel_names))
 
1077
            for src, dest in tree.move(rel_names[:-1], rel_names[-1], after=after):
 
1078
                if not is_quiet():
 
1079
                    self.outf.write("%s => %s\n" % (src, dest))
 
1080
        else:
 
1081
            if len(names_list) != 2:
 
1082
                raise errors.BzrCommandError(gettext('to mv multiple files the'
 
1083
                                                     ' destination must be a versioned'
 
1084
                                                     ' directory'))
 
1085
 
 
1086
            # for cicp file-systems: the src references an existing inventory
 
1087
            # item:
 
1088
            src = tree.get_canonical_path(rel_names[0])
 
1089
            # Find the canonical version of the destination:  In all cases, the
 
1090
            # parent of the target must be in the inventory, so we fetch the
 
1091
            # canonical version from there (we do not always *use* the
 
1092
            # canonicalized tail portion - we may be attempting to rename the
 
1093
            # case of the tail)
 
1094
            canon_dest = tree.get_canonical_path(rel_names[1])
 
1095
            dest_parent = osutils.dirname(canon_dest)
 
1096
            spec_tail = osutils.basename(rel_names[1])
 
1097
            # For a CICP file-system, we need to avoid creating 2 inventory
 
1098
            # entries that differ only by case.  So regardless of the case
 
1099
            # we *want* to use (ie, specified by the user or the file-system),
 
1100
            # we must always choose to use the case of any existing inventory
 
1101
            # items.  The only exception to this is when we are attempting a
 
1102
            # case-only rename (ie, canonical versions of src and dest are
 
1103
            # the same)
 
1104
            dest_id = tree.path2id(canon_dest)
 
1105
            if dest_id is None or tree.path2id(src) == dest_id:
 
1106
                # No existing item we care about, so work out what case we
 
1107
                # are actually going to use.
 
1108
                if after:
 
1109
                    # If 'after' is specified, the tail must refer to a file on disk.
 
1110
                    if dest_parent:
 
1111
                        dest_parent_fq = osutils.pathjoin(
 
1112
                            tree.basedir, dest_parent)
 
1113
                    else:
 
1114
                        # pathjoin with an empty tail adds a slash, which breaks
 
1115
                        # relpath :(
 
1116
                        dest_parent_fq = tree.basedir
 
1117
 
 
1118
                    dest_tail = osutils.canonical_relpath(
 
1119
                        dest_parent_fq,
 
1120
                        osutils.pathjoin(dest_parent_fq, spec_tail))
 
1121
                else:
 
1122
                    # not 'after', so case as specified is used
 
1123
                    dest_tail = spec_tail
 
1124
            else:
 
1125
                # Use the existing item so 'mv' fails with AlreadyVersioned.
 
1126
                dest_tail = os.path.basename(canon_dest)
 
1127
            dest = osutils.pathjoin(dest_parent, dest_tail)
 
1128
            mutter("attempting to move %s => %s", src, dest)
 
1129
            tree.rename_one(src, dest, after=after)
 
1130
            if not is_quiet():
 
1131
                self.outf.write("%s => %s\n" % (src, dest))
 
1132
 
 
1133
 
 
1134
class cmd_pull(Command):
 
1135
    __doc__ = """Turn this branch into a mirror of another branch.
 
1136
 
 
1137
    By default, this command only works on branches that have not diverged.
 
1138
    Branches are considered diverged if the destination branch's most recent
 
1139
    commit is one that has not been merged (directly or indirectly) into the
 
1140
    parent.
 
1141
 
 
1142
    If branches have diverged, you can use 'brz merge' to integrate the changes
 
1143
    from one into the other.  Once one branch has merged, the other should
 
1144
    be able to pull it again.
 
1145
 
 
1146
    If you want to replace your local changes and just want your branch to
 
1147
    match the remote one, use pull --overwrite. This will work even if the two
 
1148
    branches have diverged.
 
1149
 
 
1150
    If there is no default location set, the first pull will set it (use
 
1151
    --no-remember to avoid setting it). After that, you can omit the
 
1152
    location to use the default.  To change the default, use --remember. The
 
1153
    value will only be saved if the remote location can be accessed.
 
1154
 
 
1155
    The --verbose option will display the revisions pulled using the log_format
 
1156
    configuration option. You can use a different format by overriding it with
 
1157
    -Olog_format=<other_format>.
 
1158
 
 
1159
    Note: The location can be specified either in the form of a branch,
 
1160
    or in the form of a path to a file containing a merge directive generated
 
1161
    with brz send.
 
1162
    """
 
1163
 
 
1164
    _see_also = ['push', 'update', 'status-flags', 'send']
 
1165
    takes_options = ['remember', 'overwrite', 'revision',
 
1166
                     custom_help('verbose',
 
1167
                                 help='Show logs of pulled revisions.'),
 
1168
                     custom_help('directory',
 
1169
                                 help='Branch to pull into, '
 
1170
                                 'rather than the one containing the working directory.'),
 
1171
                     Option('local',
 
1172
                            help="Perform a local pull in a bound "
 
1173
                            "branch.  Local pulls are not applied to "
 
1174
                            "the master branch."
 
1175
                            ),
 
1176
                     Option('show-base',
 
1177
                            help="Show base revision text in conflicts."),
 
1178
                     Option('overwrite-tags',
 
1179
                            help="Overwrite tags only."),
 
1180
                     ]
 
1181
    takes_args = ['location?']
 
1182
    encoding_type = 'replace'
 
1183
 
 
1184
    def run(self, location=None, remember=None, overwrite=False,
 
1185
            revision=None, verbose=False,
 
1186
            directory=None, local=False,
 
1187
            show_base=False, overwrite_tags=False):
 
1188
 
 
1189
        if overwrite:
 
1190
            overwrite = ["history", "tags"]
 
1191
        elif overwrite_tags:
 
1192
            overwrite = ["tags"]
 
1193
        else:
 
1194
            overwrite = []
 
1195
        # FIXME: too much stuff is in the command class
 
1196
        revision_id = None
 
1197
        mergeable = None
 
1198
        if directory is None:
 
1199
            directory = u'.'
 
1200
        try:
 
1201
            tree_to = WorkingTree.open_containing(directory)[0]
 
1202
            branch_to = tree_to.branch
 
1203
            self.enter_context(tree_to.lock_write())
 
1204
        except errors.NoWorkingTree:
 
1205
            tree_to = None
 
1206
            branch_to = Branch.open_containing(directory)[0]
 
1207
            self.enter_context(branch_to.lock_write())
 
1208
            if show_base:
 
1209
                warning(gettext("No working tree, ignoring --show-base"))
 
1210
 
 
1211
        if local and not branch_to.get_bound_location():
 
1212
            raise errors.LocalRequiresBoundBranch()
 
1213
 
 
1214
        possible_transports = []
 
1215
        if location is not None:
 
1216
            try:
 
1217
                mergeable = _mod_mergeable.read_mergeable_from_url(
 
1218
                    location, possible_transports=possible_transports)
 
1219
            except errors.NotABundle:
 
1220
                mergeable = None
 
1221
 
 
1222
        stored_loc = branch_to.get_parent()
 
1223
        if location is None:
 
1224
            if stored_loc is None:
 
1225
                raise errors.BzrCommandError(gettext("No pull location known or"
 
1226
                                                     " specified."))
 
1227
            else:
 
1228
                display_url = urlutils.unescape_for_display(stored_loc,
 
1229
                                                            self.outf.encoding)
 
1230
                if not is_quiet():
 
1231
                    self.outf.write(
 
1232
                        gettext("Using saved parent location: %s\n") % display_url)
 
1233
                location = stored_loc
 
1234
 
 
1235
        revision = _get_one_revision('pull', revision)
 
1236
        if mergeable is not None:
 
1237
            if revision is not None:
 
1238
                raise errors.BzrCommandError(gettext(
 
1239
                    'Cannot use -r with merge directives or bundles'))
 
1240
            mergeable.install_revisions(branch_to.repository)
 
1241
            base_revision_id, revision_id, verified = \
 
1242
                mergeable.get_merge_request(branch_to.repository)
 
1243
            branch_from = branch_to
 
1244
        else:
 
1245
            branch_from = Branch.open(location,
 
1246
                                      possible_transports=possible_transports)
 
1247
            self.enter_context(branch_from.lock_read())
 
1248
            # Remembers if asked explicitly or no previous location is set
 
1249
            if (remember
 
1250
                    or (remember is None and branch_to.get_parent() is None)):
 
1251
                # FIXME: This shouldn't be done before the pull
 
1252
                # succeeds... -- vila 2012-01-02
 
1253
                branch_to.set_parent(branch_from.base)
 
1254
 
 
1255
        if revision is not None:
 
1256
            revision_id = revision.as_revision_id(branch_from)
 
1257
 
 
1258
        if tree_to is not None:
 
1259
            view_info = _get_view_info_for_change_reporter(tree_to)
 
1260
            change_reporter = delta._ChangeReporter(
 
1261
                unversioned_filter=tree_to.is_ignored,
 
1262
                view_info=view_info)
 
1263
            result = tree_to.pull(
 
1264
                branch_from, overwrite, revision_id, change_reporter,
 
1265
                local=local, show_base=show_base)
 
1266
        else:
 
1267
            result = branch_to.pull(
 
1268
                branch_from, overwrite, revision_id, local=local)
 
1269
 
 
1270
        result.report(self.outf)
 
1271
        if verbose and result.old_revid != result.new_revid:
 
1272
            log.show_branch_change(
 
1273
                branch_to, self.outf, result.old_revno,
 
1274
                result.old_revid)
 
1275
        if getattr(result, 'tag_conflicts', None):
 
1276
            return 1
 
1277
        else:
 
1278
            return 0
 
1279
 
 
1280
 
 
1281
class cmd_push(Command):
 
1282
    __doc__ = """Update a mirror of this branch.
 
1283
 
 
1284
    The target branch will not have its working tree populated because this
 
1285
    is both expensive, and is not supported on remote file systems.
 
1286
 
 
1287
    Some smart servers or protocols *may* put the working tree in place in
 
1288
    the future.
 
1289
 
 
1290
    This command only works on branches that have not diverged.  Branches are
 
1291
    considered diverged if the destination branch's most recent commit is one
 
1292
    that has not been merged (directly or indirectly) by the source branch.
 
1293
 
 
1294
    If branches have diverged, you can use 'brz push --overwrite' to replace
 
1295
    the other branch completely, discarding its unmerged changes.
 
1296
 
 
1297
    If you want to ensure you have the different changes in the other branch,
 
1298
    do a merge (see brz help merge) from the other branch, and commit that.
 
1299
    After that you will be able to do a push without '--overwrite'.
 
1300
 
 
1301
    If there is no default push location set, the first push will set it (use
 
1302
    --no-remember to avoid setting it).  After that, you can omit the
 
1303
    location to use the default.  To change the default, use --remember. The
 
1304
    value will only be saved if the remote location can be accessed.
 
1305
 
 
1306
    The --verbose option will display the revisions pushed using the log_format
 
1307
    configuration option. You can use a different format by overriding it with
 
1308
    -Olog_format=<other_format>.
 
1309
    """
 
1310
 
 
1311
    _see_also = ['pull', 'update', 'working-trees']
 
1312
    takes_options = ['remember', 'overwrite', 'verbose', 'revision',
 
1313
                     Option('create-prefix',
 
1314
                            help='Create the path leading up to the branch '
 
1315
                            'if it does not already exist.'),
 
1316
                     custom_help('directory',
 
1317
                                 help='Branch to push from, '
 
1318
                                 'rather than the one containing the working directory.'),
 
1319
                     Option('use-existing-dir',
 
1320
                            help='By default push will fail if the target'
 
1321
                            ' directory exists, but does not already'
 
1322
                            ' have a control directory.  This flag will'
 
1323
                            ' allow push to proceed.'),
 
1324
                     Option('stacked',
 
1325
                            help='Create a stacked branch that references the public location '
 
1326
                            'of the parent branch.'),
 
1327
                     Option('stacked-on',
 
1328
                            help='Create a stacked branch that refers to another branch '
 
1329
                            'for the commit history. Only the work not present in the '
 
1330
                            'referenced branch is included in the branch created.',
 
1331
                            type=str),
 
1332
                     Option('strict',
 
1333
                            help='Refuse to push if there are uncommitted changes in'
 
1334
                            ' the working tree, --no-strict disables the check.'),
 
1335
                     Option('no-tree',
 
1336
                            help="Don't populate the working tree, even for protocols"
 
1337
                            " that support it."),
 
1338
                     Option('overwrite-tags',
 
1339
                            help="Overwrite tags only."),
 
1340
                     Option('lossy', help="Allow lossy push, i.e. dropping metadata "
 
1341
                            "that can't be represented in the target.")
 
1342
                     ]
 
1343
    takes_args = ['location?']
 
1344
    encoding_type = 'replace'
 
1345
 
 
1346
    def run(self, location=None, remember=None, overwrite=False,
 
1347
            create_prefix=False, verbose=False, revision=None,
 
1348
            use_existing_dir=False, directory=None, stacked_on=None,
 
1349
            stacked=False, strict=None, no_tree=False,
 
1350
            overwrite_tags=False, lossy=False):
 
1351
        from .location import location_to_url
 
1352
        from .push import _show_push_branch
 
1353
 
 
1354
        if overwrite:
 
1355
            overwrite = ["history", "tags"]
 
1356
        elif overwrite_tags:
 
1357
            overwrite = ["tags"]
 
1358
        else:
 
1359
            overwrite = []
 
1360
 
 
1361
        if directory is None:
 
1362
            directory = '.'
 
1363
        # Get the source branch
 
1364
        (tree, br_from,
 
1365
         _unused) = controldir.ControlDir.open_containing_tree_or_branch(directory)
 
1366
        # Get the tip's revision_id
 
1367
        revision = _get_one_revision('push', revision)
 
1368
        if revision is not None:
 
1369
            revision_id = revision.in_history(br_from).rev_id
 
1370
        else:
 
1371
            revision_id = None
 
1372
        if tree is not None and revision_id is None:
 
1373
            tree.check_changed_or_out_of_date(
 
1374
                strict, 'push_strict',
 
1375
                more_error='Use --no-strict to force the push.',
 
1376
                more_warning='Uncommitted changes will not be pushed.')
 
1377
        # Get the stacked_on branch, if any
 
1378
        if stacked_on is not None:
 
1379
            stacked_on = location_to_url(stacked_on, 'read')
 
1380
            stacked_on = urlutils.normalize_url(stacked_on)
 
1381
        elif stacked:
 
1382
            parent_url = br_from.get_parent()
 
1383
            if parent_url:
 
1384
                parent = Branch.open(parent_url)
 
1385
                stacked_on = parent.get_public_branch()
 
1386
                if not stacked_on:
 
1387
                    # I considered excluding non-http url's here, thus forcing
 
1388
                    # 'public' branches only, but that only works for some
 
1389
                    # users, so it's best to just depend on the user spotting an
 
1390
                    # error by the feedback given to them. RBC 20080227.
 
1391
                    stacked_on = parent_url
 
1392
            if not stacked_on:
 
1393
                raise errors.BzrCommandError(gettext(
 
1394
                    "Could not determine branch to refer to."))
 
1395
 
 
1396
        # Get the destination location
 
1397
        if location is None:
 
1398
            stored_loc = br_from.get_push_location()
 
1399
            if stored_loc is None:
 
1400
                parent_loc = br_from.get_parent()
 
1401
                if parent_loc:
 
1402
                    raise errors.BzrCommandError(gettext(
 
1403
                        "No push location known or specified. To push to the "
 
1404
                        "parent branch (at %s), use 'brz push :parent'." %
 
1405
                        urlutils.unescape_for_display(parent_loc,
 
1406
                                                      self.outf.encoding)))
 
1407
                else:
 
1408
                    raise errors.BzrCommandError(gettext(
 
1409
                        "No push location known or specified."))
 
1410
            else:
 
1411
                display_url = urlutils.unescape_for_display(stored_loc,
 
1412
                                                            self.outf.encoding)
 
1413
                note(gettext("Using saved push location: %s") % display_url)
 
1414
                location = stored_loc
 
1415
 
 
1416
        _show_push_branch(br_from, revision_id, location, self.outf,
 
1417
                          verbose=verbose, overwrite=overwrite, remember=remember,
 
1418
                          stacked_on=stacked_on, create_prefix=create_prefix,
 
1419
                          use_existing_dir=use_existing_dir, no_tree=no_tree,
 
1420
                          lossy=lossy)
 
1421
 
 
1422
 
 
1423
class cmd_branch(Command):
 
1424
    __doc__ = """Create a new branch that is a copy of an existing branch.
 
1425
 
 
1426
    If the TO_LOCATION is omitted, the last component of the FROM_LOCATION will
 
1427
    be used.  In other words, "branch ../foo/bar" will attempt to create ./bar.
 
1428
    If the FROM_LOCATION has no / or path separator embedded, the TO_LOCATION
 
1429
    is derived from the FROM_LOCATION by stripping a leading scheme or drive
 
1430
    identifier, if any. For example, "branch lp:foo-bar" will attempt to
 
1431
    create ./foo-bar.
 
1432
 
 
1433
    To retrieve the branch as of a particular revision, supply the --revision
 
1434
    parameter, as in "branch foo/bar -r 5".
 
1435
    """
 
1436
 
 
1437
    _see_also = ['checkout']
 
1438
    takes_args = ['from_location', 'to_location?']
 
1439
    takes_options = ['revision',
 
1440
                     Option(
 
1441
                         'hardlink', help='Hard-link working tree files where possible.'),
 
1442
                     Option('files-from', type=str,
 
1443
                            help="Get file contents from this tree."),
 
1444
                     Option('no-tree',
 
1445
                            help="Create a branch without a working-tree."),
 
1446
                     Option('switch',
 
1447
                            help="Switch the checkout in the current directory "
 
1448
                            "to the new branch."),
 
1449
                     Option('stacked',
 
1450
                            help='Create a stacked branch referring to the source branch. '
 
1451
                            'The new branch will depend on the availability of the source '
 
1452
                            'branch for all operations.'),
 
1453
                     Option('standalone',
 
1454
                            help='Do not use a shared repository, even if available.'),
 
1455
                     Option('use-existing-dir',
 
1456
                            help='By default branch will fail if the target'
 
1457
                            ' directory exists, but does not already'
 
1458
                            ' have a control directory.  This flag will'
 
1459
                            ' allow branch to proceed.'),
 
1460
                     Option('bind',
 
1461
                            help="Bind new branch to from location."),
 
1462
                     Option('no-recurse-nested',
 
1463
                            help='Do not recursively check out nested trees.'),
 
1464
                     ]
 
1465
 
 
1466
    def run(self, from_location, to_location=None, revision=None,
 
1467
            hardlink=False, stacked=False, standalone=False, no_tree=False,
 
1468
            use_existing_dir=False, switch=False, bind=False,
 
1469
            files_from=None, no_recurse_nested=False):
 
1470
        from breezy import switch as _mod_switch
 
1471
        accelerator_tree, br_from = controldir.ControlDir.open_tree_or_branch(
 
1472
            from_location)
 
1473
        if no_recurse_nested:
 
1474
            recurse = 'none'
 
1475
        else:
 
1476
            recurse = 'down'
 
1477
        if not (hardlink or files_from):
 
1478
            # accelerator_tree is usually slower because you have to read N
 
1479
            # files (no readahead, lots of seeks, etc), but allow the user to
 
1480
            # explicitly request it
 
1481
            accelerator_tree = None
 
1482
        if files_from is not None and files_from != from_location:
 
1483
            accelerator_tree = WorkingTree.open(files_from)
 
1484
        revision = _get_one_revision('branch', revision)
 
1485
        self.enter_context(br_from.lock_read())
 
1486
        if revision is not None:
 
1487
            revision_id = revision.as_revision_id(br_from)
 
1488
        else:
 
1489
            # FIXME - wt.last_revision, fallback to branch, fall back to
 
1490
            # None or perhaps NULL_REVISION to mean copy nothing
 
1491
            # RBC 20060209
 
1492
            revision_id = br_from.last_revision()
 
1493
        if to_location is None:
 
1494
            to_location = urlutils.derive_to_location(from_location)
 
1495
        to_transport = transport.get_transport(to_location, purpose='write')
 
1496
        try:
 
1497
            to_transport.mkdir('.')
 
1498
        except errors.FileExists:
 
1499
            try:
 
1500
                to_dir = controldir.ControlDir.open_from_transport(
 
1501
                    to_transport)
 
1502
            except errors.NotBranchError:
 
1503
                if not use_existing_dir:
 
1504
                    raise errors.BzrCommandError(gettext('Target directory "%s" '
 
1505
                                                         'already exists.') % to_location)
 
1506
                else:
 
1507
                    to_dir = None
 
1508
            else:
 
1509
                try:
 
1510
                    to_dir.open_branch()
 
1511
                except errors.NotBranchError:
 
1512
                    pass
 
1513
                else:
 
1514
                    raise errors.AlreadyBranchError(to_location)
 
1515
        except errors.NoSuchFile:
 
1516
            raise errors.BzrCommandError(gettext('Parent of "%s" does not exist.')
 
1517
                                         % to_location)
 
1518
        else:
 
1519
            to_dir = None
 
1520
        if to_dir is None:
 
1521
            try:
 
1522
                # preserve whatever source format we have.
 
1523
                to_dir = br_from.controldir.sprout(
 
1524
                    to_transport.base, revision_id,
 
1525
                    possible_transports=[to_transport],
 
1526
                    accelerator_tree=accelerator_tree, hardlink=hardlink,
 
1527
                    stacked=stacked, force_new_repo=standalone,
 
1528
                    create_tree_if_local=not no_tree, source_branch=br_from,
 
1529
                    recurse=recurse)
 
1530
                branch = to_dir.open_branch(
 
1531
                    possible_transports=[
 
1532
                        br_from.controldir.root_transport, to_transport])
 
1533
            except errors.NoSuchRevision:
 
1534
                to_transport.delete_tree('.')
 
1535
                msg = gettext("The branch {0} has no revision {1}.").format(
 
1536
                    from_location, revision)
 
1537
                raise errors.BzrCommandError(msg)
 
1538
        else:
 
1539
            try:
 
1540
                to_repo = to_dir.open_repository()
 
1541
            except errors.NoRepositoryPresent:
 
1542
                to_repo = to_dir.create_repository()
 
1543
            to_repo.fetch(br_from.repository, revision_id=revision_id)
 
1544
            branch = br_from.sprout(
 
1545
                to_dir, revision_id=revision_id)
 
1546
        br_from.tags.merge_to(branch.tags)
 
1547
 
 
1548
        # If the source branch is stacked, the new branch may
 
1549
        # be stacked whether we asked for that explicitly or not.
 
1550
        # We therefore need a try/except here and not just 'if stacked:'
 
1551
        try:
 
1552
            note(gettext('Created new stacked branch referring to %s.') %
 
1553
                 branch.get_stacked_on_url())
 
1554
        except (errors.NotStacked, _mod_branch.UnstackableBranchFormat,
 
1555
                errors.UnstackableRepositoryFormat) as e:
 
1556
            revno = branch.revno()
 
1557
            if revno is not None:
 
1558
                note(ngettext('Branched %d revision.',
 
1559
                              'Branched %d revisions.',
 
1560
                              branch.revno()) % revno)
 
1561
            else:
 
1562
                note(gettext('Created new branch.'))
 
1563
        if bind:
 
1564
            # Bind to the parent
 
1565
            parent_branch = Branch.open(from_location)
 
1566
            branch.bind(parent_branch)
 
1567
            note(gettext('New branch bound to %s') % from_location)
 
1568
        if switch:
 
1569
            # Switch to the new branch
 
1570
            wt, _ = WorkingTree.open_containing('.')
 
1571
            _mod_switch.switch(wt.controldir, branch)
 
1572
            note(gettext('Switched to branch: %s'),
 
1573
                 urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1574
 
 
1575
 
 
1576
class cmd_branches(Command):
 
1577
    __doc__ = """List the branches available at the current location.
 
1578
 
 
1579
    This command will print the names of all the branches at the current
 
1580
    location.
 
1581
    """
 
1582
 
 
1583
    takes_args = ['location?']
 
1584
    takes_options = [
 
1585
        Option('recursive', short_name='R',
 
1586
               help='Recursively scan for branches rather than '
 
1587
               'just looking in the specified location.')]
 
1588
 
 
1589
    def run(self, location=".", recursive=False):
 
1590
        if recursive:
 
1591
            t = transport.get_transport(location, purpose='read')
 
1592
            if not t.listable():
 
1593
                raise errors.BzrCommandError(
 
1594
                    "Can't scan this type of location.")
 
1595
            for b in controldir.ControlDir.find_branches(t):
 
1596
                self.outf.write("%s\n" % urlutils.unescape_for_display(
 
1597
                    urlutils.relative_url(t.base, b.base),
 
1598
                    self.outf.encoding).rstrip("/"))
 
1599
        else:
 
1600
            dir = controldir.ControlDir.open_containing(location)[0]
 
1601
            try:
 
1602
                active_branch = dir.open_branch(name="")
 
1603
            except errors.NotBranchError:
 
1604
                active_branch = None
 
1605
            names = {}
 
1606
            for name, branch in iter_sibling_branches(dir):
 
1607
                if name == "":
 
1608
                    continue
 
1609
                active = (active_branch is not None and
 
1610
                          active_branch.user_url == branch.user_url)
 
1611
                names[name] = active
 
1612
            # Only mention the current branch explicitly if it's not
 
1613
            # one of the colocated branches
 
1614
            if not any(names.values()) and active_branch is not None:
 
1615
                self.outf.write("* %s\n" % gettext("(default)"))
 
1616
            for name in sorted(names):
 
1617
                active = names[name]
 
1618
                if active:
 
1619
                    prefix = "*"
 
1620
                else:
 
1621
                    prefix = " "
 
1622
                self.outf.write("%s %s\n" % (prefix, name))
 
1623
 
 
1624
 
 
1625
class cmd_checkout(Command):
 
1626
    __doc__ = """Create a new checkout of an existing branch.
 
1627
 
 
1628
    If BRANCH_LOCATION is omitted, checkout will reconstitute a working tree
 
1629
    for the branch found in '.'. This is useful if you have removed the working
 
1630
    tree or if it was never created - i.e. if you pushed the branch to its
 
1631
    current location using SFTP.
 
1632
 
 
1633
    If the TO_LOCATION is omitted, the last component of the BRANCH_LOCATION
 
1634
    will be used.  In other words, "checkout ../foo/bar" will attempt to create
 
1635
    ./bar.  If the BRANCH_LOCATION has no / or path separator embedded, the
 
1636
    TO_LOCATION is derived from the BRANCH_LOCATION by stripping a leading
 
1637
    scheme or drive identifier, if any. For example, "checkout lp:foo-bar" will
 
1638
    attempt to create ./foo-bar.
 
1639
 
 
1640
    To retrieve the branch as of a particular revision, supply the --revision
 
1641
    parameter, as in "checkout foo/bar -r 5". Note that this will be
 
1642
    immediately out of date [so you cannot commit] but it may be useful (i.e.
 
1643
    to examine old code.)
 
1644
    """
 
1645
 
 
1646
    _see_also = ['checkouts', 'branch', 'working-trees', 'remove-tree']
 
1647
    takes_args = ['branch_location?', 'to_location?']
 
1648
    takes_options = ['revision',
 
1649
                     Option('lightweight',
 
1650
                            help="Perform a lightweight checkout.  Lightweight "
 
1651
                                 "checkouts depend on access to the branch for "
 
1652
                                 "every operation.  Normal checkouts can perform "
 
1653
                                 "common operations like diff and status without "
 
1654
                                 "such access, and also support local commits."
 
1655
                            ),
 
1656
                     Option('files-from', type=str,
 
1657
                            help="Get file contents from this tree."),
 
1658
                     Option('hardlink',
 
1659
                            help='Hard-link working tree files where possible.'
 
1660
                            ),
 
1661
                     ]
 
1662
    aliases = ['co']
 
1663
 
 
1664
    def run(self, branch_location=None, to_location=None, revision=None,
 
1665
            lightweight=False, files_from=None, hardlink=False):
 
1666
        if branch_location is None:
 
1667
            branch_location = osutils.getcwd()
 
1668
            to_location = branch_location
 
1669
        accelerator_tree, source = controldir.ControlDir.open_tree_or_branch(
 
1670
            branch_location)
 
1671
        if not (hardlink or files_from):
 
1672
            # accelerator_tree is usually slower because you have to read N
 
1673
            # files (no readahead, lots of seeks, etc), but allow the user to
 
1674
            # explicitly request it
 
1675
            accelerator_tree = None
 
1676
        revision = _get_one_revision('checkout', revision)
 
1677
        if files_from is not None and files_from != branch_location:
 
1678
            accelerator_tree = WorkingTree.open(files_from)
 
1679
        if revision is not None:
 
1680
            revision_id = revision.as_revision_id(source)
 
1681
        else:
 
1682
            revision_id = None
 
1683
        if to_location is None:
 
1684
            to_location = urlutils.derive_to_location(branch_location)
 
1685
        # if the source and to_location are the same,
 
1686
        # and there is no working tree,
 
1687
        # then reconstitute a branch
 
1688
        if osutils.abspath(to_location) == osutils.abspath(branch_location):
 
1689
            try:
 
1690
                source.controldir.open_workingtree()
 
1691
            except errors.NoWorkingTree:
 
1692
                source.controldir.create_workingtree(revision_id)
 
1693
                return
 
1694
        source.create_checkout(to_location, revision_id, lightweight,
 
1695
                               accelerator_tree, hardlink)
 
1696
 
 
1697
 
 
1698
class cmd_renames(Command):
 
1699
    __doc__ = """Show list of renamed files.
 
1700
    """
 
1701
    # TODO: Option to show renames between two historical versions.
 
1702
 
 
1703
    # TODO: Only show renames under dir, rather than in the whole branch.
 
1704
    _see_also = ['status']
 
1705
    takes_args = ['dir?']
 
1706
 
 
1707
    @display_command
 
1708
    def run(self, dir=u'.'):
 
1709
        tree = WorkingTree.open_containing(dir)[0]
 
1710
        self.enter_context(tree.lock_read())
 
1711
        old_tree = tree.basis_tree()
 
1712
        self.enter_context(old_tree.lock_read())
 
1713
        renames = []
 
1714
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
 
1715
        for change in iterator:
 
1716
            if change.path[0] == change.path[1]:
 
1717
                continue
 
1718
            if None in change.path:
 
1719
                continue
 
1720
            renames.append(change.path)
 
1721
        renames.sort()
 
1722
        for old_name, new_name in renames:
 
1723
            self.outf.write("%s => %s\n" % (old_name, new_name))
 
1724
 
 
1725
 
 
1726
class cmd_update(Command):
 
1727
    __doc__ = """Update a working tree to a new revision.
 
1728
 
 
1729
    This will perform a merge of the destination revision (the tip of the
 
1730
    branch, or the specified revision) into the working tree, and then make
 
1731
    that revision the basis revision for the working tree.
 
1732
 
 
1733
    You can use this to visit an older revision, or to update a working tree
 
1734
    that is out of date from its branch.
 
1735
 
 
1736
    If there are any uncommitted changes in the tree, they will be carried
 
1737
    across and remain as uncommitted changes after the update.  To discard
 
1738
    these changes, use 'brz revert'.  The uncommitted changes may conflict
 
1739
    with the changes brought in by the change in basis revision.
 
1740
 
 
1741
    If the tree's branch is bound to a master branch, brz will also update
 
1742
    the branch from the master.
 
1743
 
 
1744
    You cannot update just a single file or directory, because each Breezy
 
1745
    working tree has just a single basis revision.  If you want to restore a
 
1746
    file that has been removed locally, use 'brz revert' instead of 'brz
 
1747
    update'.  If you want to restore a file to its state in a previous
 
1748
    revision, use 'brz revert' with a '-r' option, or use 'brz cat' to write
 
1749
    out the old content of that file to a new location.
 
1750
 
 
1751
    The 'dir' argument, if given, must be the location of the root of a
 
1752
    working tree to update.  By default, the working tree that contains the
 
1753
    current working directory is used.
 
1754
    """
 
1755
 
 
1756
    _see_also = ['pull', 'working-trees', 'status-flags']
 
1757
    takes_args = ['dir?']
 
1758
    takes_options = ['revision',
 
1759
                     Option('show-base',
 
1760
                            help="Show base revision text in conflicts."),
 
1761
                     ]
 
1762
    aliases = ['up']
 
1763
 
 
1764
    def run(self, dir=None, revision=None, show_base=None):
 
1765
        if revision is not None and len(revision) != 1:
 
1766
            raise errors.BzrCommandError(gettext(
 
1767
                "brz update --revision takes exactly one revision"))
 
1768
        if dir is None:
 
1769
            tree = WorkingTree.open_containing('.')[0]
 
1770
        else:
 
1771
            tree, relpath = WorkingTree.open_containing(dir)
 
1772
            if relpath:
 
1773
                # See bug 557886.
 
1774
                raise errors.BzrCommandError(gettext(
 
1775
                    "brz update can only update a whole tree, "
 
1776
                    "not a file or subdirectory"))
 
1777
        branch = tree.branch
 
1778
        possible_transports = []
 
1779
        master = branch.get_master_branch(
 
1780
            possible_transports=possible_transports)
 
1781
        if master is not None:
 
1782
            branch_location = master.base
 
1783
            self.enter_context(tree.lock_write())
 
1784
        else:
 
1785
            branch_location = tree.branch.base
 
1786
            self.enter_context(tree.lock_tree_write())
 
1787
        # get rid of the final '/' and be ready for display
 
1788
        branch_location = urlutils.unescape_for_display(
 
1789
            branch_location.rstrip('/'),
 
1790
            self.outf.encoding)
 
1791
        existing_pending_merges = tree.get_parent_ids()[1:]
 
1792
        if master is None:
 
1793
            old_tip = None
 
1794
        else:
 
1795
            # may need to fetch data into a heavyweight checkout
 
1796
            # XXX: this may take some time, maybe we should display a
 
1797
            # message
 
1798
            old_tip = branch.update(possible_transports)
 
1799
        if revision is not None:
 
1800
            revision_id = revision[0].as_revision_id(branch)
 
1801
        else:
 
1802
            revision_id = branch.last_revision()
 
1803
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
 
1804
            revno = branch.revision_id_to_dotted_revno(revision_id)
 
1805
            note(gettext("Tree is up to date at revision {0} of branch {1}"
 
1806
                         ).format('.'.join(map(str, revno)), branch_location))
 
1807
            return 0
 
1808
        view_info = _get_view_info_for_change_reporter(tree)
 
1809
        change_reporter = delta._ChangeReporter(
 
1810
            unversioned_filter=tree.is_ignored,
 
1811
            view_info=view_info)
 
1812
        try:
 
1813
            conflicts = tree.update(
 
1814
                change_reporter,
 
1815
                possible_transports=possible_transports,
 
1816
                revision=revision_id,
 
1817
                old_tip=old_tip,
 
1818
                show_base=show_base)
 
1819
        except errors.NoSuchRevision as e:
 
1820
            raise errors.BzrCommandError(gettext(
 
1821
                "branch has no revision %s\n"
 
1822
                "brz update --revision only works"
 
1823
                " for a revision in the branch history")
 
1824
                % (e.revision))
 
1825
        revno = tree.branch.revision_id_to_dotted_revno(
 
1826
            _mod_revision.ensure_null(tree.last_revision()))
 
1827
        note(gettext('Updated to revision {0} of branch {1}').format(
 
1828
             '.'.join(map(str, revno)), branch_location))
 
1829
        parent_ids = tree.get_parent_ids()
 
1830
        if parent_ids[1:] and parent_ids[1:] != existing_pending_merges:
 
1831
            note(gettext('Your local commits will now show as pending merges with '
 
1832
                         "'brz status', and can be committed with 'brz commit'."))
 
1833
        if conflicts != 0:
 
1834
            return 1
 
1835
        else:
 
1836
            return 0
 
1837
 
 
1838
 
 
1839
class cmd_info(Command):
 
1840
    __doc__ = """Show information about a working tree, branch or repository.
 
1841
 
 
1842
    This command will show all known locations and formats associated to the
 
1843
    tree, branch or repository.
 
1844
 
 
1845
    In verbose mode, statistical information is included with each report.
 
1846
    To see extended statistic information, use a verbosity level of 2 or
 
1847
    higher by specifying the verbose option multiple times, e.g. -vv.
 
1848
 
 
1849
    Branches and working trees will also report any missing revisions.
 
1850
 
 
1851
    :Examples:
 
1852
 
 
1853
      Display information on the format and related locations:
 
1854
 
 
1855
        brz info
 
1856
 
 
1857
      Display the above together with extended format information and
 
1858
      basic statistics (like the number of files in the working tree and
 
1859
      number of revisions in the branch and repository):
 
1860
 
 
1861
        brz info -v
 
1862
 
 
1863
      Display the above together with number of committers to the branch:
 
1864
 
 
1865
        brz info -vv
 
1866
    """
 
1867
    _see_also = ['revno', 'working-trees', 'repositories']
 
1868
    takes_args = ['location?']
 
1869
    takes_options = ['verbose']
 
1870
    encoding_type = 'replace'
 
1871
 
 
1872
    @display_command
 
1873
    def run(self, location=None, verbose=False):
 
1874
        if verbose:
 
1875
            noise_level = get_verbosity_level()
 
1876
        else:
 
1877
            noise_level = 0
 
1878
        from .info import show_bzrdir_info
 
1879
        show_bzrdir_info(controldir.ControlDir.open_containing(location)[0],
 
1880
                         verbose=noise_level, outfile=self.outf)
 
1881
 
 
1882
 
 
1883
class cmd_remove(Command):
 
1884
    __doc__ = """Remove files or directories.
 
1885
 
 
1886
    This makes Breezy stop tracking changes to the specified files. Breezy will
 
1887
    delete them if they can easily be recovered using revert otherwise they
 
1888
    will be backed up (adding an extension of the form .~#~). If no options or
 
1889
    parameters are given Breezy will scan for files that are being tracked by
 
1890
    Breezy but missing in your tree and stop tracking them for you.
 
1891
    """
 
1892
    takes_args = ['file*']
 
1893
    takes_options = ['verbose',
 
1894
                     Option(
 
1895
                         'new', help='Only remove files that have never been committed.'),
 
1896
                     RegistryOption.from_kwargs('file-deletion-strategy',
 
1897
                                                'The file deletion mode to be used.',
 
1898
                                                title='Deletion Strategy', value_switches=True, enum_switch=False,
 
1899
                                                safe='Backup changed files (default).',
 
1900
                                                keep='Delete from brz but leave the working copy.',
 
1901
                                                no_backup='Don\'t backup changed files.'),
 
1902
                     ]
 
1903
    aliases = ['rm', 'del']
 
1904
    encoding_type = 'replace'
 
1905
 
 
1906
    def run(self, file_list, verbose=False, new=False,
 
1907
            file_deletion_strategy='safe'):
 
1908
 
 
1909
        tree, file_list = WorkingTree.open_containing_paths(file_list)
 
1910
 
 
1911
        if file_list is not None:
 
1912
            file_list = [f for f in file_list]
 
1913
 
 
1914
        self.enter_context(tree.lock_write())
 
1915
        # Heuristics should probably all move into tree.remove_smart or
 
1916
        # some such?
 
1917
        if new:
 
1918
            added = tree.changes_from(tree.basis_tree(),
 
1919
                                      specific_files=file_list).added
 
1920
            file_list = sorted([f.path[1] for f in added], reverse=True)
 
1921
            if len(file_list) == 0:
 
1922
                raise errors.BzrCommandError(gettext('No matching files.'))
 
1923
        elif file_list is None:
 
1924
            # missing files show up in iter_changes(basis) as
 
1925
            # versioned-with-no-kind.
 
1926
            missing = []
 
1927
            for change in tree.iter_changes(tree.basis_tree()):
 
1928
                # Find paths in the working tree that have no kind:
 
1929
                if change.path[1] is not None and change.kind[1] is None:
 
1930
                    missing.append(change.path[1])
 
1931
            file_list = sorted(missing, reverse=True)
 
1932
            file_deletion_strategy = 'keep'
 
1933
        tree.remove(file_list, verbose=verbose, to_file=self.outf,
 
1934
                    keep_files=file_deletion_strategy == 'keep',
 
1935
                    force=(file_deletion_strategy == 'no-backup'))
 
1936
 
 
1937
 
 
1938
class cmd_reconcile(Command):
 
1939
    __doc__ = """Reconcile brz metadata in a branch.
 
1940
 
 
1941
    This can correct data mismatches that may have been caused by
 
1942
    previous ghost operations or brz upgrades. You should only
 
1943
    need to run this command if 'brz check' or a brz developer
 
1944
    advises you to run it.
 
1945
 
 
1946
    If a second branch is provided, cross-branch reconciliation is
 
1947
    also attempted, which will check that data like the tree root
 
1948
    id which was not present in very early brz versions is represented
 
1949
    correctly in both branches.
 
1950
 
 
1951
    At the same time it is run it may recompress data resulting in
 
1952
    a potential saving in disk space or performance gain.
 
1953
 
 
1954
    The branch *MUST* be on a listable system such as local disk or sftp.
 
1955
    """
 
1956
 
 
1957
    _see_also = ['check']
 
1958
    takes_args = ['branch?']
 
1959
    takes_options = [
 
1960
        Option('canonicalize-chks',
 
1961
               help='Make sure CHKs are in canonical form (repairs '
 
1962
                    'bug 522637).',
 
1963
               hidden=True),
 
1964
        ]
 
1965
 
 
1966
    def run(self, branch=".", canonicalize_chks=False):
 
1967
        from .reconcile import reconcile
 
1968
        dir = controldir.ControlDir.open(branch)
 
1969
        reconcile(dir, canonicalize_chks=canonicalize_chks)
 
1970
 
 
1971
 
 
1972
class cmd_revision_history(Command):
 
1973
    __doc__ = """Display the list of revision ids on a branch."""
 
1974
 
 
1975
    _see_also = ['log']
 
1976
    takes_args = ['location?']
 
1977
 
 
1978
    hidden = True
 
1979
 
 
1980
    @display_command
 
1981
    def run(self, location="."):
 
1982
        branch = Branch.open_containing(location)[0]
 
1983
        self.enter_context(branch.lock_read())
 
1984
        graph = branch.repository.get_graph()
 
1985
        history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
 
1986
                                                    [_mod_revision.NULL_REVISION]))
 
1987
        for revid in reversed(history):
 
1988
            self.outf.write(revid)
 
1989
            self.outf.write('\n')
 
1990
 
 
1991
 
 
1992
class cmd_ancestry(Command):
 
1993
    __doc__ = """List all revisions merged into this branch."""
 
1994
 
 
1995
    _see_also = ['log', 'revision-history']
 
1996
    takes_args = ['location?']
 
1997
 
 
1998
    hidden = True
 
1999
 
 
2000
    @display_command
 
2001
    def run(self, location="."):
 
2002
        try:
 
2003
            wt = WorkingTree.open_containing(location)[0]
 
2004
        except errors.NoWorkingTree:
 
2005
            b = Branch.open(location)
 
2006
            last_revision = b.last_revision()
 
2007
        else:
 
2008
            b = wt.branch
 
2009
            last_revision = wt.last_revision()
 
2010
 
 
2011
        self.enter_context(b.repository.lock_read())
 
2012
        graph = b.repository.get_graph()
 
2013
        revisions = [revid for revid, parents in
 
2014
                     graph.iter_ancestry([last_revision])]
 
2015
        for revision_id in reversed(revisions):
 
2016
            if _mod_revision.is_null(revision_id):
 
2017
                continue
 
2018
            self.outf.write(revision_id.decode('utf-8') + '\n')
 
2019
 
 
2020
 
 
2021
class cmd_init(Command):
 
2022
    __doc__ = """Make a directory into a versioned branch.
 
2023
 
 
2024
    Use this to create an empty branch, or before importing an
 
2025
    existing project.
 
2026
 
 
2027
    If there is a repository in a parent directory of the location, then
 
2028
    the history of the branch will be stored in the repository.  Otherwise
 
2029
    init creates a standalone branch which carries its own history
 
2030
    in the .bzr directory.
 
2031
 
 
2032
    If there is already a branch at the location but it has no working tree,
 
2033
    the tree can be populated with 'brz checkout'.
 
2034
 
 
2035
    Recipe for importing a tree of files::
 
2036
 
 
2037
        cd ~/project
 
2038
        brz init
 
2039
        brz add .
 
2040
        brz status
 
2041
        brz commit -m "imported project"
 
2042
    """
 
2043
 
 
2044
    _see_also = ['init-shared-repository', 'branch', 'checkout']
 
2045
    takes_args = ['location?']
 
2046
    takes_options = [
 
2047
        Option('create-prefix',
 
2048
               help='Create the path leading up to the branch '
 
2049
                    'if it does not already exist.'),
 
2050
        RegistryOption('format',
 
2051
                       help='Specify a format for this branch. '
 
2052
                       'See "help formats" for a full list.',
 
2053
                       lazy_registry=('breezy.controldir', 'format_registry'),
 
2054
                       converter=lambda name: controldir.format_registry.make_controldir(
 
2055
                            name),
 
2056
                       value_switches=True,
 
2057
                       title="Branch format",
 
2058
                       ),
 
2059
        Option('append-revisions-only',
 
2060
               help='Never change revnos or the existing log.'
 
2061
               '  Append revisions to it only.'),
 
2062
        Option('no-tree',
 
2063
               'Create a branch without a working tree.')
 
2064
        ]
 
2065
 
 
2066
    def run(self, location=None, format=None, append_revisions_only=False,
 
2067
            create_prefix=False, no_tree=False):
 
2068
        if format is None:
 
2069
            format = controldir.format_registry.make_controldir('default')
 
2070
        if location is None:
 
2071
            location = u'.'
 
2072
 
 
2073
        to_transport = transport.get_transport(location, purpose='write')
 
2074
 
 
2075
        # The path has to exist to initialize a
 
2076
        # branch inside of it.
 
2077
        # Just using os.mkdir, since I don't
 
2078
        # believe that we want to create a bunch of
 
2079
        # locations if the user supplies an extended path
 
2080
        try:
 
2081
            to_transport.ensure_base()
 
2082
        except errors.NoSuchFile:
 
2083
            if not create_prefix:
 
2084
                raise errors.BzrCommandError(gettext("Parent directory of %s"
 
2085
                                                     " does not exist."
 
2086
                                                     "\nYou may supply --create-prefix to create all"
 
2087
                                                     " leading parent directories.")
 
2088
                                             % location)
 
2089
            to_transport.create_prefix()
 
2090
 
 
2091
        try:
 
2092
            a_controldir = controldir.ControlDir.open_from_transport(
 
2093
                to_transport)
 
2094
        except errors.NotBranchError:
 
2095
            # really a NotBzrDir error...
 
2096
            create_branch = controldir.ControlDir.create_branch_convenience
 
2097
            if no_tree:
 
2098
                force_new_tree = False
 
2099
            else:
 
2100
                force_new_tree = None
 
2101
            branch = create_branch(to_transport.base, format=format,
 
2102
                                   possible_transports=[to_transport],
 
2103
                                   force_new_tree=force_new_tree)
 
2104
            a_controldir = branch.controldir
 
2105
        else:
 
2106
            from .transport.local import LocalTransport
 
2107
            if a_controldir.has_branch():
 
2108
                if (isinstance(to_transport, LocalTransport)
 
2109
                        and not a_controldir.has_workingtree()):
 
2110
                    raise errors.BranchExistsWithoutWorkingTree(location)
 
2111
                raise errors.AlreadyBranchError(location)
 
2112
            branch = a_controldir.create_branch()
 
2113
            if not no_tree and not a_controldir.has_workingtree():
 
2114
                a_controldir.create_workingtree()
 
2115
        if append_revisions_only:
 
2116
            try:
 
2117
                branch.set_append_revisions_only(True)
 
2118
            except errors.UpgradeRequired:
 
2119
                raise errors.BzrCommandError(gettext('This branch format cannot be set'
 
2120
                                                     ' to append-revisions-only.  Try --default.'))
 
2121
        if not is_quiet():
 
2122
            from .info import describe_layout, describe_format
 
2123
            try:
 
2124
                tree = a_controldir.open_workingtree(recommend_upgrade=False)
 
2125
            except (errors.NoWorkingTree, errors.NotLocalUrl):
 
2126
                tree = None
 
2127
            repository = branch.repository
 
2128
            layout = describe_layout(repository, branch, tree).lower()
 
2129
            format = describe_format(a_controldir, repository, branch, tree)
 
2130
            self.outf.write(gettext("Created a {0} (format: {1})\n").format(
 
2131
                layout, format))
 
2132
            if repository.is_shared():
 
2133
                # XXX: maybe this can be refactored into transport.path_or_url()
 
2134
                url = repository.controldir.root_transport.external_url()
 
2135
                try:
 
2136
                    url = urlutils.local_path_from_url(url)
 
2137
                except urlutils.InvalidURL:
 
2138
                    pass
 
2139
                self.outf.write(gettext("Using shared repository: %s\n") % url)
 
2140
 
 
2141
 
 
2142
class cmd_init_shared_repository(Command):
 
2143
    __doc__ = """Create a shared repository for branches to share storage space.
 
2144
 
 
2145
    New branches created under the repository directory will store their
 
2146
    revisions in the repository, not in the branch directory.  For branches
 
2147
    with shared history, this reduces the amount of storage needed and
 
2148
    speeds up the creation of new branches.
 
2149
 
 
2150
    If the --no-trees option is given then the branches in the repository
 
2151
    will not have working trees by default.  They will still exist as
 
2152
    directories on disk, but they will not have separate copies of the
 
2153
    files at a certain revision.  This can be useful for repositories that
 
2154
    store branches which are interacted with through checkouts or remote
 
2155
    branches, such as on a server.
 
2156
 
 
2157
    :Examples:
 
2158
        Create a shared repository holding just branches::
 
2159
 
 
2160
            brz init-shared-repo --no-trees repo
 
2161
            brz init repo/trunk
 
2162
 
 
2163
        Make a lightweight checkout elsewhere::
 
2164
 
 
2165
            brz checkout --lightweight repo/trunk trunk-checkout
 
2166
            cd trunk-checkout
 
2167
            (add files here)
 
2168
    """
 
2169
 
 
2170
    _see_also = ['init', 'branch', 'checkout', 'repositories']
 
2171
    takes_args = ["location"]
 
2172
    takes_options = [RegistryOption('format',
 
2173
                                    help='Specify a format for this repository. See'
 
2174
                                    ' "brz help formats" for details.',
 
2175
                                    lazy_registry=(
 
2176
                                        'breezy.controldir', 'format_registry'),
 
2177
                                    converter=lambda name: controldir.format_registry.make_controldir(
 
2178
                                        name),
 
2179
                                    value_switches=True, title='Repository format'),
 
2180
                     Option('no-trees',
 
2181
                            help='Branches in the repository will default to'
 
2182
                            ' not having a working tree.'),
 
2183
                     ]
 
2184
    aliases = ["init-shared-repo", "init-repo"]
 
2185
 
 
2186
    def run(self, location, format=None, no_trees=False):
 
2187
        if format is None:
 
2188
            format = controldir.format_registry.make_controldir('default')
 
2189
 
 
2190
        if location is None:
 
2191
            location = '.'
 
2192
 
 
2193
        to_transport = transport.get_transport(location, purpose='write')
 
2194
 
 
2195
        if format.fixed_components:
 
2196
            repo_format_name = None
 
2197
        else:
 
2198
            repo_format_name = format.repository_format.get_format_string()
 
2199
 
 
2200
        (repo, newdir, require_stacking, repository_policy) = (
 
2201
            format.initialize_on_transport_ex(to_transport,
 
2202
                                              create_prefix=True, make_working_trees=not no_trees,
 
2203
                                              shared_repo=True, force_new_repo=True,
 
2204
                                              use_existing_dir=True,
 
2205
                                              repo_format_name=repo_format_name))
 
2206
        if not is_quiet():
 
2207
            from .info import show_bzrdir_info
 
2208
            show_bzrdir_info(newdir, verbose=0, outfile=self.outf)
 
2209
 
 
2210
 
 
2211
class cmd_diff(Command):
 
2212
    __doc__ = """Show differences in the working tree, between revisions or branches.
 
2213
 
 
2214
    If no arguments are given, all changes for the current tree are listed.
 
2215
    If files are given, only the changes in those files are listed.
 
2216
    Remote and multiple branches can be compared by using the --old and
 
2217
    --new options. If not provided, the default for both is derived from
 
2218
    the first argument, if any, or the current tree if no arguments are
 
2219
    given.
 
2220
 
 
2221
    "brz diff -p1" is equivalent to "brz diff --prefix old/:new/", and
 
2222
    produces patches suitable for "patch -p1".
 
2223
 
 
2224
    Note that when using the -r argument with a range of revisions, the
 
2225
    differences are computed between the two specified revisions.  That
 
2226
    is, the command does not show the changes introduced by the first
 
2227
    revision in the range.  This differs from the interpretation of
 
2228
    revision ranges used by "brz log" which includes the first revision
 
2229
    in the range.
 
2230
 
 
2231
    :Exit values:
 
2232
        1 - changed
 
2233
        2 - unrepresentable changes
 
2234
        3 - error
 
2235
        0 - no change
 
2236
 
 
2237
    :Examples:
 
2238
        Shows the difference in the working tree versus the last commit::
 
2239
 
 
2240
            brz diff
 
2241
 
 
2242
        Difference between the working tree and revision 1::
 
2243
 
 
2244
            brz diff -r1
 
2245
 
 
2246
        Difference between revision 3 and revision 1::
 
2247
 
 
2248
            brz diff -r1..3
 
2249
 
 
2250
        Difference between revision 3 and revision 1 for branch xxx::
 
2251
 
 
2252
            brz diff -r1..3 xxx
 
2253
 
 
2254
        The changes introduced by revision 2 (equivalent to -r1..2)::
 
2255
 
 
2256
            brz diff -c2
 
2257
 
 
2258
        To see the changes introduced by revision X::
 
2259
 
 
2260
            brz diff -cX
 
2261
 
 
2262
        Note that in the case of a merge, the -c option shows the changes
 
2263
        compared to the left hand parent. To see the changes against
 
2264
        another parent, use::
 
2265
 
 
2266
            brz diff -r<chosen_parent>..X
 
2267
 
 
2268
        The changes between the current revision and the previous revision
 
2269
        (equivalent to -c-1 and -r-2..-1)
 
2270
 
 
2271
            brz diff -r-2..
 
2272
 
 
2273
        Show just the differences for file NEWS::
 
2274
 
 
2275
            brz diff NEWS
 
2276
 
 
2277
        Show the differences in working tree xxx for file NEWS::
 
2278
 
 
2279
            brz diff xxx/NEWS
 
2280
 
 
2281
        Show the differences from branch xxx to this working tree:
 
2282
 
 
2283
            brz diff --old xxx
 
2284
 
 
2285
        Show the differences between two branches for file NEWS::
 
2286
 
 
2287
            brz diff --old xxx --new yyy NEWS
 
2288
 
 
2289
        Same as 'brz diff' but prefix paths with old/ and new/::
 
2290
 
 
2291
            brz diff --prefix old/:new/
 
2292
 
 
2293
        Show the differences using a custom diff program with options::
 
2294
 
 
2295
            brz diff --using /usr/bin/diff --diff-options -wu
 
2296
    """
 
2297
    _see_also = ['status']
 
2298
    takes_args = ['file*']
 
2299
    takes_options = [
 
2300
        Option('diff-options', type=str,
 
2301
               help='Pass these options to the external diff program.'),
 
2302
        Option('prefix', type=str,
 
2303
               short_name='p',
 
2304
               help='Set prefixes added to old and new filenames, as '
 
2305
                    'two values separated by a colon. (eg "old/:new/").'),
 
2306
        Option('old',
 
2307
               help='Branch/tree to compare from.',
 
2308
               type=str,
 
2309
               ),
 
2310
        Option('new',
 
2311
               help='Branch/tree to compare to.',
 
2312
               type=str,
 
2313
               ),
 
2314
        'revision',
 
2315
        'change',
 
2316
        Option('using',
 
2317
               help='Use this command to compare files.',
 
2318
               type=str,
 
2319
               ),
 
2320
        RegistryOption('format',
 
2321
                       short_name='F',
 
2322
                       help='Diff format to use.',
 
2323
                       lazy_registry=('breezy.diff', 'format_registry'),
 
2324
                       title='Diff format'),
 
2325
        Option('context',
 
2326
               help='How many lines of context to show.',
 
2327
               type=int,
 
2328
               ),
 
2329
        RegistryOption.from_kwargs(
 
2330
            'color',
 
2331
            help='Color mode to use.',
 
2332
            title='Color Mode', value_switches=False, enum_switch=True,
 
2333
            never='Never colorize output.',
 
2334
            auto='Only colorize output if terminal supports it and STDOUT is a'
 
2335
            ' TTY.',
 
2336
            always='Always colorize output (default).'),
 
2337
        Option(
 
2338
            'check-style',
 
2339
            help=('Warn if trailing whitespace or spurious changes have been'
 
2340
                  '  added.'))
 
2341
        ]
 
2342
 
 
2343
    aliases = ['di', 'dif']
 
2344
    encoding_type = 'exact'
 
2345
 
 
2346
    @display_command
 
2347
    def run(self, revision=None, file_list=None, diff_options=None,
 
2348
            prefix=None, old=None, new=None, using=None, format=None,
 
2349
            context=None, color='never'):
 
2350
        from .diff import (get_trees_and_branches_to_diff_locked,
 
2351
                           show_diff_trees)
 
2352
 
 
2353
        if prefix == u'0':
 
2354
            # diff -p0 format
 
2355
            old_label = ''
 
2356
            new_label = ''
 
2357
        elif prefix == u'1' or prefix is None:
 
2358
            old_label = 'old/'
 
2359
            new_label = 'new/'
 
2360
        elif u':' in prefix:
 
2361
            old_label, new_label = prefix.split(u":")
 
2362
        else:
 
2363
            raise errors.BzrCommandError(gettext(
 
2364
                '--prefix expects two values separated by a colon'
 
2365
                ' (eg "old/:new/")'))
 
2366
 
 
2367
        if revision and len(revision) > 2:
 
2368
            raise errors.BzrCommandError(gettext('brz diff --revision takes exactly'
 
2369
                                                 ' one or two revision specifiers'))
 
2370
 
 
2371
        if using is not None and format is not None:
 
2372
            raise errors.BzrCommandError(gettext(
 
2373
                '{0} and {1} are mutually exclusive').format(
 
2374
                '--using', '--format'))
 
2375
 
 
2376
        (old_tree, new_tree,
 
2377
         old_branch, new_branch,
 
2378
         specific_files, extra_trees) = get_trees_and_branches_to_diff_locked(
 
2379
            file_list, revision, old, new, self._exit_stack, apply_view=True)
 
2380
        # GNU diff on Windows uses ANSI encoding for filenames
 
2381
        path_encoding = osutils.get_diff_header_encoding()
 
2382
        outf = self.outf
 
2383
        if color == 'auto':
 
2384
            from .terminal import has_ansi_colors
 
2385
            if has_ansi_colors():
 
2386
                color = 'always'
 
2387
            else:
 
2388
                color = 'never'
 
2389
        if 'always' == color:
 
2390
            from .colordiff import DiffWriter
 
2391
            outf = DiffWriter(outf)
 
2392
        return show_diff_trees(old_tree, new_tree, outf,
 
2393
                               specific_files=specific_files,
 
2394
                               external_diff_options=diff_options,
 
2395
                               old_label=old_label, new_label=new_label,
 
2396
                               extra_trees=extra_trees,
 
2397
                               path_encoding=path_encoding,
 
2398
                               using=using, context=context,
 
2399
                               format_cls=format)
 
2400
 
 
2401
 
 
2402
class cmd_deleted(Command):
 
2403
    __doc__ = """List files deleted in the working tree.
 
2404
    """
 
2405
    # TODO: Show files deleted since a previous revision, or
 
2406
    # between two revisions.
 
2407
    # TODO: Much more efficient way to do this: read in new
 
2408
    # directories with readdir, rather than stating each one.  Same
 
2409
    # level of effort but possibly much less IO.  (Or possibly not,
 
2410
    # if the directories are very large...)
 
2411
    _see_also = ['status', 'ls']
 
2412
    takes_options = ['directory', 'show-ids']
 
2413
 
 
2414
    @display_command
 
2415
    def run(self, show_ids=False, directory=u'.'):
 
2416
        tree = WorkingTree.open_containing(directory)[0]
 
2417
        self.enter_context(tree.lock_read())
 
2418
        old = tree.basis_tree()
 
2419
        self.enter_context(old.lock_read())
 
2420
        delta = tree.changes_from(old)
 
2421
        for change in delta.removed:
 
2422
            self.outf.write(change.path[0])
 
2423
            if show_ids:
 
2424
                self.outf.write(' ')
 
2425
                self.outf.write(change.file_id)
 
2426
            self.outf.write('\n')
 
2427
 
 
2428
 
 
2429
class cmd_modified(Command):
 
2430
    __doc__ = """List files modified in working tree.
 
2431
    """
 
2432
 
 
2433
    hidden = True
 
2434
    _see_also = ['status', 'ls']
 
2435
    takes_options = ['directory', 'null']
 
2436
 
 
2437
    @display_command
 
2438
    def run(self, null=False, directory=u'.'):
 
2439
        tree = WorkingTree.open_containing(directory)[0]
 
2440
        self.enter_context(tree.lock_read())
 
2441
        td = tree.changes_from(tree.basis_tree())
 
2442
        self.cleanup_now()
 
2443
        for change in td.modified:
 
2444
            if null:
 
2445
                self.outf.write(change.path[1] + '\0')
 
2446
            else:
 
2447
                self.outf.write(osutils.quotefn(change.path[1]) + '\n')
 
2448
 
 
2449
 
 
2450
class cmd_added(Command):
 
2451
    __doc__ = """List files added in working tree.
 
2452
    """
 
2453
 
 
2454
    hidden = True
 
2455
    _see_also = ['status', 'ls']
 
2456
    takes_options = ['directory', 'null']
 
2457
 
 
2458
    @display_command
 
2459
    def run(self, null=False, directory=u'.'):
 
2460
        wt = WorkingTree.open_containing(directory)[0]
 
2461
        self.enter_context(wt.lock_read())
 
2462
        basis = wt.basis_tree()
 
2463
        self.enter_context(basis.lock_read())
 
2464
        for path in wt.all_versioned_paths():
 
2465
            if basis.has_filename(path):
 
2466
                continue
 
2467
            if path == u'':
 
2468
                continue
 
2469
            if not os.access(osutils.pathjoin(wt.basedir, path), os.F_OK):
 
2470
                continue
 
2471
            if null:
 
2472
                self.outf.write(path + '\0')
 
2473
            else:
 
2474
                self.outf.write(osutils.quotefn(path) + '\n')
 
2475
 
 
2476
 
 
2477
class cmd_root(Command):
 
2478
    __doc__ = """Show the tree root directory.
 
2479
 
 
2480
    The root is the nearest enclosing directory with a control
 
2481
    directory."""
 
2482
 
 
2483
    takes_args = ['filename?']
 
2484
 
 
2485
    @display_command
 
2486
    def run(self, filename=None):
 
2487
        """Print the branch root."""
 
2488
        tree = WorkingTree.open_containing(filename)[0]
 
2489
        self.outf.write(tree.basedir + '\n')
 
2490
 
 
2491
 
 
2492
def _parse_limit(limitstring):
 
2493
    try:
 
2494
        return int(limitstring)
 
2495
    except ValueError:
 
2496
        msg = gettext("The limit argument must be an integer.")
 
2497
        raise errors.BzrCommandError(msg)
 
2498
 
 
2499
 
 
2500
def _parse_levels(s):
 
2501
    try:
 
2502
        return int(s)
 
2503
    except ValueError:
 
2504
        msg = gettext("The levels argument must be an integer.")
 
2505
        raise errors.BzrCommandError(msg)
 
2506
 
 
2507
 
 
2508
class cmd_log(Command):
 
2509
    __doc__ = """Show historical log for a branch or subset of a branch.
 
2510
 
 
2511
    log is brz's default tool for exploring the history of a branch.
 
2512
    The branch to use is taken from the first parameter. If no parameters
 
2513
    are given, the branch containing the working directory is logged.
 
2514
    Here are some simple examples::
 
2515
 
 
2516
      brz log                       log the current branch
 
2517
      brz log foo.py                log a file in its branch
 
2518
      brz log http://server/branch  log a branch on a server
 
2519
 
 
2520
    The filtering, ordering and information shown for each revision can
 
2521
    be controlled as explained below. By default, all revisions are
 
2522
    shown sorted (topologically) so that newer revisions appear before
 
2523
    older ones and descendants always appear before ancestors. If displayed,
 
2524
    merged revisions are shown indented under the revision in which they
 
2525
    were merged.
 
2526
 
 
2527
    :Output control:
 
2528
 
 
2529
      The log format controls how information about each revision is
 
2530
      displayed. The standard log formats are called ``long``, ``short``
 
2531
      and ``line``. The default is long. See ``brz help log-formats``
 
2532
      for more details on log formats.
 
2533
 
 
2534
      The following options can be used to control what information is
 
2535
      displayed::
 
2536
 
 
2537
        -l N        display a maximum of N revisions
 
2538
        -n N        display N levels of revisions (0 for all, 1 for collapsed)
 
2539
        -v          display a status summary (delta) for each revision
 
2540
        -p          display a diff (patch) for each revision
 
2541
        --show-ids  display revision-ids (and file-ids), not just revnos
 
2542
 
 
2543
      Note that the default number of levels to display is a function of the
 
2544
      log format. If the -n option is not used, the standard log formats show
 
2545
      just the top level (mainline).
 
2546
 
 
2547
      Status summaries are shown using status flags like A, M, etc. To see
 
2548
      the changes explained using words like ``added`` and ``modified``
 
2549
      instead, use the -vv option.
 
2550
 
 
2551
    :Ordering control:
 
2552
 
 
2553
      To display revisions from oldest to newest, use the --forward option.
 
2554
      In most cases, using this option will have little impact on the total
 
2555
      time taken to produce a log, though --forward does not incrementally
 
2556
      display revisions like --reverse does when it can.
 
2557
 
 
2558
    :Revision filtering:
 
2559
 
 
2560
      The -r option can be used to specify what revision or range of revisions
 
2561
      to filter against. The various forms are shown below::
 
2562
 
 
2563
        -rX      display revision X
 
2564
        -rX..    display revision X and later
 
2565
        -r..Y    display up to and including revision Y
 
2566
        -rX..Y   display from X to Y inclusive
 
2567
 
 
2568
      See ``brz help revisionspec`` for details on how to specify X and Y.
 
2569
      Some common examples are given below::
 
2570
 
 
2571
        -r-1                show just the tip
 
2572
        -r-10..             show the last 10 mainline revisions
 
2573
        -rsubmit:..         show what's new on this branch
 
2574
        -rancestor:path..   show changes since the common ancestor of this
 
2575
                            branch and the one at location path
 
2576
        -rdate:yesterday..  show changes since yesterday
 
2577
 
 
2578
      When logging a range of revisions using -rX..Y, log starts at
 
2579
      revision Y and searches back in history through the primary
 
2580
      ("left-hand") parents until it finds X. When logging just the
 
2581
      top level (using -n1), an error is reported if X is not found
 
2582
      along the way. If multi-level logging is used (-n0), X may be
 
2583
      a nested merge revision and the log will be truncated accordingly.
 
2584
 
 
2585
    :Path filtering:
 
2586
 
 
2587
      If parameters are given and the first one is not a branch, the log
 
2588
      will be filtered to show only those revisions that changed the
 
2589
      nominated files or directories.
 
2590
 
 
2591
      Filenames are interpreted within their historical context. To log a
 
2592
      deleted file, specify a revision range so that the file existed at
 
2593
      the end or start of the range.
 
2594
 
 
2595
      Historical context is also important when interpreting pathnames of
 
2596
      renamed files/directories. Consider the following example:
 
2597
 
 
2598
      * revision 1: add tutorial.txt
 
2599
      * revision 2: modify tutorial.txt
 
2600
      * revision 3: rename tutorial.txt to guide.txt; add tutorial.txt
 
2601
 
 
2602
      In this case:
 
2603
 
 
2604
      * ``brz log guide.txt`` will log the file added in revision 1
 
2605
 
 
2606
      * ``brz log tutorial.txt`` will log the new file added in revision 3
 
2607
 
 
2608
      * ``brz log -r2 -p tutorial.txt`` will show the changes made to
 
2609
        the original file in revision 2.
 
2610
 
 
2611
      * ``brz log -r2 -p guide.txt`` will display an error message as there
 
2612
        was no file called guide.txt in revision 2.
 
2613
 
 
2614
      Renames are always followed by log. By design, there is no need to
 
2615
      explicitly ask for this (and no way to stop logging a file back
 
2616
      until it was last renamed).
 
2617
 
 
2618
    :Other filtering:
 
2619
 
 
2620
      The --match option can be used for finding revisions that match a
 
2621
      regular expression in a commit message, committer, author or bug.
 
2622
      Specifying the option several times will match any of the supplied
 
2623
      expressions. --match-author, --match-bugs, --match-committer and
 
2624
      --match-message can be used to only match a specific field.
 
2625
 
 
2626
    :Tips & tricks:
 
2627
 
 
2628
      GUI tools and IDEs are often better at exploring history than command
 
2629
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
 
2630
      bzr-explorer shell, or the Loggerhead web interface.  See the Bazaar
 
2631
      Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/> and
 
2632
      <http://wiki.bazaar.canonical.com/IDEIntegration>.
 
2633
 
 
2634
      You may find it useful to add the aliases below to ``breezy.conf``::
 
2635
 
 
2636
        [ALIASES]
 
2637
        tip = log -r-1
 
2638
        top = log -l10 --line
 
2639
        show = log -v -p
 
2640
 
 
2641
      ``brz tip`` will then show the latest revision while ``brz top``
 
2642
      will show the last 10 mainline revisions. To see the details of a
 
2643
      particular revision X,  ``brz show -rX``.
 
2644
 
 
2645
      If you are interested in looking deeper into a particular merge X,
 
2646
      use ``brz log -n0 -rX``.
 
2647
 
 
2648
      ``brz log -v`` on a branch with lots of history is currently
 
2649
      very slow. A fix for this issue is currently under development.
 
2650
      With or without that fix, it is recommended that a revision range
 
2651
      be given when using the -v option.
 
2652
 
 
2653
      brz has a generic full-text matching plugin, brz-search, that can be
 
2654
      used to find revisions matching user names, commit messages, etc.
 
2655
      Among other features, this plugin can find all revisions containing
 
2656
      a list of words but not others.
 
2657
 
 
2658
      When exploring non-mainline history on large projects with deep
 
2659
      history, the performance of log can be greatly improved by installing
 
2660
      the historycache plugin. This plugin buffers historical information
 
2661
      trading disk space for faster speed.
 
2662
    """
 
2663
    takes_args = ['file*']
 
2664
    _see_also = ['log-formats', 'revisionspec']
 
2665
    takes_options = [
 
2666
        Option('forward',
 
2667
               help='Show from oldest to newest.'),
 
2668
        'timezone',
 
2669
        custom_help('verbose',
 
2670
                    help='Show files changed in each revision.'),
 
2671
        'show-ids',
 
2672
        'revision',
 
2673
        Option('change',
 
2674
               type=breezy.option._parse_revision_str,
 
2675
               short_name='c',
 
2676
               help='Show just the specified revision.'
 
2677
               ' See also "help revisionspec".'),
 
2678
        'log-format',
 
2679
        RegistryOption('authors',
 
2680
                       'What names to list as authors - first, all or committer.',
 
2681
                       title='Authors',
 
2682
                       lazy_registry=(
 
2683
                           'breezy.log', 'author_list_registry'),
 
2684
                       ),
 
2685
        Option('levels',
 
2686
               short_name='n',
 
2687
               help='Number of levels to display - 0 for all, 1 for flat.',
 
2688
               argname='N',
 
2689
               type=_parse_levels),
 
2690
        Option('message',
 
2691
               help='Show revisions whose message matches this '
 
2692
               'regular expression.',
 
2693
               type=str,
 
2694
               hidden=True),
 
2695
        Option('limit',
 
2696
               short_name='l',
 
2697
               help='Limit the output to the first N revisions.',
 
2698
               argname='N',
 
2699
               type=_parse_limit),
 
2700
        Option('show-diff',
 
2701
               short_name='p',
 
2702
               help='Show changes made in each revision as a patch.'),
 
2703
        Option('include-merged',
 
2704
               help='Show merged revisions like --levels 0 does.'),
 
2705
        Option('include-merges', hidden=True,
 
2706
               help='Historical alias for --include-merged.'),
 
2707
        Option('omit-merges',
 
2708
               help='Do not report commits with more than one parent.'),
 
2709
        Option('exclude-common-ancestry',
 
2710
               help='Display only the revisions that are not part'
 
2711
               ' of both ancestries (require -rX..Y).'
 
2712
               ),
 
2713
        Option('signatures',
 
2714
               help='Show digital signature validity.'),
 
2715
        ListOption('match',
 
2716
                   short_name='m',
 
2717
                   help='Show revisions whose properties match this '
 
2718
                   'expression.',
 
2719
                   type=str),
 
2720
        ListOption('match-message',
 
2721
                   help='Show revisions whose message matches this '
 
2722
                   'expression.',
 
2723
                   type=str),
 
2724
        ListOption('match-committer',
 
2725
                   help='Show revisions whose committer matches this '
 
2726
                   'expression.',
 
2727
                   type=str),
 
2728
        ListOption('match-author',
 
2729
                   help='Show revisions whose authors match this '
 
2730
                   'expression.',
 
2731
                   type=str),
 
2732
        ListOption('match-bugs',
 
2733
                   help='Show revisions whose bugs match this '
 
2734
                   'expression.',
 
2735
                   type=str)
 
2736
        ]
 
2737
    encoding_type = 'replace'
 
2738
 
 
2739
    @display_command
 
2740
    def run(self, file_list=None, timezone='original',
 
2741
            verbose=False,
 
2742
            show_ids=False,
 
2743
            forward=False,
 
2744
            revision=None,
 
2745
            change=None,
 
2746
            log_format=None,
 
2747
            levels=None,
 
2748
            message=None,
 
2749
            limit=None,
 
2750
            show_diff=False,
 
2751
            include_merged=None,
 
2752
            authors=None,
 
2753
            exclude_common_ancestry=False,
 
2754
            signatures=False,
 
2755
            match=None,
 
2756
            match_message=None,
 
2757
            match_committer=None,
 
2758
            match_author=None,
 
2759
            match_bugs=None,
 
2760
            omit_merges=False,
 
2761
            ):
 
2762
        from .log import (
 
2763
            Logger,
 
2764
            make_log_request_dict,
 
2765
            _get_info_for_log_files,
 
2766
            )
 
2767
        direction = (forward and 'forward') or 'reverse'
 
2768
        if include_merged is None:
 
2769
            include_merged = False
 
2770
        if (exclude_common_ancestry
 
2771
                and (revision is None or len(revision) != 2)):
 
2772
            raise errors.BzrCommandError(gettext(
 
2773
                '--exclude-common-ancestry requires -r with two revisions'))
 
2774
        if include_merged:
 
2775
            if levels is None:
 
2776
                levels = 0
 
2777
            else:
 
2778
                raise errors.BzrCommandError(gettext(
 
2779
                    '{0} and {1} are mutually exclusive').format(
 
2780
                    '--levels', '--include-merged'))
 
2781
 
 
2782
        if change is not None:
 
2783
            if len(change) > 1:
 
2784
                raise errors.RangeInChangeOption()
 
2785
            if revision is not None:
 
2786
                raise errors.BzrCommandError(gettext(
 
2787
                    '{0} and {1} are mutually exclusive').format(
 
2788
                    '--revision', '--change'))
 
2789
            else:
 
2790
                revision = change
 
2791
 
 
2792
        file_ids = []
 
2793
        filter_by_dir = False
 
2794
        if file_list:
 
2795
            # find the file ids to log and check for directory filtering
 
2796
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2797
                revision, file_list, self._exit_stack)
 
2798
            for relpath, file_id, kind in file_info_list:
 
2799
                if file_id is None:
 
2800
                    raise errors.BzrCommandError(gettext(
 
2801
                        "Path unknown at end or start of revision range: %s") %
 
2802
                        relpath)
 
2803
                # If the relpath is the top of the tree, we log everything
 
2804
                if relpath == '':
 
2805
                    file_ids = []
 
2806
                    break
 
2807
                else:
 
2808
                    file_ids.append(file_id)
 
2809
                filter_by_dir = filter_by_dir or (
 
2810
                    kind in ['directory', 'tree-reference'])
 
2811
        else:
 
2812
            # log everything
 
2813
            # FIXME ? log the current subdir only RBC 20060203
 
2814
            if revision is not None \
 
2815
                    and len(revision) > 0 and revision[0].get_branch():
 
2816
                location = revision[0].get_branch()
 
2817
            else:
 
2818
                location = '.'
 
2819
            dir, relpath = controldir.ControlDir.open_containing(location)
 
2820
            b = dir.open_branch()
 
2821
            self.enter_context(b.lock_read())
 
2822
            rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2823
 
 
2824
        if b.get_config_stack().get('validate_signatures_in_log'):
 
2825
            signatures = True
 
2826
 
 
2827
        if signatures:
 
2828
            if not gpg.GPGStrategy.verify_signatures_available():
 
2829
                raise errors.GpgmeNotInstalled(None)
 
2830
 
 
2831
        # Decide on the type of delta & diff filtering to use
 
2832
        # TODO: add an --all-files option to make this configurable & consistent
 
2833
        if not verbose:
 
2834
            delta_type = None
 
2835
        else:
 
2836
            delta_type = 'full'
 
2837
        if not show_diff:
 
2838
            diff_type = None
 
2839
        elif file_ids:
 
2840
            diff_type = 'partial'
 
2841
        else:
 
2842
            diff_type = 'full'
 
2843
 
 
2844
        # Build the log formatter
 
2845
        if log_format is None:
 
2846
            log_format = log.log_formatter_registry.get_default(b)
 
2847
        # Make a non-encoding output to include the diffs - bug 328007
 
2848
        unencoded_output = ui.ui_factory.make_output_stream(
 
2849
            encoding_type='exact')
 
2850
        lf = log_format(show_ids=show_ids, to_file=self.outf,
 
2851
                        to_exact_file=unencoded_output,
 
2852
                        show_timezone=timezone,
 
2853
                        delta_format=get_verbosity_level(),
 
2854
                        levels=levels,
 
2855
                        show_advice=levels is None,
 
2856
                        author_list_handler=authors)
 
2857
 
 
2858
        # Choose the algorithm for doing the logging. It's annoying
 
2859
        # having multiple code paths like this but necessary until
 
2860
        # the underlying repository format is faster at generating
 
2861
        # deltas or can provide everything we need from the indices.
 
2862
        # The default algorithm - match-using-deltas - works for
 
2863
        # multiple files and directories and is faster for small
 
2864
        # amounts of history (200 revisions say). However, it's too
 
2865
        # slow for logging a single file in a repository with deep
 
2866
        # history, i.e. > 10K revisions. In the spirit of "do no
 
2867
        # evil when adding features", we continue to use the
 
2868
        # original algorithm - per-file-graph - for the "single
 
2869
        # file that isn't a directory without showing a delta" case.
 
2870
        partial_history = revision and b.repository._format.supports_chks
 
2871
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2872
                              or delta_type or partial_history)
 
2873
 
 
2874
        match_dict = {}
 
2875
        if match:
 
2876
            match_dict[''] = match
 
2877
        if match_message:
 
2878
            match_dict['message'] = match_message
 
2879
        if match_committer:
 
2880
            match_dict['committer'] = match_committer
 
2881
        if match_author:
 
2882
            match_dict['author'] = match_author
 
2883
        if match_bugs:
 
2884
            match_dict['bugs'] = match_bugs
 
2885
 
 
2886
        # Build the LogRequest and execute it
 
2887
        if len(file_ids) == 0:
 
2888
            file_ids = None
 
2889
        rqst = make_log_request_dict(
 
2890
            direction=direction, specific_fileids=file_ids,
 
2891
            start_revision=rev1, end_revision=rev2, limit=limit,
 
2892
            message_search=message, delta_type=delta_type,
 
2893
            diff_type=diff_type, _match_using_deltas=match_using_deltas,
 
2894
            exclude_common_ancestry=exclude_common_ancestry, match=match_dict,
 
2895
            signature=signatures, omit_merges=omit_merges,
 
2896
            )
 
2897
        Logger(b, rqst).show(lf)
 
2898
 
 
2899
 
 
2900
def _get_revision_range(revisionspec_list, branch, command_name):
 
2901
    """Take the input of a revision option and turn it into a revision range.
 
2902
 
 
2903
    It returns RevisionInfo objects which can be used to obtain the rev_id's
 
2904
    of the desired revisions. It does some user input validations.
 
2905
    """
 
2906
    if revisionspec_list is None:
 
2907
        rev1 = None
 
2908
        rev2 = None
 
2909
    elif len(revisionspec_list) == 1:
 
2910
        rev1 = rev2 = revisionspec_list[0].in_history(branch)
 
2911
    elif len(revisionspec_list) == 2:
 
2912
        start_spec = revisionspec_list[0]
 
2913
        end_spec = revisionspec_list[1]
 
2914
        if end_spec.get_branch() != start_spec.get_branch():
 
2915
            # b is taken from revision[0].get_branch(), and
 
2916
            # show_log will use its revision_history. Having
 
2917
            # different branches will lead to weird behaviors.
 
2918
            raise errors.BzrCommandError(gettext(
 
2919
                "brz %s doesn't accept two revisions in different"
 
2920
                " branches.") % command_name)
 
2921
        if start_spec.spec is None:
 
2922
            # Avoid loading all the history.
 
2923
            rev1 = RevisionInfo(branch, None, None)
 
2924
        else:
 
2925
            rev1 = start_spec.in_history(branch)
 
2926
        # Avoid loading all of history when we know a missing
 
2927
        # end of range means the last revision ...
 
2928
        if end_spec.spec is None:
 
2929
            last_revno, last_revision_id = branch.last_revision_info()
 
2930
            rev2 = RevisionInfo(branch, last_revno, last_revision_id)
 
2931
        else:
 
2932
            rev2 = end_spec.in_history(branch)
 
2933
    else:
 
2934
        raise errors.BzrCommandError(gettext(
 
2935
            'brz %s --revision takes one or two values.') % command_name)
 
2936
    return rev1, rev2
 
2937
 
 
2938
 
 
2939
def _revision_range_to_revid_range(revision_range):
 
2940
    rev_id1 = None
 
2941
    rev_id2 = None
 
2942
    if revision_range[0] is not None:
 
2943
        rev_id1 = revision_range[0].rev_id
 
2944
    if revision_range[1] is not None:
 
2945
        rev_id2 = revision_range[1].rev_id
 
2946
    return rev_id1, rev_id2
 
2947
 
 
2948
 
 
2949
def get_log_format(long=False, short=False, line=False, default='long'):
 
2950
    log_format = default
 
2951
    if long:
 
2952
        log_format = 'long'
 
2953
    if short:
 
2954
        log_format = 'short'
 
2955
    if line:
 
2956
        log_format = 'line'
 
2957
    return log_format
 
2958
 
 
2959
 
 
2960
class cmd_touching_revisions(Command):
 
2961
    __doc__ = """Return revision-ids which affected a particular file.
 
2962
 
 
2963
    A more user-friendly interface is "brz log FILE".
 
2964
    """
 
2965
 
 
2966
    hidden = True
 
2967
    takes_args = ["filename"]
 
2968
 
 
2969
    @display_command
 
2970
    def run(self, filename):
 
2971
        tree, relpath = WorkingTree.open_containing(filename)
 
2972
        with tree.lock_read():
 
2973
            touching_revs = log.find_touching_revisions(
 
2974
                tree.branch.repository, tree.branch.last_revision(), tree, relpath)
 
2975
            for revno, revision_id, what in reversed(list(touching_revs)):
 
2976
                self.outf.write("%6d %s\n" % (revno, what))
 
2977
 
 
2978
 
 
2979
class cmd_ls(Command):
 
2980
    __doc__ = """List files in a tree.
 
2981
    """
 
2982
 
 
2983
    _see_also = ['status', 'cat']
 
2984
    takes_args = ['path?']
 
2985
    takes_options = [
 
2986
        'verbose',
 
2987
        'revision',
 
2988
        Option('recursive', short_name='R',
 
2989
               help='Recurse into subdirectories.'),
 
2990
        Option('from-root',
 
2991
               help='Print paths relative to the root of the branch.'),
 
2992
        Option('unknown', short_name='u',
 
2993
               help='Print unknown files.'),
 
2994
        Option('versioned', help='Print versioned files.',
 
2995
               short_name='V'),
 
2996
        Option('ignored', short_name='i',
 
2997
               help='Print ignored files.'),
 
2998
        Option('kind', short_name='k',
 
2999
               help=('List entries of a particular kind: file, '
 
3000
                     'directory, symlink, tree-reference.'),
 
3001
               type=str),
 
3002
        'null',
 
3003
        'show-ids',
 
3004
        'directory',
 
3005
        ]
 
3006
 
 
3007
    @display_command
 
3008
    def run(self, revision=None, verbose=False,
 
3009
            recursive=False, from_root=False,
 
3010
            unknown=False, versioned=False, ignored=False,
 
3011
            null=False, kind=None, show_ids=False, path=None, directory=None):
 
3012
 
 
3013
        if kind and kind not in ('file', 'directory', 'symlink', 'tree-reference'):
 
3014
            raise errors.BzrCommandError(gettext('invalid kind specified'))
 
3015
 
 
3016
        if verbose and null:
 
3017
            raise errors.BzrCommandError(
 
3018
                gettext('Cannot set both --verbose and --null'))
 
3019
        all = not (unknown or versioned or ignored)
 
3020
 
 
3021
        selection = {'I': ignored, '?': unknown, 'V': versioned}
 
3022
 
 
3023
        if path is None:
 
3024
            fs_path = '.'
 
3025
        else:
 
3026
            if from_root:
 
3027
                raise errors.BzrCommandError(gettext('cannot specify both --from-root'
 
3028
                                                     ' and PATH'))
 
3029
            fs_path = path
 
3030
        tree, branch, relpath = \
 
3031
            _open_directory_or_containing_tree_or_branch(fs_path, directory)
 
3032
 
 
3033
        # Calculate the prefix to use
 
3034
        prefix = None
 
3035
        if from_root:
 
3036
            if relpath:
 
3037
                prefix = relpath + '/'
 
3038
        elif fs_path != '.' and not fs_path.endswith('/'):
 
3039
            prefix = fs_path + '/'
 
3040
 
 
3041
        if revision is not None or tree is None:
 
3042
            tree = _get_one_revision_tree('ls', revision, branch=branch)
 
3043
 
 
3044
        apply_view = False
 
3045
        if isinstance(tree, WorkingTree) and tree.supports_views():
 
3046
            view_files = tree.views.lookup_view()
 
3047
            if view_files:
 
3048
                apply_view = True
 
3049
                view_str = views.view_display_str(view_files)
 
3050
                note(gettext("Ignoring files outside view. View is %s") % view_str)
 
3051
 
 
3052
        self.enter_context(tree.lock_read())
 
3053
        for fp, fc, fkind, entry in tree.list_files(
 
3054
                include_root=False, from_dir=relpath, recursive=recursive):
 
3055
            # Apply additional masking
 
3056
            if not all and not selection[fc]:
 
3057
                continue
 
3058
            if kind is not None and fkind != kind:
 
3059
                continue
 
3060
            if apply_view:
 
3061
                try:
 
3062
                    if relpath:
 
3063
                        fullpath = osutils.pathjoin(relpath, fp)
 
3064
                    else:
 
3065
                        fullpath = fp
 
3066
                    views.check_path_in_view(tree, fullpath)
 
3067
                except views.FileOutsideView:
 
3068
                    continue
 
3069
 
 
3070
            # Output the entry
 
3071
            if prefix:
 
3072
                fp = osutils.pathjoin(prefix, fp)
 
3073
            kindch = entry.kind_character()
 
3074
            outstring = fp + kindch
 
3075
            ui.ui_factory.clear_term()
 
3076
            if verbose:
 
3077
                outstring = '%-8s %s' % (fc, outstring)
 
3078
                if show_ids and getattr(entry, 'file_id', None) is not None:
 
3079
                    outstring = "%-50s %s" % (outstring, entry.file_id.decode('utf-8'))
 
3080
                self.outf.write(outstring + '\n')
 
3081
            elif null:
 
3082
                self.outf.write(fp + '\0')
 
3083
                if show_ids:
 
3084
                    if getattr(entry, 'file_id', None) is not None:
 
3085
                        self.outf.write(entry.file_id.decode('utf-8'))
 
3086
                    self.outf.write('\0')
 
3087
                self.outf.flush()
 
3088
            else:
 
3089
                if show_ids:
 
3090
                    if getattr(entry, 'file_id', None) is not None:
 
3091
                        my_id = entry.file_id.decode('utf-8')
 
3092
                    else:
 
3093
                        my_id = ''
 
3094
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
 
3095
                else:
 
3096
                    self.outf.write(outstring + '\n')
 
3097
 
 
3098
 
 
3099
class cmd_unknowns(Command):
 
3100
    __doc__ = """List unknown files.
 
3101
    """
 
3102
 
 
3103
    hidden = True
 
3104
    _see_also = ['ls']
 
3105
    takes_options = ['directory']
 
3106
 
 
3107
    @display_command
 
3108
    def run(self, directory=u'.'):
 
3109
        for f in WorkingTree.open_containing(directory)[0].unknowns():
 
3110
            self.outf.write(osutils.quotefn(f) + '\n')
 
3111
 
 
3112
 
 
3113
class cmd_ignore(Command):
 
3114
    __doc__ = """Ignore specified files or patterns.
 
3115
 
 
3116
    See ``brz help patterns`` for details on the syntax of patterns.
 
3117
 
 
3118
    If a .bzrignore file does not exist, the ignore command
 
3119
    will create one and add the specified files or patterns to the newly
 
3120
    created file. The ignore command will also automatically add the
 
3121
    .bzrignore file to be versioned. Creating a .bzrignore file without
 
3122
    the use of the ignore command will require an explicit add command.
 
3123
 
 
3124
    To remove patterns from the ignore list, edit the .bzrignore file.
 
3125
    After adding, editing or deleting that file either indirectly by
 
3126
    using this command or directly by using an editor, be sure to commit
 
3127
    it.
 
3128
 
 
3129
    Breezy also supports a global ignore file ~/.config/breezy/ignore. On
 
3130
    Windows the global ignore file can be found in the application data
 
3131
    directory as
 
3132
    C:\\Documents and Settings\\<user>\\Application Data\\Breezy\\3.0\\ignore.
 
3133
    Global ignores are not touched by this command. The global ignore file
 
3134
    can be edited directly using an editor.
 
3135
 
 
3136
    Patterns prefixed with '!' are exceptions to ignore patterns and take
 
3137
    precedence over regular ignores.  Such exceptions are used to specify
 
3138
    files that should be versioned which would otherwise be ignored.
 
3139
 
 
3140
    Patterns prefixed with '!!' act as regular ignore patterns, but have
 
3141
    precedence over the '!' exception patterns.
 
3142
 
 
3143
    :Notes:
 
3144
 
 
3145
    * Ignore patterns containing shell wildcards must be quoted from
 
3146
      the shell on Unix.
 
3147
 
 
3148
    * Ignore patterns starting with "#" act as comments in the ignore file.
 
3149
      To ignore patterns that begin with that character, use the "RE:" prefix.
 
3150
 
 
3151
    :Examples:
 
3152
        Ignore the top level Makefile::
 
3153
 
 
3154
            brz ignore ./Makefile
 
3155
 
 
3156
        Ignore .class files in all directories...::
 
3157
 
 
3158
            brz ignore "*.class"
 
3159
 
 
3160
        ...but do not ignore "special.class"::
 
3161
 
 
3162
            brz ignore "!special.class"
 
3163
 
 
3164
        Ignore files whose name begins with the "#" character::
 
3165
 
 
3166
            brz ignore "RE:^#"
 
3167
 
 
3168
        Ignore .o files under the lib directory::
 
3169
 
 
3170
            brz ignore "lib/**/*.o"
 
3171
 
 
3172
        Ignore .o files under the lib directory::
 
3173
 
 
3174
            brz ignore "RE:lib/.*\\.o"
 
3175
 
 
3176
        Ignore everything but the "debian" toplevel directory::
 
3177
 
 
3178
            brz ignore "RE:(?!debian/).*"
 
3179
 
 
3180
        Ignore everything except the "local" toplevel directory,
 
3181
        but always ignore autosave files ending in ~, even under local/::
 
3182
 
 
3183
            brz ignore "*"
 
3184
            brz ignore "!./local"
 
3185
            brz ignore "!!*~"
 
3186
    """
 
3187
 
 
3188
    _see_also = ['status', 'ignored', 'patterns']
 
3189
    takes_args = ['name_pattern*']
 
3190
    takes_options = ['directory',
 
3191
                     Option('default-rules',
 
3192
                            help='Display the default ignore rules that brz uses.')
 
3193
                     ]
 
3194
 
 
3195
    def run(self, name_pattern_list=None, default_rules=None,
 
3196
            directory=u'.'):
 
3197
        from breezy import ignores
 
3198
        if default_rules is not None:
 
3199
            # dump the default rules and exit
 
3200
            for pattern in ignores.USER_DEFAULTS:
 
3201
                self.outf.write("%s\n" % pattern)
 
3202
            return
 
3203
        if not name_pattern_list:
 
3204
            raise errors.BzrCommandError(gettext("ignore requires at least one "
 
3205
                                                 "NAME_PATTERN or --default-rules."))
 
3206
        name_pattern_list = [globbing.normalize_pattern(p)
 
3207
                             for p in name_pattern_list]
 
3208
        bad_patterns = ''
 
3209
        bad_patterns_count = 0
 
3210
        for p in name_pattern_list:
 
3211
            if not globbing.Globster.is_pattern_valid(p):
 
3212
                bad_patterns_count += 1
 
3213
                bad_patterns += ('\n  %s' % p)
 
3214
        if bad_patterns:
 
3215
            msg = (ngettext('Invalid ignore pattern found. %s',
 
3216
                            'Invalid ignore patterns found. %s',
 
3217
                            bad_patterns_count) % bad_patterns)
 
3218
            ui.ui_factory.show_error(msg)
 
3219
            raise lazy_regex.InvalidPattern('')
 
3220
        for name_pattern in name_pattern_list:
 
3221
            if (name_pattern[0] == '/' or
 
3222
                    (len(name_pattern) > 1 and name_pattern[1] == ':')):
 
3223
                raise errors.BzrCommandError(gettext(
 
3224
                    "NAME_PATTERN should not be an absolute path"))
 
3225
        tree, relpath = WorkingTree.open_containing(directory)
 
3226
        ignores.tree_ignores_add_patterns(tree, name_pattern_list)
 
3227
        ignored = globbing.Globster(name_pattern_list)
 
3228
        matches = []
 
3229
        self.enter_context(tree.lock_read())
 
3230
        for filename, fc, fkind, entry in tree.list_files():
 
3231
            id = getattr(entry, 'file_id', None)
 
3232
            if id is not None:
 
3233
                if ignored.match(filename):
 
3234
                    matches.append(filename)
 
3235
        if len(matches) > 0:
 
3236
            self.outf.write(gettext("Warning: the following files are version "
 
3237
                                    "controlled and match your ignore pattern:\n%s"
 
3238
                                    "\nThese files will continue to be version controlled"
 
3239
                                    " unless you 'brz remove' them.\n") % ("\n".join(matches),))
 
3240
 
 
3241
 
 
3242
class cmd_ignored(Command):
 
3243
    __doc__ = """List ignored files and the patterns that matched them.
 
3244
 
 
3245
    List all the ignored files and the ignore pattern that caused the file to
 
3246
    be ignored.
 
3247
 
 
3248
    Alternatively, to list just the files::
 
3249
 
 
3250
        brz ls --ignored
 
3251
    """
 
3252
 
 
3253
    encoding_type = 'replace'
 
3254
    _see_also = ['ignore', 'ls']
 
3255
    takes_options = ['directory']
 
3256
 
 
3257
    @display_command
 
3258
    def run(self, directory=u'.'):
 
3259
        tree = WorkingTree.open_containing(directory)[0]
 
3260
        self.enter_context(tree.lock_read())
 
3261
        for path, file_class, kind, entry in tree.list_files():
 
3262
            if file_class != 'I':
 
3263
                continue
 
3264
            # XXX: Slightly inefficient since this was already calculated
 
3265
            pat = tree.is_ignored(path)
 
3266
            self.outf.write('%-50s %s\n' % (path, pat))
 
3267
 
 
3268
 
 
3269
class cmd_lookup_revision(Command):
 
3270
    __doc__ = """Lookup the revision-id from a revision-number
 
3271
 
 
3272
    :Examples:
 
3273
        brz lookup-revision 33
 
3274
    """
 
3275
    hidden = True
 
3276
    takes_args = ['revno']
 
3277
    takes_options = ['directory']
 
3278
 
 
3279
    @display_command
 
3280
    def run(self, revno, directory=u'.'):
 
3281
        try:
 
3282
            revno = int(revno)
 
3283
        except ValueError:
 
3284
            raise errors.BzrCommandError(gettext("not a valid revision-number: %r")
 
3285
                                         % revno)
 
3286
        revid = WorkingTree.open_containing(
 
3287
            directory)[0].branch.get_rev_id(revno)
 
3288
        self.outf.write("%s\n" % revid.decode('utf-8'))
 
3289
 
 
3290
 
 
3291
class cmd_export(Command):
 
3292
    __doc__ = """Export current or past revision to a destination directory or archive.
 
3293
 
 
3294
    If no revision is specified this exports the last committed revision.
 
3295
 
 
3296
    Format may be an "exporter" name, such as tar, tgz, tbz2.  If none is
 
3297
    given, try to find the format with the extension. If no extension
 
3298
    is found exports to a directory (equivalent to --format=dir).
 
3299
 
 
3300
    If root is supplied, it will be used as the root directory inside
 
3301
    container formats (tar, zip, etc). If it is not supplied it will default
 
3302
    to the exported filename. The root option has no effect for 'dir' format.
 
3303
 
 
3304
    If branch is omitted then the branch containing the current working
 
3305
    directory will be used.
 
3306
 
 
3307
    Note: Export of tree with non-ASCII filenames to zip is not supported.
 
3308
 
 
3309
      =================       =========================
 
3310
      Supported formats       Autodetected by extension
 
3311
      =================       =========================
 
3312
         dir                         (none)
 
3313
         tar                          .tar
 
3314
         tbz2                    .tar.bz2, .tbz2
 
3315
         tgz                      .tar.gz, .tgz
 
3316
         zip                          .zip
 
3317
      =================       =========================
 
3318
    """
 
3319
    encoding = 'exact'
 
3320
    encoding_type = 'exact'
 
3321
    takes_args = ['dest', 'branch_or_subdir?']
 
3322
    takes_options = ['directory',
 
3323
                     Option('format',
 
3324
                            help="Type of file to export to.",
 
3325
                            type=str),
 
3326
                     'revision',
 
3327
                     Option('filters', help='Apply content filters to export the '
 
3328
                            'convenient form.'),
 
3329
                     Option('root',
 
3330
                            type=str,
 
3331
                            help="Name of the root directory inside the exported file."),
 
3332
                     Option('per-file-timestamps',
 
3333
                            help='Set modification time of files to that of the last '
 
3334
                            'revision in which it was changed.'),
 
3335
                     Option('uncommitted',
 
3336
                            help='Export the working tree contents rather than that of the '
 
3337
                            'last revision.'),
 
3338
                     ]
 
3339
 
 
3340
    def run(self, dest, branch_or_subdir=None, revision=None, format=None,
 
3341
            root=None, filters=False, per_file_timestamps=False, uncommitted=False,
 
3342
            directory=u'.'):
 
3343
        from .export import export, guess_format, get_root_name
 
3344
 
 
3345
        if branch_or_subdir is None:
 
3346
            branch_or_subdir = directory
 
3347
 
 
3348
        (tree, b, subdir) = controldir.ControlDir.open_containing_tree_or_branch(
 
3349
            branch_or_subdir)
 
3350
        if tree is not None:
 
3351
            self.enter_context(tree.lock_read())
 
3352
 
 
3353
        if uncommitted:
 
3354
            if tree is None:
 
3355
                raise errors.BzrCommandError(
 
3356
                    gettext("--uncommitted requires a working tree"))
 
3357
            export_tree = tree
 
3358
        else:
 
3359
            export_tree = _get_one_revision_tree(
 
3360
                'export', revision, branch=b,
 
3361
                tree=tree)
 
3362
 
 
3363
        if format is None:
 
3364
            format = guess_format(dest)
 
3365
 
 
3366
        if root is None:
 
3367
            root = get_root_name(dest)
 
3368
 
 
3369
        if not per_file_timestamps:
 
3370
            force_mtime = time.time()
 
3371
        else:
 
3372
            force_mtime = None
 
3373
 
 
3374
        if filters:
 
3375
            from breezy.filter_tree import ContentFilterTree
 
3376
            export_tree = ContentFilterTree(
 
3377
                export_tree, export_tree._content_filter_stack)
 
3378
 
 
3379
        try:
 
3380
            export(export_tree, dest, format, root, subdir,
 
3381
                   per_file_timestamps=per_file_timestamps)
 
3382
        except errors.NoSuchExportFormat as e:
 
3383
            raise errors.BzrCommandError(
 
3384
                gettext('Unsupported export format: %s') % e.format)
 
3385
 
 
3386
 
 
3387
class cmd_cat(Command):
 
3388
    __doc__ = """Write the contents of a file as of a given revision to standard output.
 
3389
 
 
3390
    If no revision is nominated, the last revision is used.
 
3391
 
 
3392
    Note: Take care to redirect standard output when using this command on a
 
3393
    binary file.
 
3394
    """
 
3395
 
 
3396
    _see_also = ['ls']
 
3397
    takes_options = ['directory',
 
3398
                     Option('name-from-revision',
 
3399
                            help='The path name in the old tree.'),
 
3400
                     Option('filters', help='Apply content filters to display the '
 
3401
                            'convenience form.'),
 
3402
                     'revision',
 
3403
                     ]
 
3404
    takes_args = ['filename']
 
3405
    encoding_type = 'exact'
 
3406
 
 
3407
    @display_command
 
3408
    def run(self, filename, revision=None, name_from_revision=False,
 
3409
            filters=False, directory=None):
 
3410
        if revision is not None and len(revision) != 1:
 
3411
            raise errors.BzrCommandError(gettext("brz cat --revision takes exactly"
 
3412
                                                 " one revision specifier"))
 
3413
        tree, branch, relpath = \
 
3414
            _open_directory_or_containing_tree_or_branch(filename, directory)
 
3415
        self.enter_context(branch.lock_read())
 
3416
        return self._run(tree, branch, relpath, filename, revision,
 
3417
                         name_from_revision, filters)
 
3418
 
 
3419
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
 
3420
             filtered):
 
3421
        import shutil
 
3422
        if tree is None:
 
3423
            tree = b.basis_tree()
 
3424
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
 
3425
        self.enter_context(rev_tree.lock_read())
 
3426
 
 
3427
        if name_from_revision:
 
3428
            # Try in revision if requested
 
3429
            if not rev_tree.is_versioned(relpath):
 
3430
                raise errors.BzrCommandError(gettext(
 
3431
                    "{0!r} is not present in revision {1}").format(
 
3432
                        filename, rev_tree.get_revision_id()))
 
3433
            rev_tree_path = relpath
 
3434
        else:
 
3435
            try:
 
3436
                rev_tree_path = _mod_tree.find_previous_path(
 
3437
                    tree, rev_tree, relpath)
 
3438
            except errors.NoSuchFile:
 
3439
                rev_tree_path = None
 
3440
 
 
3441
            if rev_tree_path is None:
 
3442
                # Path didn't exist in working tree
 
3443
                if not rev_tree.is_versioned(relpath):
 
3444
                    raise errors.BzrCommandError(gettext(
 
3445
                        "{0!r} is not present in revision {1}").format(
 
3446
                            filename, rev_tree.get_revision_id()))
 
3447
                else:
 
3448
                    # Fall back to the same path in the basis tree, if present.
 
3449
                    rev_tree_path = relpath
 
3450
 
 
3451
        if filtered:
 
3452
            from .filter_tree import ContentFilterTree
 
3453
            filter_tree = ContentFilterTree(
 
3454
                rev_tree, rev_tree._content_filter_stack)
 
3455
            fileobj = filter_tree.get_file(rev_tree_path)
 
3456
        else:
 
3457
            fileobj = rev_tree.get_file(rev_tree_path)
 
3458
        shutil.copyfileobj(fileobj, self.outf)
 
3459
        self.cleanup_now()
 
3460
 
 
3461
 
 
3462
class cmd_local_time_offset(Command):
 
3463
    __doc__ = """Show the offset in seconds from GMT to local time."""
 
3464
    hidden = True
 
3465
 
 
3466
    @display_command
 
3467
    def run(self):
 
3468
        self.outf.write("%s\n" % osutils.local_time_offset())
 
3469
 
 
3470
 
 
3471
class cmd_commit(Command):
 
3472
    __doc__ = """Commit changes into a new revision.
 
3473
 
 
3474
    An explanatory message needs to be given for each commit. This is
 
3475
    often done by using the --message option (getting the message from the
 
3476
    command line) or by using the --file option (getting the message from
 
3477
    a file). If neither of these options is given, an editor is opened for
 
3478
    the user to enter the message. To see the changed files in the
 
3479
    boilerplate text loaded into the editor, use the --show-diff option.
 
3480
 
 
3481
    By default, the entire tree is committed and the person doing the
 
3482
    commit is assumed to be the author. These defaults can be overridden
 
3483
    as explained below.
 
3484
 
 
3485
    :Selective commits:
 
3486
 
 
3487
      If selected files are specified, only changes to those files are
 
3488
      committed.  If a directory is specified then the directory and
 
3489
      everything within it is committed.
 
3490
 
 
3491
      When excludes are given, they take precedence over selected files.
 
3492
      For example, to commit only changes within foo, but not changes
 
3493
      within foo/bar::
 
3494
 
 
3495
        brz commit foo -x foo/bar
 
3496
 
 
3497
      A selective commit after a merge is not yet supported.
 
3498
 
 
3499
    :Custom authors:
 
3500
 
 
3501
      If the author of the change is not the same person as the committer,
 
3502
      you can specify the author's name using the --author option. The
 
3503
      name should be in the same format as a committer-id, e.g.
 
3504
      "John Doe <jdoe@example.com>". If there is more than one author of
 
3505
      the change you can specify the option multiple times, once for each
 
3506
      author.
 
3507
 
 
3508
    :Checks:
 
3509
 
 
3510
      A common mistake is to forget to add a new file or directory before
 
3511
      running the commit command. The --strict option checks for unknown
 
3512
      files and aborts the commit if any are found. More advanced pre-commit
 
3513
      checks can be implemented by defining hooks. See ``brz help hooks``
 
3514
      for details.
 
3515
 
 
3516
    :Things to note:
 
3517
 
 
3518
      If you accidentally commit the wrong changes or make a spelling
 
3519
      mistake in the commit message say, you can use the uncommit command
 
3520
      to undo it. See ``brz help uncommit`` for details.
 
3521
 
 
3522
      Hooks can also be configured to run after a commit. This allows you
 
3523
      to trigger updates to external systems like bug trackers. The --fixes
 
3524
      option can be used to record the association between a revision and
 
3525
      one or more bugs. See ``brz help bugs`` for details.
 
3526
    """
 
3527
 
 
3528
    _see_also = ['add', 'bugs', 'hooks', 'uncommit']
 
3529
    takes_args = ['selected*']
 
3530
    takes_options = [
 
3531
        ListOption(
 
3532
            'exclude', type=str, short_name='x',
 
3533
            help="Do not consider changes made to a given path."),
 
3534
        Option('message', type=str,
 
3535
               short_name='m',
 
3536
               help="Description of the new revision."),
 
3537
        'verbose',
 
3538
        Option('unchanged',
 
3539
               help='Commit even if nothing has changed.'),
 
3540
        Option('file', type=str,
 
3541
               short_name='F',
 
3542
               argname='msgfile',
 
3543
               help='Take commit message from this file.'),
 
3544
        Option('strict',
 
3545
               help="Refuse to commit if there are unknown "
 
3546
               "files in the working tree."),
 
3547
        Option('commit-time', type=str,
 
3548
               help="Manually set a commit time using commit date "
 
3549
               "format, e.g. '2009-10-10 08:00:00 +0100'."),
 
3550
        ListOption(
 
3551
            'bugs', type=str,
 
3552
            help="Link to a related bug. (see \"brz help bugs\")."),
 
3553
        ListOption(
 
3554
            'fixes', type=str,
 
3555
            help="Mark a bug as being fixed by this revision "
 
3556
                 "(see \"brz help bugs\")."),
 
3557
        ListOption(
 
3558
            'author', type=str,
 
3559
            help="Set the author's name, if it's different "
 
3560
                 "from the committer."),
 
3561
        Option('local',
 
3562
               help="Perform a local commit in a bound "
 
3563
                    "branch.  Local commits are not pushed to "
 
3564
                    "the master branch until a normal commit "
 
3565
                    "is performed."
 
3566
               ),
 
3567
        Option('show-diff', short_name='p',
 
3568
               help='When no message is supplied, show the diff along'
 
3569
               ' with the status summary in the message editor.'),
 
3570
        Option('lossy',
 
3571
               help='When committing to a foreign version control '
 
3572
               'system do not push data that can not be natively '
 
3573
               'represented.'), ]
 
3574
    aliases = ['ci', 'checkin']
 
3575
 
 
3576
    def _iter_bug_urls(self, bugs, branch, status):
 
3577
        default_bugtracker = None
 
3578
        # Configure the properties for bug fixing attributes.
 
3579
        for bug in bugs:
 
3580
            tokens = bug.split(':')
 
3581
            if len(tokens) == 1:
 
3582
                if default_bugtracker is None:
 
3583
                    branch_config = branch.get_config_stack()
 
3584
                    default_bugtracker = branch_config.get(
 
3585
                        "bugtracker")
 
3586
                if default_bugtracker is None:
 
3587
                    raise errors.BzrCommandError(gettext(
 
3588
                        "No tracker specified for bug %s. Use the form "
 
3589
                        "'tracker:id' or specify a default bug tracker "
 
3590
                        "using the `bugtracker` option.\nSee "
 
3591
                        "\"brz help bugs\" for more information on this "
 
3592
                        "feature. Commit refused.") % bug)
 
3593
                tag = default_bugtracker
 
3594
                bug_id = tokens[0]
 
3595
            elif len(tokens) != 2:
 
3596
                raise errors.BzrCommandError(gettext(
 
3597
                    "Invalid bug %s. Must be in the form of 'tracker:id'. "
 
3598
                    "See \"brz help bugs\" for more information on this "
 
3599
                    "feature.\nCommit refused.") % bug)
 
3600
            else:
 
3601
                tag, bug_id = tokens
 
3602
            try:
 
3603
                yield bugtracker.get_bug_url(tag, branch, bug_id), status
 
3604
            except bugtracker.UnknownBugTrackerAbbreviation:
 
3605
                raise errors.BzrCommandError(gettext(
 
3606
                    'Unrecognized bug %s. Commit refused.') % bug)
 
3607
            except bugtracker.MalformedBugIdentifier as e:
 
3608
                raise errors.BzrCommandError(gettext(
 
3609
                    u"%s\nCommit refused.") % (e,))
 
3610
 
 
3611
    def run(self, message=None, file=None, verbose=False, selected_list=None,
 
3612
            unchanged=False, strict=False, local=False, fixes=None, bugs=None,
 
3613
            author=None, show_diff=False, exclude=None, commit_time=None,
 
3614
            lossy=False):
 
3615
        import itertools
 
3616
        from .commit import (
 
3617
            PointlessCommit,
 
3618
            )
 
3619
        from .errors import (
 
3620
            ConflictsInTree,
 
3621
            StrictCommitFailed
 
3622
        )
 
3623
        from .msgeditor import (
 
3624
            edit_commit_message_encoded,
 
3625
            generate_commit_message_template,
 
3626
            make_commit_message_template_encoded,
 
3627
            set_commit_message,
 
3628
        )
 
3629
 
 
3630
        commit_stamp = offset = None
 
3631
        if commit_time is not None:
 
3632
            try:
 
3633
                commit_stamp, offset = timestamp.parse_patch_date(commit_time)
 
3634
            except ValueError as e:
 
3635
                raise errors.BzrCommandError(gettext(
 
3636
                    "Could not parse --commit-time: " + str(e)))
 
3637
 
 
3638
        properties = {}
 
3639
 
 
3640
        tree, selected_list = WorkingTree.open_containing_paths(selected_list)
 
3641
        if selected_list == ['']:
 
3642
            # workaround - commit of root of tree should be exactly the same
 
3643
            # as just default commit in that tree, and succeed even though
 
3644
            # selected-file merge commit is not done yet
 
3645
            selected_list = []
 
3646
 
 
3647
        if fixes is None:
 
3648
            fixes = []
 
3649
        if bugs is None:
 
3650
            bugs = []
 
3651
        bug_property = bugtracker.encode_fixes_bug_urls(
 
3652
            itertools.chain(
 
3653
                self._iter_bug_urls(bugs, tree.branch, bugtracker.RELATED),
 
3654
                self._iter_bug_urls(fixes, tree.branch, bugtracker.FIXED)))
 
3655
        if bug_property:
 
3656
            properties[u'bugs'] = bug_property
 
3657
 
 
3658
        if local and not tree.branch.get_bound_location():
 
3659
            raise errors.LocalRequiresBoundBranch()
 
3660
 
 
3661
        if message is not None:
 
3662
            try:
 
3663
                file_exists = osutils.lexists(message)
 
3664
            except UnicodeError:
 
3665
                # The commit message contains unicode characters that can't be
 
3666
                # represented in the filesystem encoding, so that can't be a
 
3667
                # file.
 
3668
                file_exists = False
 
3669
            if file_exists:
 
3670
                warning_msg = (
 
3671
                    'The commit message is a file name: "%(f)s".\n'
 
3672
                    '(use --file "%(f)s" to take commit message from that file)'
 
3673
                    % {'f': message})
 
3674
                ui.ui_factory.show_warning(warning_msg)
 
3675
            if '\r' in message:
 
3676
                message = message.replace('\r\n', '\n')
 
3677
                message = message.replace('\r', '\n')
 
3678
            if file:
 
3679
                raise errors.BzrCommandError(gettext(
 
3680
                    "please specify either --message or --file"))
 
3681
 
 
3682
        def get_message(commit_obj):
 
3683
            """Callback to get commit message"""
 
3684
            if file:
 
3685
                with open(file, 'rb') as f:
 
3686
                    my_message = f.read().decode(osutils.get_user_encoding())
 
3687
            elif message is not None:
 
3688
                my_message = message
 
3689
            else:
 
3690
                # No message supplied: make one up.
 
3691
                # text is the status of the tree
 
3692
                text = make_commit_message_template_encoded(tree,
 
3693
                                                            selected_list, diff=show_diff,
 
3694
                                                            output_encoding=osutils.get_user_encoding())
 
3695
                # start_message is the template generated from hooks
 
3696
                # XXX: Warning - looks like hooks return unicode,
 
3697
                # make_commit_message_template_encoded returns user encoding.
 
3698
                # We probably want to be using edit_commit_message instead to
 
3699
                # avoid this.
 
3700
                my_message = set_commit_message(commit_obj)
 
3701
                if my_message is None:
 
3702
                    start_message = generate_commit_message_template(
 
3703
                        commit_obj)
 
3704
                    if start_message is not None:
 
3705
                        start_message = start_message.encode(
 
3706
                            osutils.get_user_encoding())
 
3707
                    my_message = edit_commit_message_encoded(text,
 
3708
                                                             start_message=start_message)
 
3709
                if my_message is None:
 
3710
                    raise errors.BzrCommandError(gettext("please specify a commit"
 
3711
                                                         " message with either --message or --file"))
 
3712
                if my_message == "":
 
3713
                    raise errors.BzrCommandError(gettext("Empty commit message specified."
 
3714
                                                         " Please specify a commit message with either"
 
3715
                                                         " --message or --file or leave a blank message"
 
3716
                                                         " with --message \"\"."))
 
3717
            return my_message
 
3718
 
 
3719
        # The API permits a commit with a filter of [] to mean 'select nothing'
 
3720
        # but the command line should not do that.
 
3721
        if not selected_list:
 
3722
            selected_list = None
 
3723
        try:
 
3724
            tree.commit(message_callback=get_message,
 
3725
                        specific_files=selected_list,
 
3726
                        allow_pointless=unchanged, strict=strict, local=local,
 
3727
                        reporter=None, verbose=verbose, revprops=properties,
 
3728
                        authors=author, timestamp=commit_stamp,
 
3729
                        timezone=offset,
 
3730
                        exclude=tree.safe_relpath_files(exclude),
 
3731
                        lossy=lossy)
 
3732
        except PointlessCommit:
 
3733
            raise errors.BzrCommandError(gettext("No changes to commit."
 
3734
                                                 " Please 'brz add' the files you want to commit, or use"
 
3735
                                                 " --unchanged to force an empty commit."))
 
3736
        except ConflictsInTree:
 
3737
            raise errors.BzrCommandError(gettext('Conflicts detected in working '
 
3738
                                                 'tree.  Use "brz conflicts" to list, "brz resolve FILE" to'
 
3739
                                                 ' resolve.'))
 
3740
        except StrictCommitFailed:
 
3741
            raise errors.BzrCommandError(gettext("Commit refused because there are"
 
3742
                                                 " unknown files in the working tree."))
 
3743
        except errors.BoundBranchOutOfDate as e:
 
3744
            e.extra_help = (gettext("\n"
 
3745
                                    'To commit to master branch, run update and then commit.\n'
 
3746
                                    'You can also pass --local to commit to continue working '
 
3747
                                    'disconnected.'))
 
3748
            raise
 
3749
 
 
3750
 
 
3751
class cmd_check(Command):
 
3752
    __doc__ = """Validate working tree structure, branch consistency and repository history.
 
3753
 
 
3754
    This command checks various invariants about branch and repository storage
 
3755
    to detect data corruption or brz bugs.
 
3756
 
 
3757
    The working tree and branch checks will only give output if a problem is
 
3758
    detected. The output fields of the repository check are:
 
3759
 
 
3760
    revisions
 
3761
        This is just the number of revisions checked.  It doesn't
 
3762
        indicate a problem.
 
3763
 
 
3764
    versionedfiles
 
3765
        This is just the number of versionedfiles checked.  It
 
3766
        doesn't indicate a problem.
 
3767
 
 
3768
    unreferenced ancestors
 
3769
        Texts that are ancestors of other texts, but
 
3770
        are not properly referenced by the revision ancestry.  This is a
 
3771
        subtle problem that Breezy can work around.
 
3772
 
 
3773
    unique file texts
 
3774
        This is the total number of unique file contents
 
3775
        seen in the checked revisions.  It does not indicate a problem.
 
3776
 
 
3777
    repeated file texts
 
3778
        This is the total number of repeated texts seen
 
3779
        in the checked revisions.  Texts can be repeated when their file
 
3780
        entries are modified, but the file contents are not.  It does not
 
3781
        indicate a problem.
 
3782
 
 
3783
    If no restrictions are specified, all data that is found at the given
 
3784
    location will be checked.
 
3785
 
 
3786
    :Examples:
 
3787
 
 
3788
        Check the tree and branch at 'foo'::
 
3789
 
 
3790
            brz check --tree --branch foo
 
3791
 
 
3792
        Check only the repository at 'bar'::
 
3793
 
 
3794
            brz check --repo bar
 
3795
 
 
3796
        Check everything at 'baz'::
 
3797
 
 
3798
            brz check baz
 
3799
    """
 
3800
 
 
3801
    _see_also = ['reconcile']
 
3802
    takes_args = ['path?']
 
3803
    takes_options = ['verbose',
 
3804
                     Option('branch', help="Check the branch related to the"
 
3805
                                           " current directory."),
 
3806
                     Option('repo', help="Check the repository related to the"
 
3807
                                         " current directory."),
 
3808
                     Option('tree', help="Check the working tree related to"
 
3809
                                         " the current directory.")]
 
3810
 
 
3811
    def run(self, path=None, verbose=False, branch=False, repo=False,
 
3812
            tree=False):
 
3813
        from .check import check_dwim
 
3814
        if path is None:
 
3815
            path = '.'
 
3816
        if not branch and not repo and not tree:
 
3817
            branch = repo = tree = True
 
3818
        check_dwim(path, verbose, do_branch=branch, do_repo=repo, do_tree=tree)
 
3819
 
 
3820
 
 
3821
class cmd_upgrade(Command):
 
3822
    __doc__ = """Upgrade a repository, branch or working tree to a newer format.
 
3823
 
 
3824
    When the default format has changed after a major new release of
 
3825
    Bazaar/Breezy, you may be informed during certain operations that you
 
3826
    should upgrade. Upgrading to a newer format may improve performance
 
3827
    or make new features available. It may however limit interoperability
 
3828
    with older repositories or with older versions of Bazaar or Breezy.
 
3829
 
 
3830
    If you wish to upgrade to a particular format rather than the
 
3831
    current default, that can be specified using the --format option.
 
3832
    As a consequence, you can use the upgrade command this way to
 
3833
    "downgrade" to an earlier format, though some conversions are
 
3834
    a one way process (e.g. changing from the 1.x default to the
 
3835
    2.x default) so downgrading is not always possible.
 
3836
 
 
3837
    A backup.bzr.~#~ directory is created at the start of the conversion
 
3838
    process (where # is a number). By default, this is left there on
 
3839
    completion. If the conversion fails, delete the new .bzr directory
 
3840
    and rename this one back in its place. Use the --clean option to ask
 
3841
    for the backup.bzr directory to be removed on successful conversion.
 
3842
    Alternatively, you can delete it by hand if everything looks good
 
3843
    afterwards.
 
3844
 
 
3845
    If the location given is a shared repository, dependent branches
 
3846
    are also converted provided the repository converts successfully.
 
3847
    If the conversion of a branch fails, remaining branches are still
 
3848
    tried.
 
3849
 
 
3850
    For more information on upgrades, see the Breezy Upgrade Guide,
 
3851
    https://www.breezy-vcs.org/doc/en/upgrade-guide/.
 
3852
    """
 
3853
 
 
3854
    _see_also = ['check', 'reconcile', 'formats']
 
3855
    takes_args = ['url?']
 
3856
    takes_options = [
 
3857
        RegistryOption('format',
 
3858
                       help='Upgrade to a specific format.  See "brz help'
 
3859
                       ' formats" for details.',
 
3860
                       lazy_registry=('breezy.controldir', 'format_registry'),
 
3861
                       converter=lambda name: controldir.format_registry.make_controldir(
 
3862
                           name),
 
3863
                       value_switches=True, title='Branch format'),
 
3864
        Option('clean',
 
3865
               help='Remove the backup.bzr directory if successful.'),
 
3866
        Option('dry-run',
 
3867
               help="Show what would be done, but don't actually do anything."),
 
3868
    ]
 
3869
 
 
3870
    def run(self, url='.', format=None, clean=False, dry_run=False):
 
3871
        from .upgrade import upgrade
 
3872
        exceptions = upgrade(url, format, clean_up=clean, dry_run=dry_run)
 
3873
        if exceptions:
 
3874
            if len(exceptions) == 1:
 
3875
                # Compatibility with historical behavior
 
3876
                raise exceptions[0]
 
3877
            else:
 
3878
                return 3
 
3879
 
 
3880
 
 
3881
class cmd_whoami(Command):
 
3882
    __doc__ = """Show or set brz user id.
 
3883
 
 
3884
    :Examples:
 
3885
        Show the email of the current user::
 
3886
 
 
3887
            brz whoami --email
 
3888
 
 
3889
        Set the current user::
 
3890
 
 
3891
            brz whoami "Frank Chu <fchu@example.com>"
 
3892
    """
 
3893
    takes_options = ['directory',
 
3894
                     Option('email',
 
3895
                            help='Display email address only.'),
 
3896
                     Option('branch',
 
3897
                            help='Set identity for the current branch instead of '
 
3898
                            'globally.'),
 
3899
                     ]
 
3900
    takes_args = ['name?']
 
3901
    encoding_type = 'replace'
 
3902
 
 
3903
    @display_command
 
3904
    def run(self, email=False, branch=False, name=None, directory=None):
 
3905
        if name is None:
 
3906
            if directory is None:
 
3907
                # use branch if we're inside one; otherwise global config
 
3908
                try:
 
3909
                    c = Branch.open_containing(u'.')[0].get_config_stack()
 
3910
                except errors.NotBranchError:
 
3911
                    c = _mod_config.GlobalStack()
 
3912
            else:
 
3913
                c = Branch.open(directory).get_config_stack()
 
3914
            identity = c.get('email')
 
3915
            if email:
 
3916
                self.outf.write(_mod_config.extract_email_address(identity)
 
3917
                                + '\n')
 
3918
            else:
 
3919
                self.outf.write(identity + '\n')
 
3920
            return
 
3921
 
 
3922
        if email:
 
3923
            raise errors.BzrCommandError(gettext("--email can only be used to display existing "
 
3924
                                                 "identity"))
 
3925
 
 
3926
        # display a warning if an email address isn't included in the given name.
 
3927
        try:
 
3928
            _mod_config.extract_email_address(name)
 
3929
        except _mod_config.NoEmailInUsername:
 
3930
            warning('"%s" does not seem to contain an email address.  '
 
3931
                    'This is allowed, but not recommended.', name)
 
3932
 
 
3933
        # use global config unless --branch given
 
3934
        if branch:
 
3935
            if directory is None:
 
3936
                c = Branch.open_containing(u'.')[0].get_config_stack()
 
3937
            else:
 
3938
                b = Branch.open(directory)
 
3939
                self.enter_context(b.lock_write())
 
3940
                c = b.get_config_stack()
 
3941
        else:
 
3942
            c = _mod_config.GlobalStack()
 
3943
        c.set('email', name)
 
3944
 
 
3945
 
 
3946
class cmd_nick(Command):
 
3947
    __doc__ = """Print or set the branch nickname.
 
3948
 
 
3949
    If unset, the colocated branch name is used for colocated branches, and
 
3950
    the branch directory name is used for other branches.  To print the
 
3951
    current nickname, execute with no argument.
 
3952
 
 
3953
    Bound branches use the nickname of its master branch unless it is set
 
3954
    locally.
 
3955
    """
 
3956
 
 
3957
    _see_also = ['info']
 
3958
    takes_args = ['nickname?']
 
3959
    takes_options = ['directory']
 
3960
 
 
3961
    def run(self, nickname=None, directory=u'.'):
 
3962
        branch = Branch.open_containing(directory)[0]
 
3963
        if nickname is None:
 
3964
            self.printme(branch)
 
3965
        else:
 
3966
            branch.nick = nickname
 
3967
 
 
3968
    @display_command
 
3969
    def printme(self, branch):
 
3970
        self.outf.write('%s\n' % branch.nick)
 
3971
 
 
3972
 
 
3973
class cmd_alias(Command):
 
3974
    __doc__ = """Set/unset and display aliases.
 
3975
 
 
3976
    :Examples:
 
3977
        Show the current aliases::
 
3978
 
 
3979
            brz alias
 
3980
 
 
3981
        Show the alias specified for 'll'::
 
3982
 
 
3983
            brz alias ll
 
3984
 
 
3985
        Set an alias for 'll'::
 
3986
 
 
3987
            brz alias ll="log --line -r-10..-1"
 
3988
 
 
3989
        To remove an alias for 'll'::
 
3990
 
 
3991
            brz alias --remove ll
 
3992
 
 
3993
    """
 
3994
    takes_args = ['name?']
 
3995
    takes_options = [
 
3996
        Option('remove', help='Remove the alias.'),
 
3997
        ]
 
3998
 
 
3999
    def run(self, name=None, remove=False):
 
4000
        if remove:
 
4001
            self.remove_alias(name)
 
4002
        elif name is None:
 
4003
            self.print_aliases()
 
4004
        else:
 
4005
            equal_pos = name.find('=')
 
4006
            if equal_pos == -1:
 
4007
                self.print_alias(name)
 
4008
            else:
 
4009
                self.set_alias(name[:equal_pos], name[equal_pos + 1:])
 
4010
 
 
4011
    def remove_alias(self, alias_name):
 
4012
        if alias_name is None:
 
4013
            raise errors.BzrCommandError(gettext(
 
4014
                'brz alias --remove expects an alias to remove.'))
 
4015
        # If alias is not found, print something like:
 
4016
        # unalias: foo: not found
 
4017
        c = _mod_config.GlobalConfig()
 
4018
        c.unset_alias(alias_name)
 
4019
 
 
4020
    @display_command
 
4021
    def print_aliases(self):
 
4022
        """Print out the defined aliases in a similar format to bash."""
 
4023
        aliases = _mod_config.GlobalConfig().get_aliases()
 
4024
        for key, value in sorted(aliases.items()):
 
4025
            self.outf.write('brz alias %s="%s"\n' % (key, value))
 
4026
 
 
4027
    @display_command
 
4028
    def print_alias(self, alias_name):
 
4029
        from .commands import get_alias
 
4030
        alias = get_alias(alias_name)
 
4031
        if alias is None:
 
4032
            self.outf.write("brz alias: %s: not found\n" % alias_name)
 
4033
        else:
 
4034
            self.outf.write(
 
4035
                'brz alias %s="%s"\n' % (alias_name, ' '.join(alias)))
 
4036
 
 
4037
    def set_alias(self, alias_name, alias_command):
 
4038
        """Save the alias in the global config."""
 
4039
        c = _mod_config.GlobalConfig()
 
4040
        c.set_alias(alias_name, alias_command)
 
4041
 
 
4042
 
 
4043
class cmd_selftest(Command):
 
4044
    __doc__ = """Run internal test suite.
 
4045
 
 
4046
    If arguments are given, they are regular expressions that say which tests
 
4047
    should run.  Tests matching any expression are run, and other tests are
 
4048
    not run.
 
4049
 
 
4050
    Alternatively if --first is given, matching tests are run first and then
 
4051
    all other tests are run.  This is useful if you have been working in a
 
4052
    particular area, but want to make sure nothing else was broken.
 
4053
 
 
4054
    If --exclude is given, tests that match that regular expression are
 
4055
    excluded, regardless of whether they match --first or not.
 
4056
 
 
4057
    To help catch accidential dependencies between tests, the --randomize
 
4058
    option is useful. In most cases, the argument used is the word 'now'.
 
4059
    Note that the seed used for the random number generator is displayed
 
4060
    when this option is used. The seed can be explicitly passed as the
 
4061
    argument to this option if required. This enables reproduction of the
 
4062
    actual ordering used if and when an order sensitive problem is encountered.
 
4063
 
 
4064
    If --list-only is given, the tests that would be run are listed. This is
 
4065
    useful when combined with --first, --exclude and/or --randomize to
 
4066
    understand their impact. The test harness reports "Listed nn tests in ..."
 
4067
    instead of "Ran nn tests in ..." when list mode is enabled.
 
4068
 
 
4069
    If the global option '--no-plugins' is given, plugins are not loaded
 
4070
    before running the selftests.  This has two effects: features provided or
 
4071
    modified by plugins will not be tested, and tests provided by plugins will
 
4072
    not be run.
 
4073
 
 
4074
    Tests that need working space on disk use a common temporary directory,
 
4075
    typically inside $TMPDIR or /tmp.
 
4076
 
 
4077
    If you set BRZ_TEST_PDB=1 when running selftest, failing tests will drop
 
4078
    into a pdb postmortem session.
 
4079
 
 
4080
    The --coverage=DIRNAME global option produces a report with covered code
 
4081
    indicated.
 
4082
 
 
4083
    :Examples:
 
4084
        Run only tests relating to 'ignore'::
 
4085
 
 
4086
            brz selftest ignore
 
4087
 
 
4088
        Disable plugins and list tests as they're run::
 
4089
 
 
4090
            brz --no-plugins selftest -v
 
4091
    """
 
4092
    # NB: this is used from the class without creating an instance, which is
 
4093
    # why it does not have a self parameter.
 
4094
 
 
4095
    def get_transport_type(typestring):
 
4096
        """Parse and return a transport specifier."""
 
4097
        if typestring == "sftp":
 
4098
            from .tests import stub_sftp
 
4099
            return stub_sftp.SFTPAbsoluteServer
 
4100
        elif typestring == "memory":
 
4101
            from .tests import test_server
 
4102
            return memory.MemoryServer
 
4103
        elif typestring == "fakenfs":
 
4104
            from .tests import test_server
 
4105
            return test_server.FakeNFSServer
 
4106
        msg = "No known transport type %s. Supported types are: sftp\n" %\
 
4107
            (typestring)
 
4108
        raise errors.BzrCommandError(msg)
 
4109
 
 
4110
    hidden = True
 
4111
    takes_args = ['testspecs*']
 
4112
    takes_options = ['verbose',
 
4113
                     Option('one',
 
4114
                            help='Stop when one test fails.',
 
4115
                            short_name='1',
 
4116
                            ),
 
4117
                     Option('transport',
 
4118
                            help='Use a different transport by default '
 
4119
                                 'throughout the test suite.',
 
4120
                            type=get_transport_type),
 
4121
                     Option('benchmark',
 
4122
                            help='Run the benchmarks rather than selftests.',
 
4123
                            hidden=True),
 
4124
                     Option('lsprof-timed',
 
4125
                            help='Generate lsprof output for benchmarked'
 
4126
                                 ' sections of code.'),
 
4127
                     Option('lsprof-tests',
 
4128
                            help='Generate lsprof output for each test.'),
 
4129
                     Option('first',
 
4130
                            help='Run all tests, but run specified tests first.',
 
4131
                            short_name='f',
 
4132
                            ),
 
4133
                     Option('list-only',
 
4134
                            help='List the tests instead of running them.'),
 
4135
                     RegistryOption('parallel',
 
4136
                                    help="Run the test suite in parallel.",
 
4137
                                    lazy_registry=(
 
4138
                                        'breezy.tests', 'parallel_registry'),
 
4139
                                    value_switches=False,
 
4140
                                    ),
 
4141
                     Option('randomize', type=str, argname="SEED",
 
4142
                            help='Randomize the order of tests using the given'
 
4143
                                 ' seed or "now" for the current time.'),
 
4144
                     ListOption('exclude', type=str, argname="PATTERN",
 
4145
                                short_name='x',
 
4146
                                help='Exclude tests that match this regular'
 
4147
                                ' expression.'),
 
4148
                     Option('subunit1',
 
4149
                            help='Output test progress via subunit v1.'),
 
4150
                     Option('subunit2',
 
4151
                            help='Output test progress via subunit v2.'),
 
4152
                     Option('strict', help='Fail on missing dependencies or '
 
4153
                            'known failures.'),
 
4154
                     Option('load-list', type=str, argname='TESTLISTFILE',
 
4155
                            help='Load a test id list from a text file.'),
 
4156
                     ListOption('debugflag', type=str, short_name='E',
 
4157
                                help='Turn on a selftest debug flag.'),
 
4158
                     ListOption('starting-with', type=str, argname='TESTID',
 
4159
                                param_name='starting_with', short_name='s',
 
4160
                                help='Load only the tests starting with TESTID.'),
 
4161
                     Option('sync',
 
4162
                            help="By default we disable fsync and fdatasync"
 
4163
                                 " while running the test suite.")
 
4164
                     ]
 
4165
    encoding_type = 'replace'
 
4166
 
 
4167
    def __init__(self):
 
4168
        Command.__init__(self)
 
4169
        self.additional_selftest_args = {}
 
4170
 
 
4171
    def run(self, testspecs_list=None, verbose=False, one=False,
 
4172
            transport=None, benchmark=None,
 
4173
            lsprof_timed=None,
 
4174
            first=False, list_only=False,
 
4175
            randomize=None, exclude=None, strict=False,
 
4176
            load_list=None, debugflag=None, starting_with=None, subunit1=False,
 
4177
            subunit2=False, parallel=None, lsprof_tests=False, sync=False):
 
4178
 
 
4179
        # During selftest, disallow proxying, as it can cause severe
 
4180
        # performance penalties and is only needed for thread
 
4181
        # safety. The selftest command is assumed to not use threads
 
4182
        # too heavily. The call should be as early as possible, as
 
4183
        # error reporting for past duplicate imports won't have useful
 
4184
        # backtraces.
 
4185
        if sys.version_info[0] < 3:
 
4186
            # TODO(pad.lv/1696545): Allow proxying on Python 3, since
 
4187
            # disallowing it currently leads to failures in many places.
 
4188
            lazy_import.disallow_proxying()
 
4189
 
 
4190
        try:
 
4191
            from . import tests
 
4192
        except ImportError:
 
4193
            raise errors.BzrCommandError("tests not available. Install the "
 
4194
                                         "breezy tests to run the breezy testsuite.")
 
4195
 
 
4196
        if testspecs_list is not None:
 
4197
            pattern = '|'.join(testspecs_list)
 
4198
        else:
 
4199
            pattern = ".*"
 
4200
        if subunit1:
 
4201
            try:
 
4202
                from .tests import SubUnitBzrRunnerv1
 
4203
            except ImportError:
 
4204
                raise errors.BzrCommandError(gettext(
 
4205
                    "subunit not available. subunit needs to be installed "
 
4206
                    "to use --subunit."))
 
4207
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv1
 
4208
            # On Windows, disable automatic conversion of '\n' to '\r\n' in
 
4209
            # stdout, which would corrupt the subunit stream.
 
4210
            # FIXME: This has been fixed in subunit trunk (>0.0.5) so the
 
4211
            # following code can be deleted when it's sufficiently deployed
 
4212
            # -- vila/mgz 20100514
 
4213
            if (sys.platform == "win32"
 
4214
                    and getattr(sys.stdout, 'fileno', None) is not None):
 
4215
                import msvcrt
 
4216
                msvcrt.setmode(sys.stdout.fileno(), os.O_BINARY)
 
4217
        if subunit2:
 
4218
            try:
 
4219
                from .tests import SubUnitBzrRunnerv2
 
4220
            except ImportError:
 
4221
                raise errors.BzrCommandError(gettext(
 
4222
                    "subunit not available. subunit "
 
4223
                    "needs to be installed to use --subunit2."))
 
4224
            self.additional_selftest_args['runner_class'] = SubUnitBzrRunnerv2
 
4225
 
 
4226
        if parallel:
 
4227
            self.additional_selftest_args.setdefault(
 
4228
                'suite_decorators', []).append(parallel)
 
4229
        if benchmark:
 
4230
            raise errors.BzrCommandError(gettext(
 
4231
                "--benchmark is no longer supported from brz 2.2; "
 
4232
                "use bzr-usertest instead"))
 
4233
        test_suite_factory = None
 
4234
        if not exclude:
 
4235
            exclude_pattern = None
 
4236
        else:
 
4237
            exclude_pattern = '(' + '|'.join(exclude) + ')'
 
4238
        if not sync:
 
4239
            self._disable_fsync()
 
4240
        selftest_kwargs = {"verbose": verbose,
 
4241
                           "pattern": pattern,
 
4242
                           "stop_on_failure": one,
 
4243
                           "transport": transport,
 
4244
                           "test_suite_factory": test_suite_factory,
 
4245
                           "lsprof_timed": lsprof_timed,
 
4246
                           "lsprof_tests": lsprof_tests,
 
4247
                           "matching_tests_first": first,
 
4248
                           "list_only": list_only,
 
4249
                           "random_seed": randomize,
 
4250
                           "exclude_pattern": exclude_pattern,
 
4251
                           "strict": strict,
 
4252
                           "load_list": load_list,
 
4253
                           "debug_flags": debugflag,
 
4254
                           "starting_with": starting_with
 
4255
                           }
 
4256
        selftest_kwargs.update(self.additional_selftest_args)
 
4257
 
 
4258
        # Make deprecation warnings visible, unless -Werror is set
 
4259
        cleanup = symbol_versioning.activate_deprecation_warnings(
 
4260
            override=False)
 
4261
        try:
 
4262
            result = tests.selftest(**selftest_kwargs)
 
4263
        finally:
 
4264
            cleanup()
 
4265
        return int(not result)
 
4266
 
 
4267
    def _disable_fsync(self):
 
4268
        """Change the 'os' functionality to not synchronize."""
 
4269
        self._orig_fsync = getattr(os, 'fsync', None)
 
4270
        if self._orig_fsync is not None:
 
4271
            os.fsync = lambda filedes: None
 
4272
        self._orig_fdatasync = getattr(os, 'fdatasync', None)
 
4273
        if self._orig_fdatasync is not None:
 
4274
            os.fdatasync = lambda filedes: None
 
4275
 
 
4276
 
 
4277
class cmd_version(Command):
 
4278
    __doc__ = """Show version of brz."""
 
4279
 
 
4280
    encoding_type = 'replace'
 
4281
    takes_options = [
 
4282
        Option("short", help="Print just the version number."),
 
4283
        ]
 
4284
 
 
4285
    @display_command
 
4286
    def run(self, short=False):
 
4287
        from .version import show_version
 
4288
        if short:
 
4289
            self.outf.write(breezy.version_string + '\n')
 
4290
        else:
 
4291
            show_version(to_file=self.outf)
 
4292
 
 
4293
 
 
4294
class cmd_rocks(Command):
 
4295
    __doc__ = """Statement of optimism."""
 
4296
 
 
4297
    hidden = True
 
4298
 
 
4299
    @display_command
 
4300
    def run(self):
 
4301
        self.outf.write(gettext("It sure does!\n"))
 
4302
 
 
4303
 
 
4304
class cmd_find_merge_base(Command):
 
4305
    __doc__ = """Find and print a base revision for merging two branches."""
 
4306
    # TODO: Options to specify revisions on either side, as if
 
4307
    #       merging only part of the history.
 
4308
    takes_args = ['branch', 'other']
 
4309
    hidden = True
 
4310
 
 
4311
    @display_command
 
4312
    def run(self, branch, other):
 
4313
        from .revision import ensure_null
 
4314
 
 
4315
        branch1 = Branch.open_containing(branch)[0]
 
4316
        branch2 = Branch.open_containing(other)[0]
 
4317
        self.enter_context(branch1.lock_read())
 
4318
        self.enter_context(branch2.lock_read())
 
4319
        last1 = ensure_null(branch1.last_revision())
 
4320
        last2 = ensure_null(branch2.last_revision())
 
4321
 
 
4322
        graph = branch1.repository.get_graph(branch2.repository)
 
4323
        base_rev_id = graph.find_unique_lca(last1, last2)
 
4324
 
 
4325
        self.outf.write(gettext('merge base is revision %s\n') %
 
4326
                        base_rev_id.decode('utf-8'))
 
4327
 
 
4328
 
 
4329
class cmd_merge(Command):
 
4330
    __doc__ = """Perform a three-way merge.
 
4331
 
 
4332
    The source of the merge can be specified either in the form of a branch,
 
4333
    or in the form of a path to a file containing a merge directive generated
 
4334
    with brz send. If neither is specified, the default is the upstream branch
 
4335
    or the branch most recently merged using --remember.  The source of the
 
4336
    merge may also be specified in the form of a path to a file in another
 
4337
    branch:  in this case, only the modifications to that file are merged into
 
4338
    the current working tree.
 
4339
 
 
4340
    When merging from a branch, by default brz will try to merge in all new
 
4341
    work from the other branch, automatically determining an appropriate base
 
4342
    revision.  If this fails, you may need to give an explicit base.
 
4343
 
 
4344
    To pick a different ending revision, pass "--revision OTHER".  brz will
 
4345
    try to merge in all new work up to and including revision OTHER.
 
4346
 
 
4347
    If you specify two values, "--revision BASE..OTHER", only revisions BASE
 
4348
    through OTHER, excluding BASE but including OTHER, will be merged.  If this
 
4349
    causes some revisions to be skipped, i.e. if the destination branch does
 
4350
    not already contain revision BASE, such a merge is commonly referred to as
 
4351
    a "cherrypick". Unlike a normal merge, Breezy does not currently track
 
4352
    cherrypicks. The changes look like a normal commit, and the history of the
 
4353
    changes from the other branch is not stored in the commit.
 
4354
 
 
4355
    Revision numbers are always relative to the source branch.
 
4356
 
 
4357
    Merge will do its best to combine the changes in two branches, but there
 
4358
    are some kinds of problems only a human can fix.  When it encounters those,
 
4359
    it will mark a conflict.  A conflict means that you need to fix something,
 
4360
    before you can commit.
 
4361
 
 
4362
    Use brz resolve when you have fixed a problem.  See also brz conflicts.
 
4363
 
 
4364
    If there is no default branch set, the first merge will set it (use
 
4365
    --no-remember to avoid setting it). After that, you can omit the branch
 
4366
    to use the default.  To change the default, use --remember. The value will
 
4367
    only be saved if the remote location can be accessed.
 
4368
 
 
4369
    The results of the merge are placed into the destination working
 
4370
    directory, where they can be reviewed (with brz diff), tested, and then
 
4371
    committed to record the result of the merge.
 
4372
 
 
4373
    merge refuses to run if there are any uncommitted changes, unless
 
4374
    --force is given.  If --force is given, then the changes from the source
 
4375
    will be merged with the current working tree, including any uncommitted
 
4376
    changes in the tree.  The --force option can also be used to create a
 
4377
    merge revision which has more than two parents.
 
4378
 
 
4379
    If one would like to merge changes from the working tree of the other
 
4380
    branch without merging any committed revisions, the --uncommitted option
 
4381
    can be given.
 
4382
 
 
4383
    To select only some changes to merge, use "merge -i", which will prompt
 
4384
    you to apply each diff hunk and file change, similar to "shelve".
 
4385
 
 
4386
    :Examples:
 
4387
        To merge all new revisions from brz.dev::
 
4388
 
 
4389
            brz merge ../brz.dev
 
4390
 
 
4391
        To merge changes up to and including revision 82 from brz.dev::
 
4392
 
 
4393
            brz merge -r 82 ../brz.dev
 
4394
 
 
4395
        To merge the changes introduced by 82, without previous changes::
 
4396
 
 
4397
            brz merge -r 81..82 ../brz.dev
 
4398
 
 
4399
        To apply a merge directive contained in /tmp/merge::
 
4400
 
 
4401
            brz merge /tmp/merge
 
4402
 
 
4403
        To create a merge revision with three parents from two branches
 
4404
        feature1a and feature1b:
 
4405
 
 
4406
            brz merge ../feature1a
 
4407
            brz merge ../feature1b --force
 
4408
            brz commit -m 'revision with three parents'
 
4409
    """
 
4410
 
 
4411
    encoding_type = 'exact'
 
4412
    _see_also = ['update', 'remerge', 'status-flags', 'send']
 
4413
    takes_args = ['location?']
 
4414
    takes_options = [
 
4415
        'change',
 
4416
        'revision',
 
4417
        Option('force',
 
4418
               help='Merge even if the destination tree has uncommitted changes.'),
 
4419
        'merge-type',
 
4420
        'reprocess',
 
4421
        'remember',
 
4422
        Option('show-base', help="Show base revision text in "
 
4423
               "conflicts."),
 
4424
        Option('uncommitted', help='Apply uncommitted changes'
 
4425
               ' from a working copy, instead of branch changes.'),
 
4426
        Option('pull', help='If the destination is already'
 
4427
               ' completely merged into the source, pull from the'
 
4428
               ' source rather than merging.  When this happens,'
 
4429
               ' you do not need to commit the result.'),
 
4430
        custom_help('directory',
 
4431
                    help='Branch to merge into, '
 
4432
                    'rather than the one containing the working directory.'),
 
4433
        Option('preview', help='Instead of merging, show a diff of the'
 
4434
               ' merge.'),
 
4435
        Option('interactive', help='Select changes interactively.',
 
4436
               short_name='i')
 
4437
    ]
 
4438
 
 
4439
    def run(self, location=None, revision=None, force=False,
 
4440
            merge_type=None, show_base=False, reprocess=None, remember=None,
 
4441
            uncommitted=False, pull=False,
 
4442
            directory=None,
 
4443
            preview=False,
 
4444
            interactive=False,
 
4445
            ):
 
4446
        if merge_type is None:
 
4447
            merge_type = _mod_merge.Merge3Merger
 
4448
 
 
4449
        if directory is None:
 
4450
            directory = u'.'
 
4451
        possible_transports = []
 
4452
        merger = None
 
4453
        allow_pending = True
 
4454
        verified = 'inapplicable'
 
4455
 
 
4456
        tree = WorkingTree.open_containing(directory)[0]
 
4457
        if tree.branch.last_revision() == _mod_revision.NULL_REVISION:
 
4458
            raise errors.BzrCommandError(gettext('Merging into empty branches not currently supported, '
 
4459
                                                 'https://bugs.launchpad.net/bzr/+bug/308562'))
 
4460
 
 
4461
        # die as quickly as possible if there are uncommitted changes
 
4462
        if not force:
 
4463
            if tree.has_changes():
 
4464
                raise errors.UncommittedChanges(tree)
 
4465
 
 
4466
        view_info = _get_view_info_for_change_reporter(tree)
 
4467
        change_reporter = delta._ChangeReporter(
 
4468
            unversioned_filter=tree.is_ignored, view_info=view_info)
 
4469
        pb = ui.ui_factory.nested_progress_bar()
 
4470
        self.enter_context(pb)
 
4471
        self.enter_context(tree.lock_write())
 
4472
        if location is not None:
 
4473
            try:
 
4474
                mergeable = _mod_mergeable.read_mergeable_from_url(
 
4475
                    location, possible_transports=possible_transports)
 
4476
            except errors.NotABundle:
 
4477
                mergeable = None
 
4478
            else:
 
4479
                if uncommitted:
 
4480
                    raise errors.BzrCommandError(gettext('Cannot use --uncommitted'
 
4481
                                                         ' with bundles or merge directives.'))
 
4482
 
 
4483
                if revision is not None:
 
4484
                    raise errors.BzrCommandError(gettext(
 
4485
                        'Cannot use -r with merge directives or bundles'))
 
4486
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
4487
                                                                    mergeable)
 
4488
 
 
4489
        if merger is None and uncommitted:
 
4490
            if revision is not None and len(revision) > 0:
 
4491
                raise errors.BzrCommandError(gettext('Cannot use --uncommitted and'
 
4492
                                                     ' --revision at the same time.'))
 
4493
            merger = self.get_merger_from_uncommitted(tree, location, None)
 
4494
            allow_pending = False
 
4495
 
 
4496
        if merger is None:
 
4497
            merger, allow_pending = self._get_merger_from_branch(tree,
 
4498
                                                                 location, revision, remember, possible_transports, None)
 
4499
 
 
4500
        merger.merge_type = merge_type
 
4501
        merger.reprocess = reprocess
 
4502
        merger.show_base = show_base
 
4503
        self.sanity_check_merger(merger)
 
4504
        if (merger.base_rev_id == merger.other_rev_id and
 
4505
                merger.other_rev_id is not None):
 
4506
            # check if location is a nonexistent file (and not a branch) to
 
4507
            # disambiguate the 'Nothing to do'
 
4508
            if merger.interesting_files:
 
4509
                if not merger.other_tree.has_filename(
 
4510
                        merger.interesting_files[0]):
 
4511
                    note(gettext("merger: ") + str(merger))
 
4512
                    raise errors.PathsDoNotExist([location])
 
4513
            note(gettext('Nothing to do.'))
 
4514
            return 0
 
4515
        if pull and not preview:
 
4516
            if merger.interesting_files is not None:
 
4517
                raise errors.BzrCommandError(
 
4518
                    gettext('Cannot pull individual files'))
 
4519
            if (merger.base_rev_id == tree.last_revision()):
 
4520
                result = tree.pull(merger.other_branch, False,
 
4521
                                   merger.other_rev_id)
 
4522
                result.report(self.outf)
 
4523
                return 0
 
4524
        if merger.this_basis is None:
 
4525
            raise errors.BzrCommandError(gettext(
 
4526
                "This branch has no commits."
 
4527
                " (perhaps you would prefer 'brz pull')"))
 
4528
        if preview:
 
4529
            return self._do_preview(merger)
 
4530
        elif interactive:
 
4531
            return self._do_interactive(merger)
 
4532
        else:
 
4533
            return self._do_merge(merger, change_reporter, allow_pending,
 
4534
                                  verified)
 
4535
 
 
4536
    def _get_preview(self, merger):
 
4537
        tree_merger = merger.make_merger()
 
4538
        tt = tree_merger.make_preview_transform()
 
4539
        self.enter_context(tt)
 
4540
        result_tree = tt.get_preview_tree()
 
4541
        return result_tree
 
4542
 
 
4543
    def _do_preview(self, merger):
 
4544
        from .diff import show_diff_trees
 
4545
        result_tree = self._get_preview(merger)
 
4546
        path_encoding = osutils.get_diff_header_encoding()
 
4547
        show_diff_trees(merger.this_tree, result_tree, self.outf,
 
4548
                        old_label='', new_label='',
 
4549
                        path_encoding=path_encoding)
 
4550
 
 
4551
    def _do_merge(self, merger, change_reporter, allow_pending, verified):
 
4552
        merger.change_reporter = change_reporter
 
4553
        conflict_count = merger.do_merge()
 
4554
        if allow_pending:
 
4555
            merger.set_pending()
 
4556
        if verified == 'failed':
 
4557
            warning('Preview patch does not match changes')
 
4558
        if conflict_count != 0:
 
4559
            return 1
 
4560
        else:
 
4561
            return 0
 
4562
 
 
4563
    def _do_interactive(self, merger):
 
4564
        """Perform an interactive merge.
 
4565
 
 
4566
        This works by generating a preview tree of the merge, then using
 
4567
        Shelver to selectively remove the differences between the working tree
 
4568
        and the preview tree.
 
4569
        """
 
4570
        from . import shelf_ui
 
4571
        result_tree = self._get_preview(merger)
 
4572
        writer = breezy.option.diff_writer_registry.get()
 
4573
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
 
4574
                                   reporter=shelf_ui.ApplyReporter(),
 
4575
                                   diff_writer=writer(self.outf))
 
4576
        try:
 
4577
            shelver.run()
 
4578
        finally:
 
4579
            shelver.finalize()
 
4580
 
 
4581
    def sanity_check_merger(self, merger):
 
4582
        if (merger.show_base and
 
4583
                merger.merge_type is not _mod_merge.Merge3Merger):
 
4584
            raise errors.BzrCommandError(gettext("Show-base is not supported for this"
 
4585
                                                 " merge type. %s") % merger.merge_type)
 
4586
        if merger.reprocess is None:
 
4587
            if merger.show_base:
 
4588
                merger.reprocess = False
 
4589
            else:
 
4590
                # Use reprocess if the merger supports it
 
4591
                merger.reprocess = merger.merge_type.supports_reprocess
 
4592
        if merger.reprocess and not merger.merge_type.supports_reprocess:
 
4593
            raise errors.BzrCommandError(gettext("Conflict reduction is not supported"
 
4594
                                                 " for merge type %s.") %
 
4595
                                         merger.merge_type)
 
4596
        if merger.reprocess and merger.show_base:
 
4597
            raise errors.BzrCommandError(gettext("Cannot do conflict reduction and"
 
4598
                                                 " show base."))
 
4599
 
 
4600
        if (merger.merge_type.requires_file_merge_plan and
 
4601
            (not getattr(merger.this_tree, 'plan_file_merge', None) or
 
4602
             not getattr(merger.other_tree, 'plan_file_merge', None) or
 
4603
             (merger.base_tree is not None and
 
4604
                 not getattr(merger.base_tree, 'plan_file_merge', None)))):
 
4605
            raise errors.BzrCommandError(
 
4606
                gettext('Plan file merge unsupported: '
 
4607
                        'Merge type incompatible with tree formats.'))
 
4608
 
 
4609
    def _get_merger_from_branch(self, tree, location, revision, remember,
 
4610
                                possible_transports, pb):
 
4611
        """Produce a merger from a location, assuming it refers to a branch."""
 
4612
        # find the branch locations
 
4613
        other_loc, user_location = self._select_branch_location(tree, location,
 
4614
                                                                revision, -1)
 
4615
        if revision is not None and len(revision) == 2:
 
4616
            base_loc, _unused = self._select_branch_location(tree,
 
4617
                                                             location, revision, 0)
 
4618
        else:
 
4619
            base_loc = other_loc
 
4620
        # Open the branches
 
4621
        other_branch, other_path = Branch.open_containing(other_loc,
 
4622
                                                          possible_transports)
 
4623
        if base_loc == other_loc:
 
4624
            base_branch = other_branch
 
4625
        else:
 
4626
            base_branch, base_path = Branch.open_containing(base_loc,
 
4627
                                                            possible_transports)
 
4628
        # Find the revision ids
 
4629
        other_revision_id = None
 
4630
        base_revision_id = None
 
4631
        if revision is not None:
 
4632
            if len(revision) >= 1:
 
4633
                other_revision_id = revision[-1].as_revision_id(other_branch)
 
4634
            if len(revision) == 2:
 
4635
                base_revision_id = revision[0].as_revision_id(base_branch)
 
4636
        if other_revision_id is None:
 
4637
            other_revision_id = _mod_revision.ensure_null(
 
4638
                other_branch.last_revision())
 
4639
        # Remember where we merge from. We need to remember if:
 
4640
        # - user specify a location (and we don't merge from the parent
 
4641
        #   branch)
 
4642
        # - user ask to remember or there is no previous location set to merge
 
4643
        #   from and user didn't ask to *not* remember
 
4644
        if (user_location is not None
 
4645
            and ((remember or
 
4646
                 (remember is None and
 
4647
                  tree.branch.get_submit_branch() is None)))):
 
4648
            tree.branch.set_submit_branch(other_branch.base)
 
4649
        # Merge tags (but don't set them in the master branch yet, the user
 
4650
        # might revert this merge).  Commit will propagate them.
 
4651
        other_branch.tags.merge_to(tree.branch.tags, ignore_master=True)
 
4652
        merger = _mod_merge.Merger.from_revision_ids(tree,
 
4653
                                                     other_revision_id, base_revision_id, other_branch, base_branch)
 
4654
        if other_path != '':
 
4655
            allow_pending = False
 
4656
            merger.interesting_files = [other_path]
 
4657
        else:
 
4658
            allow_pending = True
 
4659
        return merger, allow_pending
 
4660
 
 
4661
    def get_merger_from_uncommitted(self, tree, location, pb):
 
4662
        """Get a merger for uncommitted changes.
 
4663
 
 
4664
        :param tree: The tree the merger should apply to.
 
4665
        :param location: The location containing uncommitted changes.
 
4666
        :param pb: The progress bar to use for showing progress.
 
4667
        """
 
4668
        location = self._select_branch_location(tree, location)[0]
 
4669
        other_tree, other_path = WorkingTree.open_containing(location)
 
4670
        merger = _mod_merge.Merger.from_uncommitted(tree, other_tree, pb)
 
4671
        if other_path != '':
 
4672
            merger.interesting_files = [other_path]
 
4673
        return merger
 
4674
 
 
4675
    def _select_branch_location(self, tree, user_location, revision=None,
 
4676
                                index=None):
 
4677
        """Select a branch location, according to possible inputs.
 
4678
 
 
4679
        If provided, branches from ``revision`` are preferred.  (Both
 
4680
        ``revision`` and ``index`` must be supplied.)
 
4681
 
 
4682
        Otherwise, the ``location`` parameter is used.  If it is None, then the
 
4683
        ``submit`` or ``parent`` location is used, and a note is printed.
 
4684
 
 
4685
        :param tree: The working tree to select a branch for merging into
 
4686
        :param location: The location entered by the user
 
4687
        :param revision: The revision parameter to the command
 
4688
        :param index: The index to use for the revision parameter.  Negative
 
4689
            indices are permitted.
 
4690
        :return: (selected_location, user_location).  The default location
 
4691
            will be the user-entered location.
 
4692
        """
 
4693
        if (revision is not None and index is not None
 
4694
                and revision[index] is not None):
 
4695
            branch = revision[index].get_branch()
 
4696
            if branch is not None:
 
4697
                return branch, branch
 
4698
        if user_location is None:
 
4699
            location = self._get_remembered(tree, 'Merging from')
 
4700
        else:
 
4701
            location = user_location
 
4702
        return location, user_location
 
4703
 
 
4704
    def _get_remembered(self, tree, verb_string):
 
4705
        """Use tree.branch's parent if none was supplied.
 
4706
 
 
4707
        Report if the remembered location was used.
 
4708
        """
 
4709
        stored_location = tree.branch.get_submit_branch()
 
4710
        stored_location_type = "submit"
 
4711
        if stored_location is None:
 
4712
            stored_location = tree.branch.get_parent()
 
4713
            stored_location_type = "parent"
 
4714
        mutter("%s", stored_location)
 
4715
        if stored_location is None:
 
4716
            raise errors.BzrCommandError(
 
4717
                gettext("No location specified or remembered"))
 
4718
        display_url = urlutils.unescape_for_display(stored_location, 'utf-8')
 
4719
        note(gettext("{0} remembered {1} location {2}").format(verb_string,
 
4720
                                                               stored_location_type, display_url))
 
4721
        return stored_location
 
4722
 
 
4723
 
 
4724
class cmd_remerge(Command):
 
4725
    __doc__ = """Redo a merge.
 
4726
 
 
4727
    Use this if you want to try a different merge technique while resolving
 
4728
    conflicts.  Some merge techniques are better than others, and remerge
 
4729
    lets you try different ones on different files.
 
4730
 
 
4731
    The options for remerge have the same meaning and defaults as the ones for
 
4732
    merge.  The difference is that remerge can (only) be run when there is a
 
4733
    pending merge, and it lets you specify particular files.
 
4734
 
 
4735
    :Examples:
 
4736
        Re-do the merge of all conflicted files, and show the base text in
 
4737
        conflict regions, in addition to the usual THIS and OTHER texts::
 
4738
 
 
4739
            brz remerge --show-base
 
4740
 
 
4741
        Re-do the merge of "foobar", using the weave merge algorithm, with
 
4742
        additional processing to reduce the size of conflict regions::
 
4743
 
 
4744
            brz remerge --merge-type weave --reprocess foobar
 
4745
    """
 
4746
    takes_args = ['file*']
 
4747
    takes_options = [
 
4748
        'merge-type',
 
4749
        'reprocess',
 
4750
        Option('show-base',
 
4751
               help="Show base revision text in conflicts."),
 
4752
        ]
 
4753
 
 
4754
    def run(self, file_list=None, merge_type=None, show_base=False,
 
4755
            reprocess=False):
 
4756
        from .conflicts import restore
 
4757
        if merge_type is None:
 
4758
            merge_type = _mod_merge.Merge3Merger
 
4759
        tree, file_list = WorkingTree.open_containing_paths(file_list)
 
4760
        self.enter_context(tree.lock_write())
 
4761
        parents = tree.get_parent_ids()
 
4762
        if len(parents) != 2:
 
4763
            raise errors.BzrCommandError(
 
4764
                gettext("Sorry, remerge only works after normal"
 
4765
                        " merges.  Not cherrypicking or multi-merges."))
 
4766
        interesting_files = None
 
4767
        new_conflicts = []
 
4768
        conflicts = tree.conflicts()
 
4769
        if file_list is not None:
 
4770
            interesting_files = set()
 
4771
            for filename in file_list:
 
4772
                if not tree.is_versioned(filename):
 
4773
                    raise errors.NotVersionedError(filename)
 
4774
                interesting_files.add(filename)
 
4775
                if tree.kind(filename) != "directory":
 
4776
                    continue
 
4777
 
 
4778
                for path, ie in tree.iter_entries_by_dir(
 
4779
                        specific_files=[filename]):
 
4780
                    interesting_files.add(path)
 
4781
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
4782
        else:
 
4783
            # Remerge only supports resolving contents conflicts
 
4784
            allowed_conflicts = ('text conflict', 'contents conflict')
 
4785
            restore_files = [c.path for c in conflicts
 
4786
                             if c.typestring in allowed_conflicts]
 
4787
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_files)
 
4788
        tree.set_conflicts(ConflictList(new_conflicts))
 
4789
        if file_list is not None:
 
4790
            restore_files = file_list
 
4791
        for filename in restore_files:
 
4792
            try:
 
4793
                restore(tree.abspath(filename))
 
4794
            except errors.NotConflicted:
 
4795
                pass
 
4796
        # Disable pending merges, because the file texts we are remerging
 
4797
        # have not had those merges performed.  If we use the wrong parents
 
4798
        # list, we imply that the working tree text has seen and rejected
 
4799
        # all the changes from the other tree, when in fact those changes
 
4800
        # have not yet been seen.
 
4801
        tree.set_parent_ids(parents[:1])
 
4802
        try:
 
4803
            merger = _mod_merge.Merger.from_revision_ids(tree, parents[1])
 
4804
            merger.interesting_files = interesting_files
 
4805
            merger.merge_type = merge_type
 
4806
            merger.show_base = show_base
 
4807
            merger.reprocess = reprocess
 
4808
            conflicts = merger.do_merge()
 
4809
        finally:
 
4810
            tree.set_parent_ids(parents)
 
4811
        if conflicts > 0:
 
4812
            return 1
 
4813
        else:
 
4814
            return 0
 
4815
 
 
4816
 
 
4817
class cmd_revert(Command):
 
4818
    __doc__ = """\
 
4819
    Set files in the working tree back to the contents of a previous revision.
 
4820
 
 
4821
    Giving a list of files will revert only those files.  Otherwise, all files
 
4822
    will be reverted.  If the revision is not specified with '--revision', the
 
4823
    working tree basis revision is used. A revert operation affects only the
 
4824
    working tree, not any revision history like the branch and repository or
 
4825
    the working tree basis revision.
 
4826
 
 
4827
    To remove only some changes, without reverting to a prior version, use
 
4828
    merge instead.  For example, "merge . -r -2..-3" (don't forget the ".")
 
4829
    will remove the changes introduced by the second last commit (-2), without
 
4830
    affecting the changes introduced by the last commit (-1).  To remove
 
4831
    certain changes on a hunk-by-hunk basis, see the shelve command.
 
4832
    To update the branch to a specific revision or the latest revision and
 
4833
    update the working tree accordingly while preserving local changes, see the
 
4834
    update command.
 
4835
 
 
4836
    Uncommitted changes to files that are reverted will be discarded.
 
4837
    However, by default, any files that have been manually changed will be
 
4838
    backed up first.  (Files changed only by merge are not backed up.)  Backup
 
4839
    files have '.~#~' appended to their name, where # is a number.
 
4840
 
 
4841
    When you provide files, you can use their current pathname or the pathname
 
4842
    from the target revision.  So you can use revert to "undelete" a file by
 
4843
    name.  If you name a directory, all the contents of that directory will be
 
4844
    reverted.
 
4845
 
 
4846
    If you have newly added files since the target revision, they will be
 
4847
    removed.  If the files to be removed have been changed, backups will be
 
4848
    created as above.  Directories containing unknown files will not be
 
4849
    deleted.
 
4850
 
 
4851
    The working tree contains a list of revisions that have been merged but
 
4852
    not yet committed. These revisions will be included as additional parents
 
4853
    of the next commit.  Normally, using revert clears that list as well as
 
4854
    reverting the files.  If any files are specified, revert leaves the list
 
4855
    of uncommitted merges alone and reverts only the files.  Use ``brz revert
 
4856
    .`` in the tree root to revert all files but keep the recorded merges,
 
4857
    and ``brz revert --forget-merges`` to clear the pending merge list without
 
4858
    reverting any files.
 
4859
 
 
4860
    Using "brz revert --forget-merges", it is possible to apply all of the
 
4861
    changes from a branch in a single revision.  To do this, perform the merge
 
4862
    as desired.  Then doing revert with the "--forget-merges" option will keep
 
4863
    the content of the tree as it was, but it will clear the list of pending
 
4864
    merges.  The next commit will then contain all of the changes that are
 
4865
    present in the other branch, but without any other parent revisions.
 
4866
    Because this technique forgets where these changes originated, it may
 
4867
    cause additional conflicts on later merges involving the same source and
 
4868
    target branches.
 
4869
    """
 
4870
 
 
4871
    _see_also = ['cat', 'export', 'merge', 'shelve']
 
4872
    takes_options = [
 
4873
        'revision',
 
4874
        Option('no-backup', "Do not save backups of reverted files."),
 
4875
        Option('forget-merges',
 
4876
               'Remove pending merge marker, without changing any files.'),
 
4877
        ]
 
4878
    takes_args = ['file*']
 
4879
 
 
4880
    def run(self, revision=None, no_backup=False, file_list=None,
 
4881
            forget_merges=None):
 
4882
        tree, file_list = WorkingTree.open_containing_paths(file_list)
 
4883
        self.enter_context(tree.lock_tree_write())
 
4884
        if forget_merges:
 
4885
            tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4886
        else:
 
4887
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
 
4888
 
 
4889
    @staticmethod
 
4890
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
 
4891
        rev_tree = _get_one_revision_tree('revert', revision, tree=tree)
 
4892
        tree.revert(file_list, rev_tree, not no_backup, None,
 
4893
                    report_changes=True)
 
4894
 
 
4895
 
 
4896
class cmd_assert_fail(Command):
 
4897
    __doc__ = """Test reporting of assertion failures"""
 
4898
    # intended just for use in testing
 
4899
 
 
4900
    hidden = True
 
4901
 
 
4902
    def run(self):
 
4903
        raise AssertionError("always fails")
 
4904
 
 
4905
 
 
4906
class cmd_help(Command):
 
4907
    __doc__ = """Show help on a command or other topic.
 
4908
    """
 
4909
 
 
4910
    _see_also = ['topics']
 
4911
    takes_options = [
 
4912
        Option('long', 'Show help on all commands.'),
 
4913
        ]
 
4914
    takes_args = ['topic?']
 
4915
    aliases = ['?', '--help', '-?', '-h']
 
4916
 
 
4917
    @display_command
 
4918
    def run(self, topic=None, long=False):
 
4919
        import breezy.help
 
4920
        if topic is None and long:
 
4921
            topic = "commands"
 
4922
        breezy.help.help(topic)
 
4923
 
 
4924
 
 
4925
class cmd_shell_complete(Command):
 
4926
    __doc__ = """Show appropriate completions for context.
 
4927
 
 
4928
    For a list of all available commands, say 'brz shell-complete'.
 
4929
    """
 
4930
    takes_args = ['context?']
 
4931
    aliases = ['s-c']
 
4932
    hidden = True
 
4933
 
 
4934
    @display_command
 
4935
    def run(self, context=None):
 
4936
        from . import shellcomplete
 
4937
        shellcomplete.shellcomplete(context)
 
4938
 
 
4939
 
 
4940
class cmd_missing(Command):
 
4941
    __doc__ = """Show unmerged/unpulled revisions between two branches.
 
4942
 
 
4943
    OTHER_BRANCH may be local or remote.
 
4944
 
 
4945
    To filter on a range of revisions, you can use the command -r begin..end
 
4946
    -r revision requests a specific revision, -r ..end or -r begin.. are
 
4947
    also valid.
 
4948
 
 
4949
    :Exit values:
 
4950
        1 - some missing revisions
 
4951
        0 - no missing revisions
 
4952
 
 
4953
    :Examples:
 
4954
 
 
4955
        Determine the missing revisions between this and the branch at the
 
4956
        remembered pull location::
 
4957
 
 
4958
            brz missing
 
4959
 
 
4960
        Determine the missing revisions between this and another branch::
 
4961
 
 
4962
            brz missing http://server/branch
 
4963
 
 
4964
        Determine the missing revisions up to a specific revision on the other
 
4965
        branch::
 
4966
 
 
4967
            brz missing -r ..-10
 
4968
 
 
4969
        Determine the missing revisions up to a specific revision on this
 
4970
        branch::
 
4971
 
 
4972
            brz missing --my-revision ..-10
 
4973
    """
 
4974
 
 
4975
    _see_also = ['merge', 'pull']
 
4976
    takes_args = ['other_branch?']
 
4977
    takes_options = [
 
4978
        'directory',
 
4979
        Option('reverse', 'Reverse the order of revisions.'),
 
4980
        Option('mine-only',
 
4981
               'Display changes in the local branch only.'),
 
4982
        Option('this', 'Same as --mine-only.'),
 
4983
        Option('theirs-only',
 
4984
               'Display changes in the remote branch only.'),
 
4985
        Option('other', 'Same as --theirs-only.'),
 
4986
        'log-format',
 
4987
        'show-ids',
 
4988
        'verbose',
 
4989
        custom_help('revision',
 
4990
                    help='Filter on other branch revisions (inclusive). '
 
4991
                    'See "help revisionspec" for details.'),
 
4992
        Option('my-revision',
 
4993
               type=_parse_revision_str,
 
4994
               help='Filter on local branch revisions (inclusive). '
 
4995
               'See "help revisionspec" for details.'),
 
4996
        Option('include-merged',
 
4997
               'Show all revisions in addition to the mainline ones.'),
 
4998
        Option('include-merges', hidden=True,
 
4999
               help='Historical alias for --include-merged.'),
 
5000
        ]
 
5001
    encoding_type = 'replace'
 
5002
 
 
5003
    @display_command
 
5004
    def run(self, other_branch=None, reverse=False, mine_only=False,
 
5005
            theirs_only=False,
 
5006
            log_format=None, long=False, short=False, line=False,
 
5007
            show_ids=False, verbose=False, this=False, other=False,
 
5008
            include_merged=None, revision=None, my_revision=None,
 
5009
            directory=u'.'):
 
5010
        from breezy.missing import find_unmerged, iter_log_revisions
 
5011
 
 
5012
        def message(s):
 
5013
            if not is_quiet():
 
5014
                self.outf.write(s)
 
5015
 
 
5016
        if include_merged is None:
 
5017
            include_merged = False
 
5018
        if this:
 
5019
            mine_only = this
 
5020
        if other:
 
5021
            theirs_only = other
 
5022
        # TODO: We should probably check that we don't have mine-only and
 
5023
        #       theirs-only set, but it gets complicated because we also have
 
5024
        #       this and other which could be used.
 
5025
        restrict = 'all'
 
5026
        if mine_only:
 
5027
            restrict = 'local'
 
5028
        elif theirs_only:
 
5029
            restrict = 'remote'
 
5030
 
 
5031
        local_branch = Branch.open_containing(directory)[0]
 
5032
        self.enter_context(local_branch.lock_read())
 
5033
 
 
5034
        parent = local_branch.get_parent()
 
5035
        if other_branch is None:
 
5036
            other_branch = parent
 
5037
            if other_branch is None:
 
5038
                raise errors.BzrCommandError(gettext("No peer location known"
 
5039
                                                     " or specified."))
 
5040
            display_url = urlutils.unescape_for_display(parent,
 
5041
                                                        self.outf.encoding)
 
5042
            message(gettext("Using saved parent location: {0}\n").format(
 
5043
                    display_url))
 
5044
 
 
5045
        remote_branch = Branch.open(other_branch)
 
5046
        if remote_branch.base == local_branch.base:
 
5047
            remote_branch = local_branch
 
5048
        else:
 
5049
            self.enter_context(remote_branch.lock_read())
 
5050
 
 
5051
        local_revid_range = _revision_range_to_revid_range(
 
5052
            _get_revision_range(my_revision, local_branch,
 
5053
                                self.name()))
 
5054
 
 
5055
        remote_revid_range = _revision_range_to_revid_range(
 
5056
            _get_revision_range(revision,
 
5057
                                remote_branch, self.name()))
 
5058
 
 
5059
        local_extra, remote_extra = find_unmerged(
 
5060
            local_branch, remote_branch, restrict,
 
5061
            backward=not reverse,
 
5062
            include_merged=include_merged,
 
5063
            local_revid_range=local_revid_range,
 
5064
            remote_revid_range=remote_revid_range)
 
5065
 
 
5066
        if log_format is None:
 
5067
            registry = log.log_formatter_registry
 
5068
            log_format = registry.get_default(local_branch)
 
5069
        lf = log_format(to_file=self.outf,
 
5070
                        show_ids=show_ids,
 
5071
                        show_timezone='original')
 
5072
 
 
5073
        status_code = 0
 
5074
        if local_extra and not theirs_only:
 
5075
            message(ngettext("You have %d extra revision:\n",
 
5076
                             "You have %d extra revisions:\n",
 
5077
                             len(local_extra)) %
 
5078
                    len(local_extra))
 
5079
            rev_tag_dict = {}
 
5080
            if local_branch.supports_tags():
 
5081
                rev_tag_dict = local_branch.tags.get_reverse_tag_dict()
 
5082
            for revision in iter_log_revisions(local_extra,
 
5083
                                               local_branch.repository,
 
5084
                                               verbose,
 
5085
                                               rev_tag_dict):
 
5086
                lf.log_revision(revision)
 
5087
            printed_local = True
 
5088
            status_code = 1
 
5089
        else:
 
5090
            printed_local = False
 
5091
 
 
5092
        if remote_extra and not mine_only:
 
5093
            if printed_local is True:
 
5094
                message("\n\n\n")
 
5095
            message(ngettext("You are missing %d revision:\n",
 
5096
                             "You are missing %d revisions:\n",
 
5097
                             len(remote_extra)) %
 
5098
                    len(remote_extra))
 
5099
            if remote_branch.supports_tags():
 
5100
                rev_tag_dict = remote_branch.tags.get_reverse_tag_dict()
 
5101
            for revision in iter_log_revisions(remote_extra,
 
5102
                                               remote_branch.repository,
 
5103
                                               verbose,
 
5104
                                               rev_tag_dict):
 
5105
                lf.log_revision(revision)
 
5106
            status_code = 1
 
5107
 
 
5108
        if mine_only and not local_extra:
 
5109
            # We checked local, and found nothing extra
 
5110
            message(gettext('This branch has no new revisions.\n'))
 
5111
        elif theirs_only and not remote_extra:
 
5112
            # We checked remote, and found nothing extra
 
5113
            message(gettext('Other branch has no new revisions.\n'))
 
5114
        elif not (mine_only or theirs_only or local_extra or
 
5115
                  remote_extra):
 
5116
            # We checked both branches, and neither one had extra
 
5117
            # revisions
 
5118
            message(gettext("Branches are up to date.\n"))
 
5119
        self.cleanup_now()
 
5120
        if not status_code and parent is None and other_branch is not None:
 
5121
            self.enter_context(local_branch.lock_write())
 
5122
            # handle race conditions - a parent might be set while we run.
 
5123
            if local_branch.get_parent() is None:
 
5124
                local_branch.set_parent(remote_branch.base)
 
5125
        return status_code
 
5126
 
 
5127
 
 
5128
class cmd_pack(Command):
 
5129
    __doc__ = """Compress the data within a repository.
 
5130
 
 
5131
    This operation compresses the data within a bazaar repository. As
 
5132
    bazaar supports automatic packing of repository, this operation is
 
5133
    normally not required to be done manually.
 
5134
 
 
5135
    During the pack operation, bazaar takes a backup of existing repository
 
5136
    data, i.e. pack files. This backup is eventually removed by bazaar
 
5137
    automatically when it is safe to do so. To save disk space by removing
 
5138
    the backed up pack files, the --clean-obsolete-packs option may be
 
5139
    used.
 
5140
 
 
5141
    Warning: If you use --clean-obsolete-packs and your machine crashes
 
5142
    during or immediately after repacking, you may be left with a state
 
5143
    where the deletion has been written to disk but the new packs have not
 
5144
    been. In this case the repository may be unusable.
 
5145
    """
 
5146
 
 
5147
    _see_also = ['repositories']
 
5148
    takes_args = ['branch_or_repo?']
 
5149
    takes_options = [
 
5150
        Option('clean-obsolete-packs',
 
5151
               'Delete obsolete packs to save disk space.'),
 
5152
        ]
 
5153
 
 
5154
    def run(self, branch_or_repo='.', clean_obsolete_packs=False):
 
5155
        dir = controldir.ControlDir.open_containing(branch_or_repo)[0]
 
5156
        try:
 
5157
            branch = dir.open_branch()
 
5158
            repository = branch.repository
 
5159
        except errors.NotBranchError:
 
5160
            repository = dir.open_repository()
 
5161
        repository.pack(clean_obsolete_packs=clean_obsolete_packs)
 
5162
 
 
5163
 
 
5164
class cmd_plugins(Command):
 
5165
    __doc__ = """List the installed plugins.
 
5166
 
 
5167
    This command displays the list of installed plugins including
 
5168
    version of plugin and a short description of each.
 
5169
 
 
5170
    --verbose shows the path where each plugin is located.
 
5171
 
 
5172
    A plugin is an external component for Breezy that extends the
 
5173
    revision control system, by adding or replacing code in Breezy.
 
5174
    Plugins can do a variety of things, including overriding commands,
 
5175
    adding new commands, providing additional network transports and
 
5176
    customizing log output.
 
5177
 
 
5178
    See the Bazaar Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/>
 
5179
    for further information on plugins including where to find them and how to
 
5180
    install them. Instructions are also provided there on how to write new
 
5181
    plugins using the Python programming language.
 
5182
    """
 
5183
    takes_options = ['verbose']
 
5184
 
 
5185
    @display_command
 
5186
    def run(self, verbose=False):
 
5187
        from . import plugin
 
5188
        # Don't give writelines a generator as some codecs don't like that
 
5189
        self.outf.writelines(
 
5190
            list(plugin.describe_plugins(show_paths=verbose)))
 
5191
 
 
5192
 
 
5193
class cmd_testament(Command):
 
5194
    __doc__ = """Show testament (signing-form) of a revision."""
 
5195
    takes_options = [
 
5196
        'revision',
 
5197
        Option('long', help='Produce long-format testament.'),
 
5198
        Option('strict',
 
5199
               help='Produce a strict-format testament.')]
 
5200
    takes_args = ['branch?']
 
5201
    encoding_type = 'exact'
 
5202
 
 
5203
    @display_command
 
5204
    def run(self, branch=u'.', revision=None, long=False, strict=False):
 
5205
        from .bzr.testament import Testament, StrictTestament
 
5206
        if strict is True:
 
5207
            testament_class = StrictTestament
 
5208
        else:
 
5209
            testament_class = Testament
 
5210
        if branch == '.':
 
5211
            b = Branch.open_containing(branch)[0]
 
5212
        else:
 
5213
            b = Branch.open(branch)
 
5214
        self.enter_context(b.lock_read())
 
5215
        if revision is None:
 
5216
            rev_id = b.last_revision()
 
5217
        else:
 
5218
            rev_id = revision[0].as_revision_id(b)
 
5219
        t = testament_class.from_revision(b.repository, rev_id)
 
5220
        if long:
 
5221
            self.outf.writelines(t.as_text_lines())
 
5222
        else:
 
5223
            self.outf.write(t.as_short_text())
 
5224
 
 
5225
 
 
5226
class cmd_annotate(Command):
 
5227
    __doc__ = """Show the origin of each line in a file.
 
5228
 
 
5229
    This prints out the given file with an annotation on the left side
 
5230
    indicating which revision, author and date introduced the change.
 
5231
 
 
5232
    If the origin is the same for a run of consecutive lines, it is
 
5233
    shown only at the top, unless the --all option is given.
 
5234
    """
 
5235
    # TODO: annotate directories; showing when each file was last changed
 
5236
    # TODO: if the working copy is modified, show annotations on that
 
5237
    #       with new uncommitted lines marked
 
5238
    aliases = ['ann', 'blame', 'praise']
 
5239
    takes_args = ['filename']
 
5240
    takes_options = [Option('all', help='Show annotations on all lines.'),
 
5241
                     Option('long', help='Show commit date in annotations.'),
 
5242
                     'revision',
 
5243
                     'show-ids',
 
5244
                     'directory',
 
5245
                     ]
 
5246
    encoding_type = 'exact'
 
5247
 
 
5248
    @display_command
 
5249
    def run(self, filename, all=False, long=False, revision=None,
 
5250
            show_ids=False, directory=None):
 
5251
        from .annotate import (
 
5252
            annotate_file_tree,
 
5253
            )
 
5254
        wt, branch, relpath = \
 
5255
            _open_directory_or_containing_tree_or_branch(filename, directory)
 
5256
        if wt is not None:
 
5257
            self.enter_context(wt.lock_read())
 
5258
        else:
 
5259
            self.enter_context(branch.lock_read())
 
5260
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
5261
        self.enter_context(tree.lock_read())
 
5262
        if wt is not None and revision is None:
 
5263
            if not wt.is_versioned(relpath):
 
5264
                raise errors.NotVersionedError(relpath)
 
5265
            # If there is a tree and we're not annotating historical
 
5266
            # versions, annotate the working tree's content.
 
5267
            annotate_file_tree(wt, relpath, self.outf, long, all,
 
5268
                               show_ids=show_ids)
 
5269
        else:
 
5270
            if not tree.is_versioned(relpath):
 
5271
                raise errors.NotVersionedError(relpath)
 
5272
            annotate_file_tree(tree, relpath, self.outf, long, all,
 
5273
                               show_ids=show_ids, branch=branch)
 
5274
 
 
5275
 
 
5276
class cmd_re_sign(Command):
 
5277
    __doc__ = """Create a digital signature for an existing revision."""
 
5278
    # TODO be able to replace existing ones.
 
5279
 
 
5280
    hidden = True  # is this right ?
 
5281
    takes_args = ['revision_id*']
 
5282
    takes_options = ['directory', 'revision']
 
5283
 
 
5284
    def run(self, revision_id_list=None, revision=None, directory=u'.'):
 
5285
        if revision_id_list is not None and revision is not None:
 
5286
            raise errors.BzrCommandError(
 
5287
                gettext('You can only supply one of revision_id or --revision'))
 
5288
        if revision_id_list is None and revision is None:
 
5289
            raise errors.BzrCommandError(
 
5290
                gettext('You must supply either --revision or a revision_id'))
 
5291
        b = WorkingTree.open_containing(directory)[0].branch
 
5292
        self.enter_context(b.lock_write())
 
5293
        return self._run(b, revision_id_list, revision)
 
5294
 
 
5295
    def _run(self, b, revision_id_list, revision):
 
5296
        from .repository import WriteGroup
 
5297
        gpg_strategy = gpg.GPGStrategy(b.get_config_stack())
 
5298
        if revision_id_list is not None:
 
5299
            with WriteGroup(b.repository):
 
5300
                for revision_id in revision_id_list:
 
5301
                    revision_id = cache_utf8.encode(revision_id)
 
5302
                    b.repository.sign_revision(revision_id, gpg_strategy)
 
5303
        elif revision is not None:
 
5304
            if len(revision) == 1:
 
5305
                revno, rev_id = revision[0].in_history(b)
 
5306
                with WriteGroup(b.repository):
 
5307
                    b.repository.sign_revision(rev_id, gpg_strategy)
 
5308
            elif len(revision) == 2:
 
5309
                # are they both on rh- if so we can walk between them
 
5310
                # might be nice to have a range helper for arbitrary
 
5311
                # revision paths. hmm.
 
5312
                from_revno, from_revid = revision[0].in_history(b)
 
5313
                to_revno, to_revid = revision[1].in_history(b)
 
5314
                if to_revid is None:
 
5315
                    to_revno = b.revno()
 
5316
                if from_revno is None or to_revno is None:
 
5317
                    raise errors.BzrCommandError(
 
5318
                        gettext('Cannot sign a range of non-revision-history revisions'))
 
5319
                with WriteGroup(b.repository):
 
5320
                    for revno in range(from_revno, to_revno + 1):
 
5321
                        b.repository.sign_revision(b.get_rev_id(revno),
 
5322
                                                   gpg_strategy)
 
5323
            else:
 
5324
                raise errors.BzrCommandError(
 
5325
                    gettext('Please supply either one revision, or a range.'))
 
5326
 
 
5327
 
 
5328
class cmd_bind(Command):
 
5329
    __doc__ = """Convert the current branch into a checkout of the supplied branch.
 
5330
    If no branch is supplied, rebind to the last bound location.
 
5331
 
 
5332
    Once converted into a checkout, commits must succeed on the master branch
 
5333
    before they will be applied to the local branch.
 
5334
 
 
5335
    Bound branches use the nickname of its master branch unless it is set
 
5336
    locally, in which case binding will update the local nickname to be
 
5337
    that of the master.
 
5338
    """
 
5339
 
 
5340
    _see_also = ['checkouts', 'unbind']
 
5341
    takes_args = ['location?']
 
5342
    takes_options = ['directory']
 
5343
 
 
5344
    def run(self, location=None, directory=u'.'):
 
5345
        b, relpath = Branch.open_containing(directory)
 
5346
        if location is None:
 
5347
            try:
 
5348
                location = b.get_old_bound_location()
 
5349
            except errors.UpgradeRequired:
 
5350
                raise errors.BzrCommandError(
 
5351
                    gettext('No location supplied.  '
 
5352
                            'This format does not remember old locations.'))
 
5353
            else:
 
5354
                if location is None:
 
5355
                    if b.get_bound_location() is not None:
 
5356
                        raise errors.BzrCommandError(
 
5357
                            gettext('Branch is already bound'))
 
5358
                    else:
 
5359
                        raise errors.BzrCommandError(
 
5360
                            gettext('No location supplied'
 
5361
                                    ' and no previous location known'))
 
5362
        b_other = Branch.open(location)
 
5363
        try:
 
5364
            b.bind(b_other)
 
5365
        except errors.DivergedBranches:
 
5366
            raise errors.BzrCommandError(
 
5367
                gettext('These branches have diverged.'
 
5368
                        ' Try merging, and then bind again.'))
 
5369
        if b.get_config().has_explicit_nickname():
 
5370
            b.nick = b_other.nick
 
5371
 
 
5372
 
 
5373
class cmd_unbind(Command):
 
5374
    __doc__ = """Convert the current checkout into a regular branch.
 
5375
 
 
5376
    After unbinding, the local branch is considered independent and subsequent
 
5377
    commits will be local only.
 
5378
    """
 
5379
 
 
5380
    _see_also = ['checkouts', 'bind']
 
5381
    takes_args = []
 
5382
    takes_options = ['directory']
 
5383
 
 
5384
    def run(self, directory=u'.'):
 
5385
        b, relpath = Branch.open_containing(directory)
 
5386
        if not b.unbind():
 
5387
            raise errors.BzrCommandError(gettext('Local branch is not bound'))
 
5388
 
 
5389
 
 
5390
class cmd_uncommit(Command):
 
5391
    __doc__ = """Remove the last committed revision.
 
5392
 
 
5393
    --verbose will print out what is being removed.
 
5394
    --dry-run will go through all the motions, but not actually
 
5395
    remove anything.
 
5396
 
 
5397
    If --revision is specified, uncommit revisions to leave the branch at the
 
5398
    specified revision.  For example, "brz uncommit -r 15" will leave the
 
5399
    branch at revision 15.
 
5400
 
 
5401
    Uncommit leaves the working tree ready for a new commit.  The only change
 
5402
    it may make is to restore any pending merges that were present before
 
5403
    the commit.
 
5404
    """
 
5405
 
 
5406
    # TODO: jam 20060108 Add an option to allow uncommit to remove
 
5407
    # unreferenced information in 'branch-as-repository' branches.
 
5408
    # TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
 
5409
    # information in shared branches as well.
 
5410
    _see_also = ['commit']
 
5411
    takes_options = ['verbose', 'revision',
 
5412
                     Option('dry-run', help='Don\'t actually make changes.'),
 
5413
                     Option('force', help='Say yes to all questions.'),
 
5414
                     Option('keep-tags',
 
5415
                            help='Keep tags that point to removed revisions.'),
 
5416
                     Option('local',
 
5417
                            help="Only remove the commits from the local "
 
5418
                            "branch when in a checkout."
 
5419
                            ),
 
5420
                     ]
 
5421
    takes_args = ['location?']
 
5422
    aliases = []
 
5423
    encoding_type = 'replace'
 
5424
 
 
5425
    def run(self, location=None, dry_run=False, verbose=False,
 
5426
            revision=None, force=False, local=False, keep_tags=False):
 
5427
        if location is None:
 
5428
            location = u'.'
 
5429
        control, relpath = controldir.ControlDir.open_containing(location)
 
5430
        try:
 
5431
            tree = control.open_workingtree()
 
5432
            b = tree.branch
 
5433
        except (errors.NoWorkingTree, errors.NotLocalUrl):
 
5434
            tree = None
 
5435
            b = control.open_branch()
 
5436
 
 
5437
        if tree is not None:
 
5438
            self.enter_context(tree.lock_write())
 
5439
        else:
 
5440
            self.enter_context(b.lock_write())
 
5441
        return self._run(b, tree, dry_run, verbose, revision, force,
 
5442
                         local, keep_tags, location)
 
5443
 
 
5444
    def _run(self, b, tree, dry_run, verbose, revision, force, local,
 
5445
             keep_tags, location):
 
5446
        from .log import log_formatter, show_log
 
5447
        from .uncommit import uncommit
 
5448
 
 
5449
        last_revno, last_rev_id = b.last_revision_info()
 
5450
 
 
5451
        rev_id = None
 
5452
        if revision is None:
 
5453
            revno = last_revno
 
5454
            rev_id = last_rev_id
 
5455
        else:
 
5456
            # 'brz uncommit -r 10' actually means uncommit
 
5457
            # so that the final tree is at revno 10.
 
5458
            # but breezy.uncommit.uncommit() actually uncommits
 
5459
            # the revisions that are supplied.
 
5460
            # So we need to offset it by one
 
5461
            revno = revision[0].in_history(b).revno + 1
 
5462
            if revno <= last_revno:
 
5463
                rev_id = b.get_rev_id(revno)
 
5464
 
 
5465
        if rev_id is None or _mod_revision.is_null(rev_id):
 
5466
            self.outf.write(gettext('No revisions to uncommit.\n'))
 
5467
            return 1
 
5468
 
 
5469
        lf = log_formatter('short',
 
5470
                           to_file=self.outf,
 
5471
                           show_timezone='original')
 
5472
 
 
5473
        show_log(b,
 
5474
                 lf,
 
5475
                 verbose=False,
 
5476
                 direction='forward',
 
5477
                 start_revision=revno,
 
5478
                 end_revision=last_revno)
 
5479
 
 
5480
        if dry_run:
 
5481
            self.outf.write(gettext('Dry-run, pretending to remove'
 
5482
                                    ' the above revisions.\n'))
 
5483
        else:
 
5484
            self.outf.write(
 
5485
                gettext('The above revision(s) will be removed.\n'))
 
5486
 
 
5487
        if not force:
 
5488
            if not ui.ui_factory.confirm_action(
 
5489
                    gettext(u'Uncommit these revisions'),
 
5490
                    'breezy.builtins.uncommit',
 
5491
                    {}):
 
5492
                self.outf.write(gettext('Canceled\n'))
 
5493
                return 0
 
5494
 
 
5495
        mutter('Uncommitting from {%s} to {%s}',
 
5496
               last_rev_id, rev_id)
 
5497
        uncommit(b, tree=tree, dry_run=dry_run, verbose=verbose,
 
5498
                 revno=revno, local=local, keep_tags=keep_tags)
 
5499
        if location != '.':
 
5500
            self.outf.write(
 
5501
                gettext('You can restore the old tip by running:\n'
 
5502
                        '  brz pull -d %s %s -r revid:%s\n')
 
5503
                % (location, location, last_rev_id.decode('utf-8')))
 
5504
        else:
 
5505
            self.outf.write(
 
5506
                gettext('You can restore the old tip by running:\n'
 
5507
                        '  brz pull . -r revid:%s\n')
 
5508
                % last_rev_id.decode('utf-8'))
 
5509
 
 
5510
 
 
5511
class cmd_break_lock(Command):
 
5512
    __doc__ = """Break a dead lock.
 
5513
 
 
5514
    This command breaks a lock on a repository, branch, working directory or
 
5515
    config file.
 
5516
 
 
5517
    CAUTION: Locks should only be broken when you are sure that the process
 
5518
    holding the lock has been stopped.
 
5519
 
 
5520
    You can get information on what locks are open via the 'brz info
 
5521
    [location]' command.
 
5522
 
 
5523
    :Examples:
 
5524
        brz break-lock
 
5525
        brz break-lock brz+ssh://example.com/brz/foo
 
5526
        brz break-lock --conf ~/.config/breezy
 
5527
    """
 
5528
 
 
5529
    takes_args = ['location?']
 
5530
    takes_options = [
 
5531
        Option('config',
 
5532
               help='LOCATION is the directory where the config lock is.'),
 
5533
        Option('force',
 
5534
               help='Do not ask for confirmation before breaking the lock.'),
 
5535
        ]
 
5536
 
 
5537
    def run(self, location=None, config=False, force=False):
 
5538
        if location is None:
 
5539
            location = u'.'
 
5540
        if force:
 
5541
            ui.ui_factory = ui.ConfirmationUserInterfacePolicy(ui.ui_factory,
 
5542
                                                               None,
 
5543
                                                               {'breezy.lockdir.break': True})
 
5544
        if config:
 
5545
            conf = _mod_config.LockableConfig(file_name=location)
 
5546
            conf.break_lock()
 
5547
        else:
 
5548
            control, relpath = controldir.ControlDir.open_containing(location)
 
5549
            try:
 
5550
                control.break_lock()
 
5551
            except NotImplementedError:
 
5552
                pass
 
5553
 
 
5554
 
 
5555
class cmd_wait_until_signalled(Command):
 
5556
    __doc__ = """Test helper for test_start_and_stop_brz_subprocess_send_signal.
 
5557
 
 
5558
    This just prints a line to signal when it is ready, then blocks on stdin.
 
5559
    """
 
5560
 
 
5561
    hidden = True
 
5562
 
 
5563
    def run(self):
 
5564
        self.outf.write("running\n")
 
5565
        self.outf.flush()
 
5566
        sys.stdin.readline()
 
5567
 
 
5568
 
 
5569
class cmd_serve(Command):
 
5570
    __doc__ = """Run the brz server."""
 
5571
 
 
5572
    aliases = ['server']
 
5573
 
 
5574
    takes_options = [
 
5575
        Option('inet',
 
5576
               help='Serve on stdin/out for use from inetd or sshd.'),
 
5577
        RegistryOption('protocol',
 
5578
                       help="Protocol to serve.",
 
5579
                       lazy_registry=('breezy.transport',
 
5580
                                      'transport_server_registry'),
 
5581
                       value_switches=True),
 
5582
        Option('listen',
 
5583
               help='Listen for connections on nominated address.',
 
5584
               type=str),
 
5585
        Option('port',
 
5586
               help='Listen for connections on nominated port.  Passing 0 as '
 
5587
                    'the port number will result in a dynamically allocated '
 
5588
                    'port.  The default port depends on the protocol.',
 
5589
               type=int),
 
5590
        custom_help('directory',
 
5591
                    help='Serve contents of this directory.'),
 
5592
        Option('allow-writes',
 
5593
               help='By default the server is a readonly server.  Supplying '
 
5594
                    '--allow-writes enables write access to the contents of '
 
5595
                    'the served directory and below.  Note that ``brz serve`` '
 
5596
                    'does not perform authentication, so unless some form of '
 
5597
                    'external authentication is arranged supplying this '
 
5598
                    'option leads to global uncontrolled write access to your '
 
5599
                    'file system.'
 
5600
               ),
 
5601
        Option('client-timeout', type=float,
 
5602
               help='Override the default idle client timeout (5min).'),
 
5603
        ]
 
5604
 
 
5605
    def run(self, listen=None, port=None, inet=False, directory=None,
 
5606
            allow_writes=False, protocol=None, client_timeout=None):
 
5607
        from . import location, transport
 
5608
        if directory is None:
 
5609
            directory = osutils.getcwd()
 
5610
        if protocol is None:
 
5611
            protocol = transport.transport_server_registry.get()
 
5612
        url = location.location_to_url(directory)
 
5613
        if not allow_writes:
 
5614
            url = 'readonly+' + url
 
5615
        t = transport.get_transport_from_url(url)
 
5616
        protocol(t, listen, port, inet, client_timeout)
 
5617
 
 
5618
 
 
5619
class cmd_join(Command):
 
5620
    __doc__ = """Combine a tree into its containing tree.
 
5621
 
 
5622
    This command requires the target tree to be in a rich-root format.
 
5623
 
 
5624
    The TREE argument should be an independent tree, inside another tree, but
 
5625
    not part of it.  (Such trees can be produced by "brz split", but also by
 
5626
    running "brz branch" with the target inside a tree.)
 
5627
 
 
5628
    The result is a combined tree, with the subtree no longer an independent
 
5629
    part.  This is marked as a merge of the subtree into the containing tree,
 
5630
    and all history is preserved.
 
5631
    """
 
5632
 
 
5633
    _see_also = ['split']
 
5634
    takes_args = ['tree']
 
5635
    takes_options = [
 
5636
        Option('reference', help='Join by reference.', hidden=True),
 
5637
        ]
 
5638
 
 
5639
    def run(self, tree, reference=False):
 
5640
        from breezy.mutabletree import BadReferenceTarget
 
5641
        sub_tree = WorkingTree.open(tree)
 
5642
        parent_dir = osutils.dirname(sub_tree.basedir)
 
5643
        containing_tree = WorkingTree.open_containing(parent_dir)[0]
 
5644
        repo = containing_tree.branch.repository
 
5645
        if not repo.supports_rich_root():
 
5646
            raise errors.BzrCommandError(gettext(
 
5647
                "Can't join trees because %s doesn't support rich root data.\n"
 
5648
                "You can use brz upgrade on the repository.")
 
5649
                % (repo,))
 
5650
        if reference:
 
5651
            try:
 
5652
                containing_tree.add_reference(sub_tree)
 
5653
            except BadReferenceTarget as e:
 
5654
                # XXX: Would be better to just raise a nicely printable
 
5655
                # exception from the real origin.  Also below.  mbp 20070306
 
5656
                raise errors.BzrCommandError(
 
5657
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5658
        else:
 
5659
            try:
 
5660
                containing_tree.subsume(sub_tree)
 
5661
            except errors.BadSubsumeSource as e:
 
5662
                raise errors.BzrCommandError(
 
5663
                    gettext("Cannot join {0}.  {1}").format(tree, e.reason))
 
5664
 
 
5665
 
 
5666
class cmd_split(Command):
 
5667
    __doc__ = """Split a subdirectory of a tree into a separate tree.
 
5668
 
 
5669
    This command will produce a target tree in a format that supports
 
5670
    rich roots, like 'rich-root' or 'rich-root-pack'.  These formats cannot be
 
5671
    converted into earlier formats like 'dirstate-tags'.
 
5672
 
 
5673
    The TREE argument should be a subdirectory of a working tree.  That
 
5674
    subdirectory will be converted into an independent tree, with its own
 
5675
    branch.  Commits in the top-level tree will not apply to the new subtree.
 
5676
    """
 
5677
 
 
5678
    _see_also = ['join']
 
5679
    takes_args = ['tree']
 
5680
 
 
5681
    def run(self, tree):
 
5682
        containing_tree, subdir = WorkingTree.open_containing(tree)
 
5683
        if not containing_tree.is_versioned(subdir):
 
5684
            raise errors.NotVersionedError(subdir)
 
5685
        try:
 
5686
            containing_tree.extract(subdir)
 
5687
        except errors.RootNotRich:
 
5688
            raise errors.RichRootUpgradeRequired(containing_tree.branch.base)
 
5689
 
 
5690
 
 
5691
class cmd_merge_directive(Command):
 
5692
    __doc__ = """Generate a merge directive for auto-merge tools.
 
5693
 
 
5694
    A directive requests a merge to be performed, and also provides all the
 
5695
    information necessary to do so.  This means it must either include a
 
5696
    revision bundle, or the location of a branch containing the desired
 
5697
    revision.
 
5698
 
 
5699
    A submit branch (the location to merge into) must be supplied the first
 
5700
    time the command is issued.  After it has been supplied once, it will
 
5701
    be remembered as the default.
 
5702
 
 
5703
    A public branch is optional if a revision bundle is supplied, but required
 
5704
    if --diff or --plain is specified.  It will be remembered as the default
 
5705
    after the first use.
 
5706
    """
 
5707
 
 
5708
    takes_args = ['submit_branch?', 'public_branch?']
 
5709
 
 
5710
    hidden = True
 
5711
 
 
5712
    _see_also = ['send']
 
5713
 
 
5714
    takes_options = [
 
5715
        'directory',
 
5716
        RegistryOption.from_kwargs(
 
5717
            'patch-type',
 
5718
            'The type of patch to include in the directive.',
 
5719
            title='Patch type',
 
5720
            value_switches=True,
 
5721
            enum_switch=False,
 
5722
            bundle='Bazaar revision bundle (default).',
 
5723
            diff='Normal unified diff.',
 
5724
            plain='No patch, just directive.'),
 
5725
        Option('sign', help='GPG-sign the directive.'), 'revision',
 
5726
        Option('mail-to', type=str,
 
5727
               help='Instead of printing the directive, email to this '
 
5728
               'address.'),
 
5729
        Option('message', type=str, short_name='m',
 
5730
               help='Message to use when committing this merge.')
 
5731
        ]
 
5732
 
 
5733
    encoding_type = 'exact'
 
5734
 
 
5735
    def run(self, submit_branch=None, public_branch=None, patch_type='bundle',
 
5736
            sign=False, revision=None, mail_to=None, message=None,
 
5737
            directory=u'.'):
 
5738
        from .revision import ensure_null, NULL_REVISION
 
5739
        include_patch, include_bundle = {
 
5740
            'plain': (False, False),
 
5741
            'diff': (True, False),
 
5742
            'bundle': (True, True),
 
5743
            }[patch_type]
 
5744
        branch = Branch.open(directory)
 
5745
        stored_submit_branch = branch.get_submit_branch()
 
5746
        if submit_branch is None:
 
5747
            submit_branch = stored_submit_branch
 
5748
        else:
 
5749
            if stored_submit_branch is None:
 
5750
                branch.set_submit_branch(submit_branch)
 
5751
        if submit_branch is None:
 
5752
            submit_branch = branch.get_parent()
 
5753
        if submit_branch is None:
 
5754
            raise errors.BzrCommandError(
 
5755
                gettext('No submit branch specified or known'))
 
5756
 
 
5757
        stored_public_branch = branch.get_public_branch()
 
5758
        if public_branch is None:
 
5759
            public_branch = stored_public_branch
 
5760
        elif stored_public_branch is None:
 
5761
            # FIXME: Should be done only if we succeed ? -- vila 2012-01-03
 
5762
            branch.set_public_branch(public_branch)
 
5763
        if not include_bundle and public_branch is None:
 
5764
            raise errors.BzrCommandError(
 
5765
                gettext('No public branch specified or known'))
 
5766
        base_revision_id = None
 
5767
        if revision is not None:
 
5768
            if len(revision) > 2:
 
5769
                raise errors.BzrCommandError(
 
5770
                    gettext('brz merge-directive takes '
 
5771
                            'at most two one revision identifiers'))
 
5772
            revision_id = revision[-1].as_revision_id(branch)
 
5773
            if len(revision) == 2:
 
5774
                base_revision_id = revision[0].as_revision_id(branch)
 
5775
        else:
 
5776
            revision_id = branch.last_revision()
 
5777
        revision_id = ensure_null(revision_id)
 
5778
        if revision_id == NULL_REVISION:
 
5779
            raise errors.BzrCommandError(gettext('No revisions to bundle.'))
 
5780
        directive = merge_directive.MergeDirective2.from_objects(
 
5781
            branch.repository, revision_id, time.time(),
 
5782
            osutils.local_time_offset(), submit_branch,
 
5783
            public_branch=public_branch, include_patch=include_patch,
 
5784
            include_bundle=include_bundle, message=message,
 
5785
            base_revision_id=base_revision_id)
 
5786
        if mail_to is None:
 
5787
            if sign:
 
5788
                self.outf.write(directive.to_signed(branch))
 
5789
            else:
 
5790
                self.outf.writelines(directive.to_lines())
 
5791
        else:
 
5792
            message = directive.to_email(mail_to, branch, sign)
 
5793
            s = SMTPConnection(branch.get_config_stack())
 
5794
            s.send_email(message)
 
5795
 
 
5796
 
 
5797
class cmd_send(Command):
 
5798
    __doc__ = """Mail or create a merge-directive for submitting changes.
 
5799
 
 
5800
    A merge directive provides many things needed for requesting merges:
 
5801
 
 
5802
    * A machine-readable description of the merge to perform
 
5803
 
 
5804
    * An optional patch that is a preview of the changes requested
 
5805
 
 
5806
    * An optional bundle of revision data, so that the changes can be applied
 
5807
      directly from the merge directive, without retrieving data from a
 
5808
      branch.
 
5809
 
 
5810
    `brz send` creates a compact data set that, when applied using brz
 
5811
    merge, has the same effect as merging from the source branch.
 
5812
 
 
5813
    By default the merge directive is self-contained and can be applied to any
 
5814
    branch containing submit_branch in its ancestory without needing access to
 
5815
    the source branch.
 
5816
 
 
5817
    If --no-bundle is specified, then Breezy doesn't send the contents of the
 
5818
    revisions, but only a structured request to merge from the
 
5819
    public_location.  In that case the public_branch is needed and it must be
 
5820
    up-to-date and accessible to the recipient.  The public_branch is always
 
5821
    included if known, so that people can check it later.
 
5822
 
 
5823
    The submit branch defaults to the parent of the source branch, but can be
 
5824
    overridden.  Both submit branch and public branch will be remembered in
 
5825
    branch.conf the first time they are used for a particular branch.  The
 
5826
    source branch defaults to that containing the working directory, but can
 
5827
    be changed using --from.
 
5828
 
 
5829
    Both the submit branch and the public branch follow the usual behavior with
 
5830
    respect to --remember: If there is no default location set, the first send
 
5831
    will set it (use --no-remember to avoid setting it). After that, you can
 
5832
    omit the location to use the default.  To change the default, use
 
5833
    --remember. The value will only be saved if the location can be accessed.
 
5834
 
 
5835
    In order to calculate those changes, brz must analyse the submit branch.
 
5836
    Therefore it is most efficient for the submit branch to be a local mirror.
 
5837
    If a public location is known for the submit_branch, that location is used
 
5838
    in the merge directive.
 
5839
 
 
5840
    The default behaviour is to send the merge directive by mail, unless -o is
 
5841
    given, in which case it is sent to a file.
 
5842
 
 
5843
    Mail is sent using your preferred mail program.  This should be transparent
 
5844
    on Windows (it uses MAPI).  On Unix, it requires the xdg-email utility.
 
5845
    If the preferred client can't be found (or used), your editor will be used.
 
5846
 
 
5847
    To use a specific mail program, set the mail_client configuration option.
 
5848
    Supported values for specific clients are "claws", "evolution", "kmail",
 
5849
    "mail.app" (MacOS X's Mail.app), "mutt", and "thunderbird"; generic options
 
5850
    are "default", "editor", "emacsclient", "mapi", and "xdg-email".  Plugins
 
5851
    may also add supported clients.
 
5852
 
 
5853
    If mail is being sent, a to address is required.  This can be supplied
 
5854
    either on the commandline, by setting the submit_to configuration
 
5855
    option in the branch itself or the child_submit_to configuration option
 
5856
    in the submit branch.
 
5857
 
 
5858
    The merge directives created by brz send may be applied using brz merge or
 
5859
    brz pull by specifying a file containing a merge directive as the location.
 
5860
 
 
5861
    brz send makes extensive use of public locations to map local locations into
 
5862
    URLs that can be used by other people.  See `brz help configuration` to
 
5863
    set them, and use `brz info` to display them.
 
5864
    """
 
5865
 
 
5866
    encoding_type = 'exact'
 
5867
 
 
5868
    _see_also = ['merge', 'pull']
 
5869
 
 
5870
    takes_args = ['submit_branch?', 'public_branch?']
 
5871
 
 
5872
    takes_options = [
 
5873
        Option('no-bundle',
 
5874
               help='Do not include a bundle in the merge directive.'),
 
5875
        Option('no-patch', help='Do not include a preview patch in the merge'
 
5876
               ' directive.'),
 
5877
        Option('remember',
 
5878
               help='Remember submit and public branch.'),
 
5879
        Option('from',
 
5880
               help='Branch to generate the submission from, '
 
5881
               'rather than the one containing the working directory.',
 
5882
               short_name='f',
 
5883
               type=str),
 
5884
        Option('output', short_name='o',
 
5885
               help='Write merge directive to this file or directory; '
 
5886
                    'use - for stdout.',
 
5887
               type=str),
 
5888
        Option('strict',
 
5889
               help='Refuse to send if there are uncommitted changes in'
 
5890
               ' the working tree, --no-strict disables the check.'),
 
5891
        Option('mail-to', help='Mail the request to this address.',
 
5892
               type=str),
 
5893
        'revision',
 
5894
        'message',
 
5895
        Option('body', help='Body for the email.', type=str),
 
5896
        RegistryOption('format',
 
5897
                       help='Use the specified output format.',
 
5898
                       lazy_registry=('breezy.send', 'format_registry')),
 
5899
        ]
 
5900
 
 
5901
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
 
5902
            no_patch=False, revision=None, remember=None, output=None,
 
5903
            format=None, mail_to=None, message=None, body=None,
 
5904
            strict=None, **kwargs):
 
5905
        from .send import send
 
5906
        return send(submit_branch, revision, public_branch, remember,
 
5907
                    format, no_bundle, no_patch, output,
 
5908
                    kwargs.get('from', '.'), mail_to, message, body,
 
5909
                    self.outf,
 
5910
                    strict=strict)
 
5911
 
 
5912
 
 
5913
class cmd_bundle_revisions(cmd_send):
 
5914
    __doc__ = """Create a merge-directive for submitting changes.
 
5915
 
 
5916
    A merge directive provides many things needed for requesting merges:
 
5917
 
 
5918
    * A machine-readable description of the merge to perform
 
5919
 
 
5920
    * An optional patch that is a preview of the changes requested
 
5921
 
 
5922
    * An optional bundle of revision data, so that the changes can be applied
 
5923
      directly from the merge directive, without retrieving data from a
 
5924
      branch.
 
5925
 
 
5926
    If --no-bundle is specified, then public_branch is needed (and must be
 
5927
    up-to-date), so that the receiver can perform the merge using the
 
5928
    public_branch.  The public_branch is always included if known, so that
 
5929
    people can check it later.
 
5930
 
 
5931
    The submit branch defaults to the parent, but can be overridden.  Both
 
5932
    submit branch and public branch will be remembered if supplied.
 
5933
 
 
5934
    If a public_branch is known for the submit_branch, that public submit
 
5935
    branch is used in the merge instructions.  This means that a local mirror
 
5936
    can be used as your actual submit branch, once you have set public_branch
 
5937
    for that mirror.
 
5938
    """
 
5939
 
 
5940
    takes_options = [
 
5941
        Option('no-bundle',
 
5942
               help='Do not include a bundle in the merge directive.'),
 
5943
        Option('no-patch', help='Do not include a preview patch in the merge'
 
5944
               ' directive.'),
 
5945
        Option('remember',
 
5946
               help='Remember submit and public branch.'),
 
5947
        Option('from',
 
5948
               help='Branch to generate the submission from, '
 
5949
               'rather than the one containing the working directory.',
 
5950
               short_name='f',
 
5951
               type=str),
 
5952
        Option('output', short_name='o', help='Write directive to this file.',
 
5953
               type=str),
 
5954
        Option('strict',
 
5955
               help='Refuse to bundle revisions if there are uncommitted'
 
5956
               ' changes in the working tree, --no-strict disables the check.'),
 
5957
        'revision',
 
5958
        RegistryOption('format',
 
5959
                       help='Use the specified output format.',
 
5960
                       lazy_registry=('breezy.send', 'format_registry')),
 
5961
        ]
 
5962
    aliases = ['bundle']
 
5963
 
 
5964
    _see_also = ['send', 'merge']
 
5965
 
 
5966
    hidden = True
 
5967
 
 
5968
    def run(self, submit_branch=None, public_branch=None, no_bundle=False,
 
5969
            no_patch=False, revision=None, remember=False, output=None,
 
5970
            format=None, strict=None, **kwargs):
 
5971
        if output is None:
 
5972
            output = '-'
 
5973
        from .send import send
 
5974
        return send(submit_branch, revision, public_branch, remember,
 
5975
                    format, no_bundle, no_patch, output,
 
5976
                    kwargs.get('from', '.'), None, None, None,
 
5977
                    self.outf, strict=strict)
 
5978
 
 
5979
 
 
5980
class cmd_tag(Command):
 
5981
    __doc__ = """Create, remove or modify a tag naming a revision.
 
5982
 
 
5983
    Tags give human-meaningful names to revisions.  Commands that take a -r
 
5984
    (--revision) option can be given -rtag:X, where X is any previously
 
5985
    created tag.
 
5986
 
 
5987
    Tags are stored in the branch.  Tags are copied from one branch to another
 
5988
    along when you branch, push, pull or merge.
 
5989
 
 
5990
    It is an error to give a tag name that already exists unless you pass
 
5991
    --force, in which case the tag is moved to point to the new revision.
 
5992
 
 
5993
    To rename a tag (change the name but keep it on the same revsion), run ``brz
 
5994
    tag new-name -r tag:old-name`` and then ``brz tag --delete oldname``.
 
5995
 
 
5996
    If no tag name is specified it will be determined through the
 
5997
    'automatic_tag_name' hook. This can e.g. be used to automatically tag
 
5998
    upstream releases by reading configure.ac. See ``brz help hooks`` for
 
5999
    details.
 
6000
    """
 
6001
 
 
6002
    _see_also = ['commit', 'tags']
 
6003
    takes_args = ['tag_name?']
 
6004
    takes_options = [
 
6005
        Option('delete',
 
6006
               help='Delete this tag rather than placing it.',
 
6007
               ),
 
6008
        custom_help('directory',
 
6009
                    help='Branch in which to place the tag.'),
 
6010
        Option('force',
 
6011
               help='Replace existing tags.',
 
6012
               ),
 
6013
        'revision',
 
6014
        ]
 
6015
 
 
6016
    def run(self, tag_name=None,
 
6017
            delete=None,
 
6018
            directory='.',
 
6019
            force=None,
 
6020
            revision=None,
 
6021
            ):
 
6022
        branch, relpath = Branch.open_containing(directory)
 
6023
        self.enter_context(branch.lock_write())
 
6024
        if delete:
 
6025
            if tag_name is None:
 
6026
                raise errors.BzrCommandError(
 
6027
                    gettext("No tag specified to delete."))
 
6028
            branch.tags.delete_tag(tag_name)
 
6029
            note(gettext('Deleted tag %s.') % tag_name)
 
6030
        else:
 
6031
            if revision:
 
6032
                if len(revision) != 1:
 
6033
                    raise errors.BzrCommandError(gettext(
 
6034
                        "Tags can only be placed on a single revision, "
 
6035
                        "not on a range"))
 
6036
                revision_id = revision[0].as_revision_id(branch)
 
6037
            else:
 
6038
                revision_id = branch.last_revision()
 
6039
            if tag_name is None:
 
6040
                tag_name = branch.automatic_tag_name(revision_id)
 
6041
                if tag_name is None:
 
6042
                    raise errors.BzrCommandError(gettext(
 
6043
                        "Please specify a tag name."))
 
6044
            try:
 
6045
                existing_target = branch.tags.lookup_tag(tag_name)
 
6046
            except errors.NoSuchTag:
 
6047
                existing_target = None
 
6048
            if not force and existing_target not in (None, revision_id):
 
6049
                raise errors.TagAlreadyExists(tag_name)
 
6050
            if existing_target == revision_id:
 
6051
                note(gettext('Tag %s already exists for that revision.') % tag_name)
 
6052
            else:
 
6053
                branch.tags.set_tag(tag_name, revision_id)
 
6054
                if existing_target is None:
 
6055
                    note(gettext('Created tag %s.') % tag_name)
 
6056
                else:
 
6057
                    note(gettext('Updated tag %s.') % tag_name)
 
6058
 
 
6059
 
 
6060
class cmd_tags(Command):
 
6061
    __doc__ = """List tags.
 
6062
 
 
6063
    This command shows a table of tag names and the revisions they reference.
 
6064
    """
 
6065
 
 
6066
    _see_also = ['tag']
 
6067
    takes_options = [
 
6068
        custom_help('directory',
 
6069
                    help='Branch whose tags should be displayed.'),
 
6070
        RegistryOption('sort',
 
6071
                       'Sort tags by different criteria.', title='Sorting',
 
6072
                       lazy_registry=('breezy.tag', 'tag_sort_methods')
 
6073
                       ),
 
6074
        'show-ids',
 
6075
        'revision',
 
6076
    ]
 
6077
 
 
6078
    @display_command
 
6079
    def run(self, directory='.', sort=None, show_ids=False, revision=None):
 
6080
        from .tag import tag_sort_methods
 
6081
        branch, relpath = Branch.open_containing(directory)
 
6082
 
 
6083
        tags = list(branch.tags.get_tag_dict().items())
 
6084
        if not tags:
 
6085
            return
 
6086
 
 
6087
        self.enter_context(branch.lock_read())
 
6088
        if revision:
 
6089
            # Restrict to the specified range
 
6090
            tags = self._tags_for_range(branch, revision)
 
6091
        if sort is None:
 
6092
            sort = tag_sort_methods.get()
 
6093
        sort(branch, tags)
 
6094
        if not show_ids:
 
6095
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
6096
            for index, (tag, revid) in enumerate(tags):
 
6097
                try:
 
6098
                    revno = branch.revision_id_to_dotted_revno(revid)
 
6099
                    if isinstance(revno, tuple):
 
6100
                        revno = '.'.join(map(str, revno))
 
6101
                except (errors.NoSuchRevision,
 
6102
                        errors.GhostRevisionsHaveNoRevno,
 
6103
                        errors.UnsupportedOperation):
 
6104
                    # Bad tag data/merges can lead to tagged revisions
 
6105
                    # which are not in this branch. Fail gracefully ...
 
6106
                    revno = '?'
 
6107
                tags[index] = (tag, revno)
 
6108
        else:
 
6109
            tags = [(tag, revid.decode('utf-8')) for (tag, revid) in tags]
 
6110
        self.cleanup_now()
 
6111
        for tag, revspec in tags:
 
6112
            self.outf.write('%-20s %s\n' % (tag, revspec))
 
6113
 
 
6114
    def _tags_for_range(self, branch, revision):
 
6115
        rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
6116
        revid1, revid2 = rev1.rev_id, rev2.rev_id
 
6117
        # _get_revision_range will always set revid2 if it's not specified.
 
6118
        # If revid1 is None, it means we want to start from the branch
 
6119
        # origin which is always a valid ancestor. If revid1 == revid2, the
 
6120
        # ancestry check is useless.
 
6121
        if revid1 and revid1 != revid2:
 
6122
            # FIXME: We really want to use the same graph than
 
6123
            # branch.iter_merge_sorted_revisions below, but this is not
 
6124
            # easily available -- vila 2011-09-23
 
6125
            if branch.repository.get_graph().is_ancestor(revid2, revid1):
 
6126
                # We don't want to output anything in this case...
 
6127
                return []
 
6128
        # only show revisions between revid1 and revid2 (inclusive)
 
6129
        tagged_revids = branch.tags.get_reverse_tag_dict()
 
6130
        found = []
 
6131
        for r in branch.iter_merge_sorted_revisions(
 
6132
                start_revision_id=revid2, stop_revision_id=revid1,
 
6133
                stop_rule='include'):
 
6134
            revid_tags = tagged_revids.get(r[0], None)
 
6135
            if revid_tags:
 
6136
                found.extend([(tag, r[0]) for tag in revid_tags])
 
6137
        return found
 
6138
 
 
6139
 
 
6140
class cmd_reconfigure(Command):
 
6141
    __doc__ = """Reconfigure the type of a brz directory.
 
6142
 
 
6143
    A target configuration must be specified.
 
6144
 
 
6145
    For checkouts, the bind-to location will be auto-detected if not specified.
 
6146
    The order of preference is
 
6147
    1. For a lightweight checkout, the current bound location.
 
6148
    2. For branches that used to be checkouts, the previously-bound location.
 
6149
    3. The push location.
 
6150
    4. The parent location.
 
6151
    If none of these is available, --bind-to must be specified.
 
6152
    """
 
6153
 
 
6154
    _see_also = ['branches', 'checkouts', 'standalone-trees', 'working-trees']
 
6155
    takes_args = ['location?']
 
6156
    takes_options = [
 
6157
        RegistryOption.from_kwargs(
 
6158
            'tree_type',
 
6159
            title='Tree type',
 
6160
            help='The relation between branch and tree.',
 
6161
            value_switches=True, enum_switch=False,
 
6162
            branch='Reconfigure to be an unbound branch with no working tree.',
 
6163
            tree='Reconfigure to be an unbound branch with a working tree.',
 
6164
            checkout='Reconfigure to be a bound branch with a working tree.',
 
6165
            lightweight_checkout='Reconfigure to be a lightweight'
 
6166
            ' checkout (with no local history).',
 
6167
            ),
 
6168
        RegistryOption.from_kwargs(
 
6169
            'repository_type',
 
6170
            title='Repository type',
 
6171
            help='Location fo the repository.',
 
6172
            value_switches=True, enum_switch=False,
 
6173
            standalone='Reconfigure to be a standalone branch '
 
6174
            '(i.e. stop using shared repository).',
 
6175
            use_shared='Reconfigure to use a shared repository.',
 
6176
            ),
 
6177
        RegistryOption.from_kwargs(
 
6178
            'repository_trees',
 
6179
            title='Trees in Repository',
 
6180
            help='Whether new branches in the repository have trees.',
 
6181
            value_switches=True, enum_switch=False,
 
6182
            with_trees='Reconfigure repository to create '
 
6183
            'working trees on branches by default.',
 
6184
            with_no_trees='Reconfigure repository to not create '
 
6185
            'working trees on branches by default.'
 
6186
            ),
 
6187
        Option('bind-to', help='Branch to bind checkout to.', type=str),
 
6188
        Option('force',
 
6189
               help='Perform reconfiguration even if local changes'
 
6190
               ' will be lost.'),
 
6191
        Option('stacked-on',
 
6192
               help='Reconfigure a branch to be stacked on another branch.',
 
6193
               type=str,
 
6194
               ),
 
6195
        Option('unstacked',
 
6196
               help='Reconfigure a branch to be unstacked.  This '
 
6197
               'may require copying substantial data into it.',
 
6198
               ),
 
6199
        ]
 
6200
 
 
6201
    def run(self, location=None, bind_to=None, force=False,
 
6202
            tree_type=None, repository_type=None, repository_trees=None,
 
6203
            stacked_on=None, unstacked=None):
 
6204
        directory = controldir.ControlDir.open(location)
 
6205
        if stacked_on and unstacked:
 
6206
            raise errors.BzrCommandError(
 
6207
                gettext("Can't use both --stacked-on and --unstacked"))
 
6208
        elif stacked_on is not None:
 
6209
            reconfigure.ReconfigureStackedOn().apply(directory, stacked_on)
 
6210
        elif unstacked:
 
6211
            reconfigure.ReconfigureUnstacked().apply(directory)
 
6212
        # At the moment you can use --stacked-on and a different
 
6213
        # reconfiguration shape at the same time; there seems no good reason
 
6214
        # to ban it.
 
6215
        if (tree_type is None and
 
6216
            repository_type is None and
 
6217
                repository_trees is None):
 
6218
            if stacked_on or unstacked:
 
6219
                return
 
6220
            else:
 
6221
                raise errors.BzrCommandError(gettext('No target configuration '
 
6222
                                                     'specified'))
 
6223
        reconfiguration = None
 
6224
        if tree_type == 'branch':
 
6225
            reconfiguration = reconfigure.Reconfigure.to_branch(directory)
 
6226
        elif tree_type == 'tree':
 
6227
            reconfiguration = reconfigure.Reconfigure.to_tree(directory)
 
6228
        elif tree_type == 'checkout':
 
6229
            reconfiguration = reconfigure.Reconfigure.to_checkout(
 
6230
                directory, bind_to)
 
6231
        elif tree_type == 'lightweight-checkout':
 
6232
            reconfiguration = reconfigure.Reconfigure.to_lightweight_checkout(
 
6233
                directory, bind_to)
 
6234
        if reconfiguration:
 
6235
            reconfiguration.apply(force)
 
6236
            reconfiguration = None
 
6237
        if repository_type == 'use-shared':
 
6238
            reconfiguration = reconfigure.Reconfigure.to_use_shared(directory)
 
6239
        elif repository_type == 'standalone':
 
6240
            reconfiguration = reconfigure.Reconfigure.to_standalone(directory)
 
6241
        if reconfiguration:
 
6242
            reconfiguration.apply(force)
 
6243
            reconfiguration = None
 
6244
        if repository_trees == 'with-trees':
 
6245
            reconfiguration = reconfigure.Reconfigure.set_repository_trees(
 
6246
                directory, True)
 
6247
        elif repository_trees == 'with-no-trees':
 
6248
            reconfiguration = reconfigure.Reconfigure.set_repository_trees(
 
6249
                directory, False)
 
6250
        if reconfiguration:
 
6251
            reconfiguration.apply(force)
 
6252
            reconfiguration = None
 
6253
 
 
6254
 
 
6255
class cmd_switch(Command):
 
6256
    __doc__ = """Set the branch of a checkout and update.
 
6257
 
 
6258
    For lightweight checkouts, this changes the branch being referenced.
 
6259
    For heavyweight checkouts, this checks that there are no local commits
 
6260
    versus the current bound branch, then it makes the local branch a mirror
 
6261
    of the new location and binds to it.
 
6262
 
 
6263
    In both cases, the working tree is updated and uncommitted changes
 
6264
    are merged. The user can commit or revert these as they desire.
 
6265
 
 
6266
    Pending merges need to be committed or reverted before using switch.
 
6267
 
 
6268
    The path to the branch to switch to can be specified relative to the parent
 
6269
    directory of the current branch. For example, if you are currently in a
 
6270
    checkout of /path/to/branch, specifying 'newbranch' will find a branch at
 
6271
    /path/to/newbranch.
 
6272
 
 
6273
    Bound branches use the nickname of its master branch unless it is set
 
6274
    locally, in which case switching will update the local nickname to be
 
6275
    that of the master.
 
6276
    """
 
6277
 
 
6278
    takes_args = ['to_location?']
 
6279
    takes_options = ['directory',
 
6280
                     Option('force',
 
6281
                            help='Switch even if local commits will be lost.'),
 
6282
                     'revision',
 
6283
                     Option('create-branch', short_name='b',
 
6284
                            help='Create the target branch from this one before'
 
6285
                            ' switching to it.'),
 
6286
                     Option('store',
 
6287
                            help='Store and restore uncommitted changes in the'
 
6288
                            ' branch.'),
 
6289
                     ]
 
6290
 
 
6291
    def run(self, to_location=None, force=False, create_branch=False,
 
6292
            revision=None, directory=u'.', store=False):
 
6293
        from . import switch
 
6294
        tree_location = directory
 
6295
        revision = _get_one_revision('switch', revision)
 
6296
        control_dir = controldir.ControlDir.open_containing(tree_location)[0]
 
6297
        possible_transports = [control_dir.root_transport]
 
6298
        if to_location is None:
 
6299
            if revision is None:
 
6300
                raise errors.BzrCommandError(gettext('You must supply either a'
 
6301
                                                     ' revision or a location'))
 
6302
            to_location = tree_location
 
6303
        try:
 
6304
            branch = control_dir.open_branch(
 
6305
                possible_transports=possible_transports)
 
6306
            had_explicit_nick = branch.get_config().has_explicit_nickname()
 
6307
        except errors.NotBranchError:
 
6308
            branch = None
 
6309
            had_explicit_nick = False
 
6310
        else:
 
6311
            possible_transports.append(branch.user_transport)
 
6312
        if create_branch:
 
6313
            if branch is None:
 
6314
                raise errors.BzrCommandError(
 
6315
                    gettext('cannot create branch without source branch'))
 
6316
            to_location = lookup_new_sibling_branch(
 
6317
                control_dir, to_location,
 
6318
                possible_transports=possible_transports)
 
6319
            if revision is not None:
 
6320
                revision = revision.as_revision_id(branch)
 
6321
            to_branch = branch.controldir.sprout(
 
6322
                to_location,
 
6323
                possible_transports=possible_transports,
 
6324
                revision_id=revision,
 
6325
                source_branch=branch).open_branch()
 
6326
        else:
 
6327
            try:
 
6328
                to_branch = Branch.open(
 
6329
                    to_location, possible_transports=possible_transports)
 
6330
            except errors.NotBranchError:
 
6331
                to_branch = open_sibling_branch(
 
6332
                    control_dir, to_location,
 
6333
                    possible_transports=possible_transports)
 
6334
            if revision is not None:
 
6335
                revision = revision.as_revision_id(to_branch)
 
6336
        possible_transports.append(to_branch.user_transport)
 
6337
        try:
 
6338
            switch.switch(control_dir, to_branch, force, revision_id=revision,
 
6339
                          store_uncommitted=store,
 
6340
                          possible_transports=possible_transports)
 
6341
        except controldir.BranchReferenceLoop:
 
6342
            raise errors.BzrCommandError(
 
6343
                gettext('switching would create a branch reference loop. '
 
6344
                        'Use the "bzr up" command to switch to a '
 
6345
                        'different revision.'))
 
6346
        if had_explicit_nick:
 
6347
            branch = control_dir.open_branch()  # get the new branch!
 
6348
            branch.nick = to_branch.nick
 
6349
        if to_branch.name:
 
6350
            if to_branch.controldir.control_url != control_dir.control_url:
 
6351
                note(gettext('Switched to branch %s at %s'),
 
6352
                     to_branch.name, urlutils.unescape_for_display(to_branch.base, 'utf-8'))
 
6353
            else:
 
6354
                note(gettext('Switched to branch %s'), to_branch.name)
 
6355
        else:
 
6356
            note(gettext('Switched to branch at %s'),
 
6357
                 urlutils.unescape_for_display(to_branch.base, 'utf-8'))
 
6358
 
 
6359
 
 
6360
class cmd_view(Command):
 
6361
    __doc__ = """Manage filtered views.
 
6362
 
 
6363
    Views provide a mask over the tree so that users can focus on
 
6364
    a subset of a tree when doing their work. After creating a view,
 
6365
    commands that support a list of files - status, diff, commit, etc -
 
6366
    effectively have that list of files implicitly given each time.
 
6367
    An explicit list of files can still be given but those files
 
6368
    must be within the current view.
 
6369
 
 
6370
    In most cases, a view has a short life-span: it is created to make
 
6371
    a selected change and is deleted once that change is committed.
 
6372
    At other times, you may wish to create one or more named views
 
6373
    and switch between them.
 
6374
 
 
6375
    To disable the current view without deleting it, you can switch to
 
6376
    the pseudo view called ``off``. This can be useful when you need
 
6377
    to see the whole tree for an operation or two (e.g. merge) but
 
6378
    want to switch back to your view after that.
 
6379
 
 
6380
    :Examples:
 
6381
      To define the current view::
 
6382
 
 
6383
        brz view file1 dir1 ...
 
6384
 
 
6385
      To list the current view::
 
6386
 
 
6387
        brz view
 
6388
 
 
6389
      To delete the current view::
 
6390
 
 
6391
        brz view --delete
 
6392
 
 
6393
      To disable the current view without deleting it::
 
6394
 
 
6395
        brz view --switch off
 
6396
 
 
6397
      To define a named view and switch to it::
 
6398
 
 
6399
        brz view --name view-name file1 dir1 ...
 
6400
 
 
6401
      To list a named view::
 
6402
 
 
6403
        brz view --name view-name
 
6404
 
 
6405
      To delete a named view::
 
6406
 
 
6407
        brz view --name view-name --delete
 
6408
 
 
6409
      To switch to a named view::
 
6410
 
 
6411
        brz view --switch view-name
 
6412
 
 
6413
      To list all views defined::
 
6414
 
 
6415
        brz view --all
 
6416
 
 
6417
      To delete all views::
 
6418
 
 
6419
        brz view --delete --all
 
6420
    """
 
6421
 
 
6422
    _see_also = []
 
6423
    takes_args = ['file*']
 
6424
    takes_options = [
 
6425
        Option('all',
 
6426
               help='Apply list or delete action to all views.',
 
6427
               ),
 
6428
        Option('delete',
 
6429
               help='Delete the view.',
 
6430
               ),
 
6431
        Option('name',
 
6432
               help='Name of the view to define, list or delete.',
 
6433
               type=str,
 
6434
               ),
 
6435
        Option('switch',
 
6436
               help='Name of the view to switch to.',
 
6437
               type=str,
 
6438
               ),
 
6439
        ]
 
6440
 
 
6441
    def run(self, file_list,
 
6442
            all=False,
 
6443
            delete=False,
 
6444
            name=None,
 
6445
            switch=None,
 
6446
            ):
 
6447
        tree, file_list = WorkingTree.open_containing_paths(file_list,
 
6448
                                                            apply_view=False)
 
6449
        current_view, view_dict = tree.views.get_view_info()
 
6450
        if name is None:
 
6451
            name = current_view
 
6452
        if delete:
 
6453
            if file_list:
 
6454
                raise errors.BzrCommandError(gettext(
 
6455
                    "Both --delete and a file list specified"))
 
6456
            elif switch:
 
6457
                raise errors.BzrCommandError(gettext(
 
6458
                    "Both --delete and --switch specified"))
 
6459
            elif all:
 
6460
                tree.views.set_view_info(None, {})
 
6461
                self.outf.write(gettext("Deleted all views.\n"))
 
6462
            elif name is None:
 
6463
                raise errors.BzrCommandError(
 
6464
                    gettext("No current view to delete"))
 
6465
            else:
 
6466
                tree.views.delete_view(name)
 
6467
                self.outf.write(gettext("Deleted '%s' view.\n") % name)
 
6468
        elif switch:
 
6469
            if file_list:
 
6470
                raise errors.BzrCommandError(gettext(
 
6471
                    "Both --switch and a file list specified"))
 
6472
            elif all:
 
6473
                raise errors.BzrCommandError(gettext(
 
6474
                    "Both --switch and --all specified"))
 
6475
            elif switch == 'off':
 
6476
                if current_view is None:
 
6477
                    raise errors.BzrCommandError(
 
6478
                        gettext("No current view to disable"))
 
6479
                tree.views.set_view_info(None, view_dict)
 
6480
                self.outf.write(gettext("Disabled '%s' view.\n") %
 
6481
                                (current_view))
 
6482
            else:
 
6483
                tree.views.set_view_info(switch, view_dict)
 
6484
                view_str = views.view_display_str(tree.views.lookup_view())
 
6485
                self.outf.write(
 
6486
                    gettext("Using '{0}' view: {1}\n").format(switch, view_str))
 
6487
        elif all:
 
6488
            if view_dict:
 
6489
                self.outf.write(gettext('Views defined:\n'))
 
6490
                for view in sorted(view_dict):
 
6491
                    if view == current_view:
 
6492
                        active = "=>"
 
6493
                    else:
 
6494
                        active = "  "
 
6495
                    view_str = views.view_display_str(view_dict[view])
 
6496
                    self.outf.write('%s %-20s %s\n' % (active, view, view_str))
 
6497
            else:
 
6498
                self.outf.write(gettext('No views defined.\n'))
 
6499
        elif file_list:
 
6500
            if name is None:
 
6501
                # No name given and no current view set
 
6502
                name = 'my'
 
6503
            elif name == 'off':
 
6504
                raise errors.BzrCommandError(gettext(
 
6505
                    "Cannot change the 'off' pseudo view"))
 
6506
            tree.views.set_view(name, sorted(file_list))
 
6507
            view_str = views.view_display_str(tree.views.lookup_view())
 
6508
            self.outf.write(
 
6509
                gettext("Using '{0}' view: {1}\n").format(name, view_str))
 
6510
        else:
 
6511
            # list the files
 
6512
            if name is None:
 
6513
                # No name given and no current view set
 
6514
                self.outf.write(gettext('No current view.\n'))
 
6515
            else:
 
6516
                view_str = views.view_display_str(tree.views.lookup_view(name))
 
6517
                self.outf.write(
 
6518
                    gettext("'{0}' view is: {1}\n").format(name, view_str))
 
6519
 
 
6520
 
 
6521
class cmd_hooks(Command):
 
6522
    __doc__ = """Show hooks."""
 
6523
 
 
6524
    hidden = True
 
6525
 
 
6526
    def run(self):
 
6527
        for hook_key in sorted(hooks.known_hooks.keys()):
 
6528
            some_hooks = hooks.known_hooks_key_to_object(hook_key)
 
6529
            self.outf.write("%s:\n" % type(some_hooks).__name__)
 
6530
            for hook_name, hook_point in sorted(some_hooks.items()):
 
6531
                self.outf.write("  %s:\n" % (hook_name,))
 
6532
                found_hooks = list(hook_point)
 
6533
                if found_hooks:
 
6534
                    for hook in found_hooks:
 
6535
                        self.outf.write("    %s\n" %
 
6536
                                        (some_hooks.get_hook_name(hook),))
 
6537
                else:
 
6538
                    self.outf.write(gettext("    <no hooks installed>\n"))
 
6539
 
 
6540
 
 
6541
class cmd_remove_branch(Command):
 
6542
    __doc__ = """Remove a branch.
 
6543
 
 
6544
    This will remove the branch from the specified location but
 
6545
    will keep any working tree or repository in place.
 
6546
 
 
6547
    :Examples:
 
6548
 
 
6549
      Remove the branch at repo/trunk::
 
6550
 
 
6551
        brz remove-branch repo/trunk
 
6552
 
 
6553
    """
 
6554
 
 
6555
    takes_args = ["location?"]
 
6556
 
 
6557
    takes_options = ['directory',
 
6558
                     Option('force', help='Remove branch even if it is the active branch.')]
 
6559
 
 
6560
    aliases = ["rmbranch"]
 
6561
 
 
6562
    def run(self, directory=None, location=None, force=False):
 
6563
        br = open_nearby_branch(near=directory, location=location)
 
6564
        if not force and br.controldir.has_workingtree():
 
6565
            try:
 
6566
                active_branch = br.controldir.open_branch(name="")
 
6567
            except errors.NotBranchError:
 
6568
                active_branch = None
 
6569
            if (active_branch is not None and
 
6570
                    br.control_url == active_branch.control_url):
 
6571
                raise errors.BzrCommandError(
 
6572
                    gettext("Branch is active. Use --force to remove it."))
 
6573
        br.controldir.destroy_branch(br.name)
 
6574
 
 
6575
 
 
6576
class cmd_shelve(Command):
 
6577
    __doc__ = """Temporarily set aside some changes from the current tree.
 
6578
 
 
6579
    Shelve allows you to temporarily put changes you've made "on the shelf",
 
6580
    ie. out of the way, until a later time when you can bring them back from
 
6581
    the shelf with the 'unshelve' command.  The changes are stored alongside
 
6582
    your working tree, and so they aren't propagated along with your branch nor
 
6583
    will they survive its deletion.
 
6584
 
 
6585
    If shelve --list is specified, previously-shelved changes are listed.
 
6586
 
 
6587
    Shelve is intended to help separate several sets of changes that have
 
6588
    been inappropriately mingled.  If you just want to get rid of all changes
 
6589
    and you don't need to restore them later, use revert.  If you want to
 
6590
    shelve all text changes at once, use shelve --all.
 
6591
 
 
6592
    If filenames are specified, only the changes to those files will be
 
6593
    shelved. Other files will be left untouched.
 
6594
 
 
6595
    If a revision is specified, changes since that revision will be shelved.
 
6596
 
 
6597
    You can put multiple items on the shelf, and by default, 'unshelve' will
 
6598
    restore the most recently shelved changes.
 
6599
 
 
6600
    For complicated changes, it is possible to edit the changes in a separate
 
6601
    editor program to decide what the file remaining in the working copy
 
6602
    should look like.  To do this, add the configuration option
 
6603
 
 
6604
        change_editor = PROGRAM {new_path} {old_path}
 
6605
 
 
6606
    where {new_path} is replaced with the path of the new version of the
 
6607
    file and {old_path} is replaced with the path of the old version of
 
6608
    the file.  The PROGRAM should save the new file with the desired
 
6609
    contents of the file in the working tree.
 
6610
 
 
6611
    """
 
6612
 
 
6613
    takes_args = ['file*']
 
6614
 
 
6615
    takes_options = [
 
6616
        'directory',
 
6617
        'revision',
 
6618
        Option('all', help='Shelve all changes.'),
 
6619
        'message',
 
6620
        RegistryOption('writer', 'Method to use for writing diffs.',
 
6621
                       breezy.option.diff_writer_registry,
 
6622
                       value_switches=True, enum_switch=False),
 
6623
 
 
6624
        Option('list', help='List shelved changes.'),
 
6625
        Option('destroy',
 
6626
               help='Destroy removed changes instead of shelving them.'),
 
6627
    ]
 
6628
    _see_also = ['unshelve', 'configuration']
 
6629
 
 
6630
    def run(self, revision=None, all=False, file_list=None, message=None,
 
6631
            writer=None, list=False, destroy=False, directory=None):
 
6632
        if list:
 
6633
            return self.run_for_list(directory=directory)
 
6634
        from .shelf_ui import Shelver
 
6635
        if writer is None:
 
6636
            writer = breezy.option.diff_writer_registry.get()
 
6637
        try:
 
6638
            shelver = Shelver.from_args(writer(self.outf), revision, all,
 
6639
                                        file_list, message, destroy=destroy, directory=directory)
 
6640
            try:
 
6641
                shelver.run()
 
6642
            finally:
 
6643
                shelver.finalize()
 
6644
        except errors.UserAbort:
 
6645
            return 0
 
6646
 
 
6647
    def run_for_list(self, directory=None):
 
6648
        if directory is None:
 
6649
            directory = u'.'
 
6650
        tree = WorkingTree.open_containing(directory)[0]
 
6651
        self.enter_context(tree.lock_read())
 
6652
        manager = tree.get_shelf_manager()
 
6653
        shelves = manager.active_shelves()
 
6654
        if len(shelves) == 0:
 
6655
            note(gettext('No shelved changes.'))
 
6656
            return 0
 
6657
        for shelf_id in reversed(shelves):
 
6658
            message = manager.get_metadata(shelf_id).get(b'message')
 
6659
            if message is None:
 
6660
                message = '<no message>'
 
6661
            self.outf.write('%3d: %s\n' % (shelf_id, message))
 
6662
        return 1
 
6663
 
 
6664
 
 
6665
class cmd_unshelve(Command):
 
6666
    __doc__ = """Restore shelved changes.
 
6667
 
 
6668
    By default, the most recently shelved changes are restored. However if you
 
6669
    specify a shelf by id those changes will be restored instead.  This works
 
6670
    best when the changes don't depend on each other.
 
6671
    """
 
6672
 
 
6673
    takes_args = ['shelf_id?']
 
6674
    takes_options = [
 
6675
        'directory',
 
6676
        RegistryOption.from_kwargs(
 
6677
            'action', help="The action to perform.",
 
6678
            enum_switch=False, value_switches=True,
 
6679
            apply="Apply changes and remove from the shelf.",
 
6680
            dry_run="Show changes, but do not apply or remove them.",
 
6681
            preview="Instead of unshelving the changes, show the diff that "
 
6682
                    "would result from unshelving.",
 
6683
            delete_only="Delete changes without applying them.",
 
6684
            keep="Apply changes but don't delete them.",
 
6685
        )
 
6686
    ]
 
6687
    _see_also = ['shelve']
 
6688
 
 
6689
    def run(self, shelf_id=None, action='apply', directory=u'.'):
 
6690
        from .shelf_ui import Unshelver
 
6691
        unshelver = Unshelver.from_args(shelf_id, action, directory=directory)
 
6692
        try:
 
6693
            unshelver.run()
 
6694
        finally:
 
6695
            unshelver.tree.unlock()
 
6696
 
 
6697
 
 
6698
class cmd_clean_tree(Command):
 
6699
    __doc__ = """Remove unwanted files from working tree.
 
6700
 
 
6701
    By default, only unknown files, not ignored files, are deleted.  Versioned
 
6702
    files are never deleted.
 
6703
 
 
6704
    Another class is 'detritus', which includes files emitted by brz during
 
6705
    normal operations and selftests.  (The value of these files decreases with
 
6706
    time.)
 
6707
 
 
6708
    If no options are specified, unknown files are deleted.  Otherwise, option
 
6709
    flags are respected, and may be combined.
 
6710
 
 
6711
    To check what clean-tree will do, use --dry-run.
 
6712
    """
 
6713
    takes_options = ['directory',
 
6714
                     Option('ignored', help='Delete all ignored files.'),
 
6715
                     Option('detritus', help='Delete conflict files, merge and revert'
 
6716
                            ' backups, and failed selftest dirs.'),
 
6717
                     Option('unknown',
 
6718
                            help='Delete files unknown to brz (default).'),
 
6719
                     Option('dry-run', help='Show files to delete instead of'
 
6720
                            ' deleting them.'),
 
6721
                     Option('force', help='Do not prompt before deleting.')]
 
6722
 
 
6723
    def run(self, unknown=False, ignored=False, detritus=False, dry_run=False,
 
6724
            force=False, directory=u'.'):
 
6725
        from .clean_tree import clean_tree
 
6726
        if not (unknown or ignored or detritus):
 
6727
            unknown = True
 
6728
        if dry_run:
 
6729
            force = True
 
6730
        clean_tree(directory, unknown=unknown, ignored=ignored,
 
6731
                   detritus=detritus, dry_run=dry_run, no_prompt=force)
 
6732
 
 
6733
 
 
6734
class cmd_reference(Command):
 
6735
    __doc__ = """list, view and set branch locations for nested trees.
 
6736
 
 
6737
    If no arguments are provided, lists the branch locations for nested trees.
 
6738
    If one argument is provided, display the branch location for that tree.
 
6739
    If two arguments are provided, set the branch location for that tree.
 
6740
    """
 
6741
 
 
6742
    hidden = True
 
6743
 
 
6744
    takes_args = ['path?', 'location?']
 
6745
    takes_options = [
 
6746
        'directory',
 
6747
        Option('force-unversioned',
 
6748
               help='Set reference even if path is not versioned.'),
 
6749
        ]
 
6750
 
 
6751
    def run(self, path=None, directory='.', location=None, force_unversioned=False):
 
6752
        tree, branch, relpath = (
 
6753
            controldir.ControlDir.open_containing_tree_or_branch(directory))
 
6754
        if tree is None:
 
6755
            tree = branch.basis_tree()
 
6756
        if path is None:
 
6757
            with tree.lock_read():
 
6758
                info = [
 
6759
                    (path, tree.get_reference_info(path, branch))
 
6760
                    for path in tree.iter_references()]
 
6761
                self._display_reference_info(tree, branch, info)
 
6762
        else:
 
6763
            if not tree.is_versioned(path) and not force_unversioned:
 
6764
                raise errors.NotVersionedError(path)
 
6765
            if location is None:
 
6766
                info = [(path, tree.get_reference_info(path, branch))]
 
6767
                self._display_reference_info(tree, branch, info)
 
6768
            else:
 
6769
                tree.set_reference_info(path, location)
 
6770
 
 
6771
    def _display_reference_info(self, tree, branch, info):
 
6772
        ref_list = []
 
6773
        for path, location in info:
 
6774
            ref_list.append((path, location))
 
6775
        for path, location in sorted(ref_list):
 
6776
            self.outf.write('%s %s\n' % (path, location))
 
6777
 
 
6778
 
 
6779
class cmd_export_pot(Command):
 
6780
    __doc__ = """Export command helps and error messages in po format."""
 
6781
 
 
6782
    hidden = True
 
6783
    takes_options = [Option('plugin',
 
6784
                            help='Export help text from named command '
 
6785
                                 '(defaults to all built in commands).',
 
6786
                            type=str),
 
6787
                     Option('include-duplicates',
 
6788
                            help='Output multiple copies of the same msgid '
 
6789
                                 'string if it appears more than once.'),
 
6790
                     ]
 
6791
 
 
6792
    def run(self, plugin=None, include_duplicates=False):
 
6793
        from .export_pot import export_pot
 
6794
        export_pot(self.outf, plugin, include_duplicates)
 
6795
 
 
6796
 
 
6797
class cmd_import(Command):
 
6798
    __doc__ = """Import sources from a directory, tarball or zip file
 
6799
 
 
6800
    This command will import a directory, tarball or zip file into a bzr
 
6801
    tree, replacing any versioned files already present.  If a directory is
 
6802
    specified, it is used as the target.  If the directory does not exist, or
 
6803
    is not versioned, it is created.
 
6804
 
 
6805
    Tarballs may be gzip or bzip2 compressed.  This is autodetected.
 
6806
 
 
6807
    If the tarball or zip has a single root directory, that directory is
 
6808
    stripped when extracting the tarball.  This is not done for directories.
 
6809
    """
 
6810
 
 
6811
    takes_args = ['source', 'tree?']
 
6812
 
 
6813
    def run(self, source, tree=None):
 
6814
        from .upstream_import import do_import
 
6815
        do_import(source, tree)
 
6816
 
 
6817
 
 
6818
class cmd_link_tree(Command):
 
6819
    __doc__ = """Hardlink matching files to another tree.
 
6820
 
 
6821
    Only files with identical content and execute bit will be linked.
 
6822
    """
 
6823
 
 
6824
    takes_args = ['location']
 
6825
 
 
6826
    def run(self, location):
 
6827
        from .transform import link_tree
 
6828
        target_tree = WorkingTree.open_containing(".")[0]
 
6829
        source_tree = WorkingTree.open(location)
 
6830
        with target_tree.lock_write(), source_tree.lock_read():
 
6831
            link_tree(target_tree, source_tree)
 
6832
 
 
6833
 
 
6834
class cmd_fetch_ghosts(Command):
 
6835
    __doc__ = """Attempt to retrieve ghosts from another branch.
 
6836
 
 
6837
    If the other branch is not supplied, the last-pulled branch is used.
 
6838
    """
 
6839
 
 
6840
    hidden = True
 
6841
    aliases = ['fetch-missing']
 
6842
    takes_args = ['branch?']
 
6843
    takes_options = [Option('no-fix', help="Skip additional synchonization.")]
 
6844
 
 
6845
    def run(self, branch=None, no_fix=False):
 
6846
        from .fetch_ghosts import GhostFetcher
 
6847
        installed, failed = GhostFetcher.from_cmdline(branch).run()
 
6848
        if len(installed) > 0:
 
6849
            self.outf.write("Installed:\n")
 
6850
            for rev in installed:
 
6851
                self.outf.write(rev.decode('utf-8') + "\n")
 
6852
        if len(failed) > 0:
 
6853
            self.outf.write("Still missing:\n")
 
6854
            for rev in failed:
 
6855
                self.outf.write(rev.decode('utf-8') + "\n")
 
6856
        if not no_fix and len(installed) > 0:
 
6857
            cmd_reconcile().run(".")
 
6858
 
 
6859
 
 
6860
class cmd_grep(Command):
 
6861
    """Print lines matching PATTERN for specified files and revisions.
 
6862
 
 
6863
    This command searches the specified files and revisions for a given
 
6864
    pattern.  The pattern is specified as a Python regular expressions[1].
 
6865
 
 
6866
    If the file name is not specified, the revisions starting with the
 
6867
    current directory are searched recursively. If the revision number is
 
6868
    not specified, the working copy is searched. To search the last committed
 
6869
    revision, use the '-r -1' or '-r last:1' option.
 
6870
 
 
6871
    Unversioned files are not searched unless explicitly specified on the
 
6872
    command line. Unversioned directores are not searched.
 
6873
 
 
6874
    When searching a pattern, the output is shown in the 'filepath:string'
 
6875
    format. If a revision is explicitly searched, the output is shown as
 
6876
    'filepath~N:string', where N is the revision number.
 
6877
 
 
6878
    --include and --exclude options can be used to search only (or exclude
 
6879
    from search) files with base name matches the specified Unix style GLOB
 
6880
    pattern.  The GLOB pattern an use *, ?, and [...] as wildcards, and \\
 
6881
    to quote wildcard or backslash character literally. Note that the glob
 
6882
    pattern is not a regular expression.
 
6883
 
 
6884
    [1] http://docs.python.org/library/re.html#regular-expression-syntax
 
6885
    """
 
6886
 
 
6887
    encoding_type = 'replace'
 
6888
    takes_args = ['pattern', 'path*']
 
6889
    takes_options = [
 
6890
        'verbose',
 
6891
        'revision',
 
6892
        Option('color', type=str, argname='when',
 
6893
               help='Show match in color. WHEN is never, always or auto.'),
 
6894
        Option('diff', short_name='p',
 
6895
               help='Grep for pattern in changeset for each revision.'),
 
6896
        ListOption('exclude', type=str, argname='glob', short_name='X',
 
6897
                   help="Skip files whose base name matches GLOB."),
 
6898
        ListOption('include', type=str, argname='glob', short_name='I',
 
6899
                   help="Search only files whose base name matches GLOB."),
 
6900
        Option('files-with-matches', short_name='l',
 
6901
               help='Print only the name of each input file in '
 
6902
               'which PATTERN is found.'),
 
6903
        Option('files-without-match', short_name='L',
 
6904
               help='Print only the name of each input file in '
 
6905
               'which PATTERN is not found.'),
 
6906
        Option('fixed-string', short_name='F',
 
6907
               help='Interpret PATTERN is a single fixed string (not regex).'),
 
6908
        Option('from-root',
 
6909
               help='Search for pattern starting from the root of the branch. '
 
6910
               '(implies --recursive)'),
 
6911
        Option('ignore-case', short_name='i',
 
6912
               help='Ignore case distinctions while matching.'),
 
6913
        Option('levels',
 
6914
               help='Number of levels to display - 0 for all, 1 for collapsed '
 
6915
               '(1 is default).',
 
6916
               argname='N',
 
6917
               type=_parse_levels),
 
6918
        Option('line-number', short_name='n',
 
6919
               help='Show 1-based line number.'),
 
6920
        Option('no-recursive',
 
6921
               help="Don't recurse into subdirectories. (default is --recursive)"),
 
6922
        Option('null', short_name='Z',
 
6923
               help='Write an ASCII NUL (\\0) separator '
 
6924
               'between output lines rather than a newline.'),
 
6925
        ]
 
6926
 
 
6927
    @display_command
 
6928
    def run(self, verbose=False, ignore_case=False, no_recursive=False,
 
6929
            from_root=False, null=False, levels=None, line_number=False,
 
6930
            path_list=None, revision=None, pattern=None, include=None,
 
6931
            exclude=None, fixed_string=False, files_with_matches=False,
 
6932
            files_without_match=False, color=None, diff=False):
 
6933
        from breezy import _termcolor
 
6934
        from . import grep
 
6935
        import re
 
6936
        if path_list is None:
 
6937
            path_list = ['.']
 
6938
        else:
 
6939
            if from_root:
 
6940
                raise errors.BzrCommandError(
 
6941
                    'cannot specify both --from-root and PATH.')
 
6942
 
 
6943
        if files_with_matches and files_without_match:
 
6944
            raise errors.BzrCommandError(
 
6945
                'cannot specify both '
 
6946
                '-l/--files-with-matches and -L/--files-without-matches.')
 
6947
 
 
6948
        global_config = _mod_config.GlobalConfig()
 
6949
 
 
6950
        if color is None:
 
6951
            color = global_config.get_user_option('grep_color')
 
6952
 
 
6953
        if color is None:
 
6954
            color = 'never'
 
6955
 
 
6956
        if color not in ['always', 'never', 'auto']:
 
6957
            raise errors.BzrCommandError('Valid values for --color are '
 
6958
                                         '"always", "never" or "auto".')
 
6959
 
 
6960
        if levels is None:
 
6961
            levels = 1
 
6962
 
 
6963
        print_revno = False
 
6964
        if revision is not None or levels == 0:
 
6965
            # print revision numbers as we may be showing multiple revisions
 
6966
            print_revno = True
 
6967
 
 
6968
        eol_marker = '\n'
 
6969
        if null:
 
6970
            eol_marker = '\0'
 
6971
 
 
6972
        if not ignore_case and grep.is_fixed_string(pattern):
 
6973
            # if the pattern isalnum, implicitly use to -F for faster grep
 
6974
            fixed_string = True
 
6975
        elif ignore_case and fixed_string:
 
6976
            # GZ 2010-06-02: Fall back to regexp rather than lowercasing
 
6977
            #                pattern and text which will cause pain later
 
6978
            fixed_string = False
 
6979
            pattern = re.escape(pattern)
 
6980
 
 
6981
        patternc = None
 
6982
        re_flags = re.MULTILINE
 
6983
        if ignore_case:
 
6984
            re_flags |= re.IGNORECASE
 
6985
 
 
6986
        if not fixed_string:
 
6987
            patternc = grep.compile_pattern(
 
6988
                pattern.encode(grep._user_encoding), re_flags)
 
6989
 
 
6990
        if color == 'always':
 
6991
            show_color = True
 
6992
        elif color == 'never':
 
6993
            show_color = False
 
6994
        elif color == 'auto':
 
6995
            show_color = _termcolor.allow_color()
 
6996
 
 
6997
        opts = grep.GrepOptions()
 
6998
 
 
6999
        opts.verbose = verbose
 
7000
        opts.ignore_case = ignore_case
 
7001
        opts.no_recursive = no_recursive
 
7002
        opts.from_root = from_root
 
7003
        opts.null = null
 
7004
        opts.levels = levels
 
7005
        opts.line_number = line_number
 
7006
        opts.path_list = path_list
 
7007
        opts.revision = revision
 
7008
        opts.pattern = pattern
 
7009
        opts.include = include
 
7010
        opts.exclude = exclude
 
7011
        opts.fixed_string = fixed_string
 
7012
        opts.files_with_matches = files_with_matches
 
7013
        opts.files_without_match = files_without_match
 
7014
        opts.color = color
 
7015
        opts.diff = False
 
7016
 
 
7017
        opts.eol_marker = eol_marker
 
7018
        opts.print_revno = print_revno
 
7019
        opts.patternc = patternc
 
7020
        opts.recursive = not no_recursive
 
7021
        opts.fixed_string = fixed_string
 
7022
        opts.outf = self.outf
 
7023
        opts.show_color = show_color
 
7024
 
 
7025
        if diff:
 
7026
            # options not used:
 
7027
            # files_with_matches, files_without_match
 
7028
            # levels(?), line_number, from_root
 
7029
            # include, exclude
 
7030
            # These are silently ignored.
 
7031
            grep.grep_diff(opts)
 
7032
        elif revision is None:
 
7033
            grep.workingtree_grep(opts)
 
7034
        else:
 
7035
            grep.versioned_grep(opts)
 
7036
 
 
7037
 
 
7038
class cmd_patch(Command):
 
7039
    """Apply a named patch to the current tree.
 
7040
 
 
7041
    """
 
7042
 
 
7043
    takes_args = ['filename?']
 
7044
    takes_options = [Option('strip', type=int, short_name='p',
 
7045
                            help=("Strip the smallest prefix containing num "
 
7046
                                  "leading slashes from filenames.")),
 
7047
                     Option('silent', help='Suppress chatter.')]
 
7048
 
 
7049
    def run(self, filename=None, strip=None, silent=False):
 
7050
        from .patch import patch_tree
 
7051
        wt = WorkingTree.open_containing('.')[0]
 
7052
        if strip is None:
 
7053
            strip = 1
 
7054
        my_file = None
 
7055
        if filename is None:
 
7056
            my_file = getattr(sys.stdin, 'buffer', sys.stdin)
 
7057
        else:
 
7058
            my_file = open(filename, 'rb')
 
7059
        patches = [my_file.read()]
 
7060
        return patch_tree(wt, patches, strip, quiet=is_quiet(), out=self.outf)
 
7061
 
 
7062
 
 
7063
class cmd_resolve_location(Command):
 
7064
    __doc__ = """Expand a location to a full URL.
 
7065
 
 
7066
    :Examples:
 
7067
        Look up a Launchpad URL.
 
7068
 
 
7069
            brz resolve-location lp:brz
 
7070
    """
 
7071
    takes_args = ['location']
 
7072
    hidden = True
 
7073
 
 
7074
    def run(self, location):
 
7075
        from .location import location_to_url
 
7076
        url = location_to_url(location)
 
7077
        display_url = urlutils.unescape_for_display(url, self.outf.encoding)
 
7078
        self.outf.write('%s\n' % display_url)
 
7079
 
 
7080
 
 
7081
def _register_lazy_builtins():
 
7082
    # register lazy builtins from other modules; called at startup and should
 
7083
    # be only called once.
 
7084
    for (name, aliases, module_name) in [
 
7085
            ('cmd_bisect', [], 'breezy.bisect'),
 
7086
            ('cmd_bundle_info', [], 'breezy.bzr.bundle.commands'),
 
7087
            ('cmd_config', [], 'breezy.config'),
 
7088
            ('cmd_dump_btree', [], 'breezy.bzr.debug_commands'),
 
7089
            ('cmd_file_id', [], 'breezy.bzr.debug_commands'),
 
7090
            ('cmd_file_path', [], 'breezy.bzr.debug_commands'),
 
7091
            ('cmd_version_info', [], 'breezy.cmd_version_info'),
 
7092
            ('cmd_resolve', ['resolved'], 'breezy.conflicts'),
 
7093
            ('cmd_conflicts', [], 'breezy.conflicts'),
 
7094
            ('cmd_ping', [], 'breezy.bzr.smart.ping'),
 
7095
            ('cmd_sign_my_commits', [], 'breezy.commit_signature_commands'),
 
7096
            ('cmd_verify_signatures', [], 'breezy.commit_signature_commands'),
 
7097
            ('cmd_test_script', [], 'breezy.cmd_test_script'),
 
7098
            ]:
 
7099
        builtin_command_registry.register_lazy(name, aliases, module_name)