~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

Add a NEWS entry and prepare submission.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004-2010 Canonical Ltd
 
1
# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
31
31
    bundle,
32
32
    btree_index,
33
33
    bzrdir,
34
 
    directory_service,
35
34
    delta,
36
35
    config,
37
36
    errors,
502
501
                wt.lock_read()
503
502
            except (errors.NoWorkingTree, errors.NotLocalUrl):
504
503
                raise errors.NoWorkingTree(location)
505
 
            self.add_cleanup(wt.unlock)
506
 
            revid = wt.last_revision()
507
504
            try:
508
 
                revno_t = wt.branch.revision_id_to_dotted_revno(revid)
509
 
            except errors.NoSuchRevision:
510
 
                revno_t = ('???',)
511
 
            revno = ".".join(str(n) for n in revno_t)
 
505
                revid = wt.last_revision()
 
506
                try:
 
507
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
 
508
                except errors.NoSuchRevision:
 
509
                    revno_t = ('???',)
 
510
                revno = ".".join(str(n) for n in revno_t)
 
511
            finally:
 
512
                wt.unlock()
512
513
        else:
513
514
            b = Branch.open_containing(location)[0]
514
515
            b.lock_read()
515
 
            self.add_cleanup(b.unlock)
516
 
            revno = b.revno()
517
 
        self.cleanup_now()
 
516
            try:
 
517
                revno = b.revno()
 
518
            finally:
 
519
                b.unlock()
 
520
 
518
521
        self.outf.write(str(revno) + '\n')
519
522
 
520
523
 
542
545
            wt = WorkingTree.open_containing(directory)[0]
543
546
            b = wt.branch
544
547
            wt.lock_read()
545
 
            self.add_cleanup(wt.unlock)
546
548
        except (errors.NoWorkingTree, errors.NotLocalUrl):
547
549
            wt = None
548
550
            b = Branch.open_containing(directory)[0]
549
551
            b.lock_read()
550
 
            self.add_cleanup(b.unlock)
551
 
        revision_ids = []
552
 
        if revision is not None:
553
 
            revision_ids.extend(rev.as_revision_id(b) for rev in revision)
554
 
        if revision_info_list is not None:
555
 
            for rev_str in revision_info_list:
556
 
                rev_spec = RevisionSpec.from_string(rev_str)
557
 
                revision_ids.append(rev_spec.as_revision_id(b))
558
 
        # No arguments supplied, default to the last revision
559
 
        if len(revision_ids) == 0:
560
 
            if tree:
561
 
                if wt is None:
562
 
                    raise errors.NoWorkingTree(directory)
563
 
                revision_ids.append(wt.last_revision())
 
552
        try:
 
553
            revision_ids = []
 
554
            if revision is not None:
 
555
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
 
556
            if revision_info_list is not None:
 
557
                for rev_str in revision_info_list:
 
558
                    rev_spec = RevisionSpec.from_string(rev_str)
 
559
                    revision_ids.append(rev_spec.as_revision_id(b))
 
560
            # No arguments supplied, default to the last revision
 
561
            if len(revision_ids) == 0:
 
562
                if tree:
 
563
                    if wt is None:
 
564
                        raise errors.NoWorkingTree(directory)
 
565
                    revision_ids.append(wt.last_revision())
 
566
                else:
 
567
                    revision_ids.append(b.last_revision())
 
568
 
 
569
            revinfos = []
 
570
            maxlen = 0
 
571
            for revision_id in revision_ids:
 
572
                try:
 
573
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
 
574
                    revno = '.'.join(str(i) for i in dotted_revno)
 
575
                except errors.NoSuchRevision:
 
576
                    revno = '???'
 
577
                maxlen = max(maxlen, len(revno))
 
578
                revinfos.append([revno, revision_id])
 
579
        finally:
 
580
            if wt is None:
 
581
                b.unlock()
564
582
            else:
565
 
                revision_ids.append(b.last_revision())
566
 
 
567
 
        revinfos = []
568
 
        maxlen = 0
569
 
        for revision_id in revision_ids:
570
 
            try:
571
 
                dotted_revno = b.revision_id_to_dotted_revno(revision_id)
572
 
                revno = '.'.join(str(i) for i in dotted_revno)
573
 
            except errors.NoSuchRevision:
574
 
                revno = '???'
575
 
            maxlen = max(maxlen, len(revno))
576
 
            revinfos.append([revno, revision_id])
577
 
 
578
 
        self.cleanup_now()
 
583
                wt.unlock()
 
584
 
579
585
        for ri in revinfos:
580
586
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
581
587
 
653
659
 
654
660
        if base_tree:
655
661
            base_tree.lock_read()
656
 
            self.add_cleanup(base_tree.unlock)
657
 
        tree, file_list = tree_files_for_add(file_list)
658
 
        added, ignored = tree.smart_add(file_list, not
659
 
            no_recurse, action=action, save=not dry_run)
660
 
        self.cleanup_now()
 
662
        try:
 
663
            tree, file_list = tree_files_for_add(file_list)
 
664
            added, ignored = tree.smart_add(file_list, not
 
665
                no_recurse, action=action, save=not dry_run)
 
666
        finally:
 
667
            if base_tree is not None:
 
668
                base_tree.unlock()
661
669
        if len(ignored) > 0:
662
670
            if verbose:
663
671
                for glob in sorted(ignored.keys()):
727
735
        revision = _get_one_revision('inventory', revision)
728
736
        work_tree, file_list = tree_files(file_list)
729
737
        work_tree.lock_read()
730
 
        self.add_cleanup(work_tree.unlock)
731
 
        if revision is not None:
732
 
            tree = revision.as_tree(work_tree.branch)
733
 
 
734
 
            extra_trees = [work_tree]
735
 
            tree.lock_read()
736
 
            self.add_cleanup(tree.unlock)
737
 
        else:
738
 
            tree = work_tree
739
 
            extra_trees = []
740
 
 
741
 
        if file_list is not None:
742
 
            file_ids = tree.paths2ids(file_list, trees=extra_trees,
743
 
                                      require_versioned=True)
744
 
            # find_ids_across_trees may include some paths that don't
745
 
            # exist in 'tree'.
746
 
            entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
747
 
                             for file_id in file_ids if file_id in tree)
748
 
        else:
749
 
            entries = tree.inventory.entries()
750
 
 
751
 
        self.cleanup_now()
 
738
        try:
 
739
            if revision is not None:
 
740
                tree = revision.as_tree(work_tree.branch)
 
741
 
 
742
                extra_trees = [work_tree]
 
743
                tree.lock_read()
 
744
            else:
 
745
                tree = work_tree
 
746
                extra_trees = []
 
747
 
 
748
            if file_list is not None:
 
749
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
 
750
                                          require_versioned=True)
 
751
                # find_ids_across_trees may include some paths that don't
 
752
                # exist in 'tree'.
 
753
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
 
754
                                 for file_id in file_ids if file_id in tree)
 
755
            else:
 
756
                entries = tree.inventory.entries()
 
757
        finally:
 
758
            tree.unlock()
 
759
            if tree is not work_tree:
 
760
                work_tree.unlock()
 
761
 
752
762
        for path, entry in entries:
753
763
            if kind and kind != entry.kind:
754
764
                continue
800
810
            raise errors.BzrCommandError("missing file argument")
801
811
        tree, rel_names = tree_files(names_list, canonicalize=False)
802
812
        tree.lock_tree_write()
803
 
        self.add_cleanup(tree.unlock)
804
 
        self._run(tree, names_list, rel_names, after)
 
813
        try:
 
814
            self._run(tree, names_list, rel_names, after)
 
815
        finally:
 
816
            tree.unlock()
805
817
 
806
818
    def run_auto(self, names_list, after, dry_run):
807
819
        if names_list is not None and len(names_list) > 1:
812
824
                                         ' --auto.')
813
825
        work_tree, file_list = tree_files(names_list, default_branch='.')
814
826
        work_tree.lock_tree_write()
815
 
        self.add_cleanup(work_tree.unlock)
816
 
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
827
        try:
 
828
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
 
829
        finally:
 
830
            work_tree.unlock()
817
831
 
818
832
    def _run(self, tree, names_list, rel_names, after):
819
833
        into_existing = osutils.isdir(names_list[-1])
997
1011
 
998
1012
        if branch_from is not branch_to:
999
1013
            branch_from.lock_read()
1000
 
            self.add_cleanup(branch_from.unlock)
1001
 
        if revision is not None:
1002
 
            revision_id = revision.as_revision_id(branch_from)
1003
 
 
1004
 
        branch_to.lock_write()
1005
 
        self.add_cleanup(branch_to.unlock)
1006
 
        if tree_to is not None:
1007
 
            view_info = _get_view_info_for_change_reporter(tree_to)
1008
 
            change_reporter = delta._ChangeReporter(
1009
 
                unversioned_filter=tree_to.is_ignored,
1010
 
                view_info=view_info)
1011
 
            result = tree_to.pull(
1012
 
                branch_from, overwrite, revision_id, change_reporter,
1013
 
                possible_transports=possible_transports, local=local)
1014
 
        else:
1015
 
            result = branch_to.pull(
1016
 
                branch_from, overwrite, revision_id, local=local)
1017
 
 
1018
 
        result.report(self.outf)
1019
 
        if verbose and result.old_revid != result.new_revid:
1020
 
            log.show_branch_change(
1021
 
                branch_to, self.outf, result.old_revno,
1022
 
                result.old_revid)
 
1014
        try:
 
1015
            if revision is not None:
 
1016
                revision_id = revision.as_revision_id(branch_from)
 
1017
 
 
1018
            branch_to.lock_write()
 
1019
            try:
 
1020
                if tree_to is not None:
 
1021
                    view_info = _get_view_info_for_change_reporter(tree_to)
 
1022
                    change_reporter = delta._ChangeReporter(
 
1023
                        unversioned_filter=tree_to.is_ignored,
 
1024
                        view_info=view_info)
 
1025
                    result = tree_to.pull(
 
1026
                        branch_from, overwrite, revision_id, change_reporter,
 
1027
                        possible_transports=possible_transports, local=local)
 
1028
                else:
 
1029
                    result = branch_to.pull(
 
1030
                        branch_from, overwrite, revision_id, local=local)
 
1031
 
 
1032
                result.report(self.outf)
 
1033
                if verbose and result.old_revid != result.new_revid:
 
1034
                    log.show_branch_change(
 
1035
                        branch_to, self.outf, result.old_revno,
 
1036
                        result.old_revid)
 
1037
            finally:
 
1038
                branch_to.unlock()
 
1039
        finally:
 
1040
            if branch_from is not branch_to:
 
1041
                branch_from.unlock()
1023
1042
 
1024
1043
 
1025
1044
class cmd_push(Command):
1180
1199
                    ' directory exists, but does not already'
1181
1200
                    ' have a control directory.  This flag will'
1182
1201
                    ' allow branch to proceed.'),
1183
 
        Option('bind',
1184
 
            help="Bind new branch to from location."),
1185
1202
        ]
1186
1203
    aliases = ['get', 'clone']
1187
1204
 
1188
1205
    def run(self, from_location, to_location=None, revision=None,
1189
1206
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1190
 
            use_existing_dir=False, switch=False, bind=False):
 
1207
            use_existing_dir=False, switch=False):
1191
1208
        from bzrlib import switch as _mod_switch
1192
1209
        from bzrlib.tag import _merge_tags_if_possible
1193
1210
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1194
1211
            from_location)
1195
1212
        revision = _get_one_revision('branch', revision)
1196
1213
        br_from.lock_read()
1197
 
        self.add_cleanup(br_from.unlock)
1198
 
        if revision is not None:
1199
 
            revision_id = revision.as_revision_id(br_from)
1200
 
        else:
1201
 
            # FIXME - wt.last_revision, fallback to branch, fall back to
1202
 
            # None or perhaps NULL_REVISION to mean copy nothing
1203
 
            # RBC 20060209
1204
 
            revision_id = br_from.last_revision()
1205
 
        if to_location is None:
1206
 
            to_location = urlutils.derive_to_location(from_location)
1207
 
        to_transport = transport.get_transport(to_location)
1208
1214
        try:
1209
 
            to_transport.mkdir('.')
1210
 
        except errors.FileExists:
1211
 
            if not use_existing_dir:
1212
 
                raise errors.BzrCommandError('Target directory "%s" '
1213
 
                    'already exists.' % to_location)
 
1215
            if revision is not None:
 
1216
                revision_id = revision.as_revision_id(br_from)
1214
1217
            else:
1215
 
                try:
1216
 
                    bzrdir.BzrDir.open_from_transport(to_transport)
1217
 
                except errors.NotBranchError:
1218
 
                    pass
 
1218
                # FIXME - wt.last_revision, fallback to branch, fall back to
 
1219
                # None or perhaps NULL_REVISION to mean copy nothing
 
1220
                # RBC 20060209
 
1221
                revision_id = br_from.last_revision()
 
1222
            if to_location is None:
 
1223
                to_location = urlutils.derive_to_location(from_location)
 
1224
            to_transport = transport.get_transport(to_location)
 
1225
            try:
 
1226
                to_transport.mkdir('.')
 
1227
            except errors.FileExists:
 
1228
                if not use_existing_dir:
 
1229
                    raise errors.BzrCommandError('Target directory "%s" '
 
1230
                        'already exists.' % to_location)
1219
1231
                else:
1220
 
                    raise errors.AlreadyBranchError(to_location)
1221
 
        except errors.NoSuchFile:
1222
 
            raise errors.BzrCommandError('Parent of "%s" does not exist.'
1223
 
                                         % to_location)
1224
 
        try:
1225
 
            # preserve whatever source format we have.
1226
 
            dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
1227
 
                                        possible_transports=[to_transport],
1228
 
                                        accelerator_tree=accelerator_tree,
1229
 
                                        hardlink=hardlink, stacked=stacked,
1230
 
                                        force_new_repo=standalone,
1231
 
                                        create_tree_if_local=not no_tree,
1232
 
                                        source_branch=br_from)
1233
 
            branch = dir.open_branch()
1234
 
        except errors.NoSuchRevision:
1235
 
            to_transport.delete_tree('.')
1236
 
            msg = "The branch %s has no revision %s." % (from_location,
1237
 
                revision)
1238
 
            raise errors.BzrCommandError(msg)
1239
 
        _merge_tags_if_possible(br_from, branch)
1240
 
        # If the source branch is stacked, the new branch may
1241
 
        # be stacked whether we asked for that explicitly or not.
1242
 
        # We therefore need a try/except here and not just 'if stacked:'
1243
 
        try:
1244
 
            note('Created new stacked branch referring to %s.' %
1245
 
                branch.get_stacked_on_url())
1246
 
        except (errors.NotStacked, errors.UnstackableBranchFormat,
1247
 
            errors.UnstackableRepositoryFormat), e:
1248
 
            note('Branched %d revision(s).' % branch.revno())
1249
 
        if bind:
1250
 
            # Bind to the parent
1251
 
            parent_branch = Branch.open(from_location)
1252
 
            branch.bind(parent_branch)
1253
 
            note('New branch bound to %s' % from_location)
1254
 
        if switch:
1255
 
            # Switch to the new branch
1256
 
            wt, _ = WorkingTree.open_containing('.')
1257
 
            _mod_switch.switch(wt.bzrdir, branch)
1258
 
            note('Switched to branch: %s',
1259
 
                urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1232
                    try:
 
1233
                        bzrdir.BzrDir.open_from_transport(to_transport)
 
1234
                    except errors.NotBranchError:
 
1235
                        pass
 
1236
                    else:
 
1237
                        raise errors.AlreadyBranchError(to_location)
 
1238
            except errors.NoSuchFile:
 
1239
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
 
1240
                                             % to_location)
 
1241
            try:
 
1242
                # preserve whatever source format we have.
 
1243
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
 
1244
                                            possible_transports=[to_transport],
 
1245
                                            accelerator_tree=accelerator_tree,
 
1246
                                            hardlink=hardlink, stacked=stacked,
 
1247
                                            force_new_repo=standalone,
 
1248
                                            create_tree_if_local=not no_tree,
 
1249
                                            source_branch=br_from)
 
1250
                branch = dir.open_branch()
 
1251
            except errors.NoSuchRevision:
 
1252
                to_transport.delete_tree('.')
 
1253
                msg = "The branch %s has no revision %s." % (from_location,
 
1254
                    revision)
 
1255
                raise errors.BzrCommandError(msg)
 
1256
            _merge_tags_if_possible(br_from, branch)
 
1257
            # If the source branch is stacked, the new branch may
 
1258
            # be stacked whether we asked for that explicitly or not.
 
1259
            # We therefore need a try/except here and not just 'if stacked:'
 
1260
            try:
 
1261
                note('Created new stacked branch referring to %s.' %
 
1262
                    branch.get_stacked_on_url())
 
1263
            except (errors.NotStacked, errors.UnstackableBranchFormat,
 
1264
                errors.UnstackableRepositoryFormat), e:
 
1265
                note('Branched %d revision(s).' % branch.revno())
 
1266
            if switch:
 
1267
                # Switch to the new branch
 
1268
                wt, _ = WorkingTree.open_containing('.')
 
1269
                _mod_switch.switch(wt.bzrdir, branch)
 
