/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

Merge cleanup into description

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2004, 2005, 2006, 2007, 2008, 2009 Canonical Ltd
 
1
# Copyright (C) 2004-2010 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
502
502
                wt.lock_read()
503
503
            except (errors.NoWorkingTree, errors.NotLocalUrl):
504
504
                raise errors.NoWorkingTree(location)
 
505
            self.add_cleanup(wt.unlock)
 
506
            revid = wt.last_revision()
505
507
            try:
506
 
                revid = wt.last_revision()
507
 
                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)
512
 
            finally:
513
 
                wt.unlock()
 
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)
514
512
        else:
515
513
            b = Branch.open_containing(location)[0]
516
514
            b.lock_read()
517
 
            try:
518
 
                revno = b.revno()
519
 
            finally:
520
 
                b.unlock()
521
 
 
 
515
            self.add_cleanup(b.unlock)
 
516
            revno = b.revno()
 
517
        self.cleanup_now()
522
518
        self.outf.write(str(revno) + '\n')
523
519
 
524
520
 
546
542
            wt = WorkingTree.open_containing(directory)[0]
547
543
            b = wt.branch
548
544
            wt.lock_read()
 
545
            self.add_cleanup(wt.unlock)
549
546
        except (errors.NoWorkingTree, errors.NotLocalUrl):
550
547
            wt = None
551
548
            b = Branch.open_containing(directory)[0]
552
549
            b.lock_read()
553
 
        try:
554
 
            revision_ids = []
555
 
            if revision is not None:
556
 
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
557
 
            if revision_info_list is not None:
558
 
                for rev_str in revision_info_list:
559
 
                    rev_spec = RevisionSpec.from_string(rev_str)
560
 
                    revision_ids.append(rev_spec.as_revision_id(b))
561
 
            # No arguments supplied, default to the last revision
562
 
            if len(revision_ids) == 0:
563
 
                if tree:
564
 
                    if wt is None:
565
 
                        raise errors.NoWorkingTree(directory)
566
 
                    revision_ids.append(wt.last_revision())
567
 
                else:
568
 
                    revision_ids.append(b.last_revision())
569
 
 
570
 
            revinfos = []
571
 
            maxlen = 0
572
 
            for revision_id in revision_ids:
573
 
                try:
574
 
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
575
 
                    revno = '.'.join(str(i) for i in dotted_revno)
576
 
                except errors.NoSuchRevision:
577
 
                    revno = '???'
578
 
                maxlen = max(maxlen, len(revno))
579
 
                revinfos.append([revno, revision_id])
580
 
        finally:
581
 
            if wt is None:
582
 
                b.unlock()
 
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())
583
564
            else:
584
 
                wt.unlock()
585
 
 
 
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()
586
579
        for ri in revinfos:
587
580
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
588
581
 
660
653
 
661
654
        if base_tree:
662
655
            base_tree.lock_read()
663
 
        try:
664
 
            tree, file_list = tree_files_for_add(file_list)
665
 
            added, ignored = tree.smart_add(file_list, not
666
 
                no_recurse, action=action, save=not dry_run)
667
 
        finally:
668
 
            if base_tree is not None:
669
 
                base_tree.unlock()
 
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()
670
661
        if len(ignored) > 0:
671
662
            if verbose:
672
663
                for glob in sorted(ignored.keys()):
736
727
        revision = _get_one_revision('inventory', revision)
737
728
        work_tree, file_list = tree_files(file_list)
738
729
        work_tree.lock_read()
739
 
        try:
740
 
            if revision is not None:
741
 
                tree = revision.as_tree(work_tree.branch)
742
 
 
743
 
                extra_trees = [work_tree]
744
 
                tree.lock_read()
745
 
            else:
746
 
                tree = work_tree
747
 
                extra_trees = []
748
 
 
749
 
            if file_list is not None:
750
 
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
751
 
                                          require_versioned=True)
752
 
                # find_ids_across_trees may include some paths that don't
753
 
                # exist in 'tree'.
754
 
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
755
 
                                 for file_id in file_ids if file_id in tree)
756
 
            else:
757
 
                entries = tree.inventory.entries()
758
 
        finally:
759
 
            tree.unlock()
760
 
            if tree is not work_tree:
761
 
                work_tree.unlock()
762
 
 
 
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()
763
752
        for path, entry in entries:
764
753
            if kind and kind != entry.kind:
765
754
                continue
811
800
            raise errors.BzrCommandError("missing file argument")
812
801
        tree, rel_names = tree_files(names_list, canonicalize=False)
813
802
        tree.lock_tree_write()
814
 
        try:
815
 
            self._run(tree, names_list, rel_names, after)
816
 
        finally:
817
 
            tree.unlock()
 
803
        self.add_cleanup(tree.unlock)
 
804
        self._run(tree, names_list, rel_names, after)
818
805
 
819
806
    def run_auto(self, names_list, after, dry_run):
820
807
        if names_list is not None and len(names_list) > 1:
825
812
                                         ' --auto.')
826
813
        work_tree, file_list = tree_files(names_list, default_branch='.')
827
814
        work_tree.lock_tree_write()
828
 
        try:
829
 
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
830
 
        finally:
831
 
            work_tree.unlock()
 
815
        self.add_cleanup(work_tree.unlock)
 
816
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
832
817
 
833
818
    def _run(self, tree, names_list, rel_names, after):
834
819
        into_existing = osutils.isdir(names_list[-1])
1012
997
 
1013
998
        if branch_from is not branch_to:
1014
999
            branch_from.lock_read()
1015
 
        try:
1016
 
            if revision is not None:
1017
 
                revision_id = revision.as_revision_id(branch_from)
1018
 
 
1019
 
            branch_to.lock_write()
1020
 
            try:
1021
 
                if tree_to is not None:
1022
 
                    view_info = _get_view_info_for_change_reporter(tree_to)
1023
 
                    change_reporter = delta._ChangeReporter(
1024
 
                        unversioned_filter=tree_to.is_ignored,
1025
 
                        view_info=view_info)
1026
 
                    result = tree_to.pull(
1027
 
                        branch_from, overwrite, revision_id, change_reporter,
1028
 
                        possible_transports=possible_transports, local=local)
1029
 
                else:
1030
 
                    result = branch_to.pull(
1031
 
                        branch_from, overwrite, revision_id, local=local)
1032
 
 
1033
 
                result.report(self.outf)
1034
 
                if verbose and result.old_revid != result.new_revid:
1035
 
                    log.show_branch_change(
1036
 
                        branch_to, self.outf, result.old_revno,
1037
 
                        result.old_revid)
1038
 
            finally:
1039
 
                branch_to.unlock()
1040
 
        finally:
1041
 
            if branch_from is not branch_to:
1042
 
                branch_from.unlock()
 
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)
1043
1023
 
1044
1024
 
1045
1025
class cmd_push(Command):
1200
1180
                    ' directory exists, but does not already'
1201
1181
                    ' have a control directory.  This flag will'
1202
1182
                    ' allow branch to proceed.'),
 
1183
        Option('bind',
 
1184
            help="Bind new branch to from location."),
1203
1185
        ]
1204
1186
    aliases = ['get', 'clone']
1205
1187
 
1206
1188
    def run(self, from_location, to_location=None, revision=None,
1207
1189
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1208
 
            use_existing_dir=False, switch=False):
 
1190
            use_existing_dir=False, switch=False, bind=False):
1209
1191
        from bzrlib import switch as _mod_switch
1210
1192
        from bzrlib.tag import _merge_tags_if_possible
1211
1193
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1212
1194
            from_location)
1213
1195
        revision = _get_one_revision('branch', revision)
1214
1196
        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)
1215
1208
        try:
1216
 
            if revision is not None:
1217
 
                revision_id = revision.as_revision_id(br_from)
 
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)
1218
1214
            else:
1219
 
                # FIXME - wt.last_revision, fallback to branch, fall back to
1220
 
                # None or perhaps NULL_REVISION to mean copy nothing
1221
 
                # RBC 20060209
1222
 
                revision_id = br_from.last_revision()
1223
 
            if to_location is None:
1224
 
                to_location = urlutils.derive_to_location(from_location)
1225
 
            to_transport = transport.get_transport(to_location)
1226
 
            try:
1227
 
                to_transport.mkdir('.')
1228
 
            except errors.FileExists:
1229
 
                if not use_existing_dir:
1230
 
                    raise errors.BzrCommandError('Target directory "%s" '
1231
 
                        'already exists.' % to_location)
 
1215
                try:
 
1216
                    bzrdir.BzrDir.open_from_transport(to_transport)
 
1217
                except errors.NotBranchError:
 
1218
                    pass
1232
1219
                else:
1233
 
                    try:
1234
 
                        bzrdir.BzrDir.open_from_transport(to_transport)
1235
 
                    except errors.NotBranchError:
1236
 
                        pass
1237
 
                    else:
1238
 
                        raise errors.AlreadyBranchError(to_location)
1239
 
            except errors.NoSuchFile:
1240
 
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1241
 
                                             % to_location)
1242
 
            try:
1243
 
                # preserve whatever source format we have.
1244
 
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
1245
 
                                            possible_transports=[to_transport],
1246
 
                                            accelerator_tree=accelerator_tree,
1247
 
                                            hardlink=hardlink, stacked=stacked,
