~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

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
32
34
from bzrlib.errors import (BzrError, BzrCheckError, BzrCommandError, 
33
35
                           NotBranchError, DivergedBranches, NotConflicted,
34
36
                           NoSuchFile, NoWorkingTree, FileInWrongBranch,
35
 
                           NotVersionedError, BadBundle)
 
37
                           NotVersionedError, NotABundle)
36
38
from bzrlib.log import show_one_log
37
39
from bzrlib.merge import Merge3Merger
38
40
from bzrlib.option import Option
 
41
import bzrlib.osutils
39
42
from bzrlib.progress import DummyProgress, ProgressPhase
40
43
from bzrlib.revision import common_ancestor
41
44
from bzrlib.revisionspec import RevisionSpec
43
46
from bzrlib.trace import mutter, note, log_error, warning, is_quiet
44
47
from bzrlib.transport.local import LocalTransport
45
48
import bzrlib.ui
 
49
import bzrlib.urlutils as urlutils
46
50
from bzrlib.workingtree import WorkingTree
47
51
 
48
52
 
152
156
    takes_args = ['file*']
153
157
    takes_options = ['all', 'show-ids', 'revision']
154
158
    aliases = ['st', 'stat']
 
159
 
 
160
    encoding_type = 'replace'
155
161
    
156
162
    @display_command
157
163
    def run(self, all=False, show_ids=False, file_list=None, revision=None):
158
 
        tree, file_list = tree_files(file_list)
159
 
            
160
164
        from bzrlib.status import show_tree_status
 
165
 
 
166
        tree, file_list = tree_files(file_list)
 
167
            
161
168
        show_tree_status(tree, show_unchanged=all, show_ids=show_ids,
162
 
                         specific_files=file_list, revision=revision)
 
169
                         specific_files=file_list, revision=revision,
 
170
                         to_file=self.outf)
163
171
 
164
172
 
165
173
class cmd_cat_revision(Command):
172
180
    hidden = True
173
181
    takes_args = ['revision_id?']
174
182
    takes_options = ['revision']
 
183
    # cat-revision is more for frontends so should be exact
 
184
    encoding = 'strict'
175
185
    
176
186
    @display_command
177
187
    def run(self, revision_id=None, revision=None):
181
191
        if revision_id is None and revision is None:
182
192
            raise BzrCommandError('You must supply either --revision or a revision_id')
183
193
        b = WorkingTree.open_containing(u'.')[0].branch
 
194
 
 
195
        # TODO: jam 20060112 should cat-revision always output utf-8?
184
196
        if revision_id is not None:
185
 
            sys.stdout.write(b.repository.get_revision_xml(revision_id))
 
197
            self.outf.write(b.repository.get_revision_xml(revision_id).decode('utf-8'))
186
198
        elif revision is not None:
187
199
            for rev in revision:
188
200
                if rev is None:
189
201
                    raise BzrCommandError('You cannot specify a NULL revision.')
190
202
                revno, rev_id = rev.in_history(b)
191
 
                sys.stdout.write(b.repository.get_revision_xml(rev_id))
 
203
                self.outf.write(b.repository.get_revision_xml(rev_id).decode('utf-8'))
192
204
    
193
205
 
194
206
class cmd_revno(Command):
195
207
    """Show current revision number.
196
208
 
197
 
    This is equal to the number of revisions on this branch."""
 
209
    This is equal to the number of revisions on this branch.
 
210
    """
 
211
 
198
212
    takes_args = ['location?']
 
213
 
199
214
    @display_command
200
215
    def run(self, location=u'.'):
201
 
        print Branch.open_containing(location)[0].revno()
 
216
        self.outf.write(str(Branch.open_containing(location)[0].revno()))
 
217
        self.outf.write('\n')
202
218
 
203
219
 
204
220
class cmd_revision_info(Command):
207
223
    hidden = True
208
224
    takes_args = ['revision_info*']
209
225
    takes_options = ['revision']
 
226
 
210
227
    @display_command
211
228
    def run(self, revision=None, revision_info_list=[]):
212
229
 
257
274
    """
258
275
    takes_args = ['file*']
259
276
    takes_options = ['no-recurse', 'dry-run', 'verbose']
 
277
    encoding_type = 'replace'
260
278
 
261
279
    def run(self, file_list, no_recurse=False, dry_run=False, verbose=False):
