~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Martin Pool
  • Date: 2006-06-20 03:30:14 UTC
  • mfrom: (1793 +trunk)
  • mto: This revision was merged to the branch mainline in revision 1797.
  • Revision ID: mbp@sourcefrog.net-20060620033014-e19ce470e2ce6561
[merge] bzr.dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
17
17
"""builtin bzr commands"""
18
18
 
19
19
 
 
20
import codecs
20
21
import errno
21
22
import os
 
23
from shutil import rmtree
22
24
import sys
23
25
 
24
26
import bzrlib
25
27
import bzrlib.branch
26
28
from bzrlib.branch import Branch
27
29
import bzrlib.bzrdir as bzrdir
 
30
from bzrlib.bundle import read_bundle_from_url
28
31
from bzrlib.bundle.read_bundle import BundleReader
29
 
from bzrlib.bundle.apply_bundle import merge_bundle
 
32
from bzrlib.bundle.apply_bundle import install_bundle, merge_bundle
30
33
from bzrlib.commands import Command, display_command
31
34
import bzrlib.errors as errors
32
35
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError, 
33
36
                           NotBranchError, DivergedBranches, NotConflicted,
34
37
                           NoSuchFile, NoWorkingTree, FileInWrongBranch,
35
 
                           NotVersionedError, BadBundle)
 
38
                           NotVersionedError, NotABundle)
36
39
from bzrlib.log import show_one_log
37
40
from bzrlib.merge import Merge3Merger
38
41
from bzrlib.option import Option
 
42
import bzrlib.osutils
39
43
from bzrlib.progress import DummyProgress, ProgressPhase
40
44
from bzrlib.revision import common_ancestor
41
45
from bzrlib.revisionspec import RevisionSpec
43
47
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
44
48
from bzrlib.transport.local import LocalTransport
45
49
import bzrlib.ui
 
50
import bzrlib.urlutils as urlutils
46
51
from bzrlib.workingtree import WorkingTree
47
52
 
48
53
 
128
133
    modified
129
134
        Text has changed since the previous revision.
130
135
 
131
 
    unchanged
132
 
        Nothing about this file has changed since the previous revision.
133
 
        Only shown with --all.
134
 
 
135
136
    unknown
136
137
        Not versioned and not matching an ignore pattern.
137
138
 
150
151
    # TODO: --no-recurse, --recurse options
151
152
    
152
153
    takes_args = ['file*']
153
 
    takes_options = ['all', 'show-ids', 'revision']
 
154
    takes_options = ['show-ids', 'revision']
154
155
    aliases = ['st', 'stat']
 
156
 
 
157
    encoding_type = 'replace'
155
158
    
156
159
    @display_command
157
 
    def run(self, all=False, show_ids=False, file_list=None, revision=None):
158
 
        tree, file_list = tree_files(file_list)
159
 
            
 
160
    def run(self, show_ids=False, file_list=None, revision=None):
160
161
        from bzrlib.status import show_tree_status
161
 
        show_tree_status(tree, show_unchanged=all, show_ids=show_ids,
162
 
                         specific_files=file_list, revision=revision)
 
162
 
 
163
        tree, file_list = tree_files(file_list)
 
164
            
 
165
        show_tree_status(tree, show_ids=show_ids,
 
166
                         specific_files=file_list, revision=revision,
 
167
                         to_file=self.outf)
163
168
 
164
169
 
165
170
class cmd_cat_revision(Command):
172
177
    hidden = True
173
178
    takes_args = ['revision_id?']
174
179
    takes_options = ['revision']
 
180
    # cat-revision is more for frontends so should be exact
 
181
    encoding = 'strict'
175
182
    
176
183
    @display_command
177
184
    def run(self, revision_id=None, revision=None):
181
188
        if revision_id is None and revision is None:
182
189
            raise BzrCommandError('You must supply either --revision or a revision_id')
183
190
        b = WorkingTree.open_containing(u'.')[0].branch
 
191
 
 
192
        # TODO: jam 20060112 should cat-revision always output utf-8?
184
193
        if revision_id is not None:
185
 
            sys.stdout.write(b.repository.get_revision_xml(revision_id))
 
194
            self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
186
195
        elif revision is not None:
187
196
            for rev in revision:
188
197
                if rev is None:
189
198
                    raise BzrCommandError('You cannot specify a NULL revision.')
190
199
                revno, rev_id = rev.in_history(b)
