~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2006-05-23 11:06:22 UTC
  • mfrom: (1704.2.28 bzr.mbp.integration)
  • Revision ID: pqm@pqm.ubuntu.com-20060523110622-5ad5c2bf9dee4fc5
(mbp) use bisect to find revisions by date

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