1248
 
                                            force_new_repo=standalone,
1249
 
                                            create_tree_if_local=not no_tree,
1250
 
                                            source_branch=br_from)
1251
 
                branch = dir.open_branch()
1252
 
            except errors.NoSuchRevision:
1253
 
                to_transport.delete_tree('.')
1254
 
                msg = "The branch %s has no revision %s." % (from_location,
1255
 
                    revision)
1256
 
                raise errors.BzrCommandError(msg)
1257
 
            _merge_tags_if_possible(br_from, branch)
1258
 
            # If the source branch is stacked, the new branch may
1259
 
            # be stacked whether we asked for that explicitly or not.
1260
 
            # We therefore need a try/except here and not just 'if stacked:'
1261
 
            try:
1262
 
                note('Created new stacked branch referring to %s.' %
1263
 
                    branch.get_stacked_on_url())
1264
 
            except (errors.NotStacked, errors.UnstackableBranchFormat,
1265
 
                errors.UnstackableRepositoryFormat), e:
1266
 
                note('Branched %d revision(s).' % branch.revno())
1267
 
            if switch:
1268
 
                # Switch to the new branch
1269
 
                wt, _ = WorkingTree.open_containing('.')
1270
 
                _mod_switch.switch(wt.bzrdir, branch)
1271
 
                note('Switched to branch: %s',
1272
 
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
1273
 
        finally:
1274
 
            br_from.unlock()
 
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'))
1275
1260
 
1276
1261
 
1277
1262
class cmd_checkout(Command):
1356
1341
    def run(self, dir=u'.'):
1357
1342
        tree = WorkingTree.open_containing(dir)[0]
1358
1343
        tree.lock_read()
1359
 
        try:
1360
 
            new_inv = tree.inventory
1361
 
            old_tree = tree.basis_tree()
1362
 
            old_tree.lock_read()
1363
 
            try:
1364
 
                old_inv = old_tree.inventory
1365
 
                renames = []
1366
 
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
1367
 
                for f, paths, c, v, p, n, k, e in iterator:
1368
 
                    if paths[0] == paths[1]:
1369
 
                        continue
1370
 
                    if None in (paths):
1371
 
                        continue
1372
 
                    renames.append(paths)
1373
 
                renames.sort()
1374
 
                for old_name, new_name in renames:
1375
 
                    self.outf.write("%s => %s\n" % (old_name, new_name))
1376
 
            finally:
1377
 
                old_tree.unlock()
1378
 
        finally:
1379
 
            tree.unlock()
 
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))
1380
1361
 
1381
1362
 
1382
1363
class cmd_update(Command):
1413
1394
        else:
1414
1395
            tree.lock_tree_write()
1415
1396
            branch_location = tree.branch.base
 
1397
        self.add_cleanup(tree.unlock)
1416
1398
        # get rid of the final '/' and be ready for display
1417
1399
        branch_location = urlutils.unescape_for_display(branch_location[:-1],
1418
1400
                                                        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)
1419
1422
        try:
1420
 
            existing_pending_merges = tree.get_parent_ids()[1:]
1421
 
            if master is None:
1422
 
                old_tip = None
1423
 
            else:
1424
 
                # may need to fetch data into a heavyweight checkout
1425
 
                # XXX: this may take some time, maybe we should display a
1426
 
                # message
1427
 
                old_tip = branch.update(possible_transports)
1428
 
            if revision is not None:
1429
 
                revision_id = revision[0].as_revision_id(branch)
1430
 
            else:
1431
 
                revision_id = branch.last_revision()
1432
 
            if revision_id == _mod_revision.ensure_null(tree.last_revision()):
1433
 
                revno = branch.revision_id_to_revno(revision_id)
1434
 
                note("Tree is up to date at revision %d of branch %s" %
1435
 
                    (revno, branch_location))
1436
 
                return 0
1437
 
            view_info = _get_view_info_for_change_reporter(tree)
1438
 
            change_reporter = delta._ChangeReporter(
1439
 
                unversioned_filter=tree.is_ignored,
1440
 
                view_info=view_info)
1441
 
            try:
1442
 
                conflicts = tree.update(
1443
 
                    change_reporter,
1444
 
                    possible_transports=possible_transports,
1445
 
                    revision=revision_id,
1446
 
                    old_tip=old_tip)
1447
 
            except errors.NoSuchRevision, e:
1448
 
                raise errors.BzrCommandError(
1449
 
                                      "branch has no revision %s\n"
1450
 
                                      "bzr update --revision only works"
1451
 
                                      " for a revision in the branch history"
1452
 
                                      % (e.revision))
1453
 
            revno = tree.branch.revision_id_to_revno(
1454
 
                _mod_revision.ensure_null(tree.last_revision()))
1455
 
            note('Updated to revision %d of branch %s' %
1456
 
                 (revno, branch_location))
1457
 
            if tree.get_parent_ids()[1:] != existing_pending_merges:
1458
 
                note('Your local commits will now show as pending merges with '
1459
 
                     "'bzr status', and can be committed with 'bzr commit'.")
1460
 
            if conflicts != 0:
1461
 
                return 1
1462
 
            else:
1463
 
                return 0
1464
 
        finally:
1465
 
            tree.unlock()
 
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
1466
1445
 
1467
1446
 
1468
1447
class cmd_info(Command):
1539
1518
            file_list = [f for f in file_list]
1540
1519
 
1541
1520
        tree.lock_write()
1542
 
        try:
1543
 
            # Heuristics should probably all move into tree.remove_smart or
1544
 
            # some such?
1545
 
            if new:
1546
 
                added = tree.changes_from(tree.basis_tree(),
1547
 
                    specific_files=file_list).added
1548
 
                file_list = sorted([f[0] for f in added], reverse=True)
1549
 
                if len(file_list) == 0:
1550
 
                    raise errors.BzrCommandError('No matching files.')
1551
 
            elif file_list is None:
1552
 
                # missing files show up in iter_changes(basis) as
1553
 
                # versioned-with-no-kind.
1554
 
                missing = []
1555
 
                for change in tree.iter_changes(tree.basis_tree()):
1556
 
                    # Find paths in the working tree that have no kind:
1557
 
                    if change[1][1] is not None and change[6][1] is None:
1558
 
                        missing.append(change[1][1])
1559
 
                file_list = sorted(missing, reverse=True)
1560
 
                file_deletion_strategy = 'keep'
1561
 
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
1562
 
                keep_files=file_deletion_strategy=='keep',
1563
 
                force=file_deletion_strategy=='force')
1564
 
        finally:
1565
 
            tree.unlock()
 
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')
1566
1543
 
1567
1544
 
1568
1545
class cmd_file_id(Command):
1994
1971
    def run(self, show_ids=False):
1995
1972
        tree = WorkingTree.open_containing(u'.')[0]
1996
1973
        tree.lock_read()
1997
 
        try:
1998
 
            old = tree.basis_tree()
1999
 
            old.lock_read()
2000
 
            try:
2001
 
                for path, ie in old.inventory.iter_entries():
2002
 
                    if not tree.has_id(ie.file_id):
2003
 
                        self.outf.write(path)
2004
 
                        if show_ids:
2005
 
                            self.outf.write(' ')
2006
 
                            self.outf.write(ie.file_id)
2007
 
                        self.outf.write('\n')
2008
 
            finally:
2009
 
                old.unlock()
2010
 
        finally:
2011
 
            tree.unlock()
 
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')
2012
1985
 
2013
1986
 
2014
1987
class cmd_modified(Command):
2050
2023
    def run(self, null=False):
2051
2024
        wt = WorkingTree.open_containing(u'.')[0]
2052
2025
        wt.lock_read()
2053
 
        try:
2054
 
            basis = wt.basis_tree()
2055
 
            basis.lock_read()
2056
 
            try:
2057
 
                basis_inv = basis.inventory
2058
 
                inv = wt.inventory
2059
 
                for file_id in inv:
2060
 
                    if file_id in basis_inv:
2061
 
                        continue
2062
 
                    if inv.is_root(file_id) and len(basis_inv) == 0:
2063
 
                        continue
2064
 
                    path = inv.id2path(file_id)
2065
 
                    if not os.access(osutils.abspath(path), os.F_OK):
2066
 
                        continue
2067
 
                    if null:
2068
 
                        self.outf.write(path + '\0')
2069
 
                    else:
2070
 
                        self.outf.write(osutils.quotefn(path) + '\n')
2071
 
            finally:
2072
 
                basis.unlock()
2073
 
        finally:
2074
 
            wt.unlock()
 
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')
2075
2044
 
2076
2045
 
2077
2046
class cmd_root(Command):
2335
2304
 
2336
2305
        file_ids = []
2337
2306
        filter_by_dir = False
2338
 
        b = None
2339
 
        try:
2340
 
            if file_list:
2341
 
                # find the file ids to log and check for directory filtering
2342
 
                b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2343
 
                    revision, file_list)
2344
 
                for relpath, file_id, kind in file_info_list:
2345
 
                    if file_id is None:
2346
 
                        raise errors.BzrCommandError(
2347
 
                            "Path unknown at end or start of revision range: %s" %
2348
 
                            relpath)
2349
 
                    # If the relpath is the top of the tree, we log everything
2350
 
                    if relpath == '':
2351
 
                        file_ids = []
2352
 
                        break
2353
 
                    else:
2354
 
                        file_ids.append(file_id)
2355
 
                    filter_by_dir = filter_by_dir or (
2356
 
                        kind in ['directory', 'tree-reference'])
2357
 
            else:
2358
 
                # log everything
2359
 
                # FIXME ? log the current subdir only RBC 20060203
2360
 
                if revision is not None \
2361
 
                        and len(revision) > 0 and revision[0].get_branch():
2362
 
                    location = revision[0].get_branch()
 
2307
        if file_list:
 
2308
            # find the file ids to log and check for directory filtering
 
2309
            b, file_info_list, rev1, rev2 = _get_info_for_log_files(
 
2310
                revision, file_list)
 
2311
            self.add_cleanup(b.unlock)
 
2312
            for relpath, file_id, kind in file_info_list:
 
2313
                if file_id is None:
 
2314
                    raise errors.BzrCommandError(
 
2315
                        "Path unknown at end or start of revision range: %s" %
 
2316
                        relpath)
 
2317
                # If the relpath is the top of the tree, we log everything
 
2318
                if relpath == '':
 
2319
                    file_ids = []
 
2320
                    break
2363
2321
                else:
2364
 
                    location = '.'
2365
 
                dir, relpath = bzrdir.BzrDir.open_containing(location)
2366
 
                b = dir.open_branch()
2367
 
                b.lock_read()
2368
 
                rev1, rev2 = _get_revision_range(revision, b, self.name())
2369
 
 
2370
 
            # Decide on the type of delta & diff filtering to use
2371
 
            # TODO: add an --all-files option to make this configurable & consistent
2372
 
            if not verbose:
2373
 
                delta_type = None
2374
 
            else:
2375
 
                delta_type = 'full'
2376
 
            if not show_diff:
2377
 
                diff_type = None
2378
 
            elif file_ids:
2379
 
                diff_type = 'partial'
2380
 
            else:
2381
 
                diff_type = 'full'
2382
 
 
2383
 
            # Build the log formatter
2384
 
            if log_format is None:
2385
 
                log_format = log.log_formatter_registry.get_default(b)
2386
 
            # Make a non-encoding output to include the diffs - bug 328007
2387
 
            unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2388
 
            lf = log_format(show_ids=show_ids, to_file=self.outf,
2389
 
                            to_exact_file=unencoded_output,
2390
 
                            show_timezone=timezone,
2391
 
                            delta_format=get_verbosity_level(),
2392
 
                            levels=levels,
2393
 
                            show_advice=levels is None)
2394
 
 
2395
 
            # Choose the algorithm for doing the logging. It's annoying
2396
 
            # having multiple code paths like this but necessary until
2397
 
            # the underlying repository format is faster at generating
2398
 
            # deltas or can provide everything we need from the indices.
2399
 
            # The default algorithm - match-using-deltas - works for
2400
 
            # multiple files and directories and is faster for small
2401
 
            # amounts of history (200 revisions say). However, it's too
2402
 
            # slow for logging a single file in a repository with deep
2403
 
            # history, i.e. > 10K revisions. In the spirit of "do no
2404
 
            # evil when adding features", we continue to use the
2405
 
            # original algorithm - per-file-graph - for the "single
2406
 
            # file that isn't a directory without showing a delta" case.
2407
 
            partial_history = revision and b.repository._format.supports_chks
2408
 
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2409
 
                or delta_type or partial_history)
2410
 
 
2411
 
            # Build the LogRequest and execute it
2412
 
            if len(file_ids) == 0:
2413
 
                file_ids = None
2414
 
            rqst = make_log_request_dict(
2415
 
                direction=direction, specific_fileids=file_ids,
2416
 
                start_revision=rev1, end_revision=rev2, limit=limit,
2417
 
                message_search=message, delta_type=delta_type,
2418
 
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
2419
 
            Logger(b, rqst).show(lf)
2420
 
        finally:
2421
 
            if b is not None:
2422
 
                b.unlock()
 
2322
                    file_ids.append(file_id)
 
2323
                filter_by_dir = filter_by_dir or (
 
2324
                    kind in ['directory', 'tree-reference'])
 
2325
        else:
 
2326
            # log everything
 
2327
            # FIXME ? log the current subdir only RBC 20060203
 
2328
            if revision is not None \
 
2329
                    and len(revision) > 0 and revision[0].get_branch():
 
2330
                location = revision[0].get_branch()
 
2331
            else:
 
2332
                location = '.'
 
2333
            dir, relpath = bzrdir.BzrDir.open_containing(location)
 
2334
            b = dir.open_branch()
 
2335
            b.lock_read()
 
2336
            self.add_cleanup(b.unlock)
 
2337
            rev1, rev2 = _get_revision_range(revision, b, self.name())
 
2338
 
 
2339
        # Decide on the type of delta & diff filtering to use
 
2340
        # TODO: add an --all-files option to make this configurable & consistent
 
2341
        if not verbose:
 
2342
            delta_type = None
 
2343
        else:
 
2344
            delta_type = 'full'
 
2345
        if not show_diff:
 
2346
            diff_type = None
 
2347
        elif file_ids:
 
2348
            diff_type = 'partial'
 
2349
        else:
 
2350
            diff_type = 'full'
 
2351
 
 
2352
        # Build the log formatter
 
2353
        if log_format is None:
 
2354
            log_format = log.log_formatter_registry.get_default(b)
 
2355
        # Make a non-encoding output to include the diffs - bug 328007
 
2356
        unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
 
2357
        lf = log_format(show_ids=show_ids, to_file=self.outf,
 
2358
                        to_exact_file=unencoded_output,
 
2359
                        show_timezone=timezone,
 
2360
                        delta_format=get_verbosity_level(),
 
2361
                        levels=levels,
 
2362
                        show_advice=levels is None)
 
2363
 
 
2364
        # Choose the algorithm for doing the logging. It's annoying
 
2365
        # having multiple code paths like this but necessary until
 
2366
        # the underlying repository format is faster at generating
 
2367
        # deltas or can provide everything we need from the indices.
 
2368
        # The default algorithm - match-using-deltas - works for
 
2369
        # multiple files and directories and is faster for small
 
2370
        # amounts of history (200 revisions say). However, it's too
 
2371
        # slow for logging a single file in a repository with deep
 
2372
        # history, i.e. > 10K revisions. In the spirit of "do no
 
2373
        # evil when adding features", we continue to use the
 
2374
        # original algorithm - per-file-graph - for the "single
 
2375
        # file that isn't a directory without showing a delta" case.
 
2376
        partial_history = revision and b.repository._format.supports_chks
 
2377
        match_using_deltas = (len(file_ids) != 1 or filter_by_dir
 
2378
            or delta_type or partial_history)
 
2379
 
 
2380
        # Build the LogRequest and execute it
 
2381
        if len(file_ids) == 0:
 
2382
            file_ids = None
 
2383
        rqst = make_log_request_dict(
 
2384
            direction=direction, specific_fileids=file_ids,
 
2385
            start_revision=rev1, end_revision=rev2, limit=limit,
 
2386
            message_search=message, delta_type=delta_type,
 
2387
            diff_type=diff_type, _match_using_deltas=match_using_deltas)
 
2388
        Logger(b, rqst).show(lf)
2423
2389
 
2424
2390
 
2425
2391
def _get_revision_range(revisionspec_list, branch, command_name):
2492
2458
        file_id = tree.path2id(relpath)
2493
2459
        b = tree.branch
2494
2460
        b.lock_read()
2495
 
        try:
2496
 
            touching_revs = log.find_touching_revisions(b, file_id)
2497
 
            for revno, revision_id, what in touching_revs:
2498
 
                self.outf.write("%6d %s\n" % (revno, what))
2499
 
        finally:
2500
 
            b.unlock()
 
2461
        self.add_cleanup(b.unlock)
 
2462
        touching_revs = log.find_touching_revisions(b, file_id)
 
2463
        for revno, revision_id, what in touching_revs:
 
2464
            self.outf.write("%6d %s\n" % (revno, what))
2501
2465
 
2502
2466
 
2503
2467
class cmd_ls(Command):
2570
2534
                note("Ignoring files outside view. View is %s" % view_str)
2571
2535
 
2572
2536
        tree.lock_read()
2573
 
        try:
2574
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2575
 
                from_dir=relpath, recursive=recursive):
2576
 
                # Apply additional masking
2577
 
                if not all and not selection[fc]:
2578
 
                    continue
2579
 
                if kind is not None and fkind != kind:
2580
 
                    continue
2581
 
                if apply_view:
2582
 
                    try:
2583
 
                        if relpath:
2584
 
                            fullpath = osutils.pathjoin(relpath, fp)
2585
 
                        else:
2586
 
                            fullpath = fp
2587
 
                        views.check_path_in_view(tree, fullpath)
2588
 
                    except errors.FileOutsideView:
2589
 
                        continue
 
2537
        self.add_cleanup(tree.unlock)
 
2538
        for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
 
2539
            from_dir=relpath, recursive=recursive):
 
2540
            # Apply additional masking
 
2541
            if not all and not selection[fc]:
 
2542
                continue
 
2543
            if kind is not None and fkind != kind:
 
2544
                continue
 
2545
            if apply_view:
 
2546
                try:
 
2547
                    if relpath:
 
2548
                        fullpath = osutils.pathjoin(relpath, fp)
 
