~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-06-20 03:50:35 UTC
  • mfrom: (1740.5.9 bzr.mbp.traceback)
  • Revision ID: pqm@pqm.ubuntu.com-20060620035035-a9a7dc096fed5060
(mbp) show traceback on stderr on unexpected errors

Show diffs side-by-side

added added

removed removed

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