~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2005-09-16 07:03:16 UTC
  • Revision ID: mbp@sourcefrog.net-20050916070316-29f4b33b6f86fdc3
- cElementTree is typically not installed in util

  this was probably causing it to not be imported in many cases

Show diffs side-by-side

added added

removed removed

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