1270
                note('Switched to branch: %s',
 
1271
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
 
1272
        finally:
 
1273
            br_from.unlock()
1260
1274
 
1261
1275
 
1262
1276
class cmd_checkout(Command):
1341
1355
    def run(self, dir=u'.'):
1342
1356
        tree = WorkingTree.open_containing(dir)[0]
1343
1357
        tree.lock_read()
1344
 
        self.add_cleanup(tree.unlock)
1345
 
        new_inv = tree.inventory
1346
 
        old_tree = tree.basis_tree()
1347
 
        old_tree.lock_read()
1348
 
        self.add_cleanup(old_tree.unlock)
1349
 
        old_inv = old_tree.inventory
1350
 
        renames = []
1351
 
        iterator = tree.iter_changes(old_tree, include_unchanged=True)
1352
 
        for f, paths, c, v, p, n, k, e in iterator:
1353
 
            if paths[0] == paths[1]:
1354
 
                continue
1355
 
            if None in (paths):
1356
 
                continue
1357
 
            renames.append(paths)
1358
 
        renames.sort()
1359
 
        for old_name, new_name in renames:
1360
 
            self.outf.write("%s => %s\n" % (old_name, new_name))
 
1358
        try:
 
1359
            new_inv = tree.inventory
 
1360
            old_tree = tree.basis_tree()
 
1361
            old_tree.lock_read()
 
1362
            try:
 
1363
                old_inv = old_tree.inventory
 
1364
                renames = []
 
1365
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
 
1366
                for f, paths, c, v, p, n, k, e in iterator:
 
1367
                    if paths[0] == paths[1]:
 
1368
                        continue
 
1369
                    if None in (paths):
 
1370
                        continue
 
1371
                    renames.append(paths)
 
1372
                renames.sort()
 
1373
                for old_name, new_name in renames:
 
1374
                    self.outf.write("%s => %s\n" % (old_name, new_name))
 
1375
            finally:
 
1376
                old_tree.unlock()
 
1377
        finally:
 
1378
            tree.unlock()
1361
1379
 
1362
1380
 
1363
1381
class cmd_update(Command):
1369
1387
 
1370
1388
    If you want to discard your local changes, you can just do a
1371
1389
    'bzr revert' instead of 'bzr commit' after the update.
1372
 
 
1373
 
    If the tree's branch is bound to a master branch, it will also update
1374
 
    the branch from the master.
1375
1390
    """
1376
1391
 
1377
1392
    _see_also = ['pull', 'working-trees', 'status-flags']
1378
1393
    takes_args = ['dir?']
1379
 
    takes_options = ['revision']
1380
1394
    aliases = ['up']
1381
1395
 
1382
 
    def run(self, dir='.', revision=None):
1383
 
        if revision is not None and len(revision) != 1:
1384
 
            raise errors.BzrCommandError(
1385
 
                        "bzr update --revision takes exactly one revision")
 
1396
    def run(self, dir='.'):
1386
1397
        tree = WorkingTree.open_containing(dir)[0]
1387
 
        branch = tree.branch
1388
1398
        possible_transports = []
1389
 
        master = branch.get_master_branch(
 
1399
        master = tree.branch.get_master_branch(
1390
1400
            possible_transports=possible_transports)
1391
1401
        if master is not None:
1392
1402
            tree.lock_write()
1394
1404
        else:
1395
1405
            tree.lock_tree_write()
1396
1406
            branch_location = tree.branch.base
1397
 
        self.add_cleanup(tree.unlock)
1398
1407
        # get rid of the final '/' and be ready for display
1399
1408
        branch_location = urlutils.unescape_for_display(branch_location[:-1],
1400
1409
                                                        self.outf.encoding)
1401
 
        existing_pending_merges = tree.get_parent_ids()[1:]
1402
 
        if master is None:
1403
 
            old_tip = None
1404
 
        else:
1405
 
            # may need to fetch data into a heavyweight checkout
1406
 
            # XXX: this may take some time, maybe we should display a
1407
 
            # message
1408
 
            old_tip = branch.update(possible_transports)
1409
 
        if revision is not None:
1410
 
            revision_id = revision[0].as_revision_id(branch)
1411
 
        else:
1412
 
            revision_id = branch.last_revision()
1413
 
        if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1414
 
            revno = branch.revision_id_to_revno(revision_id)
1415
 
            note("Tree is up to date at revision %d of branch %s" %
1416
 
                (revno, branch_location))
1417
 
            return 0
1418
 
        view_info = _get_view_info_for_change_reporter(tree)
1419
 
        change_reporter = delta._ChangeReporter(
1420
 
            unversioned_filter=tree.is_ignored,
1421
 
            view_info=view_info)
1422
1410
        try:
 
1411
            existing_pending_merges = tree.get_parent_ids()[1:]
 
1412
            last_rev = _mod_revision.ensure_null(tree.last_revision())
 
1413
            if last_rev == _mod_revision.ensure_null(
 
1414
                tree.branch.last_revision()):
 
1415
                # may be up to date, check master too.
 
1416
                if master is None or last_rev == _mod_revision.ensure_null(
 
1417
                    master.last_revision()):
 
1418
                    revno = tree.branch.revision_id_to_revno(last_rev)
 
1419
                    note('Tree is up to date at revision %d of branch %s'
 
1420
                         % (revno, branch_location))
 
1421
                    return 0
 
1422
            view_info = _get_view_info_for_change_reporter(tree)
1423
1423
            conflicts = tree.update(
1424
 
                change_reporter,
1425
 
                possible_transports=possible_transports,
1426
 
                revision=revision_id,
1427
 
                old_tip=old_tip)
1428
 
        except errors.NoSuchRevision, e:
1429
 
            raise errors.BzrCommandError(
1430
 
                                  "branch has no revision %s\n"
1431
 
                                  "bzr update --revision only works"
1432
 
                                  " for a revision in the branch history"
1433
 
                                  % (e.revision))
1434
 
        revno = tree.branch.revision_id_to_revno(
1435
 
            _mod_revision.ensure_null(tree.last_revision()))
1436
 
        note('Updated to revision %d of branch %s' %
1437
 
             (revno, branch_location))
1438
 
        if tree.get_parent_ids()[1:] != existing_pending_merges:
1439
 
            note('Your local commits will now show as pending merges with '
1440
 
                 "'bzr status', and can be committed with 'bzr commit'.")
1441
 
        if conflicts != 0:
1442
 
            return 1
1443
 
        else:
1444
 
            return 0
 
1424
                delta._ChangeReporter(unversioned_filter=tree.is_ignored,
 
1425
                view_info=view_info), possible_transports=possible_transports)
 
1426
            revno = tree.branch.revision_id_to_revno(
 
1427
                _mod_revision.ensure_null(tree.last_revision()))
 
1428
            note('Updated to revision %d of branch %s' %
 
1429
                 (revno, branch_location))
 
1430
            if tree.get_parent_ids()[1:] != existing_pending_merges:
 
1431
                note('Your local commits will now show as pending merges with '
 
1432
                     "'bzr status', and can be committed with 'bzr commit'.")
 
1433
            if conflicts != 0:
 
1434
                return 1
 
1435
            else:
 
1436
                return 0
 
1437
        finally:
 
1438
            tree.unlock()
1445
1439
 
1446
1440
 
1447
1441
class cmd_info(Command):
1518
1512
            file_list = [f for f in file_list]
1519
1513
 
1520
1514
        tree.lock_write()
1521
 
        self.add_cleanup(tree.unlock)
1522
 
        # Heuristics should probably all move into tree.remove_smart or
1523
 
        # some such?
1524
 
        if new:
1525
 
            added = tree.changes_from(tree.basis_tree(),
1526
 
                specific_files=file_list).added
1527
 
            file_list = sorted([f[0] for f in added], reverse=True)
1528
 
            if len(file_list) == 0:
1529
 
                raise errors.BzrCommandError('No matching files.')
1530
 
        elif file_list is None:
1531
 
            # missing files show up in iter_changes(basis) as
1532
 
            # versioned-with-no-kind.
1533
 
            missing = []
1534
 
            for change in tree.iter_changes(tree.basis_tree()):
1535
 
                # Find paths in the working tree that have no kind:
1536
 
                if change[1][1] is not None and change[6][1] is None:
1537
 
                    missing.append(change[1][1])
1538
 
            file_list = sorted(missing, reverse=True)
1539
 
            file_deletion_strategy = 'keep'
1540
 
        tree.remove(file_list, verbose=verbose, to_file=self.outf,
1541
 
            keep_files=file_deletion_strategy=='keep',
1542
 
            force=file_deletion_strategy=='force')
 
1515
        try:
 
1516
            # Heuristics should probably all move into tree.remove_smart or
 
1517
            # some such?
 
1518
            if new:
 
1519
                added = tree.changes_from(tree.basis_tree(),
 
1520
                    specific_files=file_list).added
 
1521
                file_list = sorted([f[0] for f in added], reverse=True)
 
1522
                if len(file_list) == 0:
 
1523
                    raise errors.BzrCommandError('No matching files.')
 
1524
            elif file_list is None:
 
1525
                # missing files show up in iter_changes(basis) as
 
1526
                # versioned-with-no-kind.
 
1527
                missing = []
 
1528
                for change in tree.iter_changes(tree.basis_tree()):
 
1529
                    # Find paths in the working tree that have no kind:
 
1530
                    if change[1][1] is not None and change[6][1] is None:
 
1531
                        missing.append(change[1][1])
 
1532
                file_list = sorted(missing, reverse=True)
 
1533
                file_deletion_strategy = 'keep'
 
1534
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
 
1535
                keep_files=file_deletion_strategy=='keep',
 
1536
                force=file_deletion_strategy=='force')
 
1537
        finally:
 
1538
            tree.unlock()
1543
1539
 
1544
1540
 
1545
1541
class cmd_file_id(Command):
1971
1967
    def run(self, show_ids=False):
1972
1968
        tree = WorkingTree.open_containing(u'.')[0]
1973
1969
        tree.lock_read()
1974
 
        self.add_cleanup(tree.unlock)
1975
 
        old = tree.basis_tree()
1976
 
        old.lock_read()
1977
 
        self.add_cleanup(old.unlock)
1978
 
        for path, ie in old.inventory.iter_entries():
1979
 
            if not tree.has_id(ie.file_id):
1980
 
                self.outf.write(path)
1981
 
                if show_ids:
1982
 
                    self.outf.write(' ')
1983
 
                    self.outf.write(ie.file_id)
1984
 
                self.outf.write('\n')
 
1970
        try:
 
1971
            old = tree.basis_tree()
 
1972
            old.lock_read()
 
1973
            try:
 
1974
                for path, ie in old.inventory.iter_entries():
 
1975
                    if not tree.has_id(ie.file_id):
 
1976
                        self.outf.write(path)
 
1977
                        if show_ids:
 
1978
                            self.outf.write(' ')
 
1979
                            self.outf.write(ie.file_id)
 
1980
                        self.outf.write('\n')
 
1981
            finally:
 
1982
                old.unlock()
 
1983
        finally:
 
1984
            tree.unlock()
1985
1985
 
1986
1986
 
1987
1987
class cmd_modified(Command):
2023
2023
    def run(self, null=False):
2024
2024
        wt = WorkingTree.open_containing(u'.')[0]
2025
2025
        wt.lock_read()
2026
 
        self.add_cleanup(wt.unlock)
2027
 
        basis = wt.basis_tree()
2028
 
        basis.lock_read()
2029
 
        self.add_cleanup(basis.unlock)
2030
 
        basis_inv = basis.inventory
2031
 
        inv = wt.inventory
2032
 
        for file_id in inv:
2033
 
            if file_id in basis_inv:
2034
 
                continue
2035
 
            if inv.is_root(file_id) and len(basis_inv) == 0:
2036
 
                continue
2037
 
            path = inv.id2path(file_id)
2038
 
            if not os.access(osutils.abspath(path), os.F_OK):
2039
 
                continue
2040
 
            if null:
2041
 
                self.outf.write(path + '\0')
2042
 
            else:
2043
 
                self.outf.write(osutils.quotefn(path) + '\n')
 
2026
        try:
 
2027
            basis = wt.basis_tree()
 
2028
            basis.lock_read()
 
2029
            try:
 
2030
                basis_inv = basis.inventory
 
2031
                inv = wt.inventory
 
2032
                for file_id in inv:
 
2033
                    if file_id in basis_inv:
 
2034
                        continue
 
2035
                    if inv.is_root(file_id) and len(basis_inv) == 0:
 
2036
                        continue
 
2037
                    path = inv.id2path(file_id)
 
2038
                    if not os.access(osutils.abspath(path), os.F_OK):
 
2039
                        continue
 
2040
                    if null:
 
2041
                        self.outf.write(path + '\0')
 
2042
                    else:
 
2043
                        self.outf.write(osutils.quotefn(path) + '\n')
 
2044
            finally:
 
2045
                basis.unlock()
 
2046
        finally:
 
2047
            wt.unlock()
2044
2048
 
2045
2049
 
2046
2050
class cmd_root(Command):
2191
2195
    :Tips & tricks:
2192
2196
 
2193
2197
      GUI tools and IDEs are often better at exploring history than command
2194
 
      line tools: you may prefer qlog or viz from qbzr or bzr-gtk, the
2195
 
      bzr-explorer shell, or the Loggerhead web interface.  See the Plugin
2196
 
      Guide <http://doc.bazaar.canonical.com/plugins/en/> and
2197
 
      <http://wiki.bazaar.canonical.com/IDEIntegration>.  
 
2198
      line tools. You may prefer qlog or glog from the QBzr and Bzr-Gtk packages
 
2199
      respectively for example. (TortoiseBzr uses qlog for displaying logs.) See
 
2200
      http://bazaar-vcs.org/BzrPlugins and http://bazaar-vcs.org/IDEIntegration.
 
2201
 
 
2202
      Web interfaces are often better at exploring history than command line
 
2203
      tools, particularly for branches on servers. You may prefer Loggerhead
 
2204
      or one of its alternatives. See http://bazaar-vcs.org/WebInterface.
2198
2205
 
2199
2206
      You may find it useful to add the aliases below to ``bazaar.conf``::
2200
2207
 
2301
2308
 
2302
2309
        file_ids = []
2303
2310
        filter_by_dir = False
2304
 
        if file_list:
2305
 
            # find the file ids to log and check for directory filtering
2306
 
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2307
 
                revision, file_list)
2308
 
            self.add_cleanup(b.unlock)
2309
 
            for relpath, file_id, kind in file_info_list:
2310
 
                if file_id is None:
2311
 
                    raise errors.BzrCommandError(
2312
 
                        "Path unknown at end or start of revision range: %s" %
2313
 
                        relpath)
2314
 
                # If the relpath is the top of the tree, we log everything
2315
 
                if relpath == '':
2316
 
                    file_ids = []
2317
 
                    break
 
2311
        b = None
 
2312
        try:
 
2313
            if file_list:
 
2314
                # find the file ids to log and check for directory filtering
 
2315
                b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2316
                    revision, file_list)
 
2317
                for relpath, file_id, kind in file_info_list:
 
2318
                    if file_id is None:
 
2319
                        raise errors.BzrCommandError(
 
2320
                            "Path unknown at end or start of revision range: %s" %
 
2321
                            relpath)
 
2322
                    # If the relpath is the top of the tree, we log everything
 
2323
                    if relpath == '':
 
2324
                        file_ids = []
 
2325
                        break
 
2326
                    else:
 
2327
                        file_ids.append(file_id)
 
2328
                    filter_by_dir = filter_by_dir or (
 
2329
                        kind in ['directory', 'tree-reference'])
 
2330
            else:
 
2331
                # log everything
 
2332
                # FIXME ? log the current subdir only RBC 20060203
 
2333
                if revision is not None \
 
2334
                        and len(revision) > 0 and revision[0].get_branch():
 
2335
                    location = revision[0].get_branch()
2318
2336
                else:
2319
 
                    file_ids.append(file_id)
2320
 
                filter_by_dir = filter_by_dir or (
2321
 
                    kind in ['directory', 'tree-reference'])
2322
 
        else:
2323
 
            # log everything
2324
 
            # FIXME ? log the current subdir only RBC 20060203
2325
 
            if revision is not None \
2326
 
                    and len(revision) > 0 and revision[0].get_branch():
2327
 
                location = revision[0].get_branch()
2328
 
            else:
2329
 
                location = '.'
2330
 
            dir, relpath = bzrdir.BzrDir.open_containing(location)
2331
 
            b = dir.open_branch()
2332
 
            b.lock_read()
2333
 
            self.add_cleanup(b.unlock)
2334
 
            rev1, rev2 = _get_revision_range(revision, b, self.name())
2335
 
 
2336
 
        # Decide on the type of delta & diff filtering to use
2337
 
        # TODO: add an --all-files option to make this configurable & consistent
2338
 
        if not verbose:
2339
 
            delta_type = None
2340
 
        else:
2341
 
            delta_type = 'full'
2342
 
        if not show_diff:
2343
 
            diff_type = None
2344
 
        elif file_ids:
2345
 
            diff_type = 'partial'
2346
 
        else:
2347
 
            diff_type = 'full'
2348
 
 
2349
 
        # Build the log formatter
2350
 
        if log_format is None:
2351
 
            log_format = log.log_formatter_registry.get_default(b)
2352
 
        # Make a non-encoding output to include the diffs - bug 328007
2353
 
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2354
 
        lf = log_format(show_ids=show_ids, to_file=self.outf,
2355
 
                        to_exact_file=unencoded_output,
2356
 
                        show_timezone=timezone,
2357
 
                        delta_format=get_verbosity_level(),
2358
 
                        levels=levels,
2359
 
                        show_advice=levels is None)
2360
 
 
2361
 
        # Choose the algorithm for doing the logging. It's annoying
2362
 
        # having multiple code paths like this but necessary until
2363
 
        # the underlying repository format is faster at generating
2364
 
        # deltas or can provide everything we need from the indices.
2365
 
        # The default algorithm - match-using-deltas - works for
2366
 
        # multiple files and directories and is faster for small
2367
 
        # amounts of history (200 revisions say). However, it's too
2368
 
        # slow for logging a single file in a repository with deep
2369
 
        # history, i.e. > 10K revisions. In the spirit of "do no
2370
 
        # evil when adding features", we continue to use the
2371
 
        # original algorithm - per-file-graph - for the "single
2372
 
        # file that isn't a directory without showing a delta" case.
2373
 
        partial_history = revision and b.repository._format.supports_chks
2374
 
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2375
 
            or delta_type or partial_history)
2376
 
 
2377
 
        # Build the LogRequest and execute it
2378
 
        if len(file_ids) == 0:
2379
 
            file_ids = None
2380
 
        rqst = make_log_request_dict(
2381
 
            direction=direction, specific_fileids=file_ids,
2382
 
            start_revision=rev1, end_revision=rev2, limit=limit,
2383
 
            message_search=message, delta_type=delta_type,
2384
 
            diff_type=diff_type, _match_using_deltas=match_using_deltas)
2385
 
        Logger(b, rqst).show(lf)
 
2337
                    location = '.'
 
2338
                dir, relpath = bzrdir.BzrDir.open_containing(location)
 
2339
                b = dir.open_branch()
 
2340
                b.lock_read()
 
2341
                rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2342
 
 
2343
            # Decide on the type of delta & diff filtering to use
 
2344
            # TODO: add an --all-files option to make this configurable & consistent
 
2345
            if not verbose:
 
2346
                delta_type = None
 
2347
            else:
 
2348
                delta_type = 'full'
 
2349
            if not show_diff:
 
2350
                diff_type = None
 
2351
            elif file_ids:
 
2352
                diff_type = 'partial'
 
2353
            else:
 
2354
                diff_type = 'full'
 
2355
 
 
2356
            # Build the log formatter
 
2357
            if log_format is None:
 
2358
                log_format = log.log_formatter_registry.get_default(b)
 
2359
            # Make a non-encoding output to include the diffs - bug 328007
 
2360
            unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
 
2361
            lf = log_format(show_ids=show_ids, to_file=self.outf,
 
2362
                            to_exact_file=unencoded_output,
 
2363
                            show_timezone=timezone,
 
2364
                            delta_format=get_verbosity_level(),
 
2365
                            levels=levels,
 
2366
                            show_advice=levels is None)
 
2367
 
 
2368
            # Choose the algorithm for doing the logging. It's annoying
 
2369
            # having multiple code paths like this but necessary until
 
2370
            # the underlying repository format is faster at generating
 
2371
            # deltas or can provide everything we need from the indices.
 
2372
            # The default algorithm - match-using-deltas - works for
 
2373
            # multiple files and directories and is faster for small
 
2374
            # amounts of history (200 revisions say). However, it's too
 
2375
            # slow for logging a single file in a repository with deep
 
2376
            # history, i.e. > 10K revisions. In the spirit of "do no
 
2377
            # evil when adding features", we continue to use the
 
2378
            # original algorithm - per-file-graph - for the "single
 
2379
            # file that isn't a directory without showing a delta" case.
 
2380
            partial_history = revision and b.repository._format.supports_chks
 
2381
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2382
                or delta_type or partial_history)
 
2383
 
 
2384
            # Build the LogRequest and execute it
 
2385
            if len(file_ids) == 0:
 
2386
                file_ids = None
 
2387
            rqst = make_log_request_dict(
 
2388
                direction=direction, specific_fileids=file_ids,
 
2389
                start_revision=rev1, end_revision=rev2, limit=limit,
 
2390
                message_search=message, delta_type=delta_type,
 
2391
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2392
            Logger(b, rqst).show(lf)
 
2393
        finally:
 
2394
            if b is not None:
 
2395
                b.unlock()
2386
2396
 
2387
2397
 
2388
2398
def _get_revision_range(revisionspec_list, branch, command_name):
2455
2465
        file_id = tree.path2id(relpath)
2456
2466
        b = tree.branch
2457
2467
        b.lock_read()
2458
 
        self.add_cleanup(b.unlock)
2459
 
        touching_revs = log.find_touching_revisions(b, file_id)
2460
 
        for revno, revision_id, what in touching_revs:
2461
 
            self.outf.write("%6d %s\n" % (revno, what))
 
2468
        try:
 
2469
            touching_revs = log.find_touching_revisions(b, file_id)
 
2470
            for revno, revision_id, what in touching_revs:
 
2471
                self.outf.write("%6d %s\n" % (revno, what))
 
2472
        finally:
 
2473
            b.unlock()
2462
2474
 
2463
2475
 
2464
2476
class cmd_ls(Command):
2531
2543
                note("Ignoring files outside view. View is %s" % view_str)
2532
2544
 
2533
2545
        tree.lock_read()
2534
 
        self.add_cleanup(tree.unlock)
2535
 
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2536
 
            from_dir=relpath, recursive=recursive):
2537
 
            # Apply additional masking
2538
 
            if not all and not selection[fc]:
2539
 
                continue
2540
 
            if kind is not None and fkind != kind:
2541
 
                continue
2542
 
            if apply_view:
2543
 
                try:
2544
 
                    if relpath:
2545
 
                        fullpath = osutils.pathjoin(relpath, fp)
2546
 
                    else:
2547
 
                        fullpath = fp
2548
 
                    views.check_path_in_view(tree, fullpath)
2549
 
                except errors.FileOutsideView:
2550
 
                    continue
 
2546
        try:
 
2547
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2548
                from_dir=relpath, recursive=recursive):
 
2549
                # Apply additional masking
 
2550
                if not all and not selection[fc]:
 
2551
                    continue
 
2552
                if kind is not None and fkind != kind:
 
2553
                    continue
 
2554
                if apply_view:
 
2555
                    try:
 
2556
                        if relpath:
 
2557
                            fullpath = osutils.pathjoin(relpath, fp)
 
2558
                        else:
 
2559
                            fullpath = fp
 
2560
                        views.check_path_in_view(tree, fullpath)
 
2561
                    except errors.FileOutsideView:
 
2562
                        continue
2551
2563
 
2552
 
            # Output the entry
2553
 
            if prefix:
2554
 
                fp = osutils.pathjoin(prefix, fp)
2555
 
            kindch = entry.kind_character()
2556
 
            outstring = fp + kindch
2557
 
            ui.ui_factory.clear_term()
2558
 
            if verbose:
2559
 
                outstring = '%-8s %s' % (fc, outstring)
2560
 
                if show_ids and fid is not None:
2561
 
                    outstring = "%-50s %s" % (outstring, fid)
2562
 
                self.outf.write(outstring + '\n')
2563
 
            elif null:
2564
 
                self.outf.write(fp + '\0')
2565
 
                if show_ids:
2566
 
                    if fid is not None:
2567
 
                        self.outf.write(fid)
2568
 
                    self.outf.write('\0')
2569
 
                self.outf.flush()
2570
 
            else:
2571
 
                if show_ids:
2572
 
                    if fid is not None:
2573
 
                        my_id = fid
2574
 
                    else:
2575
 
                        my_id = ''
2576
 
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
2577
 
                else:
 
2564
                # Output the entry
 
2565
                if prefix:
 
2566
                    fp = osutils.pathjoin(prefix, fp)
 
2567
                kindch = entry.kind_character()
 
2568
                outstring = fp + kindch
 
2569
                ui.ui_factory.clear_term()
 
2570
                if verbose:
 
2571
                    outstring = '%-8s %s' % (fc, outstring)
 
2572
                    if show_ids and fid is not None:
 
2573
                        outstring = "%-50s %s" % (outstring, fid)
2578
2574
                    self.outf.write(outstring + '\n')
 
2575
                elif null:
 
2576
                    self.outf.write(fp + '\0')
 
2577
                    if show_ids:
 
2578
                        if fid is not None:
 
2579
                            self.outf.write(fid)
 
2580
                        self.outf.write('\0')
 
2581
                    self.outf.flush()
 
2582
                else:
 
2583
                    if show_ids:
 
2584
                        if fid is not None:
 
2585
                            my_id = fid
 
2586
                        else:
 
2587
                            my_id = ''
 
2588
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2589
                    else:
 
2590
                        self.outf.write(outstring + '\n')
 
2591
        finally:
 
2592
            tree.unlock()
2579
2593
 
2580
2594
 
2581
2595
class cmd_unknowns(Command):
2606
2620
    After adding, editing or deleting that file either indirectly by
2607
2621
    using this command or directly by using an editor, be sure to commit
2608
2622
    it.
2609
 
    
2610
 
    Patterns prefixed with '!' are exceptions to ignore patterns and take
2611
 
    precedence over regular ignores.  Such exceptions are used to specify
2612
 
    files that should be versioned which would otherwise be ignored.
2613
 
    
2614
 
    Patterns prefixed with '!!' act as regular ignore patterns, but have
2615
 
    precedence over the '!' exception patterns.
2616
2623
 
2617
2624
    Note: ignore patterns containing shell wildcards must be quoted from
2618
2625
    the shell on Unix.
2622
2629
 
2623
2630
            bzr ignore ./Makefile
2624
2631
 
2625
 
        Ignore .class files in all directories...::
 
2632
        Ignore class files in all directories::
2626
2633
 
2627
2634
            bzr ignore "*.class"
2628
2635
 
2629
 
        ...but do not ignore "special.class"::
2630
 
 
2631
 
            bzr ignore "!special.class"
2632
 
 
2633
2636
        Ignore .o files under the lib directory::
2634
2637
 
2635
2638
            bzr ignore "lib/**/*.o"
2641
2644
        Ignore everything but the "debian" toplevel directory::
2642
2645
 
2643
2646
            bzr ignore "RE:(?!debian/).*"
2644
 
        
2645
 
        Ignore everything except the "local" toplevel directory,
2646
 
        but always ignore "*~" autosave files, even under local/::
2647
 
        
2648
 
            bzr ignore "*"
2649
 
            bzr ignore "!./local"
2650
 
            bzr ignore "!!*~"
2651
2647
    """
2652
2648
 
2653
2649
    _see_also = ['status', 'ignored', 'patterns']
2711
2707
    def run(self):
2712
2708
        tree = WorkingTree.open_containing(u'.')[0]
2713
2709
        tree.lock_read()
2714
 
        self.add_cleanup(tree.unlock)
2715
 
        for path, file_class, kind, file_id, entry in tree.list_files():
2716
 
            if file_class != 'I':
2717
 
                continue
2718
 
            ## XXX: Slightly inefficient since this was already calculated
2719
 
            pat = tree.is_ignored(path)
2720
 
            self.outf.write('%-50s %s\n' % (path, pat))
 
2710
        try:
 
2711
            for path, file_class, kind, file_id, entry in tree.list_files():
 
2712
                if file_class != 'I':
 
2713
                    continue
 
2714
                ## XXX: Slightly inefficient since this was already calculated
 
2715
                pat = tree.is_ignored(path)
 
2716
                self.outf.write('%-50s %s\n' % (path, pat))
 
2717
        finally:
 
2718
            tree.unlock()
2721
2719
 
2722
2720
 
2723
2721
class cmd_lookup_revision(Command):
2826
2824
        tree, branch, relpath = \
2827
2825
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2828
2826
        branch.lock_read()
2829
 
        self.add_cleanup(branch.unlock)
2830
 
        return self._run(tree, branch, relpath, filename, revision,
2831
 
                         name_from_revision, filters)
 
2827
        try:
 
2828
            return self._run(tree, branch, relpath, filename, revision,
 
2829
                             name_from_revision, filters)
 
2830
        finally:
 
2831
            branch.unlock()
2832
2832
 
2833
2833
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2834
2834
        filtered):