262
280
        import bzrlib.add
263
281
 
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
 
282
        action = bzrlib.add.AddAction(to_file=self.outf,
 
283
            should_add=(not dry_run), should_print=(not is_quiet()))
274
284
 
275
285
        added, ignored = bzrlib.add.smart_add(file_list, not no_recurse, 
276
 
                                              action)
 
286
                                              action=action)
277
287
        if len(ignored) > 0:
278
288
            if verbose:
279
289
                for glob in sorted(ignored.keys()):
280
290
                    for path in ignored[glob]:
281
 
                        print "ignored %s matching \"%s\"" % (path, glob)
 
291
                        self.outf.write("ignored %s matching \"%s\"\n" 
 
292
                                        % (path, glob))
282
293
            else:
283
294
                match_len = 0
284
295
                for glob, paths in ignored.items():
285
296
                    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."
 
297
                self.outf.write("ignored %d file(s).\n" % match_len)
 
298
            self.outf.write("If you wish to add some of these files,"
 
299
                            " please add them by name.\n")
289
300
 
290
301
 
291
302
class cmd_mkdir(Command):
293
304
 
294
305
    This is equivalent to creating the directory and then adding it.
295
306
    """
 
307
 
296
308
    takes_args = ['dir+']
 
309
    encoding_type = 'replace'
297
310
 
298
311
    def run(self, dir_list):
299
312
        for d in dir_list:
300
313
            os.mkdir(d)
301
314
            wt, dd = WorkingTree.open_containing(d)
302
315
            wt.add([dd])
303
 
            print 'added', d
 
316
            self.outf.write('added %s\n' % d)
304
317
 
305
318
 
306
319
class cmd_relpath(Command):
307
320
    """Show path of a file relative to root"""
 
321
 
308
322
    takes_args = ['filename']
309
323
    hidden = True
310
324
    
311
325
    @display_command
312
326
    def run(self, filename):
 
327
        # TODO: jam 20050106 Can relpath return a munged path if
 
328
        #       sys.stdout encoding cannot represent it?
313
329
        tree, relpath = WorkingTree.open_containing(filename)
314
 
        print relpath
 
330
        self.outf.write(relpath)
 
331
        self.outf.write('\n')
315
332
 
316
333
 
317
334
class cmd_inventory(Command):
320
337
    It is possible to limit the output to a particular entry
321
338
    type using the --kind option.  For example; --kind file.
322
339
    """
 
340
 
323
341
    takes_options = ['revision', 'show-ids', 'kind']
324
342
    
325
343
    @display_command
340
358
            if kind and kind != entry.kind:
341
359
                continue
342
360
            if show_ids:
343
 
                print '%-50s %s' % (path, entry.file_id)
 
361
                self.outf.write('%-50s %s\n' % (path, entry.file_id))
344
362
            else:
345
 
                print path
 
363
                self.outf.write(path)
 
364
                self.outf.write('\n')
346
365
 
347
366
 
348
367
class cmd_mv(Command):
358
377
 
359
378
    Files cannot be moved between branches.
360
379
    """
 
380
 
361
381
    takes_args = ['names*']
362
382
    aliases = ['move', 'rename']
 
383
    encoding_type = 'replace'
363
384
 
364
385
    def run(self, names_list):
365
386
        if len(names_list) < 2:
369
390
        if os.path.isdir(names_list[-1]):
370
391
            # move into existing directory
371
392
            for pair in tree.move(rel_names[:-1], rel_names[-1]):
372
 
                print "%s => %s" % pair
 
393
                self.outf.write("%s => %s\n" % pair)
373
394
        else:
374
395
            if len(names_list) != 2:
375
396
                raise BzrCommandError('to mv multiple files the destination '
376
397
                                      'must be a versioned directory')
377
398
            tree.rename_one(rel_names[0], rel_names[1])
378
 
            print "%s => %s" % (rel_names[0], rel_names[1])
 
399
            self.outf.write("%s => %s\n" % (rel_names[0], rel_names[1]))
379
400
            
380
401
    
381
402
class cmd_pull(Command):
400
421
    that, you can omit the location to use the default.  To change the
401
422
    default, use --remember.
402
423
    """
 
424
 
403
425
    takes_options = ['remember', 'overwrite', 'revision', 'verbose']
404
426
    takes_args = ['location?']
 