191
 
                sys.stdout.write(b.repository.get_revision_xml(rev_id))
 
200
                self.outf.write(b.repository.get_revision_xml(rev_id).decode('utf-8'))
192
201
    
193
202
 
194
203
class cmd_revno(Command):
195
204
    """Show current revision number.
196
205
 
197
 
    This is equal to the number of revisions on this branch."""
 
206
    This is equal to the number of revisions on this branch.
 
207
    """
 
208
 
198
209
    takes_args = ['location?']
 
210
 
199
211
    @display_command
200
212
    def run(self, location=u'.'):
201
 
        print Branch.open_containing(location)[0].revno()
 
213
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
 
214
        self.outf.write('\n')
202
215
 
203
216
 
204
217
class cmd_revision_info(Command):
207
220
    hidden = True
208
221
    takes_args = ['revision_info*']
209
222
    takes_options = ['revision']
 
223
 
210
224
    @display_command
211
225
    def run(self, revision=None, revision_info_list=[]):
212
226
 
249
263
 
250
264
    Adding a file whose parent directory is not versioned will
251
265
    implicitly add the parent, and so on up to the root. This means
252
 
    you should never need to explictly add a directory, they'll just
 
266
    you should never need to explicitly add a directory, they'll just
253
267
    get added when you add a file in the directory.
254
268
 
255
269
    --dry-run will show which files would be added, but not actually 
257
271
    """
258
272
    takes_args = ['file*']
259
273
    takes_options = ['no-recurse', 'dry-run', 'verbose']
 
274
    encoding_type = 'replace'
260
275
 
261
276
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
262
277
        import bzrlib.add
263
278
 
264
 
        if dry_run:
265
 
            if is_quiet():
266
 
                # This is pointless, but I'd rather not raise an error
267
 
                action = bzrlib.add.add_action_null
268
 
            else:
269
 
                action = bzrlib.add.add_action_print
270
 
        elif is_quiet():
271
 
            action = bzrlib.add.add_action_add
272
 
        else:
273
 
            action = bzrlib.add.add_action_add_and_print
 
279
        action = bzrlib.add.AddAction(to_file=self.outf,
 
280
            should_print=(not is_quiet()))
274
281
 
275
282
        added, ignored = bzrlib.add.smart_add(file_list, not no_recurse, 
276
 
                                              action)
 
283
                                              action=action, save=not dry_run)
277
284
        if len(ignored) > 0:
278
285
            if verbose:
279
286
                for glob in sorted(ignored.keys()):
280
287
                    for path in ignored[glob]:
281
 
                        print "ignored %s matching \"%s\"" % (path, glob)
 
288
                        self.outf.write("ignored %s matching \"%s\"\n" 
 
289
                                        % (path, glob))
282
290
            else:
283
291
                match_len = 0
284
292
                for glob, paths in ignored.items():
285
293
                    match_len += len(paths)
286
 
                print "ignored %d file(s)." % match_len
287
 
            print "If you wish to add some of these files, please add them"\
288
 
                " by name."
 
294
                self.outf.write("ignored %d file(s).\n" % match_len)
 
295
            self.outf.write("If you wish to add some of these files,"
 
296
                            " please add them by name.\n")
289
297
 
290
298
 
291
299
class cmd_mkdir(Command):
293
301
 
294
302
    This is equivalent to creating the directory and then adding it.
295
303
    """
 
304
 
296
305
    takes_args = ['dir+']
 
306
    encoding_type = 'replace'
297
307
 
298
308
    def run(self, dir_list):
299
309
        for d in dir_list:
300
310
            os.mkdir(d)
301
311
            wt, dd = WorkingTree.open_containing(d)
302
312
            wt.add([dd])
303
 
            print 'added', d
 
313
            self.outf.write('added %s\n' % d)
304
314
 
305
315
 
306
316
class cmd_relpath(Command):
307
317
    """Show path of a file relative to root"""
 
318
 
308
319
    takes_args = ['filename']
309
320
    hidden = True
310
321
    
311
322
    @display_command
312
323
    def run(self, filename):
 
324
        # TODO: jam 20050106 Can relpath return a munged path if
 
325
        #       sys.stdout encoding cannot represent it?
313
326
        tree, relpath = WorkingTree.open_containing(filename)
314
 
        print relpath
 
327
        self.outf.write(relpath)
 
328
        self.outf.write('\n')
315
329
 
316
330
 
317
331
class cmd_inventory(Command):
320
334
    It is possible to limit the output to a particular entry
321
335
    type using the --kind option.  For example; --kind file.
322
336
    """
 