2835
2835
        if tree is None:
2836
2836
            tree = b.basis_tree()
2837
2837
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
2838
 
        rev_tree.lock_read()
2839
 
        self.add_cleanup(rev_tree.unlock)
2840
2838
 
2841
2839
        old_file_id = rev_tree.path2id(relpath)
2842
2840
 
2877
2875
            chunks = content.splitlines(True)
2878
2876
            content = filtered_output_bytes(chunks, filters,
2879
2877
                ContentFilterContext(relpath, rev_tree))
2880
 
            self.cleanup_now()
2881
2878
            self.outf.writelines(content)
2882
2879
        else:
2883
 
            self.cleanup_now()
2884
2880
            self.outf.write(content)
2885
2881
 
2886
2882
 
3547
3543
            verbose = not is_quiet()
3548
3544
            # TODO: should possibly lock the history file...
3549
3545
            benchfile = open(".perf_history", "at", buffering=1)
3550
 
            self.add_cleanup(benchfile.close)
3551
3546
        else:
3552
3547
            test_suite_factory = None
3553
3548
            benchfile = None
3554
 
        selftest_kwargs = {"verbose": verbose,
3555
 
                          "pattern": pattern,
3556
 
                          "stop_on_failure": one,
3557
 
                          "transport": transport,
3558
 
                          "test_suite_factory": test_suite_factory,
3559
 
                          "lsprof_timed": lsprof_timed,
3560
 
                          "lsprof_tests": lsprof_tests,
3561
 
                          "bench_history": benchfile,
3562
 
                          "matching_tests_first": first,
3563
 
                          "list_only": list_only,
3564
 
                          "random_seed": randomize,
3565
 
                          "exclude_pattern": exclude,
3566
 
                          "strict": strict,
3567
 
                          "load_list": load_list,
3568
 
                          "debug_flags": debugflag,
3569
 
                          "starting_with": starting_with
3570
 
                          }