2549
                    else:
 
2550
                        fullpath = fp
 
2551
                    views.check_path_in_view(tree, fullpath)
 
2552
                except errors.FileOutsideView:
 
2553
                    continue
2590
2554
 
2591
 
                # Output the entry
2592
 
                if prefix:
2593
 
                    fp = osutils.pathjoin(prefix, fp)
2594
 
                kindch = entry.kind_character()
2595
 
                outstring = fp + kindch
2596
 
                ui.ui_factory.clear_term()
2597
 
                if verbose:
2598
 
                    outstring = '%-8s %s' % (fc, outstring)
2599
 
                    if show_ids and fid is not None:
2600
 
                        outstring = "%-50s %s" % (outstring, fid)
 
2555
            # Output the entry
 
2556
            if prefix:
 
2557
                fp = osutils.pathjoin(prefix, fp)
 
2558
            kindch = entry.kind_character()
 
2559
            outstring = fp + kindch
 
2560
            ui.ui_factory.clear_term()
 
2561
            if verbose:
 
2562
                outstring = '%-8s %s' % (fc, outstring)
 
2563
                if show_ids and fid is not None:
 
2564
                    outstring = "%-50s %s" % (outstring, fid)
 
2565
                self.outf.write(outstring + '\n')
 
2566
            elif null:
 
2567
                self.outf.write(fp + '\0')
 
2568
                if show_ids:
 
2569
                    if fid is not None:
 
2570
                        self.outf.write(fid)
 
2571
                    self.outf.write('\0')
 
2572
                self.outf.flush()
 
2573
            else:
 
2574
                if show_ids:
 
2575
                    if fid is not None:
 
2576
                        my_id = fid
 
2577
                    else:
 
2578
                        my_id = ''
 
2579
                    self.outf.write('%-50s %s\n' % (outstring, my_id))
 
2580
                else:
2601
2581
                    self.outf.write(outstring + '\n')
2602
 
                elif null:
2603
 
                    self.outf.write(fp + '\0')
2604
 
                    if show_ids:
2605
 
                        if fid is not None:
2606
 
                            self.outf.write(fid)
2607
 
                        self.outf.write('\0')
2608
 
                    self.outf.flush()
2609
 
                else:
2610
 
                    if show_ids:
2611
 
                        if fid is not None:
2612
 
                            my_id = fid
2613
 
                        else:
2614
 
                            my_id = ''
2615
 
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
2616
 
                    else:
2617
 
                        self.outf.write(outstring + '\n')
2618
 
        finally:
2619
 
            tree.unlock()
2620
2582
 
2621
2583
 
2622
2584
class cmd_unknowns(Command):
2734
2696
    def run(self):
2735
2697
        tree = WorkingTree.open_containing(u'.')[0]
2736
2698
        tree.lock_read()
2737
 
        try:
2738
 
            for path, file_class, kind, file_id, entry in tree.list_files():
2739
 
                if file_class != 'I':
2740
 
                    continue
2741
 
                ## XXX: Slightly inefficient since this was already calculated
2742
 
                pat = tree.is_ignored(path)
2743
 
                self.outf.write('%-50s %s\n' % (path, pat))
2744
 
        finally:
2745
 
            tree.unlock()
 
2699
        self.add_cleanup(tree.unlock)
 
2700
        for path, file_class, kind, file_id, entry in tree.list_files():
 
2701
            if file_class != 'I':
 
2702
                continue
 
2703
            ## XXX: Slightly inefficient since this was already calculated
 
2704
            pat = tree.is_ignored(path)
 
2705
            self.outf.write('%-50s %s\n' % (path, pat))
2746
2706
 
2747
2707
 
2748
2708
class cmd_lookup_revision(Command):
2851
2811
        tree, branch, relpath = \
2852
2812
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2853
2813
        branch.lock_read()
2854
 
        try:
2855
 
            return self._run(tree, branch, relpath, filename, revision,
2856
 
                             name_from_revision, filters)
2857
 
        finally:
2858
 
            branch.unlock()
 
2814
        self.add_cleanup(branch.unlock)
 
2815
        return self._run(tree, branch, relpath, filename, revision,
 
2816
                         name_from_revision, filters)
2859
2817
 
2860
2818
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2861
2819
        filtered):
2862
2820
        if tree is None:
2863
2821
            tree = b.basis_tree()
2864
2822
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
 
2823
        rev_tree.lock_read()
 
2824
        self.add_cleanup(rev_tree.unlock)
2865
2825
 
2866
2826
        old_file_id = rev_tree.path2id(relpath)
2867
2827
 
2902
2862
            chunks = content.splitlines(True)
2903
2863
            content = filtered_output_bytes(chunks, filters,
2904
2864
                ContentFilterContext(relpath, rev_tree))
 
2865
            self.cleanup_now()
2905
2866
            self.outf.writelines(content)
2906
2867
        else:
 
2868
            self.cleanup_now()
2907
2869
            self.outf.write(content)
2908
2870
 
2909
2871
 
3570
3532
            verbose = not is_quiet()
3571
3533
            # TODO: should possibly lock the history file...
3572
3534
            benchfile = open(".perf_history", "at", buffering=1)
 
3535
            self.add_cleanup(benchfile.close)
3573
3536
        else:
3574
3537
            test_suite_factory = None
3575
3538
            benchfile = None
3576
 
        try:
3577
 
            selftest_kwargs = {"verbose": verbose,
3578
 
                              "pattern": pattern,
3579
 
                              "stop_on_failure": one,
3580
 
                              "transport": transport,
3581
 
                              "test_suite_factory": test_suite_factory,
3582
 
                              "lsprof_timed": lsprof_timed,
3583
 
                              "lsprof_tests": lsprof_tests,
3584
 
                              "bench_history": benchfile,
3585
 
                              "matching_tests_first": first,
3586
 
                              "list_only": list_only,
3587
 
                              "random_seed": randomize,
3588
 
                              "exclude_pattern": exclude,
3589
 
                              "strict": strict,
3590
 
                              "load_list": load_list,
3591
 
                              "debug_flags": debugflag,
3592
 
                              "starting_with": starting_with
3593
 
                              }
3594
 
            selftest_kwargs.update(self.additional_selftest_args)
3595
 
            result = selftest(**selftest_kwargs)
3596
 
        finally:
3597
 
            if benchfile is not None:
3598
 
                benchfile.close()
 
3539
        selftest_kwargs = {"verbose": verbose,
 
3540
                          "pattern": pattern,
 
3541
                          "stop_on_failure": one,
 
3542
                          "transport": transport,
 
3543
                          "test_suite_factory": test_suite_factory,
 
3544
                          "lsprof_timed": lsprof_timed,
 
3545
                          "lsprof_tests": lsprof_tests,
 
3546
                          "bench_history": benchfile,
 
3547
                          "matching_tests_first": first,
 
3548
                          "list_only": list_only,
 
3549
                          "random_seed": randomize,
 
3550
                          "exclude_pattern": exclude,
 
3551
                          "strict": strict,
 
3552
                          "load_list": load_list,
 
3553
                          "debug_flags": debugflag,
 
3554
                          "starting_with": starting_with
 
3555
                          }
 
3556
        selftest_kwargs.update(self.additional_selftest_args)
 
3557
        result = selftest(**selftest_kwargs)
3599
3558
        return int(not result)
3600
3559
 
3601
3560
 
3640
3599
        branch1 = Branch.open_containing(branch)[0]
3641
3600
        branch2 = Branch.open_containing(other)[0]
3642
3601
        branch1.lock_read()
3643
 
        try:
3644
 
            branch2.lock_read()
3645
 
            try:
3646
 
                last1 = ensure_null(branch1.last_revision())
3647
 
                last2 = ensure_null(branch2.last_revision())
3648
 
 
3649
 
                graph = branch1.repository.get_graph(branch2.repository)
3650
 
                base_rev_id = graph.find_unique_lca(last1, last2)
3651
 
 
3652
 
                print 'merge base is revision %s' % base_rev_id
3653
 
            finally:
3654
 
                branch2.unlock()
3655
 
        finally:
3656
 
            branch1.unlock()
 
3602
        self.add_cleanup(branch1.unlock)
 
3603
        branch2.lock_read()
 
3604
        self.add_cleanup(branch2.unlock)
 
3605
        last1 = ensure_null(branch1.last_revision())
 
3606
        last2 = ensure_null(branch2.last_revision())
 
3607
 
 
3608
        graph = branch1.repository.get_graph(branch2.repository)
 
3609
        base_rev_id = graph.find_unique_lca(last1, last2)
 
3610
 
 
3611
        print 'merge base is revision %s' % base_rev_id
3657
3612
 
3658
3613
 
3659
3614
class cmd_merge(Command):
3776
3731
        view_info = _get_view_info_for_change_reporter(tree)
3777
3732
        change_reporter = delta._ChangeReporter(
3778
3733
            unversioned_filter=tree.is_ignored, view_info=view_info)
3779
 
        cleanups = []
3780
 
        try:
3781
 
            pb = ui.ui_factory.nested_progress_bar()
3782
 
            cleanups.append(pb.finished)
3783
 
            tree.lock_write()
3784
 
            cleanups.append(tree.unlock)
3785
 
            if location is not None:
3786
 
                try:
3787
 
                    mergeable = bundle.read_mergeable_from_url(location,
3788
 
                        possible_transports=possible_transports)
3789
 
                except errors.NotABundle:
3790
 
                    mergeable = None
3791
 
                else:
3792
 
                    if uncommitted:
3793
 
                        raise errors.BzrCommandError('Cannot use --uncommitted'
3794
 
                            ' with bundles or merge directives.')
3795
 
 
3796
 
                    if revision is not None:
3797
 
                        raise errors.BzrCommandError(
3798
 
                            'Cannot use -r with merge directives or bundles')
3799
 
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
3800
 
                       mergeable, pb)
3801
 
 
3802
 
            if merger is None and uncommitted:
3803
 
                if revision is not None and len(revision) > 0:
3804
 
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3805
 
                        ' --revision at the same time.')
3806
 
                merger = self.get_merger_from_uncommitted(tree, location, pb,
3807
 
                                                          cleanups)
3808
 
                allow_pending = False
3809
 
 
3810
 
            if merger is None:
3811
 
                merger, allow_pending = self._get_merger_from_branch(tree,
3812
 
                    location, revision, remember, possible_transports, pb)
3813
 
 
3814
 
            merger.merge_type = merge_type
3815
 
            merger.reprocess = reprocess
3816
 
            merger.show_base = show_base
3817
 
            self.sanity_check_merger(merger)
3818
 
            if (merger.base_rev_id == merger.other_rev_id and
3819
 
                merger.other_rev_id is not None):
3820
 
                note('Nothing to do.')
 
3734
        pb = ui.ui_factory.nested_progress_bar()
 
3735
        self.add_cleanup(pb.finished)
 
3736
        tree.lock_write()
 
3737
        self.add_cleanup(tree.unlock)
 
3738
        if location is not None:
 
3739
            try:
 
3740
                mergeable = bundle.read_mergeable_from_url(location,
 
3741
                    possible_transports=possible_transports)
 
3742
            except errors.NotABundle:
 
3743
                mergeable = None
 
3744
            else:
 
3745
                if uncommitted:
 
3746
                    raise errors.BzrCommandError('Cannot use --uncommitted'
 
3747
                        ' with bundles or merge directives.')
 
3748
 
 
3749
                if revision is not None:
 
3750
                    raise errors.BzrCommandError(
 
3751
                        'Cannot use -r with merge directives or bundles')
 
3752
                merger, verified = _mod_merge.Merger.from_mergeable(tree,
 
3753
                   mergeable, pb)
 
3754
 
 
3755
        if merger is None and uncommitted:
 
3756
            if revision is not None and len(revision) > 0:
 
3757
                raise errors.BzrCommandError('Cannot use --uncommitted and'
 
3758
                    ' --revision at the same time.')
 
3759
            merger = self.get_merger_from_uncommitted(tree, location, pb)
 
3760
            allow_pending = False
 
3761
 
 
3762
        if merger is None:
 
3763
            merger, allow_pending = self._get_merger_from_branch(tree,
 
3764
                location, revision, remember, possible_transports, pb)
 
3765
 
 
3766
        merger.merge_type = merge_type
 
3767
        merger.reprocess = reprocess
 
3768
        merger.show_base = show_base
 
3769
        self.sanity_check_merger(merger)
 
3770
        if (merger.base_rev_id == merger.other_rev_id and
 
3771
            merger.other_rev_id is not None):
 
3772
            note('Nothing to do.')
 
3773
            return 0
 
3774
        if pull:
 
3775
            if merger.interesting_files is not None:
 
3776
                raise errors.BzrCommandError('Cannot pull individual files')
 
3777
            if (merger.base_rev_id == tree.last_revision()):
 
3778
                result = tree.pull(merger.other_branch, False,
 
3779
                                   merger.other_rev_id)
 
3780
                result.report(self.outf)
3821
3781
                return 0
3822
 
            if pull:
3823
 
                if merger.interesting_files is not None:
3824
 
                    raise errors.BzrCommandError('Cannot pull individual files')
3825
 
                if (merger.base_rev_id == tree.last_revision()):
3826
 
                    result = tree.pull(merger.other_branch, False,
3827
 
                                       merger.other_rev_id)
3828
 
                    result.report(self.outf)
3829
 
                    return 0
3830
 
            if merger.this_basis is None:
3831
 
                raise errors.BzrCommandError(
3832
 
                    "This branch has no commits."
3833
 
                    " (perhaps you would prefer 'bzr pull')")
3834
 
            if preview:
3835
 
                return self._do_preview(merger, cleanups)
3836
 
            elif interactive:
3837
 
                return self._do_interactive(merger, cleanups)
3838
 
            else:
3839
 
                return self._do_merge(merger, change_reporter, allow_pending,
3840
 
                                      verified)
3841
 
        finally:
3842
 
            for cleanup in reversed(cleanups):
3843
 
                cleanup()
 
3782
        if merger.this_basis is None:
 
3783
            raise errors.BzrCommandError(
 
3784
                "This branch has no commits."
 
3785
                " (perhaps you would prefer 'bzr pull')")
 
3786
        if preview:
 
3787
            return self._do_preview(merger)
 
3788
        elif interactive:
 
3789
            return self._do_interactive(merger)
 
3790
        else:
 
3791
            return self._do_merge(merger, change_reporter, allow_pending,
 
3792
                                  verified)
3844
3793
 
3845
 
    def _get_preview(self, merger, cleanups):
 
3794
    def _get_preview(self, merger):
3846
3795
        tree_merger = merger.make_merger()
3847
3796
        tt = tree_merger.make_preview_transform()
3848
 
        cleanups.append(tt.finalize)
 
3797
        self.add_cleanup(tt.finalize)
3849
3798
        result_tree = tt.get_preview_tree()
3850
3799
        return result_tree
3851
3800
 
3852
 
    def _do_preview(self, merger, cleanups):
 
3801
    def _do_preview(self, merger):
3853
3802
        from bzrlib.diff import show_diff_trees
3854
 
        result_tree = self._get_preview(merger, cleanups)
 
3803
        result_tree = self._get_preview(merger)
3855
3804
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3856
3805
                        old_label='', new_label='')
3857
3806
 
3867
3816
        else:
3868
3817
            return 0
3869
3818
 
3870
 
    def _do_interactive(self, merger, cleanups):
 
3819
    def _do_interactive(self, merger):
3871
3820
        """Perform an interactive merge.
3872
3821
 
3873
3822
        This works by generating a preview tree of the merge, then using
3875
3824
        and the preview tree.
3876
3825
        """
3877
3826
        from bzrlib import shelf_ui
3878
 
        result_tree = self._get_preview(merger, cleanups)
 
3827
        result_tree = self._get_preview(merger)
3879
3828
        writer = bzrlib.option.diff_writer_registry.get()
3880
3829
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3881
3830
                                   reporter=shelf_ui.ApplyReporter(),
3949
3898
            allow_pending = True
3950
3899
        return merger, allow_pending
3951
3900
 
3952
 
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3901
    def get_merger_from_uncommitted(self, tree, location, pb):
3953
3902
        """Get a merger for uncommitted changes.
3954
3903
 
3955
3904
        :param tree: The tree the merger should apply to.
3956
3905
        :param location: The location containing uncommitted changes.
3957
3906
        :param pb: The progress bar to use for showing progress.
3958
 
        :param cleanups: A list of operations to perform to clean up the
3959
 
            temporary directories, unfinalized objects, etc.
3960
3907
        """
3961
3908
        location = self._select_branch_location(tree, location)[0]
3962
3909
        other_tree, other_path = WorkingTree.open_containing(location)
4049
3996
            merge_type = _mod_merge.Merge3Merger
4050
3997
        tree, file_list = tree_files(file_list)
4051
3998
        tree.lock_write()
4052
 
        try:
4053
 
            parents = tree.get_parent_ids()
4054
 
            if len(parents) != 2:
4055
 
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
4056
 
                                             " merges.  Not cherrypicking or"
4057
 
                                             " multi-merges.")
4058
 
            repository = tree.branch.repository
4059
 
            interesting_ids = None
4060
 
            new_conflicts = []
4061
 
            conflicts = tree.conflicts()
4062
 
            if file_list is not None:
4063
 
                interesting_ids = set()
4064
 
                for filename in file_list:
4065
 
                    file_id = tree.path2id(filename)
4066
 
                    if file_id is None:
4067
 
                        raise errors.NotVersionedError(filename)
4068
 
                    interesting_ids.add(file_id)
4069
 
                    if tree.kind(file_id) != "directory":
4070
 
                        continue
 
3999
        self.add_cleanup(tree.unlock)
 
4000
        parents = tree.get_parent_ids()
 
4001
        if len(parents) != 2:
 
4002
            raise errors.BzrCommandError("Sorry, remerge only works after normal"
 
4003
                                         " merges.  Not cherrypicking or"
 
4004
                                         " multi-merges.")
 
4005
        repository = tree.branch.repository
 
4006
        interesting_ids = None
 
4007
        new_conflicts = []
 
4008
        conflicts = tree.conflicts()
 
4009
        if file_list is not None:
 
4010
            interesting_ids = set()
 
4011
            for filename in file_list:
 
4012
                file_id = tree.path2id(filename)
 
4013
                if file_id is None:
 
4014
                    raise errors.NotVersionedError(filename)
 
4015
                interesting_ids.add(file_id)
 