337
 
323
338
    takes_options = ['revision', 'show-ids', 'kind']
324
339
    
325
340
    @display_command
340
355
            if kind and kind != entry.kind:
341
356
                continue
342
357
            if show_ids:
343
 
                print '%-50s %s' % (path, entry.file_id)
 
358
                self.outf.write('%-50s %s\n' % (path, entry.file_id))
344
359
            else:
345
 
                print path
 
360
                self.outf.write(path)
 
361
                self.outf.write('\n')
346
362
 
347
363
 
348
364
class cmd_mv(Command):
358
374
 
359
375
    Files cannot be moved between branches.
360
376
    """
 
377
 
361
378
    takes_args = ['names*']
362
379
    aliases = ['move', 'rename']
 
380
    encoding_type = 'replace'
363
381
 
364
382
    def run(self, names_list):
365
383
        if len(names_list) < 2:
369
387
        if os.path.isdir(names_list[-1]):
370
388
            # move into existing directory
371
389
            for pair in tree.move(rel_names[:-1], rel_names[-1]):
372
 
                print "%s => %s" % pair
 
390
                self.outf.write("%s => %s\n" % pair)
373
391
        else:
374
392
            if len(names_list) != 2:
375
393
                raise BzrCommandError('to mv multiple files the destination '
376
394
                                      'must be a versioned directory')
377
395
            tree.rename_one(rel_names[0], rel_names[1])
378
 
            print "%s => %s" % (rel_names[0], rel_names[1])
 
396
            self.outf.write("%s => %s\n" % (rel_names[0], rel_names[1]))
379
397
            
380
398
    
381
399
class cmd_pull(Command):
400
418
    that, you can omit the location to use the default.  To change the
401
419
    default, use --remember.
402
420
    """
 
421
 
403
422
    takes_options = ['remember', 'overwrite', 'revision', 'verbose']
404
423
    takes_args = ['location?']
 
424
    encoding_type = 'replace'
405
425
 
406
426
    def run(self, location=None, remember=False, overwrite=False, revision=None, verbose=False):
407
427
        # FIXME: too much stuff is in the command class
410
430
            branch_to = tree_to.branch
411
431
        except NoWorkingTree:
412
432
            tree_to = None
413
 
            branch_to = Branch.open_containing(u'.')[0] 
 
433
            branch_to = Branch.open_containing(u'.')[0]
 
434
 
 
435
        reader = None
 
436
        if location is not None:
 
437
            try:
 
438
                reader = read_bundle_from_url(location)
 
439
            except NotABundle:
 
440
                pass # Continue on considering this url a Branch
 
441
 
414
442
        stored_loc = branch_to.get_parent()
415
443
        if location is None:
416
444
            if stored_loc is None:
417
445
                raise BzrCommandError("No pull location known or specified.")
418
446
            else:
419
 
                print "Using saved location: %s" % stored_loc
 
447
                display_url = urlutils.unescape_for_display(stored_loc,
 
448
                        self.outf.encoding)
 
449
                self.outf.write("Using saved location: %s\n" % display_url)
420
450
                location = stored_loc
421
451
 
422
 
        if branch_to.get_parent() is None or remember:
423
 
            branch_to.set_parent(location)
424
 
 
425
 
        branch_from = Branch.open(location)
426
 
 
 
452
 
 
453
        if reader is not None:
 
454
            install_bundle(branch_to.repository, reader)
 
455
            branch_from = branch_to
 
456
        else:
 
457
            branch_from = Branch.open(location)
 
458
 
 
459
            if branch_to.get_parent() is None or remember:
 
460
                branch_to.set_parent(branch_from.base)
 
461
 
 
462
        rev_id = None
427
463
        if revision is None:
428
 
            rev_id = None
 
464
            if reader is not None:
 
465
                rev_id = reader.info.target
429
466
        elif len(revision) == 1:
430
467
            rev_id = revision[0].in_history(branch_from).rev_id
431
468
        else:
443
480
            if old_rh != new_rh:
444
481
                # Something changed
445
482
                from bzrlib.log import show_changed_revisions
446
 
                show_changed_revisions(branch_to, old_rh, new_rh)
 
483
                show_changed_revisions(branch_to, old_rh, new_rh,
 
484
                                       to_file=self.outf)
447
485
 
448
486
 