3571
 
        selftest_kwargs.update(self.additional_selftest_args)
3572
 
        result = selftest(**selftest_kwargs)
 
3549
        try:
 
3550
            selftest_kwargs = {"verbose": verbose,
 
3551
                              "pattern": pattern,
 
3552
                              "stop_on_failure": one,
 
3553
                              "transport": transport,
 
3554
                              "test_suite_factory": test_suite_factory,
 
3555
                              "lsprof_timed": lsprof_timed,
 
3556
                              "lsprof_tests": lsprof_tests,
 
3557
                              "bench_history": benchfile,
 
3558
                              "matching_tests_first": first,
 
3559
                              "list_only": list_only,
 
3560
                              "random_seed": randomize,
 
3561
                              "exclude_pattern": exclude,
 
3562
                              "strict": strict,
 
3563
                              "load_list": load_list,
 
3564
                              "debug_flags": debugflag,
 
3565
                              "starting_with": starting_with
 
3566
                              }
 
3567
            selftest_kwargs.update(self.additional_selftest_args)
 
3568
            result = selftest(**selftest_kwargs)
 
3569
        finally:
 
3570
            if benchfile is not None:
 
3571
                benchfile.close()
3573
3572
        return int(not result)
3574
3573
 
3575
3574
 
3614
3613
        branch1 = Branch.open_containing(branch)[0]
3615
3614
        branch2 = Branch.open_containing(other)[0]
3616
3615
        branch1.lock_read()
3617
 
        self.add_cleanup(branch1.unlock)
3618
 
        branch2.lock_read()
3619
 
        self.add_cleanup(branch2.unlock)
3620
 
        last1 = ensure_null(branch1.last_revision())
3621
 
        last2 = ensure_null(branch2.last_revision())
3622
 
 
3623
 
        graph = branch1.repository.get_graph(branch2.repository)
3624
 
        base_rev_id = graph.find_unique_lca(last1, last2)
3625
 
 
3626
 
        print 'merge base is revision %s' % base_rev_id
 
3616
        try:
 
3617
            branch2.lock_read()
 
3618
            try:
 
3619
                last1 = ensure_null(branch1.last_revision())
 
3620
                last2 = ensure_null(branch2.last_revision())
 
3621
 
 
3622
                graph = branch1.repository.get_graph(branch2.repository)
 
3623
                base_rev_id = graph.find_unique_lca(last1, last2)
 
3624
 
 
3625
                print 'merge base is revision %s' % base_rev_id
 
3626
            finally:
 
3627
                branch2.unlock()
 
3628
        finally:
 
3629
            branch1.unlock()
3627
3630
 
3628
3631
 
3629
3632
class cmd_merge(Command):
3662
3665
    committed to record the result of the merge.
3663
3666
 
3664
3667
    merge refuses to run if there are any uncommitted changes, unless
3665
 
    --force is given. The --force option can also be used to create a
3666
 
    merge revision which has more than two parents.
3667
 
 
3668
 
    If one would like to merge changes from the working tree of the other
3669
 
    branch without merging any committed revisions, the --uncommitted option
3670
 
    can be given.
 
3668
    --force is given.
3671
3669
 
3672
3670
    To select only some changes to merge, use "merge -i", which will prompt
3673
3671
    you to apply each diff hunk and file change, similar to "shelve".
3688
3686
        To apply a merge directive contained in /tmp/merge::
3689
3687
 
3690
3688
            bzr merge /tmp/merge
3691
 
 
3692
 
        To create a merge revision with three parents from two branches
3693
 
        feature1a and feature1b:
3694
 
 
3695
 
            bzr merge ../feature1a
3696
 
            bzr merge ../feature1b --force
3697
 
            bzr commit -m 'revision with three parents'
3698
3689
    """
3699
3690
 
3700
3691
    encoding_type = 'exact'
3758
3749
        view_info = _get_view_info_for_change_reporter(tree)
3759
3750
        change_reporter = delta._ChangeReporter(
3760
3751
            unversioned_filter=tree.is_ignored, view_info=view_info)
3761
 
        pb = ui.ui_factory.nested_progress_bar()
3762
 
        self.add_cleanup(pb.finished)
3763
 
        tree.lock_write()
3764
 
        self.add_cleanup(tree.unlock)
3765
 
        if location is not None:
3766
 
            try:
3767
 
                mergeable = bundle.read_mergeable_from_url(location,
3768
 
                    possible_transports=possible_transports)
3769
 
            except errors.NotABundle:
3770
 
                mergeable = None
 
3752
        cleanups = []
 
3753
        try:
 
3754
            pb = ui.ui_factory.nested_progress_bar()
 
3755
            cleanups.append(pb.finished)
 
3756
            tree.lock_write()
 
3757
            cleanups.append(tree.unlock)
 
3758
            if location is not None:
 
3759
                try:
 
3760
                    mergeable = bundle.read_mergeable_from_url(location,
 
3761
                        possible_transports=possible_transports)
 
3762
                except errors.NotABundle:
 
3763
                    mergeable = None
 
3764
                else:
 
3765
                    if uncommitted:
 
3766
                        raise errors.BzrCommandError('Cannot use --uncommitted'
 
3767
                            ' with bundles or merge directives.')
 
3768
 
 
3769
                    if revision is not None:
 
3770
                        raise errors.BzrCommandError(
 
3771
                            'Cannot use -r with merge directives or bundles')
 
3772
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
3773
                       mergeable, pb)
 
3774
 
 
3775
            if merger is None and uncommitted:
 
3776
                if revision is not None and len(revision) > 0:
 
3777
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
 
3778
                        ' --revision at the same time.')
 
3779
                merger = self.get_merger_from_uncommitted(tree, location, pb,
 
3780
                                                          cleanups)
 
3781
                allow_pending = False
 
3782
 
 
3783
            if merger is None:
 
3784
                merger, allow_pending = self._get_merger_from_branch(tree,
 
3785
                    location, revision, remember, possible_transports, pb)
 
3786
 
 
3787
            merger.merge_type = merge_type
 
3788
            merger.reprocess = reprocess
 
3789
            merger.show_base = show_base
 
3790
            self.sanity_check_merger(merger)
 
3791
            if (merger.base_rev_id == merger.other_rev_id and
 
3792
                merger.other_rev_id is not None):
 
3793
                note('Nothing to do.')
 
3794
                return 0
 
3795
            if pull:
 
3796
                if merger.interesting_files is not None:
 
3797
                    raise errors.BzrCommandError('Cannot pull individual files')
 
3798
                if (merger.base_rev_id == tree.last_revision()):
 
3799
                    result = tree.pull(merger.other_branch, False,
 
3800
                                       merger.other_rev_id)
 
3801
                    result.report(self.outf)
 
3802
                    return 0
 
3803
            if merger.this_basis is None:
 
3804
                raise errors.BzrCommandError(
 
3805
                    "This branch has no commits."
 
3806
                    " (perhaps you would prefer 'bzr pull')")
 
3807
            if preview:
 
3808
                return self._do_preview(merger, cleanups)
 
3809
            elif interactive:
 
3810
                return self._do_interactive(merger, cleanups)
3771
3811
            else:
3772
 
                if uncommitted:
3773
 
                    raise errors.BzrCommandError('Cannot use --uncommitted'
3774
 
                        ' with bundles or merge directives.')
3775
 
 
3776
 
                if revision is not None:
3777
 
                    raise errors.BzrCommandError(
3778
 
                        'Cannot use -r with merge directives or bundles')
3779
 
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
3780
 
                   mergeable, pb)
3781
 
 
3782
 
        if merger is None and uncommitted:
3783
 
            if revision is not None and len(revision) > 0:
3784
 
                raise errors.BzrCommandError('Cannot use --uncommitted and'
3785
 
                    ' --revision at the same time.')
3786
 
            merger = self.get_merger_from_uncommitted(tree, location, pb)
3787
 
            allow_pending = False
3788
 
 
3789
 
        if merger is None:
3790
 
            merger, allow_pending = self._get_merger_from_branch(tree,
3791
 
                location, revision, remember, possible_transports, pb)
3792
 
 
3793
 
        merger.merge_type = merge_type
3794
 
        merger.reprocess = reprocess
3795
 
        merger.show_base = show_base
3796
 
        self.sanity_check_merger(merger)
3797
 
        if (merger.base_rev_id == merger.other_rev_id and
3798
 
            merger.other_rev_id is not None):
3799
 
            note('Nothing to do.')
3800
 
            return 0
3801
 
        if pull:
3802
 
            if merger.interesting_files is not None:
3803
 
                raise errors.BzrCommandError('Cannot pull individual files')
3804
 
            if (merger.base_rev_id == tree.last_revision()):
3805
 
                result = tree.pull(merger.other_branch, False,
3806
 
                                   merger.other_rev_id)
3807
 
                result.report(self.outf)
3808
 
                return 0
3809
 
        if merger.this_basis is None:
3810
 
            raise errors.BzrCommandError(
3811
 
                "This branch has no commits."
3812
 
                " (perhaps you would prefer 'bzr pull')")
3813
 
        if preview:
3814
 
            return self._do_preview(merger)
3815
 
        elif interactive:
3816
 
            return self._do_interactive(merger)
3817
 
        else:
3818
 
            return self._do_merge(merger, change_reporter, allow_pending,
3819
 
                                  verified)
3820
 
 
3821
 
    def _get_preview(self, merger):
 
3812
                return self._do_merge(merger, change_reporter, allow_pending,
 
3813
                                      verified)
 
3814
        finally:
 
3815
            for cleanup in reversed(cleanups):
 
3816
                cleanup()
 
3817
 
 
3818
    def _get_preview(self, merger, cleanups):
3822
3819
        tree_merger = merger.make_merger()
3823
3820
        tt = tree_merger.make_preview_transform()
3824
 
        self.add_cleanup(tt.finalize)
 
3821
        cleanups.append(tt.finalize)
3825
3822
        result_tree = tt.get_preview_tree()
3826
3823
        return result_tree
3827
3824
 
3828
 
    def _do_preview(self, merger):
 
3825
    def _do_preview(self, merger, cleanups):
3829
3826
        from bzrlib.diff import show_diff_trees
3830
 
        result_tree = self._get_preview(merger)
 
3827
        result_tree = self._get_preview(merger, cleanups)
3831
3828
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3832
3829
                        old_label='', new_label='')
3833
3830
 
3843
3840
        else:
3844
3841
            return 0
3845
3842
 
3846
 
    def _do_interactive(self, merger):
 
3843
    def _do_interactive(self, merger, cleanups):
3847
3844
        """Perform an interactive merge.