427
    encoding_type = 'replace'
405
428
 
406
429
    def run(self, location=None, remember=False, overwrite=False, revision=None, verbose=False):
407
430
        # FIXME: too much stuff is in the command class
410
433
            branch_to = tree_to.branch
411
434
        except NoWorkingTree:
412
435
            tree_to = None
413
 
            branch_to = Branch.open_containing(u'.')[0] 
 
436
            branch_to = Branch.open_containing(u'.')[0]
414
437
        stored_loc = branch_to.get_parent()
415
438
        if location is None:
416
439
            if stored_loc is None:
417
440
                raise BzrCommandError("No pull location known or specified.")
418
441
            else:
419
 
                print "Using saved location: %s" % stored_loc
 
442
                display_url = urlutils.unescape_for_display(stored_loc,
 
443
                        self.outf.encoding)
 
444
                self.outf.write("Using saved location: %s\n" % display_url)
420
445
                location = stored_loc
421
446
 
 
447
        branch_from = Branch.open(location)
 
448
 
422
449
        if branch_to.get_parent() is None or remember:
423
 
            branch_to.set_parent(location)
424
 
 
425
 
        branch_from = Branch.open(location)
 
450
            branch_to.set_parent(branch_from.base)
426
451
 
427
452
        if revision is None:
428
453
            rev_id = None
443
468
            if old_rh != new_rh:
444
469
                # Something changed
445
470
                from bzrlib.log import show_changed_revisions
446
 
                show_changed_revisions(branch_to, old_rh, new_rh)
 
471
                show_changed_revisions(branch_to, old_rh, new_rh,
 
472
                                       to_file=self.outf)
447
473
 
448
474
 
449
475
class cmd_push(Command):
470
496
    After that, you can omit the location to use the default.  To change the
471
497
    default, use --remember.