449
487
class cmd_push(Command):
470
508
    After that, you can omit the location to use the default.  To change the
471
509
    default, use --remember.
472
510
    """
473
 
    takes_options = ['remember', 'overwrite', 
 
511
 
 
512
    takes_options = ['remember', 'overwrite', 'verbose',
474
513
                     Option('create-prefix', 
475
514
                            help='Create the path leading up to the branch '
476
515
                                 'if it does not already exist')]
477
516
    takes_args = ['location?']
 
517
    encoding_type = 'replace'
478
518
 
479
519
    def run(self, location=None, remember=False, overwrite=False,
480
520
            create_prefix=False, verbose=False):
488
528
            if stored_loc is None:
489
529
                raise BzrCommandError("No push location known or specified.")
490
530
            else:
491
 
                print "Using saved location: %s" % stored_loc
 
531
                display_url = urlutils.unescape_for_display(stored_loc,
 
532
                        self.outf.encoding)
 
533
                self.outf.write("Using saved location: %s" % display_url)
492
534
                location = stored_loc
 
535
 
 
536
        transport = get_transport(location)
 
537
        location_url = transport.base
493
538
        if br_from.get_push_location() is None or remember:
494
 
            br_from.set_push_location(location)
 
539
            br_from.set_push_location(location_url)
 
540
 
 
541
        old_rh = []
495
542
        try:
496
 
            dir_to = bzrlib.bzrdir.BzrDir.open(location)
 
543
            dir_to = bzrlib.bzrdir.BzrDir.open(location_url)
497
544
            br_to = dir_to.open_branch()
498
545
        except NotBranchError:
499
546
            # create a branch.
500
 
            transport = get_transport(location).clone('..')
 
547
            transport = transport.clone('..')
501
548
            if not create_prefix:
502
549
                try:
503
 
                    transport.mkdir(transport.relpath(location))
 
550
                    relurl = transport.relpath(location_url)
 
551
                    mutter('creating directory %s => %s', location_url, relurl)
 
552
                    transport.mkdir(relurl)
504
553
                except NoSuchFile:
505
554
                    raise BzrCommandError("Parent directory of %s "
506
555
                                          "does not exist." % location)
507
556
            else:
508
557
                current = transport.base
509
 
                needed = [(transport, transport.relpath(location))]
 
558
                needed = [(transport, transport.relpath(location_url))]
510
559
                while needed:
511
560
                    try:
512
561
                        transport, relpath = needed[-1]
519
568
                        if new_transport.base == transport.base:
520
569
                            raise BzrCommandError("Could not create "
521
570
                                                  "path prefix.")
522
 
            dir_to = br_from.bzrdir.clone(location,
 
571
            dir_to = br_from.bzrdir.clone(location_url,
523
572
                revision_id=br_from.last_revision())
524
573
            br_to = dir_to.open_branch()
525
574
            count = len(br_to.revision_history())
546
595
            if old_rh != new_rh:
547
596
                # Something changed
548
597
                from bzrlib.log import show_changed_revisions
549
 
                show_changed_revisions(br_to, old_rh, new_rh)
 
598
                show_changed_revisions(br_to, old_rh, new_rh,
 
599
                                       to_file=self.outf)
550
600
 
551
601
 
552
602
class cmd_branch(Command):
567
617
    aliases = ['get', 'clone']
568
618
 
569
619
    def run(self, from_location, to_location=None, revision=None, basis=None):
 
620
        from bzrlib.transport import get_transport
570
621
        from bzrlib.osutils import rmtree
571
622
        if revision is None:
572
623
            revision = [None]
599
650
                name = None
600
651
            else:
601
652
                name = os.path.basename(to_location) + '\n'
 
653
 
 
654
            to_transport = get_transport(to_location)
602
655
            try:
603
 
                os.mkdir(to_location)
604
 
            except OSError, e:
605
 
                if e.errno == errno.EEXIST:
606
 
                    raise BzrCommandError('Target directory "%s" already'
607
 
                                          ' exists.' % to_location)
608
 
                if e.errno == errno.ENOENT:
609
 
                    raise BzrCommandError('Parent of "%s" does not exist.' %
610
 
                                          to_location)
611
 
                else:
612
 
                    raise
 
656
                to_transport.mkdir('.')
 
657
            except bzrlib.errors.FileExists:
 
658
                raise BzrCommandError('Target directory "%s" already'
 
659
                                      ' exists.' % to_location)
 
660
            except bzrlib.errors.NoSuchFile:
 
661
                raise BzrCommandError('Parent of "%s" does not exist.' %
 
662
                                      to_location)
613
663
            try:
614
664
                # preserve whatever source format we have.
615
 
                dir = br_from.bzrdir.sprout(to_location, revision_id, basis_dir)
 
665
                dir = br_from.bzrdir.sprout(to_transport.base,
 
666
                        revision_id, basis_dir)
616
667
                branch = dir.open_branch()
617
668
            except bzrlib.errors.NoSuchRevision:
618
 
                rmtree(to_location)
 
669
                to_transport.delete_tree('.')
619
670
                msg = "The branch %s has no revision %s." % (from_location, revision[0])
620
671
                raise BzrCommandError(msg)
621
672
            except bzrlib.errors.UnlistableBranch:
624
675
                raise BzrCommandError(msg)
625
676
            if name:
626
677
                branch.control_files.put_utf8('branch-name', name)
627
 
 
628
678
            note('Branched %d revision(s).' % branch.revno())
629
679
        finally:
630
680
            br_from.unlock()
735
785
        renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
736
786
        renames.sort()
737
787
        for old_name, new_name in renames:
738
 
            print "%s => %s" % (old_name, new_name)        
 
788
            self.outf.write("%s => %s\n" % (old_name, new_name))
739
789
 
740
790
 
741
791
class cmd_update(Command):
804
854
    takes_args = ['file*']
805
855
    takes_options = ['verbose', Option('new', help='remove newly-added files')]
806
856
    aliases = ['rm']
 
857
    encoding_type = 'replace'
807
858
    
808
859
    def run(self, file_list, verbose=False, new=False):
809
860
        tree, file_list = tree_files(file_list)
818
869
            file_list = sorted([f[0] for f in added[0]], reverse=True)
819
870
            if len(file_list) == 0:
820
871
                raise BzrCommandError('No matching files.')
821
 
        tree.remove(file_list, verbose=verbose)
 
872
        tree.remove(file_list, verbose=verbose, to_file=self.outf)
822
873
 
823
874
 
824
875
class cmd_file_id(Command):
828
879
    same through all revisions where the file exists, even when it is
829
880
    moved or renamed.
830
881
    """
 