3848
3845
 
3849
3846
        This works by generating a preview tree of the merge, then using
3851
3848
        and the preview tree.
3852
3849
        """
3853
3850
        from bzrlib import shelf_ui
3854
 
        result_tree = self._get_preview(merger)
 
3851
        result_tree = self._get_preview(merger, cleanups)
3855
3852
        writer = bzrlib.option.diff_writer_registry.get()
3856
3853
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3857
3854
                                   reporter=shelf_ui.ApplyReporter(),
3925
3922
            allow_pending = True
3926
3923
        return merger, allow_pending
3927
3924
 
3928
 
    def get_merger_from_uncommitted(self, tree, location, pb):
 
3925
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
3929
3926
        """Get a merger for uncommitted changes.
3930
3927
 
3931
3928
        :param tree: The tree the merger should apply to.
3932
3929
        :param location: The location containing uncommitted changes.
3933
3930
        :param pb: The progress bar to use for showing progress.
 
3931
        :param cleanups: A list of operations to perform to clean up the
 
3932
            temporary directories, unfinalized objects, etc.
3934
3933
        """
3935
3934
        location = self._select_branch_location(tree, location)[0]
3936
3935
        other_tree, other_path = WorkingTree.open_containing(location)
4023
4022
            merge_type = _mod_merge.Merge3Merger
4024
4023
        tree, file_list = tree_files(file_list)
4025
4024
        tree.lock_write()
4026
 
        self.add_cleanup(tree.unlock)
4027
 
        parents = tree.get_parent_ids()
4028
 
        if len(parents) != 2:
4029
 
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
4030
 
                                         " merges.  Not cherrypicking or"
4031
 
                                         " multi-merges.")
4032
 
        repository = tree.branch.repository
4033
 
        interesting_ids = None
4034
 
        new_conflicts = []
4035
 
        conflicts = tree.conflicts()
4036
 
        if file_list is not None:
4037
 
            interesting_ids = set()
4038
 
            for filename in file_list:
4039
 
                file_id = tree.path2id(filename)
4040
 
                if file_id is None:
4041
 
                    raise errors.NotVersionedError(filename)
4042
 
                interesting_ids.add(file_id)
4043
 
                if tree.kind(file_id) != "directory":
4044
 
                    continue
 
4025
        try:
 
4026
            parents = tree.get_parent_ids()
 
4027
            if len(parents) != 2:
 
4028
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
4029
                                             " merges.  Not cherrypicking or"
 
4030
                                             " multi-merges.")
 
4031
            repository = tree.branch.repository
 
4032
            interesting_ids = None
 
4033
            new_conflicts = []
 
4034
            conflicts = tree.conflicts()
 
4035
            if file_list is not None:
 
4036
                interesting_ids = set()
 
4037
                for filename in file_list:
 
4038
                    file_id = tree.path2id(filename)
 
4039
                    if file_id is None:
 
4040
                        raise errors.NotVersionedError(filename)
 
4041
                    interesting_ids.add(file_id)
 
4042
                    if tree.kind(file_id) != "directory":
 
4043
                        continue
4045
4044
 
4046
 
                for name, ie in tree.inventory.iter_entries(file_id):
4047
 
                    interesting_ids.add(ie.file_id)
4048
 
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4049
 
        else:
4050
 
            # Remerge only supports resolving contents conflicts
4051
 
            allowed_conflicts = ('text conflict', 'contents conflict')
4052
 
            restore_files = [c.path for c in conflicts
4053
 
                             if c.typestring in allowed_conflicts]
4054
 
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
4055
 
        tree.set_conflicts(ConflictList(new_conflicts))
4056
 
        if file_list is not None:
4057
 
            restore_files = file_list
4058
 
        for filename in restore_files:
 
4045
                    for name, ie in tree.inventory.iter_entries(file_id):
 
4046
                        interesting_ids.add(ie.file_id)
 
4047
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
4048
            else:
 
4049
                # Remerge only supports resolving contents conflicts
 
4050
                allowed_conflicts = ('text conflict', 'contents conflict')
 
4051
                restore_files = [c.path for c in conflicts
 
4052
                                 if c.typestring in allowed_conflicts]
 
4053
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
4054
            tree.set_conflicts(ConflictList(new_conflicts))
 
4055
            if file_list is not None:
 
4056
                restore_files = file_list
 
4057
            for filename in restore_files:
 
4058
                try:
 
4059
                    restore(tree.abspath(filename))
 
4060
                except errors.NotConflicted:
 
4061
                    pass
 
4062
            # Disable pending merges, because the file texts we are remerging
 
4063
            # have not had those merges performed.  If we use the wrong parents
 
4064
            # list, we imply that the working tree text has seen and rejected
 
4065
            # all the changes from the other tree, when in fact those changes
 
4066
            # have not yet been seen.
 
4067
            pb = ui.ui_factory.nested_progress_bar()
 
4068
            tree.set_parent_ids(parents[:1])
4059
4069
            try:
4060
 
                restore(tree.abspath(filename))
4061
 
            except errors.NotConflicted:
4062
 
                pass
4063
 
        # Disable pending merges, because the file texts we are remerging
4064
 
        # have not had those merges performed.  If we use the wrong parents
4065
 
        # list, we imply that the working tree text has seen and rejected
4066
 
        # all the changes from the other tree, when in fact those changes
4067
 
        # have not yet been seen.
4068
 
        pb = ui.ui_factory.nested_progress_bar()
4069
 
        tree.set_parent_ids(parents[:1])
4070
 
        try:
4071
 
            merger = _mod_merge.Merger.from_revision_ids(pb,
4072
 
                                                         tree, parents[1])
4073
 
            merger.interesting_ids = interesting_ids
4074
 
            merger.merge_type = merge_type
4075
 
            merger.show_base = show_base
4076
 
            merger.reprocess = reprocess
4077
 
            conflicts = merger.do_merge()
 
4070
                merger = _mod_merge.Merger.from_revision_ids(pb,
 
4071
                                                             tree, parents[1])
 
4072
                merger.interesting_ids = interesting_ids
 
4073
                merger.merge_type = merge_type
 
4074
                merger.show_base = show_base
 
4075
                merger.reprocess = reprocess
 
4076
                conflicts = merger.do_merge()
 
4077
            finally:
 
4078
                tree.set_parent_ids(parents)
 
4079
                pb.finished()
4078
4080
        finally:
4079
 
            tree.set_parent_ids(parents)
4080
 
            pb.finished()
 
4081
            tree.unlock()
4081
4082
        if conflicts > 0:
4082
4083
            return 1
4083
4084
        else:
4110
4111
    created as above.  Directories containing unknown files will not be
4111
4112
    deleted.
4112
4113
 
4113
 
    The working tree contains a list of revisions that have been merged but
4114
 
    not yet committed. These revisions will be included as additional parents
4115
 
    of the next commit.  Normally, using revert clears that list as well as
4116
 
    reverting the files.  If any files are specified, revert leaves the list
4117
 
    of uncommitted merges alone and reverts only the files.  Use ``bzr revert
4118
 
    .`` in the tree root to revert all files but keep the recorded merges,
4119
 
    and ``bzr revert --forget-merges`` to clear the pending merge list without
 
4114
    The working tree contains a list of pending merged revisions, which will
 
4115
    be included as parents in the next commit.  Normally, revert clears that
 
4116
    list as well as reverting the files.  If any files are specified, revert
 
4117
    leaves the pending merge list alone and reverts only the files.  Use "bzr
 
4118
    revert ." in the tree root to revert all files but keep the merge record,
 
4119
    and "bzr revert --forget-merges" to clear the pending merge list without
4120
4120
    reverting any files.
4121
4121
 
4122
 
    Using "bzr revert --forget-merges", it is possible to apply all of the
4123
 
    changes from a branch in a single revision.  To do this, perform the merge
4124
 
    as desired.  Then doing revert with the "--forget-merges" option will keep
4125
 
    the content of the tree as it was, but it will clear the list of pending
4126
 
    merges.  The next commit will then contain all of the changes that are
4127
 
    present in the other branch, but without any other parent revisions.
4128
 
    Because this technique forgets where these changes originated, it may
4129
 
    cause additional conflicts on later merges involving the same source and
 
4122
    Using "bzr revert --forget-merges", it is possible to apply the changes
 
4123
    from an arbitrary merge as a single revision.  To do this, perform the
 
4124
    merge as desired.  Then doing revert with the "--forget-merges" option will
 
4125
    keep the content of the tree as it was, but it will clear the list of
 
4126
    pending merges.  The next commit will then contain all of the changes that
 
4127
    would have been in the merge, but without any mention of the other parent
 
4128
    revisions.  Because this technique forgets where these changes originated,
 
4129
    it may cause additional conflicts on later merges involving the source and
4130
4130
    target branches.
4131
4131
    """
4132
4132
 
4143
4143
            forget_merges=None):
4144
4144
        tree, file_list = tree_files(file_list)
4145
4145
        tree.lock_write()
4146
 
        self.add_cleanup(tree.unlock)
4147
 
        if forget_merges:
4148
 
            tree.set_parent_ids(tree.get_parent_ids()[:1])
4149
 
        else:
4150
 
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
 
4146
        try:
 
4147
            if forget_merges:
 
4148
                tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4149
            else:
 
4150
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
 
4151
        finally:
 
4152
            tree.unlock()
4151
4153
 
4152
4154
    @staticmethod
4153
4155
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4304
4306
        if remote_branch.base == local_branch.base:
4305
4307
            remote_branch = local_branch
4306
4308
 
4307
 
        local_branch.lock_read()
4308
 
        self.add_cleanup(local_branch.unlock)
4309
4309
        local_revid_range = _revision_range_to_revid_range(
4310
4310
            _get_revision_range(my_revision, local_branch,
4311
4311
                self.name()))
4312
4312
 
4313
 
        remote_branch.lock_read()
4314
 
        self.add_cleanup(remote_branch.unlock)
4315
4313
        remote_revid_range = _revision_range_to_revid_range(
4316
4314
            _get_revision_range(revision,
4317
4315
                remote_branch, self.name()))
4318
4316
 
4319
 
        local_extra, remote_extra = find_unmerged(
4320
 
            local_branch, remote_branch, restrict,
4321
 
            backward=not reverse,
4322
 
            include_merges=include_merges,
4323
 
            local_revid_range=local_revid_range,
4324
 
            remote_revid_range=remote_revid_range)