4016
                if tree.kind(file_id) != "directory":
 
4017
                    continue
4071
4018
 
4072
 
                    for name, ie in tree.inventory.iter_entries(file_id):
4073
 
                        interesting_ids.add(ie.file_id)
4074
 
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4075
 
            else:
4076
 
                # Remerge only supports resolving contents conflicts
4077
 
                allowed_conflicts = ('text conflict', 'contents conflict')
4078
 
                restore_files = [c.path for c in conflicts
4079
 
                                 if c.typestring in allowed_conflicts]
4080
 
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
4081
 
            tree.set_conflicts(ConflictList(new_conflicts))
4082
 
            if file_list is not None:
4083
 
                restore_files = file_list
4084
 
            for filename in restore_files:
4085
 
                try:
4086
 
                    restore(tree.abspath(filename))
4087
 
                except errors.NotConflicted:
4088
 
                    pass
4089
 
            # Disable pending merges, because the file texts we are remerging
4090
 
            # have not had those merges performed.  If we use the wrong parents
4091
 
            # list, we imply that the working tree text has seen and rejected
4092
 
            # all the changes from the other tree, when in fact those changes
4093
 
            # have not yet been seen.
4094
 
            pb = ui.ui_factory.nested_progress_bar()
4095
 
            tree.set_parent_ids(parents[:1])
 
4019
                for name, ie in tree.inventory.iter_entries(file_id):
 
4020
                    interesting_ids.add(ie.file_id)
 
4021
            new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
 
4022
        else:
 
4023
            # Remerge only supports resolving contents conflicts
 
4024
            allowed_conflicts = ('text conflict', 'contents conflict')
 
4025
            restore_files = [c.path for c in conflicts
 
4026
                             if c.typestring in allowed_conflicts]
 
4027
        _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
 
4028
        tree.set_conflicts(ConflictList(new_conflicts))
 
4029
        if file_list is not None:
 
4030
            restore_files = file_list
 
4031
        for filename in restore_files:
4096
4032
            try:
4097
 
                merger = _mod_merge.Merger.from_revision_ids(pb,
4098
 
                                                             tree, parents[1])
4099
 
                merger.interesting_ids = interesting_ids
4100
 
                merger.merge_type = merge_type
4101
 
                merger.show_base = show_base
4102
 
                merger.reprocess = reprocess
4103
 
                conflicts = merger.do_merge()
4104
 
            finally:
4105
 
                tree.set_parent_ids(parents)
4106
 
                pb.finished()
 
4033
                restore(tree.abspath(filename))
 
4034
            except errors.NotConflicted:
 
4035
                pass
 
4036
        # Disable pending merges, because the file texts we are remerging
 
4037
        # have not had those merges performed.  If we use the wrong parents
 
4038
        # list, we imply that the working tree text has seen and rejected
 
4039
        # all the changes from the other tree, when in fact those changes
 
4040
        # have not yet been seen.
 
4041
        pb = ui.ui_factory.nested_progress_bar()
 
4042
        tree.set_parent_ids(parents[:1])
 
4043
        try:
 
4044
            merger = _mod_merge.Merger.from_revision_ids(pb,
 
4045
                                                         tree, parents[1])
 
4046
            merger.interesting_ids = interesting_ids
 
4047
            merger.merge_type = merge_type
 
4048
            merger.show_base = show_base
 
4049
            merger.reprocess = reprocess
 
4050
            conflicts = merger.do_merge()
4107
4051
        finally:
4108
 
            tree.unlock()
 
4052
            tree.set_parent_ids(parents)
 
4053
            pb.finished()
4109
4054
        if conflicts > 0:
4110
4055
            return 1
4111
4056
        else:
4170
4115
            forget_merges=None):
4171
4116
        tree, file_list = tree_files(file_list)
4172
4117
        tree.lock_write()
4173
 
        try:
4174
 
            if forget_merges:
4175
 
                tree.set_parent_ids(tree.get_parent_ids()[:1])
4176
 
            else:
4177
 
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4178
 
        finally:
4179
 
            tree.unlock()
 
4118
        self.add_cleanup(tree.unlock)
 
4119
        if forget_merges:
 
4120
            tree.set_parent_ids(tree.get_parent_ids()[:1])
 
4121
        else:
 
4122
            self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4180
4123
 
4181
4124
    @staticmethod
4182
4125
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4333
4276
        if remote_branch.base == local_branch.base:
4334
4277
            remote_branch = local_branch
4335
4278
 
 
4279
        local_branch.lock_read()
 
4280
        self.add_cleanup(local_branch.unlock)
4336
4281
        local_revid_range = _revision_range_to_revid_range(
4337
4282
            _get_revision_range(my_revision, local_branch,
4338
4283
                self.name()))
4339
4284
 
 
4285
        remote_branch.lock_read()
 
4286
        self.add_cleanup(remote_branch.unlock)
4340
4287
        remote_revid_range = _revision_range_to_revid_range(
4341
4288
            _get_revision_range(revision,
4342
4289
                remote_branch, self.name()))
4343
4290
 
4344
 
        local_branch.lock_read()
4345
 
        try:
4346
 
            remote_branch.lock_read()
4347
 
            try:
4348
 
                local_extra, remote_extra = find_unmerged(
4349
 
                    local_branch, remote_branch, restrict,
4350
 
                    backward=not reverse,
4351
 
                    include_merges=include_merges,
4352
 
                    local_revid_range=local_revid_range,
4353
 
                    remote_revid_range=remote_revid_range)
4354
 
 
4355
 
                if log_format is None:
4356
 
                    registry = log.log_formatter_registry
4357
 
                    log_format = registry.get_default(local_branch)
4358
 
                lf = log_format(to_file=self.outf,
4359
 
                                show_ids=show_ids,
4360
 
                                show_timezone='original')
4361
 
 
4362
 
                status_code = 0
4363
 
                if local_extra and not theirs_only:
4364
 
                    message("You have %d extra revision(s):\n" %
4365
 
                        len(local_extra))
4366
 
                    for revision in iter_log_revisions(local_extra,
4367
 
                                        local_branch.repository,
4368
 
                                        verbose):
4369
 
                        lf.log_revision(revision)
4370
 
                    printed_local = True
4371
 
                    status_code = 1
4372
 
                else:
4373
 
                    printed_local = False
4374
 
 
4375
 
                if remote_extra and not mine_only:
4376
 
                    if printed_local is True:
4377
 
                        message("\n\n\n")
4378
 
                    message("You are missing %d revision(s):\n" %
4379
 
                        len(remote_extra))
4380
 
                    for revision in iter_log_revisions(remote_extra,
4381
 
                                        remote_branch.repository,
4382
 
                                        verbose):
4383
 
                        lf.log_revision(revision)
4384
 
                    status_code = 1
4385
 
 
4386
 
                if mine_only and not local_extra:
4387
 
                    # We checked local, and found nothing extra
4388
 
                    message('This branch is up to date.\n')
4389
 
                elif theirs_only and not remote_extra:
4390
 
                    # We checked remote, and found nothing extra
4391
 
                    message('Other branch is up to date.\n')
4392
 
                elif not (mine_only or theirs_only or local_extra or
4393
 
                          remote_extra):
4394
 
                    # We checked both branches, and neither one had extra
4395
 
                    # revisions
4396
 
                    message("Branches are up to date.\n")
4397
 
            finally:
4398
 
                remote_branch.unlock()
4399
 
        finally:
4400
 
            local_branch.unlock()
 
4291
        local_extra, remote_extra = find_unmerged(
 
4292
            local_branch, remote_branch, restrict,
 
4293
            backward=not reverse,
 
4294
            include_merges=include_merges,
 
4295
            local_revid_range=local_revid_range,
 
4296
            remote_revid_range=remote_revid_range)
 
4297
 
 
4298
        if log_format is None:
 
4299
            registry = log.log_formatter_registry
 
4300
            log_format = registry.get_default(local_branch)
 
4301
        lf = log_format(to_file=self.outf,
 
4302
                        show_ids=show_ids,
 
4303
                        show_timezone='original')
 
4304
 
 
4305
        status_code = 0
 
4306
        if local_extra and not theirs_only:
 
4307
            message("You have %d extra revision(s):\n" %
 
4308
                len(local_extra))
 
4309
            for revision in iter_log_revisions(local_extra,
 
4310
                                local_branch.repository,
 
4311
                                verbose):
 
4312
                lf.log_revision(revision)
 
4313
            printed_local = True
 
4314
            status_code = 1
 
4315
        else:
 
4316
            printed_local = False
 
4317
 
 
4318
        if remote_extra and not mine_only:
 
4319
            if printed_local is True:
 
4320
                message("\n\n\n")
 
4321
            message("You are missing %d revision(s):\n" %
 
4322
                len(remote_extra))
 
4323
            for revision in iter_log_revisions(remote_extra,
 
4324
                                remote_branch.repository,
 
4325
                                verbose):
 
4326
                lf.log_revision(revision)
 
4327
            status_code = 1
 
4328
 
 
4329
        if mine_only and not local_extra:
 
4330
            # We checked local, and found nothing extra
 
4331
            message('This branch is up to date.\n')
 
4332
        elif theirs_only and not remote_extra:
 
4333
            # We checked remote, and found nothing extra
 
4334
            message('Other branch is up to date.\n')
 
