~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: John Arbash Meinel
  • Date: 2006-07-25 15:10:57 UTC
  • mto: (1946.2.6 reduce-knit-churn)
  • mto: This revision was merged to the branch mainline in revision 1886.
  • Revision ID: john@arbash-meinel.com-20060725151057-2c2137712774f59d
Update HACKING to be rst compliant

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