4325
 
 
4326
 
        if log_format is None:
4327
 
            registry = log.log_formatter_registry
4328
 
            log_format = registry.get_default(local_branch)
4329
 
        lf = log_format(to_file=self.outf,
4330
 
                        show_ids=show_ids,
4331
 
                        show_timezone='original')
4332
 
 
4333
 
        status_code = 0
4334
 
        if local_extra and not theirs_only:
4335
 
            message("You have %d extra revision(s):\n" %
4336
 
                len(local_extra))
4337
 
            for revision in iter_log_revisions(local_extra,
4338
 
                                local_branch.repository,
4339
 
                                verbose):
4340
 
                lf.log_revision(revision)
4341
 
            printed_local = True
4342
 
            status_code = 1
4343
 
        else:
4344
 
            printed_local = False
4345
 
 
4346
 
        if remote_extra and not mine_only:
4347
 
            if printed_local is True:
4348
 
                message("\n\n\n")
4349
 
            message("You are missing %d revision(s):\n" %
4350
 
                len(remote_extra))
4351
 
            for revision in iter_log_revisions(remote_extra,
4352
 
                                remote_branch.repository,
4353
 
                                verbose):
4354
 
                lf.log_revision(revision)
4355
 
            status_code = 1
4356
 
 
4357
 
        if mine_only and not local_extra:
4358
 
            # We checked local, and found nothing extra
4359
 
            message('This branch is up to date.\n')
4360
 
        elif theirs_only and not remote_extra:
4361
 
            # We checked remote, and found nothing extra
4362
 
            message('Other branch is up to date.\n')
4363
 
        elif not (mine_only or theirs_only or local_extra or
4364
 
                  remote_extra):
4365
 
            # We checked both branches, and neither one had extra
4366
 
            # revisions
4367
 
            message("Branches are up to date.\n")
4368
 
        self.cleanup_now()
 
4317
        local_branch.lock_read()
 
4318
        try:
 
4319
            remote_branch.lock_read()
 
4320
            try:
 
4321
                local_extra, remote_extra = find_unmerged(
 
4322
                    local_branch, remote_branch, restrict,
 
4323
                    backward=not reverse,
 
4324
                    include_merges=include_merges,
 
4325
                    local_revid_range=local_revid_range,
 
4326
                    remote_revid_range=remote_revid_range)
 
4327
 
 
4328
                if log_format is None:
 
4329
                    registry = log.log_formatter_registry
 
4330
                    log_format = registry.get_default(local_branch)
 
4331
                lf = log_format(to_file=self.outf,
 
4332
                                show_ids=show_ids,
 
4333
                                show_timezone='original')
 
4334
 
 
4335
                status_code = 0
 
4336
                if local_extra and not theirs_only:
 
4337
                    message("You have %d extra revision(s):\n" %
 
4338
                        len(local_extra))
 
4339
                    for revision in iter_log_revisions(local_extra,
 
4340
                                        local_branch.repository,
 
4341
                                        verbose):
 
4342
                        lf.log_revision(revision)
 
4343
                    printed_local = True
 
4344
                    status_code = 1
 
4345
                else:
 
4346
                    printed_local = False
 
4347
 
 
4348
                if remote_extra and not mine_only:
 
4349
                    if printed_local is True:
 
4350
                        message("\n\n\n")
 
4351
                    message("You are missing %d revision(s):\n" %
 
4352
                        len(remote_extra))
 
4353
                    for revision in iter_log_revisions(remote_extra,
 
4354
                                        remote_branch.repository,
 
4355
                                        verbose):
 
4356
                        lf.log_revision(revision)
 
4357
                    status_code = 1
 
4358
 
 
4359
                if mine_only and not local_extra:
 
4360
                    # We checked local, and found nothing extra
 
4361
                    message('This branch is up to date.\n')
 
4362
                elif theirs_only and not remote_extra:
 
4363
                    # We checked remote, and found nothing extra
 
4364
                    message('Other branch is up to date.\n')
 
4365
                elif not (mine_only or theirs_only or local_extra or
 
4366
                          remote_extra):
 
4367
                    # We checked both branches, and neither one had extra
 
4368
                    # revisions
 
4369
                    message("Branches are up to date.\n")
 
4370
            finally:
 
4371
                remote_branch.unlock()
 
4372
        finally:
 
4373
            local_branch.unlock()
4369
4374
        if not status_code and parent is None and other_branch is not None:
4370
4375
            local_branch.lock_write()
4371
 
            self.add_cleanup(local_branch.unlock)
4372
 
            # handle race conditions - a parent might be set while we run.
4373
 
            if local_branch.get_parent() is None:
4374
 
                local_branch.set_parent(remote_branch.base)
 
4376
            try:
 
4377
                # handle race conditions - a parent might be set while we run.
 
4378
                if local_branch.get_parent() is None:
 
4379
                    local_branch.set_parent(remote_branch.base)
 
4380
            finally:
 
4381
                local_branch.unlock()
4375
4382
        return status_code
4376
4383
 
4377
4384
 
4405
4412
    adding new commands, providing additional network transports and
4406
4413
    customizing log output.
4407
4414
 
4408
 
    See the Bazaar Plugin Guide <http://doc.bazaar.canonical.com/plugins/en/>
4409
 
    for further information on plugins including where to find them and how to
4410
 
    install them. Instructions are also provided there on how to write new
4411
 
    plugins using the Python programming language.
 
4415
    See the Bazaar web site, http://bazaar-vcs.org, for further
 
4416
    information on plugins including where to find them and how to
 
4417
    install them. Instructions are also provided there on how to
 
4418
    write new plugins using the Python programming language.
4412
4419
    """
4413
4420
    takes_options = ['verbose']
4414
4421
 
4456
4463
        else:
4457
4464
            b = Branch.open(branch)
4458
4465
        b.lock_read()
4459
 
        self.add_cleanup(b.unlock)
4460
 
        if revision is None:
4461
 
            rev_id = b.last_revision()
4462
 
        else:
4463
 
            rev_id = revision[0].as_revision_id(b)
4464
 
        t = testament_class.from_revision(b.repository, rev_id)
4465
 
        if long:
4466
 
            sys.stdout.writelines(t.as_text_lines())
4467
 
        else:
4468
 
            sys.stdout.write(t.as_short_text())
 
4466
        try:
 
4467
            if revision is None:
 
4468
                rev_id = b.last_revision()
 
4469
            else:
 
4470
                rev_id = revision[0].as_revision_id(b)
 
4471
            t = testament_class.from_revision(b.repository, rev_id)
 
4472
            if long:
 
4473
                sys.stdout.writelines(t.as_text_lines())
 
4474
            else:
 
4475
                sys.stdout.write(t.as_short_text())
 
4476
        finally:
 
4477
            b.unlock()
4469
4478
 
4470
4479
 
4471
4480
class cmd_annotate(Command):
4497
4506
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4498
4507
        if wt is not None:
4499
4508
            wt.lock_read()
4500
 
            self.add_cleanup(wt.unlock)
4501
4509
        else:
4502
4510
            branch.lock_read()
4503
 
            self.add_cleanup(branch.unlock)
4504
 
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
4505
 
        tree.lock_read()
4506
 
        self.add_cleanup(tree.unlock)
4507
 
        if wt is not None:
4508
 
            file_id = wt.path2id(relpath)
4509
 
        else:
4510
 
            file_id = tree.path2id(relpath)
4511
 
        if file_id is None:
4512
 
            raise errors.NotVersionedError(filename)
4513
 
        file_version = tree.inventory[file_id].revision
4514
 
        if wt is not None and revision is None:
4515
 
            # If there is a tree and we're not annotating historical
4516
 
            # versions, annotate the working tree's content.
4517
 
            annotate_file_tree(wt, file_id, self.outf, long, all,
4518
 
                show_ids=show_ids)
4519
 
        else:
4520
 
            annotate_file(branch, file_version, file_id, long, all, self.outf,
4521
 
                          show_ids=show_ids)
 
4511
        try:
 
4512
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
4513
            if wt is not None:
 
4514
                file_id = wt.path2id(relpath)
 
4515
            else:
 
4516
                file_id = tree.path2id(relpath)
 
4517
            if file_id is None:
 
4518
                raise errors.NotVersionedError(filename)
 
4519
            file_version = tree.inventory[file_id].revision
 
4520
            if wt is not None and revision is None:
 
4521
                # If there is a tree and we're not annotating historical
 
4522
                # versions, annotate the working tree's content.
 
4523
                annotate_file_tree(wt, file_id, self.outf, long, all,
 
4524
                    show_ids=show_ids)
 
4525
            else:
 
4526
                annotate_file(branch, file_version, file_id, long, all, self.outf,
 
4527
                              show_ids=show_ids)
 
4528
        finally:
 
4529
            if wt is not None:
 
4530
                wt.unlock()
 
4531
            else:
 
4532
                branch.unlock()
4522
4533
 
4523
4534
 
4524
4535
class cmd_re_sign(Command):
4536
4547
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4537
4548
        b = WorkingTree.open_containing(u'.')[0].branch
4538
4549
        b.lock_write()
4539
 
        self.add_cleanup(b.unlock)
4540
 
        return self._run(b, revision_id_list, revision)
 
4550
        try:
 
4551
            return self._run(b, revision_id_list, revision)
 
4552
        finally:
 
4553
            b.unlock()
4541
4554
 
4542
4555
    def _run(self, b, revision_id_list, revision):
4543
4556
        import bzrlib.gpg as gpg
4689
4702
 
4690
4703
        if tree is not None:
4691
4704
            tree.lock_write()
4692
 
            self.add_cleanup(tree.unlock)
4693
4705
        else:
4694
4706
            b.lock_write()
4695
 
            self.add_cleanup(b.unlock)
4696
 
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
 
4707
        try:
 
4708
            return self._run(b, tree, dry_run, verbose, revision, force,
 
4709
                             local=local)
 
4710
        finally:
 
4711
            if tree is not None:
 
4712
                tree.unlock()
 
4713
            else:
 
4714
                b.unlock()
4697
4715
 
4698
4716
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4699
4717
        from bzrlib.log import log_formatter, show_log
4756
4774
    CAUTION: Locks should only be broken when you are sure that the process
4757
4775
    holding the lock has been stopped.
4758
4776
 
4759
 
    You can get information on what locks are open via the 'bzr info
4760
 
    [location]' command.
 
4777
    You can get information on what locks are open via the 'bzr info' command.
4761
4778
 
4762
4779
    :Examples:
4763
4780
        bzr break-lock
4764
 
        bzr break-lock bzr+ssh://example.com/bzr/foo
4765
4781
    """
4766
4782
    takes_args = ['location?']
4767
4783
 
5039
5055
      directly from the merge directive, without retrieving data from a
5040
5056
      branch.
5041
5057
 
5042
 
    `bzr send` creates a compact data set that, when applied using bzr
5043
 
    merge, has the same effect as merging from the source branch.  
5044
 
    
5045
 
    By default the merge directive is self-contained and can be applied to any
5046
 
    branch containing submit_branch in its ancestory without needing access to
5047
 
    the source branch.
5048
 
    
5049
 
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
5050
 
    revisions, but only a structured request to merge from the
5051
 
    public_location.  In that case the public_branch is needed and it must be
5052
 
    up-to-date and accessible to the recipient.  The public_branch is always
5053
 
    included if known, so that people can check it later.
5054
 
 
5055
 
    The submit branch defaults to the parent of the source branch, but can be
5056
 
    overridden.  Both submit branch and public branch will be remembered in
5057
 
    branch.conf the first time they are used for a particular branch.  The
5058
 
    source branch defaults to that containing the working directory, but can
5059
 
    be changed using --from.