882
 
831
883
    hidden = True
832
884
    takes_args = ['filename']
 
885
 
833
886
    @display_command
834
887
    def run(self, filename):
835
888
        tree, relpath = WorkingTree.open_containing(filename)
837
890
        if i == None:
838
891
            raise BzrError("%r is not a versioned file" % filename)
839
892
        else:
840
 
            print i
 
893
            self.outf.write(i + '\n')
841
894
 
842
895
 
843
896
class cmd_file_path(Command):
844
897
    """Print path of file_ids to a file or directory.
845
898
 
846
899
    This prints one line for each directory down to the target,
847
 
    starting at the branch root."""
 
900
    starting at the branch root.
 
901
    """
 
902
 
848
903
    hidden = True
849
904
    takes_args = ['filename']
 
905
 
850
906
    @display_command
851
907
    def run(self, filename):
852
908
        tree, relpath = WorkingTree.open_containing(filename)
855
911
        if fid == None:
856
912
            raise BzrError("%r is not a versioned file" % filename)
857
913
        for fip in inv.get_idpath(fid):
858
 
            print fip
 
914
            self.outf.write(fip + '\n')
859
915
 
860
916
 
861
917
class cmd_reconcile(Command):
885
941
 
886
942
 
887
943
class cmd_revision_history(Command):
888
 
    """Display list of revision ids on this branch."""
 
944
    """Display the list of revision ids on a branch."""
 
945
    takes_args = ['location?']
 
946
 
889
947
    hidden = True
 
948
 
890
949
    @display_command
891
 
    def run(self):
892
 
        branch = WorkingTree.open_containing(u'.')[0].branch
893
 
        for patchid in branch.revision_history():
894
 
            print patchid
 
950
    def run(self, location="."):
 
951
        branch = Branch.open_containing(location)[0]
 
952
        for revid in branch.revision_history():
 
953
            self.outf.write(revid)
 
954
            self.outf.write('\n')
895
955
 
896
956
 
897
957
class cmd_ancestry(Command):
898
958
    """List all revisions merged into this branch."""
 
959
    takes_args = ['location?']
 
960
 
899
961
    hidden = True
 
962
 
900
963
    @display_command
901
 
    def run(self):
902
 
        tree = WorkingTree.open_containing(u'.')[0]
903
 
        b = tree.branch
904
 
        # FIXME. should be tree.last_revision
905
 
        revision_ids = b.repository.get_ancestry(b.last_revision())
 
964
    def run(self, location="."):
 
965
        try:
 
966
            wt = WorkingTree.open_containing(location)[0]
 
967
        except errors.NoWorkingTree:
 
968
            b = Branch.open(location)
 
969
            last_revision = b.last_revision()
 
970
        else:
 
971
            b = wt.branch
 
972
            last_revision = wt.last_revision()
 
973
 
 
974
        revision_ids = b.repository.get_ancestry(last_revision)
906
975
        assert revision_ids[0] == None
907
976
        revision_ids.pop(0)
908
977
        for revision_id in revision_ids:
909
 
            print revision_id
 
978
            self.outf.write(revision_id + '\n')
910
979
 
911
980
 
912
981
class cmd_init(Command):
1037
1106
    takes_args = ['file*']
1038
1107
    takes_options = ['revision', 'diff-options', 'prefix']
1039
1108
    aliases = ['di', 'dif']
 
1109
    encoding_type = 'exact'
1040
1110
 
1041
1111
    @display_command
1042
1112
    def run(self, revision=None, file_list=None, diff_options=None,
1103
1173
    # directories with readdir, rather than stating each one.  Same
1104
1174
    # level of effort but possibly much less IO.  (Or possibly not,
1105
1175
    # if the directories are very large...)
 
1176
    takes_options = ['show-ids']
 
1177
 
1106
1178
    @display_command
1107
1179
    def run(self, show_ids=False):
1108
1180
        tree = WorkingTree.open_containing(u'.')[0]
1109
1181
        old = tree.basis_tree()
1110
1182
        for path, ie in old.inventory.iter_entries():
1111
1183
            if not tree.has_id(ie.file_id):
 
1184
                self.outf.write(path)
1112
1185
                if show_ids:
1113
 
                    print '%-50s %s' % (path, ie.file_id)
1114
 
                else:
1115
 
                    print path
 
1186
                    self.outf.write(' ')
 
1187
                    self.outf.write(ie.file_id)
 
1188
                self.outf.write('\n')
1116
1189
 
1117
1190
 
1118
1191
class cmd_modified(Command):
1126
1199
        td = compare_trees(tree.basis_tree(), tree)
1127
1200
 
1128
1201
        for path, id, kind, text_modified, meta_modified in td.modified:
1129
 
            print path
1130
 
 
 
1202
            self.outf.write(path + '\n')
1131
1203
 
1132
1204
 
1133
1205
class cmd_added(Command):
1144
1216
            path = inv.id2path(file_id)
1145
1217
            if not os.access(bzrlib.osutils.abspath(path), os.F_OK):
1146
1218
                continue
1147
 
            print path
1148
 
                
1149
 
        
 
1219
            self.outf.write(path + '\n')
 
1220
 
1150
1221
 
1151
1222
class cmd_root(Command):
1152
1223
    """Show the tree root directory.