472
498
    """
473
 
    takes_options = ['remember', 'overwrite', 
 
499
 
 
500
    takes_options = ['remember', 'overwrite', 'verbose',
474
501
                     Option('create-prefix', 
475
502
                            help='Create the path leading up to the branch '
476
503
                                 'if it does not already exist')]
477
504
    takes_args = ['location?']
 
505
    encoding_type = 'replace'
478
506
 
479
507
    def run(self, location=None, remember=False, overwrite=False,
480
508
            create_prefix=False, verbose=False):
488
516
            if stored_loc is None:
489
517
                raise BzrCommandError("No push location known or specified.")
490
518
            else:
491
 
                print "Using saved location: %s" % stored_loc
 
519
                display_url = urlutils.unescape_for_display(stored_loc,
 
520
                        self.outf.encoding)
 
521
                self.outf.write("Using saved location: %s" % display_url)
492
522
                location = stored_loc
 
523
 
 
524
        transport = get_transport(location)
 
525
        location_url = transport.base
493
526
        if br_from.get_push_location() is None or remember:
494
 
            br_from.set_push_location(location)
 
527
            br_from.set_push_location(location_url)
 
528
 
 
529
        old_rh = []
495
530
        try:
496
 
            dir_to = bzrlib.bzrdir.BzrDir.open(location)
 
531
            dir_to = bzrlib.bzrdir.BzrDir.open(location_url)
497
532
            br_to = dir_to.open_branch()
498
533
        except NotBranchError:
499
534
            # create a branch.
500
 
            transport = get_transport(location).clone('..')
 
535
            transport = transport.clone('..')
501
536
            if not create_prefix:
502
537
                try:
503
 
                    transport.mkdir(transport.relpath(location))
 
538
                    relurl = transport.relpath(location_url)
 
539
                    mutter('creating directory %s => %s', location_url, relurl)
 
540
                    transport.mkdir(relurl)
504
541
                except NoSuchFile:
505
542
                    raise BzrCommandError("Parent directory of %s "
506
543
                                          "does not exist." % location)
507
544
            else:
508
545
                current = transport.base
509
 
                needed = [(transport, transport.relpath(location))]
 
546
                needed = [(transport, transport.relpath(location_url))]
510
547
                while needed:
511
548
                    try:
512
549
                        transport, relpath = needed[-1]
519
556
                        if new_transport.base == transport.base:
520
557
                            raise BzrCommandError("Could not create "
521
558
                                                  "path prefix.")
522
 
            dir_to = br_from.bzrdir.clone(location,
 
559
            dir_to = br_from.bzrdir.clone(location_url,
523
560
                revision_id=br_from.last_revision())
524
561
            br_to = dir_to.open_branch()
525
562
            count = len(br_to.revision_history())
546
583
            if old_rh != new_rh:
547
584
                # Something changed
548
585
                from bzrlib.log import show_changed_revisions
549
 
                show_changed_revisions(br_to, old_rh, new_rh)
 
586
                show_changed_revisions(br_to, old_rh, new_rh,
 
587
                                       to_file=self.outf)
550
588
 
551
589
 
552
590
class cmd_branch(Command):
567
605
    aliases = ['get', 'clone']
568
606
 
569
607
    def run(self, from_location, to_location=None, revision=None, basis=None):
 
608
        from bzrlib.transport import get_transport
570
609
        from bzrlib.osutils import rmtree
571
610
        if revision is None:
572
611
            revision = [None]
599
638
                name = None
600
639
            else:
601
640
                name = os.path.basename(to_location) + '\n'
 
641
 
 
642
            to_transport = get_transport(to_location)
602
643
            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
 
644
                to_transport.mkdir('.')
 
645
            except bzrlib.errors.FileExists:
 
646
                raise BzrCommandError('Target directory "%s" already'
 
647
                                      ' exists.' % to_location)
 
648
            except bzrlib.errors.NoSuchFile:
 
649
                raise BzrCommandError('Parent of "%s" does not exist.' %
 
650
                                      to_location)
613
651
            try:
614
652
                # preserve whatever source format we have.
615
 
                dir = br_from.bzrdir.sprout(to_location, revision_id, basis_dir)
 
653
                dir = br_from.bzrdir.sprout(to_transport.base,
 
654
                        revision_id, basis_dir)
616
655
                branch = dir.open_branch()
617
656
            except bzrlib.errors.NoSuchRevision:
618
 
                rmtree(to_location)
 
657
                to_transport.delete_tree('.')
619
658
                msg = "The branch %s has no revision %s." % (from_location, revision[0])
620
659
                raise BzrCommandError(msg)
621
660
            except bzrlib.errors.UnlistableBranch:
624
663
                raise BzrCommandError(msg)
625
664
            if name:
626
665
                branch.control_files.put_utf8('branch-name', name)
627
 
 
628
666
            note('Branched %d revision(s).' % branch.revno())
629
667
        finally:
630
668
            br_from.unlock()
725
763
        renames = list(bzrlib.tree.find_renames(old_inv, new_inv))
726
764
        renames.sort()
727
765
        for old_name, new_name in renames:
728
 
            print "%s => %s" % (old_name, new_name)        
 
766
            self.outf.write("%s => %s\n" % (old_name, new_name))
729
767
 
730
768
 
731
769
class cmd_update(Command):
794
832
    takes_args = ['file*']
795
833
    takes_options = ['verbose', Option('new', help='remove newly-added files')]
796
834
    aliases = ['rm']
 
835
    encoding_type = 'replace'
797
836
    
798
837
    def run(self, file_list, verbose=False, new=False):
799
838
        tree, file_list = tree_files(file_list)
808
847
            file_list = sorted([f[0] for f in added[0]], reverse=True)
809
848
            if len(file_list) == 0:
810
849
                raise BzrCommandError('No matching files.')
811
 
        tree.remove(file_list, verbose=verbose)
 
850
        tree.remove(file_list, verbose=verbose, to_file=self.outf)
812
851
 
813
852
 
814
853
class cmd_file_id(Command):
818
857
    same through all revisions where the file exists, even when it is
819
858
    moved or renamed.
820
859
    """
 
860
 
821
861
    hidden = True
822
862
    takes_args = ['filename']
 
863
 
823
864
    @display_command
824
865
    def run(self, filename):
825
866
        tree, relpath = WorkingTree.open_containing(filename)
827
868
        if i == None:
828
869
            raise BzrError("%r is not a versioned file" % filename)
829
870
        else:
830
 
            print i
 
871
            self.outf.write(i + '\n')
831
872
 
832
873
 
833
874
class cmd_file_path(Command):
834
875
    """Print path of file_ids to a file or directory.
835
876
 
836
877
    This prints one line for each directory down to the target,
837
 
    starting at the branch root."""
 
878
    starting at the branch root.
 