4335
        elif not (mine_only or theirs_only or local_extra or
 
4336
                  remote_extra):
 
4337
            # We checked both branches, and neither one had extra
 
4338
            # revisions
 
4339
            message("Branches are up to date.\n")
 
4340
        self.cleanup_now()
4401
4341
        if not status_code and parent is None and other_branch is not None:
4402
4342
            local_branch.lock_write()
4403
 
            try:
4404
 
                # handle race conditions - a parent might be set while we run.
4405
 
                if local_branch.get_parent() is None:
4406
 
                    local_branch.set_parent(remote_branch.base)
4407
 
            finally:
4408
 
                local_branch.unlock()
 
4343
            self.add_cleanup(local_branch.unlock)
 
4344
            # handle race conditions - a parent might be set while we run.
 
4345
            if local_branch.get_parent() is None:
 
4346
                local_branch.set_parent(remote_branch.base)
4409
4347
        return status_code
4410
4348
 
4411
4349
 
4490
4428
        else:
4491
4429
            b = Branch.open(branch)
4492
4430
        b.lock_read()
4493
 
        try:
4494
 
            if revision is None:
4495
 
                rev_id = b.last_revision()
4496
 
            else:
4497
 
                rev_id = revision[0].as_revision_id(b)
4498
 
            t = testament_class.from_revision(b.repository, rev_id)
4499
 
            if long:
4500
 
                sys.stdout.writelines(t.as_text_lines())
4501
 
            else:
4502
 
                sys.stdout.write(t.as_short_text())
4503
 
        finally:
4504
 
            b.unlock()
 
4431
        self.add_cleanup(b.unlock)
 
4432
        if revision is None:
 
4433
            rev_id = b.last_revision()
 
4434
        else:
 
4435
            rev_id = revision[0].as_revision_id(b)
 
4436
        t = testament_class.from_revision(b.repository, rev_id)
 
4437
        if long:
 
4438
            sys.stdout.writelines(t.as_text_lines())
 
4439
        else:
 
4440
            sys.stdout.write(t.as_short_text())
4505
4441
 
4506
4442
 
4507
4443
class cmd_annotate(Command):
4533
4469
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4534
4470
        if wt is not None:
4535
4471
            wt.lock_read()
 
4472
            self.add_cleanup(wt.unlock)
4536
4473
        else:
4537
4474
            branch.lock_read()
4538
 
        try:
4539
 
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
4540
 
            if wt is not None:
4541
 
                file_id = wt.path2id(relpath)
4542
 
            else:
4543
 
                file_id = tree.path2id(relpath)
4544
 
            if file_id is None:
4545
 
                raise errors.NotVersionedError(filename)
4546
 
            file_version = tree.inventory[file_id].revision
4547
 
            if wt is not None and revision is None:
4548
 
                # If there is a tree and we're not annotating historical
4549
 
                # versions, annotate the working tree's content.
4550
 
                annotate_file_tree(wt, file_id, self.outf, long, all,
4551
 
                    show_ids=show_ids)
4552
 
            else:
4553
 
                annotate_file(branch, file_version, file_id, long, all, self.outf,
4554
 
                              show_ids=show_ids)
4555
 
        finally:
4556
 
            if wt is not None:
4557
 
                wt.unlock()
4558
 
            else:
4559
 
                branch.unlock()
 
4475
            self.add_cleanup(branch.unlock)
 
4476
        tree = _get_one_revision_tree('annotate', revision, branch=branch)
 
4477
        tree.lock_read()
 
4478
        self.add_cleanup(tree.unlock)
 
4479
        if wt is not None:
 
4480
            file_id = wt.path2id(relpath)
 
4481
        else:
 
4482
            file_id = tree.path2id(relpath)
 
4483
        if file_id is None:
 
4484
            raise errors.NotVersionedError(filename)
 
4485
        file_version = tree.inventory[file_id].revision
 
4486
        if wt is not None and revision is None:
 
4487
            # If there is a tree and we're not annotating historical
 
4488
            # versions, annotate the working tree's content.
 
4489
            annotate_file_tree(wt, file_id, self.outf, long, all,
 
4490
                show_ids=show_ids)
 
4491
        else:
 
4492
            annotate_file(branch, file_version, file_id, long, all, self.outf,
 
4493
                          show_ids=show_ids)
4560
4494
 
4561
4495
 
4562
4496
class cmd_re_sign(Command):
4574
4508
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4575
4509
        b = WorkingTree.open_containing(u'.')[0].branch
4576
4510
        b.lock_write()
4577
 
        try:
4578
 
            return self._run(b, revision_id_list, revision)
4579
 
        finally:
4580
 
            b.unlock()
 
4511
        self.add_cleanup(b.unlock)
 
4512
        return self._run(b, revision_id_list, revision)
4581
4513
 
4582
4514
    def _run(self, b, revision_id_list, revision):
4583
4515
        import bzrlib.gpg as gpg
4729
4661
 
4730
4662
        if tree is not None:
4731
4663
            tree.lock_write()
 
4664
            self.add_cleanup(tree.unlock)
4732
4665
        else:
4733
4666
            b.lock_write()
4734
 
        try:
4735
 
            return self._run(b, tree, dry_run, verbose, revision, force,
4736
 
                             local=local)
4737
 
        finally:
4738
 
            if tree is not None:
4739
 
                tree.unlock()
4740
 
            else:
4741
 
                b.unlock()
 
4667
            self.add_cleanup(b.unlock)
 
4668
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
4742
4669
 
4743
4670
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4744
4671
        from bzrlib.log import log_formatter, show_log
4801
4728
    CAUTION: Locks should only be broken when you are sure that the process
4802
4729
    holding the lock has been stopped.
4803
4730
 
4804
 
    You can get information on what locks are open via the 'bzr info' command.
 
4731
    You can get information on what locks are open via the 'bzr info
 
4732
    [location]' command.
4805
4733
 
4806
4734
    :Examples:
4807
4735
        bzr break-lock
 
4736
        bzr break-lock bzr+ssh://example.com/bzr/foo
4808
4737
    """
4809
4738
    takes_args = ['location?']
4810
4739
 
5082
5011
      directly from the merge directive, without retrieving data from a
5083
5012
      branch.
5084
5013
 
5085
 
    If --no-bundle is specified, then public_branch is needed (and must be
5086
 
    up-to-date), so that the receiver can perform the merge using the
5087
 
    public_branch.  The public_branch is always included if known, so that
5088
 
    people can check it later.
5089
 
 
5090
 
    The submit branch defaults to the parent, but can be overridden.  Both
5091
 
    submit branch and public branch will be remembered if supplied.
5092
 
 
5093
 
    If a public_branch is known for the submit_branch, that public submit
5094
 
    branch is used in the merge instructions.  This means that a local mirror
5095
 
    can be used as your actual submit branch, once you have set public_branch
5096
 
    for that mirror.
 
5014
    `bzr send` creates a compact data set that, when applied using bzr
 
5015
    merge, has the same effect as merging from the source branch.  
 
5016
    
 
5017
    By default the merge directive is self-contained and can be applied to any
 
5018
    branch containing submit_branch in its ancestory without needing access to
 
5019
    the source branch.
 
5020
    
 
5021
    If --no-bundle is specified, then Bazaar doesn't send the contents of the
 
5022
    revisions, but only a structured request to merge from the
 
5023
    public_location.  In that case the public_branch is needed and it must be
 
5024
    up-to-date and accessible to the recipient.  The public_branch is always
 
5025
    included if known, so that people can check it later.
 
5026
 
 
5027
    The submit branch defaults to the parent of the source branch, but can be
 
5028
    overridden.  Both submit branch and public branch will be remembered in
 
5029
    branch.conf the first time they are used for a particular branch.  The
 
5030
    source branch defaults to that containing the working directory, but can
 
5031
    be changed using --from.
 
5032
 
 
5033
    In order to calculate those changes, bzr must analyse the submit branch.
 
5034
    Therefore it is most efficient for the submit branch to be a local mirror.
 
5035
    If a public location is known for the submit_branch, that location is used
 
5036
    in the merge directive.
 
5037
 
 
5038
    The default behaviour is to send the merge directive by mail, unless -o is
 
5039
    given, in which case it is sent to a file.
5097
5040
 
5098
5041
    Mail is sent using your preferred mail program.  This should be transparent
5099
5042
    on Windows (it uses MAPI).  On Linux, it requires the xdg-email utility.
5119
5062
 
5120
5063
    The merge directives created by bzr send may be applied using bzr merge or
5121
5064
    bzr pull by specifying a file containing a merge directive as the location.
 
5065
 
 
5066
    bzr send makes extensive use of public locations to map local locations into
 
5067
    URLs that can be used by other people.  See `bzr help configuration` to
 
5068
    set them, and use `bzr info` to display them.
5122
5069
    """
5123
5070
 
5124
5071
    encoding_type = 'exact'
5283
5230
            ):
5284
5231
        branch, relpath = Branch.open_containing(directory)
5285
5232
        branch.lock_write()
5286
 
        try:
5287
 
            if delete:
5288
 
                branch.tags.delete_tag(tag_name)
5289
 
                self.outf.write('Deleted tag %s.\n' % tag_name)
 
5233
        self.add_cleanup(branch.unlock)
 
5234
        if delete:
 