1158
1229
    def run(self, filename=None):
1159
1230
        """Print the branch root."""
1160
1231
        tree = WorkingTree.open_containing(filename)[0]
1161
 
        print tree.basedir
 
1232
        self.outf.write(tree.basedir + '\n')
1162
1233
 
1163
1234
 
1164
1235
class cmd_log(Command):
1192
1263
                            type=str),
1193
1264
                     'short',
1194
1265
                     ]
 
1266
    encoding_type = 'replace'
 
1267
 
1195
1268
    @display_command
1196
1269
    def run(self, location=None, timezone='original',
1197
1270
            verbose=False,
1204
1277
            short=False,
1205
1278
            line=False):
1206
1279
        from bzrlib.log import log_formatter, show_log
1207
 
        import codecs
1208
1280
        assert message is None or isinstance(message, basestring), \
1209
1281
            "invalid message argument %r" % message
1210
1282
        direction = (forward and 'forward') or 'reverse'
1256
1328
        if rev1 > rev2:
1257
1329
            (rev2, rev1) = (rev1, rev2)
1258
1330
 
1259
 
        mutter('encoding log as %r', bzrlib.user_encoding)
1260
 
 
1261
 
        # use 'replace' so that we don't abort if trying to write out
1262
 
        # in e.g. the default C locale.