879
    """
 
880
 
838
881
    hidden = True
839
882
    takes_args = ['filename']
 
883
 
840
884
    @display_command
841
885
    def run(self, filename):
842
886
        tree, relpath = WorkingTree.open_containing(filename)
845
889
        if fid == None:
846
890
            raise BzrError("%r is not a versioned file" % filename)
847
891
        for fip in inv.get_idpath(fid):
848
 
            print fip
 
892
            self.outf.write(fip + '\n')
849
893
 
850
894
 
851
895
class cmd_reconcile(Command):
877
921
class cmd_revision_history(Command):
878
922
    """Display list of revision ids on this branch."""
879
923
    hidden = True
 
924
 
880
925
    @display_command
881
926
    def run(self):
882
927
        branch = WorkingTree.open_containing(u'.')[0].branch
883
928
        for patchid in branch.revision_history():
884
 
            print patchid
 
929
            self.outf.write(patchid)
 
930
            self.outf.write('\n')
885
931
 
886
932
 
887
933
class cmd_ancestry(Command):
888
934
    """List all revisions merged into this branch."""
889
935
    hidden = True
 
936
 
890
937
    @display_command
891
938
    def run(self):
892
939
        tree = WorkingTree.open_containing(u'.')[0]
896
943
        assert revision_ids[0] == None
897
944
        revision_ids.pop(0)
898
945
        for revision_id in revision_ids:
899
 
            print revision_id
 
946
            self.outf.write(revision_id + '\n')
900
947
 
901
948
 
902
949
class cmd_init(Command):
1027
1074
    takes_args = ['file*']
1028
1075
    takes_options = ['revision', 'diff-options', 'prefix']
1029
1076
    aliases = ['di', 'dif']
 
1077
    encoding_type = 'exact'
1030
1078
 
1031
1079
    @display_command
1032
1080
    def run(self, revision=None, file_list=None, diff_options=None,
1093
1141
    # directories with readdir, rather than stating each one.  Same
1094
1142
    # level of effort but possibly much less IO.  (Or possibly not,
1095
1143
    # if the directories are very large...)
 
1144
    takes_options = ['show-ids']
 
1145
 
1096
1146
    @display_command
1097
1147
    def run(self, show_ids=False):
1098
1148
        tree = WorkingTree.open_containing(u'.')[0]
1099
1149
        old = tree.basis_tree()
1100
1150
        for path, ie in old.inventory.iter_entries():
1101
1151
            if not tree.has_id(ie.file_id):
 
1152
                self.outf.write(path)
1102
1153
                if show_ids:
1103
 
                    print '%-50s %s' % (path, ie.file_id)
1104
 
                else:
1105
 
                    print path
 
1154
                    self.outf.write(' ')
 
1155
                    self.outf.write(ie.file_id)
 
1156
                self.outf.write('\n')
1106
1157
 
1107
1158
 
1108
1159
class cmd_modified(Command):
1116
1167
        td = compare_trees(tree.basis_tree(), tree)
1117
1168
 
1118
1169
        for path, id, kind, text_modified, meta_modified in td.modified:
1119
 
            print path
1120
 
 
 
1170
            self.outf.write(path + '\n')
1121
1171
 
1122
1172
 
1123
1173
class cmd_added(Command):
1136
1186
            path = inv.id2path(file_id)
1137
1187
            if not os.access(bzrlib.osutils.abspath(path), os.F_OK):
1138
1188
                continue
1139
 
            print path
1140
 
                
1141
 
        
 
1189
            self.outf.write(path + '\n')
 
1190
 
1142
1191
 
1143
1192
class cmd_root(Command):
1144
1193
    """Show the tree root directory.
1150
1199
    def run(self, filename=None):
1151
1200
        """Print the branch root."""
1152
1201
        tree = WorkingTree.open_containing(filename)[0]
1153
 
        print tree.basedir
 
1202
        self.outf.write(tree.basedir + '\n')
1154
1203
 
1155
1204
 
1156
1205
class cmd_log(Command):
1184
1233
                            type=str),
1185
1234
                     'short',
1186
1235
                     ]
 
1236
    encoding_type = 'replace'
 
1237
 
1187
1238
    @display_command
1188
1239
    def run(self, location=None, timezone='original',
1189
1240
            verbose=False,
1196
1247
            short=False,
1197
1248
            line=False):
