~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: John Arbash Meinel
  • Date: 2006-03-08 14:31:23 UTC
  • mfrom: (1598 +trunk)
  • mto: (1685.1.1 bzr-encoding)
  • mto: This revision was merged to the branch mainline in revision 1752.
  • Revision ID: john@arbash-meinel.com-20060308143123-448308b0db4de410
[merge] bzr.dev 1573, lots of updates

Show diffs side-by-side

added added

removed removed

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