5235
            branch.tags.delete_tag(tag_name)
 
5236
            self.outf.write('Deleted tag %s.\n' % tag_name)
 
5237
        else:
 
5238
            if revision:
 
5239
                if len(revision) != 1:
 
5240
                    raise errors.BzrCommandError(
 
5241
                        "Tags can only be placed on a single revision, "
 
5242
                        "not on a range")
 
5243
                revision_id = revision[0].as_revision_id(branch)
5290
5244
            else:
5291
 
                if revision:
5292
 
                    if len(revision) != 1:
5293
 
                        raise errors.BzrCommandError(
5294
 
                            "Tags can only be placed on a single revision, "
5295
 
                            "not on a range")
5296
 
                    revision_id = revision[0].as_revision_id(branch)
5297
 
                else:
5298
 
                    revision_id = branch.last_revision()
5299
 
                if (not force) and branch.tags.has_tag(tag_name):
5300
 
                    raise errors.TagAlreadyExists(tag_name)
5301
 
                branch.tags.set_tag(tag_name, revision_id)
5302
 
                self.outf.write('Created tag %s.\n' % tag_name)
5303
 
        finally:
5304
 
            branch.unlock()
 
5245
                revision_id = branch.last_revision()
 
5246
            if (not force) and branch.tags.has_tag(tag_name):
 
5247
                raise errors.TagAlreadyExists(tag_name)
 
5248
            branch.tags.set_tag(tag_name, revision_id)
 
5249
            self.outf.write('Created tag %s.\n' % tag_name)
5305
5250
 
5306
5251
 
5307
5252
class cmd_tags(Command):
5340
5285
            return
5341
5286
 
5342
5287
        branch.lock_read()
5343
 
        try:
5344
 
            if revision:
5345
 
                graph = branch.repository.get_graph()
5346
 
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5347
 
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5348
 
                # only show revisions between revid1 and revid2 (inclusive)
5349
 
                tags = [(tag, revid) for tag, revid in tags if
5350
 
                    graph.is_between(revid, revid1, revid2)]
5351
 
            if sort == 'alpha':
5352
 
                tags.sort()
5353
 
            elif sort == 'time':
5354
 
                timestamps = {}
5355
 
                for tag, revid in tags:
5356
 
                    try:
5357
 
                        revobj = branch.repository.get_revision(revid)
5358
 
                    except errors.NoSuchRevision:
5359
 
                        timestamp = sys.maxint # place them at the end
5360
 
                    else:
5361
 
                        timestamp = revobj.timestamp
5362
 
                    timestamps[revid] = timestamp
5363
 
                tags.sort(key=lambda x: timestamps[x[1]])
5364
 
            if not show_ids:
5365
 
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5366
 
                for index, (tag, revid) in enumerate(tags):
5367
 
                    try:
5368
 
                        revno = branch.revision_id_to_dotted_revno(revid)
5369
 
                        if isinstance(revno, tuple):
5370
 
                            revno = '.'.join(map(str, revno))
5371
 
                    except errors.NoSuchRevision:
5372
 
                        # Bad tag data/merges can lead to tagged revisions
5373
 
                        # which are not in this branch. Fail gracefully ...
5374
 
                        revno = '?'
5375
 
                    tags[index] = (tag, revno)
5376
 
        finally:
5377
 
            branch.unlock()
 
5288
        self.add_cleanup(branch.unlock)
 
5289
        if revision:
 
5290
            graph = branch.repository.get_graph()
 
5291
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5292
            revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5293
            # only show revisions between revid1 and revid2 (inclusive)
 
5294
            tags = [(tag, revid) for tag, revid in tags if
 
5295
                graph.is_between(revid, revid1, revid2)]
 
5296
        if sort == 'alpha':
 
5297
            tags.sort()
 
5298
        elif sort == 'time':
 
5299
            timestamps = {}
 
5300
            for tag, revid in tags:
 
5301
                try:
 
5302
                    revobj = branch.repository.get_revision(revid)
 
5303
                except errors.NoSuchRevision:
 
5304
                    timestamp = sys.maxint # place them at the end
 
5305
                else:
 
5306
                    timestamp = revobj.timestamp
 
5307
                timestamps[revid] = timestamp
 
5308
            tags.sort(key=lambda x: timestamps[x[1]])
 
5309
        if not show_ids:
 
5310
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5311
            for index, (tag, revid) in enumerate(tags):
 
5312
                try:
 
5313
                    revno = branch.revision_id_to_dotted_revno(revid)
 
5314
                    if isinstance(revno, tuple):
 
5315
                        revno = '.'.join(map(str, revno))
 
5316
                except errors.NoSuchRevision:
 
5317
                    # Bad tag data/merges can lead to tagged revisions
 
5318
                    # which are not in this branch. Fail gracefully ...
 
5319
                    revno = '?'
 
5320
                tags[index] = (tag, revno)
 
5321
        self.cleanup_now()
5378
5322
        for tag, revspec in tags:
5379
5323
            self.outf.write('%-20s %s\n' % (tag, revspec))
5380
5324
 
5493
5437
    that of the master.
5494
5438
    """
5495
5439
 
5496
 
    takes_args = ['to_location']
 
5440
    takes_args = ['to_location?']
5497
5441
    takes_options = [Option('force',
5498
5442
                        help='Switch even if local commits will be lost.'),
 
5443
                     'revision',
5499
5444
                     Option('create-branch', short_name='b',
5500
5445
                        help='Create the target branch from this one before'
5501
5446
                             ' switching to it.'),
5502
 
                     ]
 
5447
                    ]
5503
5448
 
5504
 
    def run(self, to_location, force=False, create_branch=False):
 
5449
    def run(self, to_location=None, force=False, create_branch=False,
 
5450
            revision=None):
5505
5451
        from bzrlib import switch
5506
5452
        tree_location = '.'
 
5453
        revision = _get_one_revision('switch', revision)
5507
5454
        control_dir = bzrdir.BzrDir.open_containing(tree_location)[0]
 
5455
        if to_location is None:
 
5456
            if revision is None:
 
5457
                raise errors.BzrCommandError('You must supply either a'
 
5458
                                             ' revision or a location')
 
5459
            to_location = '.'
5508
5460
        try:
5509
5461
            branch = control_dir.open_branch()
5510
5462
            had_explicit_nick = branch.get_config().has_explicit_nickname()
5524
5476
            to_branch = branch.bzrdir.sprout(to_location,
5525
5477
                                 possible_transports=[branch.bzrdir.root_transport],
5526
5478
                                 source_branch=branch).open_branch()
5527
 
            # try:
5528
 
            #     from_branch = control_dir.open_branch()
5529
 
            # except errors.NotBranchError:
5530
 
            #     raise BzrCommandError('Cannot create a branch from this'
5531
 
            #         ' location when we cannot open this branch')
5532
 
            # from_branch.bzrdir.sprout(
5533
 
            pass
5534
5479
        else:
5535
5480
            try:
5536
5481
                to_branch = Branch.open(to_location)
5538
5483
                this_url = self._get_branch_location(control_dir)
5539
5484
                to_branch = Branch.open(
5540
5485
                    urlutils.join(this_url, '..', to_location))
5541
 
        switch.switch(control_dir, to_branch, force)
 
5486
        if revision is not None:
 
5487
            revision = revision.as_revision_id(to_branch)
 
5488
        switch.switch(control_dir, to_branch, force, revision_id=revision)
5542
5489
        if had_explicit_nick:
5543
5490
            branch = control_dir.open_branch() #get the new branch!
5544
5491
            branch.nick = to_branch.nick
5798
5745
    def run_for_list(self):
5799
5746
        tree = WorkingTree.open_containing('.')[0]
5800
5747
        tree.lock_read()
5801
 
        try:
5802
 
            manager = tree.get_shelf_manager()
5803
 
            shelves = manager.active_shelves()
5804
 
            if len(shelves) == 0:
5805
 
                note('No shelved changes.')
5806
 
                return 0
5807
 
            for shelf_id in reversed(shelves):
5808
 
                message = manager.get_metadata(shelf_id).get('message')
5809
 
                if message is None:
5810
 
                    message = '<no message>'
5811
 
                self.outf.write('%3d: %s\n' % (shelf_id, message))
5812
 
            return 1
5813
 
        finally:
5814
 
            tree.unlock()
 
5748
        self.add_cleanup(tree.unlock)
 
5749
        manager = tree.get_shelf_manager()
 
5750
        shelves = manager.active_shelves()
 
5751
        if len(shelves) == 0:
 
5752
            note('No shelved changes.')
 
5753
            return 0
 
5754
        for shelf_id in reversed(shelves):
 
5755
            message = manager.get_metadata(shelf_id).get('message')
 
5756
            if message is None:
 
5757
                message = '<no message>'
 
5758
            self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5759
        return 1
5815
5760
 
5816
5761
 
5817
5762
class cmd_unshelve(Command):
5829
5774
            enum_switch=False, value_switches=True,
5830
5775
            apply="Apply changes and remove from the shelf.",
5831
5776
            dry_run="Show changes, but do not apply or remove them.",
 
5777
            preview="Instead of unshelving the changes, show the diff that "
 
5778
                    "would result from unshelving.",
5832
5779
            delete_only="Delete changes without applying them.",
5833
5780
            keep="Apply changes but don't delete them.",
5834
5781
        )