1198
1249
        from bzrlib.log import log_formatter, show_log
1199
 
        import codecs
1200
1250
        assert message is None or isinstance(message, basestring), \
1201
1251
            "invalid message argument %r" % message
1202
1252
        direction = (forward and 'forward') or 'reverse'
1248
1298
        if rev1 > rev2:
1249
1299
            (rev2, rev1) = (rev1, rev2)
1250
1300
 
1251
 
        mutter('encoding log as %r', bzrlib.user_encoding)
1252
 
 
1253
 
        # use 'replace' so that we don't abort if trying to write out
1254
 
        # in e.g. the default C locale.
1255
 
        outf = codecs.getwriter(bzrlib.user_encoding)(sys.stdout, errors='replace')
1256
 
 
1257
1301
        if (log_format == None):
1258
1302
            default = bzrlib.config.BranchConfig(b).log_format()
1259
1303
            log_format = get_log_format(long=long, short=short, line=line, default=default)
1260
 
 
1261
1304
        lf = log_formatter(log_format,
1262
1305
                           show_ids=show_ids,
1263
 
                           to_file=outf,
 
1306
                           to_file=self.outf,
1264
1307
                           show_timezone=timezone)
1265
1308
 
1266
1309
        show_log(b,
1287
1330
class cmd_touching_revisions(Command):
1288
1331
    """Return revision-ids which affected a particular file.
1289
1332
 
1290
 
    A more user-friendly interface is "bzr log FILE"."""
 
1333
    A more user-friendly interface is "bzr log FILE".
 
1334
    """
 
1335
 
1291
1336
    hidden = True
1292
1337
    takes_args = ["filename"]
 
1338
 
1293
1339
    @display_command
1294
1340
    def run(self, filename):
1295
1341
        tree, relpath = WorkingTree.open_containing(filename)
1297
1343
        inv = tree.read_working_inventory()
1298
1344
        file_id = inv.path2id(relpath)
1299
1345
        for revno, revision_id, what in bzrlib.log.find_touching_revisions(b, file_id):
1300
 
            print "%6d %s" % (revno, what)
 
1346
            self.outf.write("%6d %s\n" % (revno, what))
1301
1347
 
1302
1348
 
1303
1349
class cmd_ls(Command):
1336
1382
        if revision is not None:
1337
1383
            tree = tree.branch.repository.revision_tree(
1338
1384
                revision[0].in_history(tree.branch).rev_id)
 
1385
 
1339
1386
        for fp, fc, kind, fid, entry in tree.list_files():
1340
1387
            if fp.startswith(relpath):
1341
1388
                fp = fp[len(relpath):]
1345
1392
                    continue
1346
1393
                if verbose:
1347
1394
                    kindch = entry.kind_character()
1348
 
                    print '%-8s %s%s' % (fc, fp, kindch)
 
1395
                    self.outf.write('%-8s %s%s\n' % (fc, fp, kindch))
1349
1396
                elif null:
1350
 
                    sys.stdout.write(fp)
1351
 
                    sys.stdout.write('\0')
1352
 
                    sys.stdout.flush()
 
1397
                    self.outf.write(fp + '\0')
 
1398
                    self.outf.flush()
1353
1399
                else:
1354
 
                    print fp
 
1400
                    self.outf.write(fp + '\n')
1355
1401
 
1356
1402
 
1357
1403
class cmd_unknowns(Command):
1360
1406
    def run(self):
1361
1407
        from bzrlib.osutils import quotefn
1362
1408
        for f in WorkingTree.open_containing(u'.')[0].unknowns():
1363
 
            print quotefn(f)
 
1409
            self.outf.write(quotefn(f) + '\n')
1364
1410
 
1365
1411
 
1366
1412
class cmd_ignore(Command):
1583
1629
        from bzrlib.msgeditor import edit_commit_message, \
1584
1630
                make_commit_message_template
1585
1631
        from tempfile import TemporaryFile
1586
 
        import codecs
1587
1632
 
1588
1633
        # TODO: Need a blackbox test for invoking the external editor; may be
1589
1634
        # slightly problematic to run this cross-platform.
1612
1657
            raise BzrCommandError("please specify either --message or --file")
1613
1658
        
1614
1659
        if file:
1615
 
            import codecs
1616
1660
            message = codecs.open(file, 'rt', bzrlib.user_encoding).read()
1617
1661
 
1618
1662
        if message == "":
1998
2042
        if merge_type is None:
1999
2043
            merge_type = Merge3Merger
2000
2044
 
2001
 
 
2002
2045
        tree = WorkingTree.open_containing(u'.')[0]
 
2046
 
2003
2047
        try:
2004
2048
            if branch is not None:
2005
2049
                reader = BundleReader(file(branch, 'rb'))
2009
2053
            if e.errno not in (errno.ENOENT, errno.EISDIR):
2010
2054
                raise
2011
2055
            reader = None
2012
 
        except BadBundle:
 
2056
        except NotABundle:
2013
2057
            reader = None
2014
2058
        if reader is not None:
2015
2059
            conflicts = merge_bundle(reader, tree, not force, merge_type,
2019
2063
            else:
2020
2064
                return 1
2021
2065
 
2022
 
        stored_loc = tree.branch.get_parent()
2023
 
        if branch is None:
2024
 
            if stored_loc is None:
2025
 
                raise BzrCommandError("No merge branch known or specified.")
2026
 
            else:
2027
 
                print "Using saved branch: %s" % stored_loc
2028
 
                branch = stored_loc
2029
 
 
2030
 
        if tree.branch.get_parent() is None or remember:
2031
 
            tree.branch.set_parent(branch)
 
2066
        branch = self._get_remembered_parent(tree, branch, 'Merging from')
2032
2067
 
2033
2068
        if revision is None or len(revision) < 1:
2034
2069
            base = [None, None]
2045
2080
                if None in revision:
2046
2081
                    raise BzrCommandError(
2047
2082
                        "Merge doesn't permit that revision specifier.")
2048
 
                b, path = Branch.open_containing(branch)
2049
 
 
2050
 
                base = [branch, revision[0].in_history(b).revno]
2051
 
                other = [branch, revision[1].in_history(b).revno]
 
2083
                other_branch, path = Branch.open_containing(branch)
 
2084
 
 
2085
                base = [branch, revision[0].in_history(other_branch).revno]
 
2086
                other = [branch, revision[1].in_history(other_branch).revno]
 
2087
 
 
2088
        if tree.branch.get_parent() is None or remember:
 
2089
            tree.branch.set_parent(other_branch.base)
 
2090
 
2052
2091
        if path != "":
2053
2092
            interesting_files = [path]
2054
2093
        else:
2057
2096
        try:
2058
2097
            try:
2059
2098
                conflict_count = merge(other, base, check_clean=(not force),
2060
 
                                       merge_type=merge_type, 
 
2099
                                       merge_type=merge_type,
2061
2100
                                       reprocess=reprocess,
2062
 
                                       show_base=show_base, 
 
2101
                                       show_base=show_base,
2063
2102
                                       pb=pb, file_list=interesting_files)
2064
2103
            finally:
2065
2104
                pb.finished()
2076
2115
                 "and (if you want) report this to the bzr developers\n")
2077
2116
            log_error(m)
2078
2117
 
 
2118
    # TODO: move up to common parent; this isn't merge-specific anymore. 
 
2119
    def _get_remembered_parent(self, tree, supplied_location, verb_string):
 
2120
        """Use tree.branch's parent if none was supplied.
 
2121
 
 
2122
        Report if the remembered location was used.
 
2123
        """
 
2124
        if supplied_location is not None:
 
2125
            return supplied_location
 
2126
        stored_location = tree.branch.get_parent()
 
2127
        mutter("%s", stored_location)
 
2128
        if stored_location is None:
 
2129
            raise BzrCommandError("No location specified or remembered")
 
2130
        display_url = urlutils.unescape_for_display(stored_location, self.outf.encoding)
 
2131
        self.outf.write("%s remembered location %s\n" % (verb_string, display_url))
 
2132
        return stored_location
 
2133
 
2079
2134
 
2080
2135
class cmd_remerge(Command):
2081
2136
    """Redo a merge.
2326
2381
            try:
2327
2382
                # handle race conditions - a parent might be set while we run.
2328
2383
                if local_branch.get_parent() is None:
2329
 
                    local_branch.set_parent(other_branch)
 
2384
                    local_branch.set_parent(remote_branch.base)
2330
2385
            finally:
2331
2386
                local_branch.unlock()
2332
2387
        return status_code