1263
 
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
1264
 
 
1265
1331
        if (log_format == None):
1266
1332
            default = bzrlib.config.BranchConfig(b).log_format()
1267
1333
            log_format = get_log_format(long=long, short=short, line=line, default=default)
1268
 
 
1269
1334
        lf = log_formatter(log_format,
1270
1335
                           show_ids=show_ids,
1271
 
                           to_file=outf,
 
1336
                           to_file=self.outf,
1272
1337
                           show_timezone=timezone)
1273
1338
 
1274
1339
        show_log(b,
1295
1360
class cmd_touching_revisions(Command):
1296
1361
    """Return revision-ids which affected a particular file.
1297
1362
 
1298
 
    A more user-friendly interface is "bzr log FILE"."""
 
1363
    A more user-friendly interface is "bzr log FILE".
 
1364
    """
 
1365
 
1299
1366
    hidden = True
1300
1367
    takes_args = ["filename"]
 
1368
 
1301
1369
    @display_command
1302
1370
    def run(self, filename):
1303
1371
        tree, relpath = WorkingTree.open_containing(filename)
1305
1373
        inv = tree.read_working_inventory()
1306
1374
        file_id = inv.path2id(relpath)
1307
1375
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1308
 
            print "%6d %s" % (revno, what)
 
1376
            self.outf.write("%6d %s\n" % (revno, what))
1309
1377
 
1310
1378
 
1311
1379
class cmd_ls(Command):
1344
1412
        if revision is not None:
1345
1413
            tree = tree.branch.repository.revision_tree(
1346
1414
                revision[0].in_history(tree.branch).rev_id)
 
1415
 
1347
1416
        for fp, fc, kind, fid, entry in tree.list_files():
1348
1417
            if fp.startswith(relpath):
1349
1418
                fp = fp[len(relpath):]
1353
1422
                    continue
1354
1423
                if verbose:
1355
1424
                    kindch = entry.kind_character()
1356
 
                    print '%-8s %s%s' % (fc, fp, kindch)
 
1425
                    self.outf.write('%-8s %s%s\n' % (fc, fp, kindch))
1357
1426
                elif null:
1358
 
                    sys.stdout.write(fp)
1359
 
                    sys.stdout.write('\0')
1360
 
                    sys.stdout.flush()
 
1427
                    self.outf.write(fp + '\0')
 
1428
                    self.outf.flush()
1361
1429
                else:
1362
 
                    print fp
 
1430
                    self.outf.write(fp + '\n')
1363
1431
 
1364
1432
 
1365
1433
class cmd_unknowns(Command):
1368
1436
    def run(self):
1369
1437
        from bzrlib.osutils import quotefn
1370
1438
        for f in WorkingTree.open_containing(u'.')[0].unknowns():
1371
 
            print quotefn(f)
 
1439
            self.outf.write(quotefn(f) + '\n')
1372
1440
 
1373
1441
 
1374
1442
class cmd_ignore(Command):
1591
1659
        from bzrlib.msgeditor import edit_commit_message, \
1592
1660
                make_commit_message_template
1593
1661
        from tempfile import TemporaryFile
1594
 
        import codecs
1595
1662
 
1596
1663
        # TODO: Need a blackbox test for invoking the external editor; may be
1597
1664
        # slightly problematic to run this cross-platform.
1620
1687
            raise BzrCommandError("please specify either --message or --file")
1621
1688
        
1622
1689
        if file:
1623
 
            import codecs
1624
1690
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1625
1691
 
1626
1692
        if message == "":
1994
2060
        if merge_type is None:
1995
2061
            merge_type = Merge3Merger
1996
2062
 
1997
 
 
1998
2063
        tree = WorkingTree.open_containing(u'.')[0]
1999
 
        try:
2000
 
            if branch is not None:
2001
 
                reader = BundleReader(file(branch, 'rb'))
2002
 
            else:
2003
 
                reader = None
2004
 
        except IOError, e:
2005
 
            if e.errno not in (errno.ENOENT, errno.EISDIR):
2006
 
                raise
2007
 
            reader = None
2008
 
        except BadBundle:
2009
 
            reader = None
2010
 
        if reader is not None:
2011
 
            conflicts = merge_bundle(reader, tree, not force, merge_type,
2012
 
                                        reprocess, show_base)
2013
 
            if conflicts == 0:
2014
 
                return 0
2015
 
            else:
2016
 
                return 1
2017
 
 
2018
 
        stored_loc = tree.branch.get_parent()
2019
 
        if branch is None:
2020
 
            if stored_loc is None:
2021
 
                raise BzrCommandError("No merge branch known or specified.")
2022
 
            else:
2023
 
                print "Using saved branch: %s" % stored_loc
2024
 
                branch = stored_loc
2025
 
 
2026
 
        if tree.branch.get_parent() is None or remember:
2027
 
            tree.branch.set_parent(branch)
 
2064
 
 
2065
        if branch is not None:
 
2066
            try:
 
2067
                reader = read_bundle_from_url(branch)
 
2068
            except NotABundle:
 
2069
                pass # Continue on considering this url a Branch
 
2070
            else:
 
2071
                conflicts = merge_bundle(reader, tree, not force, merge_type,
 
2072
                                            reprocess, show_base)
 
2073
                if conflicts == 0:
 
2074
                    return 0
 
2075
                else:
 
2076
                    return 1
 
2077
 
 
2078
        branch = self._get_remembered_parent(tree, branch, 'Merging from')
2028
2079
 
2029
2080
        if revision is None or len(revision) < 1:
2030
2081
            base = [None, None]
2041
2092
                if None in revision:
2042
2093
                    raise BzrCommandError(
2043
2094
                        "Merge doesn't permit that revision specifier.")
2044
 
                b, path = Branch.open_containing(branch)
2045
 
 
2046
 
                base = [branch, revision[0].in_history(b).revno]
2047
 
                other = [branch, revision[1].in_history(b).revno]
 
2095
                other_branch, path = Branch.open_containing(branch)
 
2096
 
 
2097
                base = [branch, revision[0].in_history(other_branch).revno]
 
2098
                other = [branch, revision[1].in_history(other_branch).revno]
 
2099
 
 
2100
        if tree.branch.get_parent() is None or remember:
 
2101
            tree.branch.set_parent(other_branch.base)
 
2102
 
2048
2103
        if path != "":
2049
2104
            interesting_files = [path]
2050
2105
        else:
2053
2108
        try:
2054
2109
            try:
2055
2110
                conflict_count = merge(other, base, check_clean=(not force),
2056
 
                                       merge_type=merge_type, 
 
2111
                                       merge_type=merge_type,
2057
2112
                                       reprocess=reprocess,
2058
 
                                       show_base=show_base, 
 
2113
                                       show_base=show_base,
2059
2114
                                       pb=pb, file_list=interesting_files)
2060
2115
            finally:
2061
2116
                pb.finished()
2072
2127
                 "and (if you want) report this to the bzr developers\n")
2073
2128
            log_error(m)
2074
2129
 
 
2130
    # TODO: move up to common parent; this isn't merge-specific anymore. 
 
2131
    def _get_remembered_parent(self, tree, supplied_location, verb_string):
 
2132
        """Use tree.branch's parent if none was supplied.
 
2133
 
 
2134
        Report if the remembered location was used.
 
2135
        """
 
2136
        if supplied_location is not None:
 
2137
            return supplied_location
 
2138
        stored_location = tree.branch.get_parent()
 
2139
        mutter("%s", stored_location)
 
2140
        if stored_location is None:
 
2141
            raise BzrCommandError("No location specified or remembered")
 
2142
        display_url = urlutils.unescape_for_display(stored_location, self.outf.encoding)
 
2143
        self.outf.write("%s remembered location %s\n" % (verb_string, display_url))
 
2144
        return stored_location
 
2145
 
2075
2146
 
2076
2147
class cmd_remerge(Command):
2077
2148
    """Redo a merge.
2322
2393
            try:
2323
2394
                # handle race conditions - a parent might be set while we run.
2324
2395
                if local_branch.get_parent() is None:
2325
 
                    local_branch.set_parent(other_branch)
 
2396
                    local_branch.set_parent(remote_branch.base)
2326
2397
            finally:
2327
2398
                local_branch.unlock()
2328
2399
        return status_code
2500
2571
    """
2501
2572
 
2502
2573
    # TODO: jam 20060108 Add an option to allow uncommit to remove
2503
 
    # unreferenced information in 'branch-as-repostory' branches.
 
2574
    # unreferenced information in 'branch-as-repository' branches.
2504
2575
    # TODO: jam 20060108 Add the ability for uncommit to remove unreferenced
2505
2576
    # information in shared branches as well.
2506
2577
    takes_options = ['verbose', 'revision',