5060
 
 
5061
 
    In order to calculate those changes, bzr must analyse the submit branch.
5062
 
    Therefore it is most efficient for the submit branch to be a local mirror.
5063
 
    If a public location is known for the submit_branch, that location is used
5064
 
    in the merge directive.
5065
 
 
5066
 
    The default behaviour is to send the merge directive by mail, unless -o is
5067
 
    given, in which case it is sent to a file.
 
5058
    If --no-bundle is specified, then public_branch is needed (and must be
 
5059
    up-to-date), so that the receiver can perform the merge using the
 
5060
    public_branch.  The public_branch is always included if known, so that
 
5061
    people can check it later.
 
5062
 
 
5063
    The submit branch defaults to the parent, but can be overridden.  Both
 
5064
    submit branch and public branch will be remembered if supplied.
 
5065
 
 
5066
    If a public_branch is known for the submit_branch, that public submit
 
5067
    branch is used in the merge instructions.  This means that a local mirror
 
5068
    can be used as your actual submit branch, once you have set public_branch
 
5069
    for that mirror.
5068
5070
 
5069
5071
    Mail is sent using your preferred mail program.  This should be transparent
5070
5072
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
5090
5092
 
5091
5093
    The merge directives created by bzr send may be applied using bzr merge or
5092
5094
    bzr pull by specifying a file containing a merge directive as the location.
5093
 
 
5094
 
    bzr send makes extensive use of public locations to map local locations into
5095
 
    URLs that can be used by other people.  See `bzr help configuration` to
5096
 
    set them, and use `bzr info` to display them.
5097
5095
    """
5098
5096
 
5099
5097
    encoding_type = 'exact'
5258
5256
            ):
5259
5257
        branch, relpath = Branch.open_containing(directory)
5260
5258
        branch.lock_write()
5261
 
        self.add_cleanup(branch.unlock)
5262
 
        if delete:
5263
 
            branch.tags.delete_tag(tag_name)
5264
 
            self.outf.write('Deleted tag %s.\n' % tag_name)
5265
 
        else:
5266
 
            if revision:
5267
 
                if len(revision) != 1:
5268
 
                    raise errors.BzrCommandError(
5269
 
                        "Tags can only be placed on a single revision, "
5270
 
                        "not on a range")
5271
 
                revision_id = revision[0].as_revision_id(branch)
 
5259
        try:
 
5260
            if delete:
 
5261
                branch.tags.delete_tag(tag_name)
 
5262
                self.outf.write('Deleted tag %s.\n' % tag_name)
5272
5263
            else:
5273
 
                revision_id = branch.last_revision()
5274
 
            if (not force) and branch.tags.has_tag(tag_name):
5275
 
                raise errors.TagAlreadyExists(tag_name)
5276
 
            branch.tags.set_tag(tag_name, revision_id)
5277
 
            self.outf.write('Created tag %s.\n' % tag_name)
 
5264
                if revision:
 
5265
                    if len(revision) != 1:
 
5266
                        raise errors.BzrCommandError(
 
5267
                            "Tags can only be placed on a single revision, "
 
5268
                            "not on a range")
 
5269
                    revision_id = revision[0].as_revision_id(branch)
 
5270
                else:
 
5271
                    revision_id = branch.last_revision()
 
5272
                if (not force) and branch.tags.has_tag(tag_name):
 
5273
                    raise errors.TagAlreadyExists(tag_name)
 
5274
                branch.tags.set_tag(tag_name, revision_id)
 
5275
                self.outf.write('Created tag %s.\n' % tag_name)
 
5276
        finally:
 
5277
            branch.unlock()
5278
5278
 
5279
5279
 
5280
5280
class cmd_tags(Command):
5313
5313
            return
5314
5314
 
5315
5315
        branch.lock_read()
5316
 
        self.add_cleanup(branch.unlock)
5317
 
        if revision:
5318
 
            graph = branch.repository.get_graph()
5319
 
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
5320
 
            revid1, revid2 = rev1.rev_id, rev2.rev_id
5321
 
            # only show revisions between revid1 and revid2 (inclusive)
5322
 
            tags = [(tag, revid) for tag, revid in tags if
5323
 
                graph.is_between(revid, revid1, revid2)]
5324
 
        if sort == 'alpha':
5325
 
            tags.sort()
5326
 
        elif sort == 'time':
5327
 
            timestamps = {}
5328
 
            for tag, revid in tags:
5329
 
                try:
5330
 
                    revobj = branch.repository.get_revision(revid)
5331
 
                except errors.NoSuchRevision:
5332
 
                    timestamp = sys.maxint # place them at the end
5333
 
                else:
5334
 
                    timestamp = revobj.timestamp
5335
 
                timestamps[revid] = timestamp
5336
 
            tags.sort(key=lambda x: timestamps[x[1]])
5337
 
        if not show_ids:
5338
 
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5339
 
            for index, (tag, revid) in enumerate(tags):
5340
 
                try:
5341
 
                    revno = branch.revision_id_to_dotted_revno(revid)
5342
 
                    if isinstance(revno, tuple):
5343
 
                        revno = '.'.join(map(str, revno))
5344
 
                except errors.NoSuchRevision:
5345
 
                    # Bad tag data/merges can lead to tagged revisions
5346
 
                    # which are not in this branch. Fail gracefully ...
5347
 
                    revno = '?'
5348
 
                tags[index] = (tag, revno)
5349
 
        self.cleanup_now()
 
5316
        try:
 
5317
            if revision:
 
5318
                graph = branch.repository.get_graph()
 
5319
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5320
                revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5321
                # only show revisions between revid1 and revid2 (inclusive)
 
5322
                tags = [(tag, revid) for tag, revid in tags if
 
5323
                    graph.is_between(revid, revid1, revid2)]
 
5324
            if sort == 'alpha':
 
5325
                tags.sort()
 
5326
            elif sort == 'time':
 
5327
                timestamps = {}
 
5328
                for tag, revid in tags:
 
5329
                    try:
 
5330
                        revobj = branch.repository.get_revision(revid)
 
5331
                    except errors.NoSuchRevision:
 
5332
                        timestamp = sys.maxint # place them at the end
 
5333
                    else:
 
5334
                        timestamp = revobj.timestamp
 
5335
                    timestamps[revid] = timestamp
 
5336
                tags.sort(key=lambda x: timestamps[x[1]])
 
5337
            if not show_ids:
 
5338
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5339
                for index, (tag, revid) in enumerate(tags):
 
5340
                    try:
 
5341
                        revno = branch.revision_id_to_dotted_revno(revid)
 
5342
                        if isinstance(revno, tuple):
 
5343
                            revno = '.'.join(map(str, revno))
 
5344
                    except errors.NoSuchRevision:
 
5345
                        # Bad tag data/merges can lead to tagged revisions
 
5346
                        # which are not in this branch. Fail gracefully ...
 
5347
                        revno = '?'
 
5348
                    tags[index] = (tag, revno)
 
5349
        finally:
 
5350
            branch.unlock()
5350
5351
        for tag, revspec in tags:
5351
5352
            self.outf.write('%-20s %s\n' % (tag, revspec))
5352
5353
 
5465
5466
    that of the master.
5466
5467
    """
5467
5468
 
5468
 
    takes_args = ['to_location?']
 
5469
    takes_args = ['to_location']
5469
5470
    takes_options = [Option('force',
5470
5471
                        help='Switch even if local commits will be lost.'),
5471
 
                     'revision',
5472
5472
                     Option('create-branch', short_name='b',
5473
5473
                        help='Create the target branch from this one before'
5474
5474
                             ' switching to it.'),
5475
 
                    ]
 
5475
                     ]
5476
5476
 
5477
 
    def run(self, to_location=None, force=False, create_branch=False,
5478
 
            revision=None):
 
5477
    def run(self, to_location, force=False, create_branch=False):
5479
5478
        from bzrlib import switch
5480
5479
        tree_location = '.'
5481
 
        revision = _get_one_revision('switch', revision)
5482
5480
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
5483
 
        if to_location is None:
5484
 
            if revision is None:
5485
 
                raise errors.BzrCommandError('You must supply either a'
5486
 
                                             ' revision or a location')
5487
 
            to_location = '.'
5488
5481
        try:
5489
5482
            branch = control_dir.open_branch()
5490
5483
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5495
5488
            if branch is None:
5496
5489
                raise errors.BzrCommandError('cannot create branch without'
5497
5490
                                             ' source branch')
5498
 
            to_location = directory_service.directories.dereference(
5499
 
                              to_location)
5500
5491
            if '/' not in to_location and '\\' not in to_location:
5501
5492
                # This path is meant to be relative to the existing branch
5502
5493
                this_url = self._get_branch_location(control_dir)
5504
5495
            to_branch = branch.bzrdir.sprout(to_location,
5505
5496
                                 possible_transports=[branch.bzrdir.root_transport],
5506
5497
                                 source_branch=branch).open_branch()
 
5498
            # try:
 
5499
            #     from_branch = control_dir.open_branch()
 
5500
            # except errors.NotBranchError:
 
5501
            #     raise BzrCommandError('Cannot create a branch from this'
 
5502
            #         ' location when we cannot open this branch')
 
5503
            # from_branch.bzrdir.sprout(
 
5504
            pass
5507
5505
        else:
5508
5506
            try:
5509
5507
                to_branch = Branch.open(to_location)
5511
5509
                this_url = self._get_branch_location(control_dir)
5512
5510
                to_branch = Branch.open(
5513
5511
                    urlutils.join(this_url, '..', to_location))
5514
 
        if revision is not None:
5515
 
            revision = revision.as_revision_id(to_branch)
5516
 
        switch.switch(control_dir, to_branch, force, revision_id=revision)
 
5512
        switch.switch(control_dir, to_branch, force)
5517
5513
        if had_explicit_nick:
5518
5514
            branch = control_dir.open_branch() #get the new branch!
5519
5515
            branch.nick = to_branch.nick
5773
5769
    def run_for_list(self):
5774
5770
        tree = WorkingTree.open_containing('.')[0]
5775
5771
        tree.lock_read()
5776
 
        self.add_cleanup(tree.unlock)
5777
 
        manager = tree.get_shelf_manager()
5778
 
        shelves = manager.active_shelves()
5779
 
        if len(shelves) == 0:
5780
 
            note('No shelved changes.')
5781
 
            return 0
5782
 
        for shelf_id in reversed(shelves):
5783
 
            message = manager.get_metadata(shelf_id).get('message')
5784
 
            if message is None:
5785
 
                message = '<no message>'
5786
 
            self.outf.write('%3d: %s\n' % (shelf_id, message))
5787
 
        return 1
 
5772
        try:
 
5773
            manager = tree.get_shelf_manager()
 
5774
            shelves = manager.active_shelves()
 
5775
            if len(shelves) == 0:
 
5776
                note('No shelved changes.')
 
5777
                return 0
 
5778
            for shelf_id in reversed(shelves):
 
5779
                message = manager.get_metadata(shelf_id).get('message')
 
5780
                if message is None:
 
5781
                    message = '<no message>'
 
5782
                self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5783
            return 1
 
5784
        finally:
 
5785
            tree.unlock()
5788
5786
 
5789
5787
 
5790
5788
class cmd_unshelve(Command):
5802
5800
            enum_switch=False, value_switches=True,
5803
5801
            apply="Apply changes and remove from the shelf.",
5804
5802
            dry_run="Show changes, but do not apply or remove them.",
5805
 
            preview="Instead of unshelving the changes, show the diff that "
5806
 
                    "would result from unshelving.",
5807
5803
            delete_only="Delete changes without applying them.",
5808
5804
            keep="Apply changes but don't delete them.",
5809
5805
        )