/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

  • Committer: John Arbash Meinel
  • Date: 2010-01-12 22:36:23 UTC
  • mfrom: (4953 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4955.
  • Revision ID: john@arbash-meinel.com-20100112223623-836x5mou0gm5vsep
merge bzr.dev 4953 to clean up the ui factory issues

Show diffs side-by-side

added added

removed removed

Lines of Context:
31
31
    bundle,
32
32
    btree_index,
33
33
    bzrdir,
 
34
    directory_service,
34
35
    delta,
35
36
    config,
36
37
    errors,
501
502
                wt.lock_read()
502
503
            except (errors.NoWorkingTree, errors.NotLocalUrl):
503
504
                raise errors.NoWorkingTree(location)
 
505
            self.add_cleanup(wt.unlock)
 
506
            revid = wt.last_revision()
504
507
            try:
505
 
                revid = wt.last_revision()
506
 
                try:
507
 
                    revno_t = wt.branch.revision_id_to_dotted_revno(revid)
508
 
                except errors.NoSuchRevision:
509
 
                    revno_t = ('???',)
510
 
                revno = ".".join(str(n) for n in revno_t)
511
 
            finally:
512
 
                wt.unlock()
 
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)
513
512
        else:
514
513
            b = Branch.open_containing(location)[0]
515
514
            b.lock_read()
516
 
            try:
517
 
                revno = b.revno()
518
 
            finally:
519
 
                b.unlock()
520
 
 
 
515
            self.add_cleanup(b.unlock)
 
516
            revno = b.revno()
 
517
        self.cleanup_now()
521
518
        self.outf.write(str(revno) + '\n')
522
519
 
523
520
 
545
542
            wt = WorkingTree.open_containing(directory)[0]
546
543
            b = wt.branch
547
544
            wt.lock_read()
 
545
            self.add_cleanup(wt.unlock)
548
546
        except (errors.NoWorkingTree, errors.NotLocalUrl):
549
547
            wt = None
550
548
            b = Branch.open_containing(directory)[0]
551
549
            b.lock_read()
552
 
        try:
553
 
            revision_ids = []
554
 
            if revision is not None:
555
 
                revision_ids.extend(rev.as_revision_id(b) for rev in revision)
556
 
            if revision_info_list is not None:
557
 
                for rev_str in revision_info_list:
558
 
                    rev_spec = RevisionSpec.from_string(rev_str)
559
 
                    revision_ids.append(rev_spec.as_revision_id(b))
560
 
            # No arguments supplied, default to the last revision
561
 
            if len(revision_ids) == 0:
562
 
                if tree:
563
 
                    if wt is None:
564
 
                        raise errors.NoWorkingTree(directory)
565
 
                    revision_ids.append(wt.last_revision())
566
 
                else:
567
 
                    revision_ids.append(b.last_revision())
568
 
 
569
 
            revinfos = []
570
 
            maxlen = 0
571
 
            for revision_id in revision_ids:
572
 
                try:
573
 
                    dotted_revno = b.revision_id_to_dotted_revno(revision_id)
574
 
                    revno = '.'.join(str(i) for i in dotted_revno)
575
 
                except errors.NoSuchRevision:
576
 
                    revno = '???'
577
 
                maxlen = max(maxlen, len(revno))
578
 
                revinfos.append([revno, revision_id])
579
 
        finally:
580
 
            if wt is None:
581
 
                b.unlock()
 
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())
582
564
            else:
583
 
                wt.unlock()
584
 
 
 
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()
585
579
        for ri in revinfos:
586
580
            self.outf.write('%*s %s\n' % (maxlen, ri[0], ri[1]))
587
581
 
659
653
 
660
654
        if base_tree:
661
655
            base_tree.lock_read()
662
 
        try:
663
 
            tree, file_list = tree_files_for_add(file_list)
664
 
            added, ignored = tree.smart_add(file_list, not
665
 
                no_recurse, action=action, save=not dry_run)
666
 
        finally:
667
 
            if base_tree is not None:
668
 
                base_tree.unlock()
 
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()
669
661
        if len(ignored) > 0:
670
662
            if verbose:
671
663
                for glob in sorted(ignored.keys()):
735
727
        revision = _get_one_revision('inventory', revision)
736
728
        work_tree, file_list = tree_files(file_list)
737
729
        work_tree.lock_read()
738
 
        try:
739
 
            if revision is not None:
740
 
                tree = revision.as_tree(work_tree.branch)
741
 
 
742
 
                extra_trees = [work_tree]
743
 
                tree.lock_read()
744
 
            else:
745
 
                tree = work_tree
746
 
                extra_trees = []
747
 
 
748
 
            if file_list is not None:
749
 
                file_ids = tree.paths2ids(file_list, trees=extra_trees,
750
 
                                          require_versioned=True)
751
 
                # find_ids_across_trees may include some paths that don't
752
 
                # exist in 'tree'.
753
 
                entries = sorted((tree.id2path(file_id), tree.inventory[file_id])
754
 
                                 for file_id in file_ids if file_id in tree)
755
 
            else:
756
 
                entries = tree.inventory.entries()
757
 
        finally:
758
 
            tree.unlock()
759
 
            if tree is not work_tree:
760
 
                work_tree.unlock()
761
 
 
 
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()
762
752
        for path, entry in entries:
763
753
            if kind and kind != entry.kind:
764
754
                continue
810
800
            raise errors.BzrCommandError("missing file argument")
811
801
        tree, rel_names = tree_files(names_list, canonicalize=False)
812
802
        tree.lock_tree_write()
813
 
        try:
814
 
            self._run(tree, names_list, rel_names, after)
815
 
        finally:
816
 
            tree.unlock()
 
803
        self.add_cleanup(tree.unlock)
 
804
        self._run(tree, names_list, rel_names, after)
817
805
 
818
806
    def run_auto(self, names_list, after, dry_run):
819
807
        if names_list is not None and len(names_list) > 1:
824
812
                                         ' --auto.')
825
813
        work_tree, file_list = tree_files(names_list, default_branch='.')
826
814
        work_tree.lock_tree_write()
827
 
        try:
828
 
            rename_map.RenameMap.guess_renames(work_tree, dry_run)
829
 
        finally:
830
 
            work_tree.unlock()
 
815
        self.add_cleanup(work_tree.unlock)
 
816
        rename_map.RenameMap.guess_renames(work_tree, dry_run)
831
817
 
832
818
    def _run(self, tree, names_list, rel_names, after):
833
819
        into_existing = osutils.isdir(names_list[-1])
1011
997
 
1012
998
        if branch_from is not branch_to:
1013
999
            branch_from.lock_read()
1014
 
        try:
1015
 
            if revision is not None:
1016
 
                revision_id = revision.as_revision_id(branch_from)
1017
 
 
1018
 
            branch_to.lock_write()
1019
 
            try:
1020
 
                if tree_to is not None:
1021
 
                    view_info = _get_view_info_for_change_reporter(tree_to)
1022
 
                    change_reporter = delta._ChangeReporter(
1023
 
                        unversioned_filter=tree_to.is_ignored,
1024
 
                        view_info=view_info)
1025
 
                    result = tree_to.pull(
1026
 
                        branch_from, overwrite, revision_id, change_reporter,
1027
 
                        possible_transports=possible_transports, local=local)
1028
 
                else:
1029
 
                    result = branch_to.pull(
1030
 
                        branch_from, overwrite, revision_id, local=local)
1031
 
 
1032
 
                result.report(self.outf)
1033
 
                if verbose and result.old_revid != result.new_revid:
1034
 
                    log.show_branch_change(
1035
 
                        branch_to, self.outf, result.old_revno,
1036
 
                        result.old_revid)
1037
 
            finally:
1038
 
                branch_to.unlock()
1039
 
        finally:
1040
 
            if branch_from is not branch_to:
1041
 
                branch_from.unlock()
 
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)
1042
1023
 
1043
1024
 
1044
1025
class cmd_push(Command):
1199
1180
                    ' directory exists, but does not already'
1200
1181
                    ' have a control directory.  This flag will'
1201
1182
                    ' allow branch to proceed.'),
 
1183
        Option('bind',
 
1184
            help="Bind new branch to from location."),
1202
1185
        ]
1203
1186
    aliases = ['get', 'clone']
1204
1187
 
1205
1188
    def run(self, from_location, to_location=None, revision=None,
1206
1189
            hardlink=False, stacked=False, standalone=False, no_tree=False,
1207
 
            use_existing_dir=False, switch=False):
 
1190
            use_existing_dir=False, switch=False, bind=False):
1208
1191
        from bzrlib import switch as _mod_switch
1209
1192
        from bzrlib.tag import _merge_tags_if_possible
1210
1193
        accelerator_tree, br_from = bzrdir.BzrDir.open_tree_or_branch(
1211
1194
            from_location)
1212
1195
        revision = _get_one_revision('branch', revision)
1213
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)
1214
1208
        try:
1215
 
            if revision is not None:
1216
 
                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)
1217
1214
            else:
1218
 
                # FIXME - wt.last_revision, fallback to branch, fall back to
1219
 
                # None or perhaps NULL_REVISION to mean copy nothing
1220
 
                # RBC 20060209
1221
 
                revision_id = br_from.last_revision()
1222
 
            if to_location is None:
1223
 
                to_location = urlutils.derive_to_location(from_location)
1224
 
            to_transport = transport.get_transport(to_location)
1225
 
            try:
1226
 
                to_transport.mkdir('.')
1227
 
            except errors.FileExists:
1228
 
                if not use_existing_dir:
1229
 
                    raise errors.BzrCommandError('Target directory "%s" '
1230
 
                        'already exists.' % to_location)
 
1215
                try:
 
1216
                    bzrdir.BzrDir.open_from_transport(to_transport)
 
1217
                except errors.NotBranchError:
 
1218
                    pass
1231
1219
                else:
1232
 
                    try:
1233
 
                        bzrdir.BzrDir.open_from_transport(to_transport)
1234
 
                    except errors.NotBranchError:
1235
 
                        pass
1236
 
                    else:
1237
 
                        raise errors.AlreadyBranchError(to_location)
1238
 
            except errors.NoSuchFile:
1239
 
                raise errors.BzrCommandError('Parent of "%s" does not exist.'
1240
 
                                             % to_location)
1241
 
            try:
1242
 
                # preserve whatever source format we have.
1243
 
                dir = br_from.bzrdir.sprout(to_transport.base, revision_id,
1244
 
                                            possible_transports=[to_transport],
1245
 
                                            accelerator_tree=accelerator_tree,
1246
 
                                            hardlink=hardlink, stacked=stacked,
1247
 
                                            force_new_repo=standalone,
1248
 
                                            create_tree_if_local=not no_tree,
1249
 
                                            source_branch=br_from)
1250
 
                branch = dir.open_branch()
1251
 
            except errors.NoSuchRevision:
1252
 
                to_transport.delete_tree('.')
1253
 
                msg = "The branch %s has no revision %s." % (from_location,
1254
 
                    revision)
1255
 
                raise errors.BzrCommandError(msg)
1256
 
            _merge_tags_if_possible(br_from, branch)
1257
 
            # If the source branch is stacked, the new branch may
1258
 
            # be stacked whether we asked for that explicitly or not.
1259
 
            # We therefore need a try/except here and not just 'if stacked:'
1260
 
            try:
1261
 
                note('Created new stacked branch referring to %s.' %
1262
 
                    branch.get_stacked_on_url())
1263
 
            except (errors.NotStacked, errors.UnstackableBranchFormat,
1264
 
                errors.UnstackableRepositoryFormat), e:
1265
 
                note('Branched %d revision(s).' % branch.revno())
1266
 
            if switch:
1267
 
                # Switch to the new branch
1268
 
                wt, _ = WorkingTree.open_containing('.')
1269
 
                _mod_switch.switch(wt.bzrdir, branch)
1270
 
                note('Switched to branch: %s',
1271
 
                    urlutils.unescape_for_display(branch.base, 'utf-8'))
1272
 
        finally:
1273
 
            br_from.unlock()
 
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'))
1274
1260
 
1275
1261
 
1276
1262
class cmd_checkout(Command):
1355
1341
    def run(self, dir=u'.'):
1356
1342
        tree = WorkingTree.open_containing(dir)[0]
1357
1343
        tree.lock_read()
1358
 
        try:
1359
 
            new_inv = tree.inventory
1360
 
            old_tree = tree.basis_tree()
1361
 
            old_tree.lock_read()
1362
 
            try:
1363
 
                old_inv = old_tree.inventory
1364
 
                renames = []
1365
 
                iterator = tree.iter_changes(old_tree, include_unchanged=True)
1366
 
                for f, paths, c, v, p, n, k, e in iterator:
1367
 
                    if paths[0] == paths[1]:
1368
 
                        continue
1369
 
                    if None in (paths):
1370
 
                        continue
1371
 
                    renames.append(paths)
1372
 
                renames.sort()
1373
 
                for old_name, new_name in renames:
1374
 
                    self.outf.write("%s => %s\n" % (old_name, new_name))
1375
 
            finally:
1376
 
                old_tree.unlock()
1377
 
        finally:
1378
 
            tree.unlock()
 
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))
1379
1361
 
1380
1362
 
1381
1363
class cmd_update(Command):
1387
1369
 
1388
1370
    If you want to discard your local changes, you can just do a
1389
1371
    'bzr revert' instead of 'bzr commit' after the update.
 
1372
 
 
1373
    If the tree's branch is bound to a master branch, it will also update
 
1374
    the branch from the master.
1390
1375
    """
1391
1376
 
1392
1377
    _see_also = ['pull', 'working-trees', 'status-flags']
1393
1378
    takes_args = ['dir?']
 
1379
    takes_options = ['revision']
1394
1380
    aliases = ['up']
1395
1381
 
1396
 
    def run(self, dir='.'):
 
1382
    def run(self, dir='.', revision=None):
 
1383
        if revision is not None and len(revision) != 1:
 
1384
            raise errors.BzrCommandError(
 
1385
                        "bzr update --revision takes exactly one revision")
1397
1386
        tree = WorkingTree.open_containing(dir)[0]
 
1387
        branch = tree.branch
1398
1388
        possible_transports = []
1399
 
        master = tree.branch.get_master_branch(
 
1389
        master = branch.get_master_branch(
1400
1390
            possible_transports=possible_transports)
1401
1391
        if master is not None:
1402
1392
            tree.lock_write()
1404
1394
        else:
1405
1395
            tree.lock_tree_write()
1406
1396
            branch_location = tree.branch.base
 
1397
        self.add_cleanup(tree.unlock)
1407
1398
        # get rid of the final '/' and be ready for display
1408
1399
        branch_location = urlutils.unescape_for_display(branch_location[:-1],
1409
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)
1410
1422
        try:
1411
 
            existing_pending_merges = tree.get_parent_ids()[1:]
1412
 
            last_rev = _mod_revision.ensure_null(tree.last_revision())
1413
 
            if last_rev == _mod_revision.ensure_null(
1414
 
                tree.branch.last_revision()):
1415
 
                # may be up to date, check master too.
1416
 
                if master is None or last_rev == _mod_revision.ensure_null(
1417
 
                    master.last_revision()):
1418
 
                    revno = tree.branch.revision_id_to_revno(last_rev)
1419
 
                    note('Tree is up to date at revision %d of branch %s'
1420
 
                         % (revno, branch_location))
1421
 
                    return 0
1422
 
            view_info = _get_view_info_for_change_reporter(tree)
1423
1423
            conflicts = tree.update(
1424
 
                delta._ChangeReporter(unversioned_filter=tree.is_ignored,
1425
 
                view_info=view_info), possible_transports=possible_transports)
1426
 
            revno = tree.branch.revision_id_to_revno(
1427
 
                _mod_revision.ensure_null(tree.last_revision()))
1428
 
            note('Updated to revision %d of branch %s' %
1429
 
                 (revno, branch_location))
1430
 
            if tree.get_parent_ids()[1:] != existing_pending_merges:
1431
 
                note('Your local commits will now show as pending merges with '
1432
 
                     "'bzr status', and can be committed with 'bzr commit'.")
1433
 
            if conflicts != 0:
1434
 
                return 1
1435
 
            else:
1436
 
                return 0
1437
 
        finally:
1438
 
            tree.unlock()
 
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
1439
1445
 
1440
1446
 
1441
1447
class cmd_info(Command):
1512
1518
            file_list = [f for f in file_list]
1513
1519
 
1514
1520
        tree.lock_write()
1515
 
        try:
1516
 
            # Heuristics should probably all move into tree.remove_smart or
1517
 
            # some such?
1518
 
            if new:
1519
 
                added = tree.changes_from(tree.basis_tree(),
1520
 
                    specific_files=file_list).added
1521
 
                file_list = sorted([f[0] for f in added], reverse=True)
1522
 
                if len(file_list) == 0:
1523
 
                    raise errors.BzrCommandError('No matching files.')
1524
 
            elif file_list is None:
1525
 
                # missing files show up in iter_changes(basis) as
1526
 
                # versioned-with-no-kind.
1527
 
                missing = []
1528
 
                for change in tree.iter_changes(tree.basis_tree()):
1529
 
                    # Find paths in the working tree that have no kind:
1530
 
                    if change[1][1] is not None and change[6][1] is None:
1531
 
                        missing.append(change[1][1])
1532
 
                file_list = sorted(missing, reverse=True)
1533
 
                file_deletion_strategy = 'keep'
1534
 
            tree.remove(file_list, verbose=verbose, to_file=self.outf,
1535
 
                keep_files=file_deletion_strategy=='keep',
1536
 
                force=file_deletion_strategy=='force')
1537
 
        finally:
1538
 
            tree.unlock()
 
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')
1539
1543
 
1540
1544
 
1541
1545
class cmd_file_id(Command):
1967
1971
    def run(self, show_ids=False):
1968
1972
        tree = WorkingTree.open_containing(u'.')[0]
1969
1973
        tree.lock_read()
1970
 
        try:
1971
 
            old = tree.basis_tree()
1972
 
            old.lock_read()
1973
 
            try:
1974
 
                for path, ie in old.inventory.iter_entries():
1975
 
                    if not tree.has_id(ie.file_id):
1976
 
                        self.outf.write(path)
1977
 
                        if show_ids:
1978
 
                            self.outf.write(' ')
1979
 
                            self.outf.write(ie.file_id)
1980
 
                        self.outf.write('\n')
1981
 
            finally:
1982
 
                old.unlock()
1983
 
        finally:
1984
 
            tree.unlock()
 
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')
1985
1985
 
1986
1986
 
1987
1987
class cmd_modified(Command):
2023
2023
    def run(self, null=False):
2024
2024
        wt = WorkingTree.open_containing(u'.')[0]
2025
2025
        wt.lock_read()
2026
 
        try:
2027
 
            basis = wt.basis_tree()
2028
 
            basis.lock_read()
2029
 
            try:
2030
 
                basis_inv = basis.inventory
2031
 
                inv = wt.inventory
2032
 
                for file_id in inv:
2033
 
                    if file_id in basis_inv:
2034
 
                        continue
2035
 
                    if inv.is_root(file_id) and len(basis_inv) == 0:
2036
 
                        continue
2037
 
                    path = inv.id2path(file_id)
2038
 
                    if not os.access(osutils.abspath(path), os.F_OK):
2039
 
                        continue
2040
 
                    if null:
2041
 
                        self.outf.write(path + '\0')
2042
 
                    else:
2043
 
                        self.outf.write(osutils.quotefn(path) + '\n')
2044
 
            finally:
2045
 
                basis.unlock()
2046
 
        finally:
2047
 
            wt.unlock()
 
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')
2048
2044
 
2049
2045
 
2050
2046
class cmd_root(Command):
2308
2304
 
2309
2305
        file_ids = []
2310
2306
        filter_by_dir = False
2311
 
        b = None
2312
 
        try:
2313
 
            if file_list:
2314
 
                # find the file ids to log and check for directory filtering
2315
 
                b, file_info_list, rev1, rev2 = _get_info_for_log_files(
2316
 
                    revision, file_list)
2317
 
                for relpath, file_id, kind in file_info_list:
2318
 
                    if file_id is None:
2319
 
                        raise errors.BzrCommandError(
2320
 
                            "Path unknown at end or start of revision range: %s" %
2321
 
                            relpath)
2322
 
                    # If the relpath is the top of the tree, we log everything
2323
 
                    if relpath == '':
2324
 
                        file_ids = []
2325
 
                        break
2326
 
                    else:
2327
 
                        file_ids.append(file_id)
2328
 
                    filter_by_dir = filter_by_dir or (
2329
 
                        kind in ['directory', 'tree-reference'])
2330
 
            else:
2331
 
                # log everything
2332
 
                # FIXME ? log the current subdir only RBC 20060203
2333
 
                if revision is not None \
2334
 
                        and len(revision) > 0 and revision[0].get_branch():
2335
 
                    location = revision[0].get_branch()
 
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
2336
2321
                else:
2337
 
                    location = '.'
2338
 
                dir, relpath = bzrdir.BzrDir.open_containing(location)
2339
 
                b = dir.open_branch()
2340
 
                b.lock_read()
2341
 
                rev1, rev2 = _get_revision_range(revision, b, self.name())
2342
 
 
2343
 
            # Decide on the type of delta & diff filtering to use
2344
 
            # TODO: add an --all-files option to make this configurable & consistent
2345
 
            if not verbose:
2346
 
                delta_type = None
2347
 
            else:
2348
 
                delta_type = 'full'
2349
 
            if not show_diff:
2350
 
                diff_type = None
2351
 
            elif file_ids:
2352
 
                diff_type = 'partial'
2353
 
            else:
2354
 
                diff_type = 'full'
2355
 
 
2356
 
            # Build the log formatter
2357
 
            if log_format is None:
2358
 
                log_format = log.log_formatter_registry.get_default(b)
2359
 
            # Make a non-encoding output to include the diffs - bug 328007
2360
 
            unencoded_output = ui.ui_factory.make_output_stream(encoding_type='exact')
2361
 
            lf = log_format(show_ids=show_ids, to_file=self.outf,
2362
 
                            to_exact_file=unencoded_output,
2363
 
                            show_timezone=timezone,
2364
 
                            delta_format=get_verbosity_level(),
2365
 
                            levels=levels,
2366
 
                            show_advice=levels is None)
2367
 
 
2368
 
            # Choose the algorithm for doing the logging. It's annoying
2369
 
            # having multiple code paths like this but necessary until
2370
 
            # the underlying repository format is faster at generating
2371
 
            # deltas or can provide everything we need from the indices.
2372
 
            # The default algorithm - match-using-deltas - works for
2373
 
            # multiple files and directories and is faster for small
2374
 
            # amounts of history (200 revisions say). However, it's too
2375
 
            # slow for logging a single file in a repository with deep
2376
 
            # history, i.e. > 10K revisions. In the spirit of "do no
2377
 
            # evil when adding features", we continue to use the
2378
 
            # original algorithm - per-file-graph - for the "single
2379
 
            # file that isn't a directory without showing a delta" case.
2380
 
            partial_history = revision and b.repository._format.supports_chks
2381
 
            match_using_deltas = (len(file_ids) != 1 or filter_by_dir
2382
 
                or delta_type or partial_history)
2383
 
 
2384
 
            # Build the LogRequest and execute it
2385
 
            if len(file_ids) == 0:
2386
 
                file_ids = None
2387
 
            rqst = make_log_request_dict(
2388
 
                direction=direction, specific_fileids=file_ids,
2389
 
                start_revision=rev1, end_revision=rev2, limit=limit,
2390
 
                message_search=message, delta_type=delta_type,
2391
 
                diff_type=diff_type, _match_using_deltas=match_using_deltas)
2392
 
            Logger(b, rqst).show(lf)
2393
 
        finally:
2394
 
            if b is not None:
2395
 
                b.unlock()
 
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)
2396
2389
 
2397
2390
 
2398
2391
def _get_revision_range(revisionspec_list, branch, command_name):
2465
2458
        file_id = tree.path2id(relpath)
2466
2459
        b = tree.branch
2467
2460
        b.lock_read()
2468
 
        try:
2469
 
            touching_revs = log.find_touching_revisions(b, file_id)
2470
 
            for revno, revision_id, what in touching_revs:
2471
 
                self.outf.write("%6d %s\n" % (revno, what))
2472
 
        finally:
2473
 
            b.unlock()
 
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))
2474
2465
 
2475
2466
 
2476
2467
class cmd_ls(Command):
2543
2534
                note("Ignoring files outside view. View is %s" % view_str)
2544
2535
 
2545
2536
        tree.lock_read()
2546
 
        try:
2547
 
            for fp, fc, fkind, fid, entry in tree.list_files(include_root=False,
2548
 
                from_dir=relpath, recursive=recursive):
2549
 
                # Apply additional masking
2550
 
                if not all and not selection[fc]:
2551
 
                    continue
2552
 
                if kind is not None and fkind != kind:
2553
 
                    continue
2554
 
                if apply_view:
2555
 
                    try:
2556
 
                        if relpath:
2557
 
                            fullpath = osutils.pathjoin(relpath, fp)
2558
 
                        else:
2559
 
                            fullpath = fp
2560
 
                        views.check_path_in_view(tree, fullpath)
2561
 
                    except errors.FileOutsideView:
2562
 
                        continue
 
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
2563
2554
 
2564
 
                # Output the entry
2565
 
                if prefix:
2566
 
                    fp = osutils.pathjoin(prefix, fp)
2567
 
                kindch = entry.kind_character()
2568
 
                outstring = fp + kindch
2569
 
                ui.ui_factory.clear_term()
2570
 
                if verbose:
2571
 
                    outstring = '%-8s %s' % (fc, outstring)
2572
 
                    if show_ids and fid is not None:
2573
 
                        outstring = "%-50s %s" % (outstring, fid)
 
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:
2574
2581
                    self.outf.write(outstring + '\n')
2575
 
                elif null:
2576
 
                    self.outf.write(fp + '\0')
2577
 
                    if show_ids:
2578
 
                        if fid is not None:
2579
 
                            self.outf.write(fid)
2580
 
                        self.outf.write('\0')
2581
 
                    self.outf.flush()
2582
 
                else:
2583
 
                    if show_ids:
2584
 
                        if fid is not None:
2585
 
                            my_id = fid
2586
 
                        else:
2587
 
                            my_id = ''
2588
 
                        self.outf.write('%-50s %s\n' % (outstring, my_id))
2589
 
                    else:
2590
 
                        self.outf.write(outstring + '\n')
2591
 
        finally:
2592
 
            tree.unlock()
2593
2582
 
2594
2583
 
2595
2584
class cmd_unknowns(Command):
2707
2696
    def run(self):
2708
2697
        tree = WorkingTree.open_containing(u'.')[0]
2709
2698
        tree.lock_read()
2710
 
        try:
2711
 
            for path, file_class, kind, file_id, entry in tree.list_files():
2712
 
                if file_class != 'I':
2713
 
                    continue
2714
 
                ## XXX: Slightly inefficient since this was already calculated
2715
 
                pat = tree.is_ignored(path)
2716
 
                self.outf.write('%-50s %s\n' % (path, pat))
2717
 
        finally:
2718
 
            tree.unlock()
 
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))
2719
2706
 
2720
2707
 
2721
2708
class cmd_lookup_revision(Command):
2824
2811
        tree, branch, relpath = \
2825
2812
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
2826
2813
        branch.lock_read()
2827
 
        try:
2828
 
            return self._run(tree, branch, relpath, filename, revision,
2829
 
                             name_from_revision, filters)
2830
 
        finally:
2831
 
            branch.unlock()
 
2814
        self.add_cleanup(branch.unlock)
 
2815
        return self._run(tree, branch, relpath, filename, revision,
 
2816
                         name_from_revision, filters)
2832
2817
 
2833
2818
    def _run(self, tree, b, relpath, filename, revision, name_from_revision,
2834
2819
        filtered):
2835
2820
        if tree is None:
2836
2821
            tree = b.basis_tree()
2837
2822
        rev_tree = _get_one_revision_tree('cat', revision, branch=b)
 
2823
        rev_tree.lock_read()
 
2824
        self.add_cleanup(rev_tree.unlock)
2838
2825
 
2839
2826
        old_file_id = rev_tree.path2id(relpath)
2840
2827
 
2875
2862
            chunks = content.splitlines(True)
2876
2863
            content = filtered_output_bytes(chunks, filters,
2877
2864
                ContentFilterContext(relpath, rev_tree))
 
2865
            self.cleanup_now()
2878
2866
            self.outf.writelines(content)
2879
2867
        else:
 
2868
            self.cleanup_now()
2880
2869
            self.outf.write(content)
2881
2870
 
2882
2871
 
3543
3532
            verbose = not is_quiet()
3544
3533
            # TODO: should possibly lock the history file...
3545
3534
            benchfile = open(".perf_history", "at", buffering=1)
 
3535
            self.add_cleanup(benchfile.close)
3546
3536
        else:
3547
3537
            test_suite_factory = None
3548
3538
            benchfile = None
3549
 
        try:
3550
 
            selftest_kwargs = {"verbose": verbose,
3551
 
                              "pattern": pattern,
3552
 
                              "stop_on_failure": one,
3553
 
                              "transport": transport,
3554
 
                              "test_suite_factory": test_suite_factory,
3555
 
                              "lsprof_timed": lsprof_timed,
3556
 
                              "lsprof_tests": lsprof_tests,
3557
 
                              "bench_history": benchfile,
3558
 
                              "matching_tests_first": first,
3559
 
                              "list_only": list_only,
3560
 
                              "random_seed": randomize,
3561
 
                              "exclude_pattern": exclude,
3562
 
                              "strict": strict,
3563
 
                              "load_list": load_list,
3564
 
                              "debug_flags": debugflag,
3565
 
                              "starting_with": starting_with
3566
 
                              }
3567
 
            selftest_kwargs.update(self.additional_selftest_args)
3568
 
            result = selftest(**selftest_kwargs)
3569
 
        finally:
3570
 
            if benchfile is not None:
3571
 
                benchfile.close()
 
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)
3572
3558
        return int(not result)
3573
3559
 
3574
3560
 
3613
3599
        branch1 = Branch.open_containing(branch)[0]
3614
3600
        branch2 = Branch.open_containing(other)[0]
3615
3601
        branch1.lock_read()
3616
 
        try:
3617
 
            branch2.lock_read()
3618
 
            try:
3619
 
                last1 = ensure_null(branch1.last_revision())
3620
 
                last2 = ensure_null(branch2.last_revision())
3621
 
 
3622
 
                graph = branch1.repository.get_graph(branch2.repository)
3623
 
                base_rev_id = graph.find_unique_lca(last1, last2)
3624
 
 
3625
 
                print 'merge base is revision %s' % base_rev_id
3626
 
            finally:
3627
 
                branch2.unlock()
3628
 
        finally:
3629
 
            branch1.unlock()
 
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
3630
3612
 
3631
3613
 
3632
3614
class cmd_merge(Command):
3749
3731
        view_info = _get_view_info_for_change_reporter(tree)
3750
3732
        change_reporter = delta._ChangeReporter(
3751
3733
            unversioned_filter=tree.is_ignored, view_info=view_info)
3752
 
        cleanups = []
3753
 
        try:
3754
 
            pb = ui.ui_factory.nested_progress_bar()
3755
 
            cleanups.append(pb.finished)
3756
 
            tree.lock_write()
3757
 
            cleanups.append(tree.unlock)
3758
 
            if location is not None:
3759
 
                try:
3760
 
                    mergeable = bundle.read_mergeable_from_url(location,
3761
 
                        possible_transports=possible_transports)
3762
 
                except errors.NotABundle:
3763
 
                    mergeable = None
3764
 
                else:
3765
 
                    if uncommitted:
3766
 
                        raise errors.BzrCommandError('Cannot use --uncommitted'
3767
 
                            ' with bundles or merge directives.')
3768
 
 
3769
 
                    if revision is not None:
3770
 
                        raise errors.BzrCommandError(
3771
 
                            'Cannot use -r with merge directives or bundles')
3772
 
                    merger, verified = _mod_merge.Merger.from_mergeable(tree,
3773
 
                       mergeable, pb)
3774
 
 
3775
 
            if merger is None and uncommitted:
3776
 
                if revision is not None and len(revision) > 0:
3777
 
                    raise errors.BzrCommandError('Cannot use --uncommitted and'
3778
 
                        ' --revision at the same time.')
3779
 
                merger = self.get_merger_from_uncommitted(tree, location, pb,
3780
 
                                                          cleanups)
3781
 
                allow_pending = False
3782
 
 
3783
 
            if merger is None:
3784
 
                merger, allow_pending = self._get_merger_from_branch(tree,
3785
 
                    location, revision, remember, possible_transports, pb)
3786
 
 
3787
 
            merger.merge_type = merge_type
3788
 
            merger.reprocess = reprocess
3789
 
            merger.show_base = show_base
3790
 
            self.sanity_check_merger(merger)
3791
 
            if (merger.base_rev_id == merger.other_rev_id and
3792
 
                merger.other_rev_id is not None):
3793
 
                note('Nothing to do.')
 
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)
3794
3781
                return 0
3795
 
            if pull:
3796
 
                if merger.interesting_files is not None:
3797
 
                    raise errors.BzrCommandError('Cannot pull individual files')
3798
 
                if (merger.base_rev_id == tree.last_revision()):
3799
 
                    result = tree.pull(merger.other_branch, False,
3800
 
                                       merger.other_rev_id)
3801
 
                    result.report(self.outf)
3802
 
                    return 0
3803
 
            if merger.this_basis is None:
3804
 
                raise errors.BzrCommandError(
3805
 
                    "This branch has no commits."
3806
 
                    " (perhaps you would prefer 'bzr pull')")
3807
 
            if preview:
3808
 
                return self._do_preview(merger, cleanups)
3809
 
            elif interactive:
3810
 
                return self._do_interactive(merger, cleanups)
3811
 
            else:
3812
 
                return self._do_merge(merger, change_reporter, allow_pending,
3813
 
                                      verified)
3814
 
        finally:
3815
 
            for cleanup in reversed(cleanups):
3816
 
                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)
3817
3793
 
3818
 
    def _get_preview(self, merger, cleanups):
 
3794
    def _get_preview(self, merger):
3819
3795
        tree_merger = merger.make_merger()
3820
3796
        tt = tree_merger.make_preview_transform()
3821
 
        cleanups.append(tt.finalize)
 
3797
        self.add_cleanup(tt.finalize)
3822
3798
        result_tree = tt.get_preview_tree()
3823
3799
        return result_tree
3824
3800
 
3825
 
    def _do_preview(self, merger, cleanups):
 
3801
    def _do_preview(self, merger):
3826
3802
        from bzrlib.diff import show_diff_trees
3827
 
        result_tree = self._get_preview(merger, cleanups)
 
3803
        result_tree = self._get_preview(merger)
3828
3804
        show_diff_trees(merger.this_tree, result_tree, self.outf,
3829
3805
                        old_label='', new_label='')
3830
3806
 
3840
3816
        else:
3841
3817
            return 0
3842
3818
 
3843
 
    def _do_interactive(self, merger, cleanups):
 
3819
    def _do_interactive(self, merger):
3844
3820
        """Perform an interactive merge.
3845
3821
 
3846
3822
        This works by generating a preview tree of the merge, then using
3848
3824
        and the preview tree.
3849
3825
        """
3850
3826
        from bzrlib import shelf_ui
3851
 
        result_tree = self._get_preview(merger, cleanups)
 
3827
        result_tree = self._get_preview(merger)
3852
3828
        writer = bzrlib.option.diff_writer_registry.get()
3853
3829
        shelver = shelf_ui.Shelver(merger.this_tree, result_tree, destroy=True,
3854
3830
                                   reporter=shelf_ui.ApplyReporter(),
3922
3898
            allow_pending = True
3923
3899
        return merger, allow_pending
3924
3900
 
3925
 
    def get_merger_from_uncommitted(self, tree, location, pb, cleanups):
 
3901
    def get_merger_from_uncommitted(self, tree, location, pb):
3926
3902
        """Get a merger for uncommitted changes.
3927
3903
 
3928
3904
        :param tree: The tree the merger should apply to.
3929
3905
        :param location: The location containing uncommitted changes.
3930
3906
        :param pb: The progress bar to use for showing progress.
3931
 
        :param cleanups: A list of operations to perform to clean up the
3932
 
            temporary directories, unfinalized objects, etc.
3933
3907
        """
3934
3908
        location = self._select_branch_location(tree, location)[0]
3935
3909
        other_tree, other_path = WorkingTree.open_containing(location)
4022
3996
            merge_type = _mod_merge.Merge3Merger
4023
3997
        tree, file_list = tree_files(file_list)
4024
3998
        tree.lock_write()
4025
 
        try:
4026
 
            parents = tree.get_parent_ids()
4027
 
            if len(parents) != 2:
4028
 
                raise errors.BzrCommandError("Sorry, remerge only works after normal"
4029
 
                                             " merges.  Not cherrypicking or"
4030
 
                                             " multi-merges.")
4031
 
            repository = tree.branch.repository
4032
 
            interesting_ids = None
4033
 
            new_conflicts = []
4034
 
            conflicts = tree.conflicts()
4035
 
            if file_list is not None:
4036
 
                interesting_ids = set()
4037
 
                for filename in file_list:
4038
 
                    file_id = tree.path2id(filename)
4039
 
                    if file_id is None:
4040
 
                        raise errors.NotVersionedError(filename)
4041
 
                    interesting_ids.add(file_id)
4042
 
                    if tree.kind(file_id) != "directory":
4043
 
                        continue
 
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
4044
4018
 
4045
 
                    for name, ie in tree.inventory.iter_entries(file_id):
4046
 
                        interesting_ids.add(ie.file_id)
4047
 
                new_conflicts = conflicts.select_conflicts(tree, file_list)[0]
4048
 
            else:
4049
 
                # Remerge only supports resolving contents conflicts
4050
 
                allowed_conflicts = ('text conflict', 'contents conflict')
4051
 
                restore_files = [c.path for c in conflicts
4052
 
                                 if c.typestring in allowed_conflicts]
4053
 
            _mod_merge.transform_tree(tree, tree.basis_tree(), interesting_ids)
4054
 
            tree.set_conflicts(ConflictList(new_conflicts))
4055
 
            if file_list is not None:
4056
 
                restore_files = file_list
4057
 
            for filename in restore_files:
4058
 
                try:
4059
 
                    restore(tree.abspath(filename))
4060
 
                except errors.NotConflicted:
4061
 
                    pass
4062
 
            # Disable pending merges, because the file texts we are remerging
4063
 
            # have not had those merges performed.  If we use the wrong parents
4064
 
            # list, we imply that the working tree text has seen and rejected
4065
 
            # all the changes from the other tree, when in fact those changes
4066
 
            # have not yet been seen.
4067
 
            pb = ui.ui_factory.nested_progress_bar()
4068
 
            tree.set_parent_ids(parents[:1])
 
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:
4069
4032
            try:
4070
 
                merger = _mod_merge.Merger.from_revision_ids(pb,
4071
 
                                                             tree, parents[1])
4072
 
                merger.interesting_ids = interesting_ids
4073
 
                merger.merge_type = merge_type
4074
 
                merger.show_base = show_base
4075
 
                merger.reprocess = reprocess
4076
 
                conflicts = merger.do_merge()
4077
 
            finally:
4078
 
                tree.set_parent_ids(parents)
4079
 
                pb.finished()
 
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()
4080
4051
        finally:
4081
 
            tree.unlock()
 
4052
            tree.set_parent_ids(parents)
 
4053
            pb.finished()
4082
4054
        if conflicts > 0:
4083
4055
            return 1
4084
4056
        else:
4143
4115
            forget_merges=None):
4144
4116
        tree, file_list = tree_files(file_list)
4145
4117
        tree.lock_write()
4146
 
        try:
4147
 
            if forget_merges:
4148
 
                tree.set_parent_ids(tree.get_parent_ids()[:1])
4149
 
            else:
4150
 
                self._revert_tree_to_revision(tree, revision, file_list, no_backup)
4151
 
        finally:
4152
 
            tree.unlock()
 
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)
4153
4123
 
4154
4124
    @staticmethod
4155
4125
    def _revert_tree_to_revision(tree, revision, file_list, no_backup):
4306
4276
        if remote_branch.base == local_branch.base:
4307
4277
            remote_branch = local_branch
4308
4278
 
 
4279
        local_branch.lock_read()
 
4280
        self.add_cleanup(local_branch.unlock)
4309
4281
        local_revid_range = _revision_range_to_revid_range(
4310
4282
            _get_revision_range(my_revision, local_branch,
4311
4283
                self.name()))
4312
4284
 
 
4285
        remote_branch.lock_read()
 
4286
        self.add_cleanup(remote_branch.unlock)
4313
4287
        remote_revid_range = _revision_range_to_revid_range(
4314
4288
            _get_revision_range(revision,
4315
4289
                remote_branch, self.name()))
4316
4290
 
4317
 
        local_branch.lock_read()
4318
 
        try:
4319
 
            remote_branch.lock_read()
4320
 
            try:
4321
 
                local_extra, remote_extra = find_unmerged(
4322
 
                    local_branch, remote_branch, restrict,
4323
 
                    backward=not reverse,
4324
 
                    include_merges=include_merges,
4325
 
                    local_revid_range=local_revid_range,
4326
 
                    remote_revid_range=remote_revid_range)
4327
 
 
4328
 
                if log_format is None:
4329
 
                    registry = log.log_formatter_registry
4330
 
                    log_format = registry.get_default(local_branch)
4331
 
                lf = log_format(to_file=self.outf,
4332
 
                                show_ids=show_ids,
4333
 
                                show_timezone='original')
4334
 
 
4335
 
                status_code = 0
4336
 
                if local_extra and not theirs_only:
4337
 
                    message("You have %d extra revision(s):\n" %
4338
 
                        len(local_extra))
4339
 
                    for revision in iter_log_revisions(local_extra,
4340
 
                                        local_branch.repository,
4341
 
                                        verbose):
4342
 
                        lf.log_revision(revision)
4343
 
                    printed_local = True
4344
 
                    status_code = 1
4345
 
                else:
4346
 
                    printed_local = False
4347
 
 
4348
 
                if remote_extra and not mine_only:
4349
 
                    if printed_local is True:
4350
 
                        message("\n\n\n")
4351
 
                    message("You are missing %d revision(s):\n" %
4352
 
                        len(remote_extra))
4353
 
                    for revision in iter_log_revisions(remote_extra,
4354
 
                                        remote_branch.repository,
4355
 
                                        verbose):
4356
 
                        lf.log_revision(revision)
4357
 
                    status_code = 1
4358
 
 
4359
 
                if mine_only and not local_extra:
4360
 
                    # We checked local, and found nothing extra
4361
 
                    message('This branch is up to date.\n')
4362
 
                elif theirs_only and not remote_extra:
4363
 
                    # We checked remote, and found nothing extra
4364
 
                    message('Other branch is up to date.\n')
4365
 
                elif not (mine_only or theirs_only or local_extra or
4366
 
                          remote_extra):
4367
 
                    # We checked both branches, and neither one had extra
4368
 
                    # revisions
4369
 
                    message("Branches are up to date.\n")
4370
 
            finally:
4371
 
                remote_branch.unlock()
4372
 
        finally:
4373
 
            local_branch.unlock()
 
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()
4374
4341
        if not status_code and parent is None and other_branch is not None:
4375
4342
            local_branch.lock_write()
4376
 
            try:
4377
 
                # handle race conditions - a parent might be set while we run.
4378
 
                if local_branch.get_parent() is None:
4379
 
                    local_branch.set_parent(remote_branch.base)
4380
 
            finally:
4381
 
                local_branch.unlock()
 
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)
4382
4347
        return status_code
4383
4348
 
4384
4349
 
4463
4428
        else:
4464
4429
            b = Branch.open(branch)
4465
4430
        b.lock_read()
4466
 
        try:
4467
 
            if revision is None:
4468
 
                rev_id = b.last_revision()
4469
 
            else:
4470
 
                rev_id = revision[0].as_revision_id(b)
4471
 
            t = testament_class.from_revision(b.repository, rev_id)
4472
 
            if long:
4473
 
                sys.stdout.writelines(t.as_text_lines())
4474
 
            else:
4475
 
                sys.stdout.write(t.as_short_text())
4476
 
        finally:
4477
 
            b.unlock()
 
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())
4478
4441
 
4479
4442
 
4480
4443
class cmd_annotate(Command):
4506
4469
            bzrdir.BzrDir.open_containing_tree_or_branch(filename)
4507
4470
        if wt is not None:
4508
4471
            wt.lock_read()
 
4472
            self.add_cleanup(wt.unlock)
4509
4473
        else:
4510
4474
            branch.lock_read()
4511
 
        try:
4512
 
            tree = _get_one_revision_tree('annotate', revision, branch=branch)
4513
 
            if wt is not None:
4514
 
                file_id = wt.path2id(relpath)
4515
 
            else:
4516
 
                file_id = tree.path2id(relpath)
4517
 
            if file_id is None:
4518
 
                raise errors.NotVersionedError(filename)
4519
 
            file_version = tree.inventory[file_id].revision
4520
 
            if wt is not None and revision is None:
4521
 
                # If there is a tree and we're not annotating historical
4522
 
                # versions, annotate the working tree's content.
4523
 
                annotate_file_tree(wt, file_id, self.outf, long, all,
4524
 
                    show_ids=show_ids)
4525
 
            else:
4526
 
                annotate_file(branch, file_version, file_id, long, all, self.outf,
4527
 
                              show_ids=show_ids)
4528
 
        finally:
4529
 
            if wt is not None:
4530
 
                wt.unlock()
4531
 
            else:
4532
 
                branch.unlock()
 
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)
4533
4494
 
4534
4495
 
4535
4496
class cmd_re_sign(Command):
4547
4508
            raise errors.BzrCommandError('You must supply either --revision or a revision_id')
4548
4509
        b = WorkingTree.open_containing(u'.')[0].branch
4549
4510
        b.lock_write()
4550
 
        try:
4551
 
            return self._run(b, revision_id_list, revision)
4552
 
        finally:
4553
 
            b.unlock()
 
4511
        self.add_cleanup(b.unlock)
 
4512
        return self._run(b, revision_id_list, revision)
4554
4513
 
4555
4514
    def _run(self, b, revision_id_list, revision):
4556
4515
        import bzrlib.gpg as gpg
4702
4661
 
4703
4662
        if tree is not None:
4704
4663
            tree.lock_write()
 
4664
            self.add_cleanup(tree.unlock)
4705
4665
        else:
4706
4666
            b.lock_write()
4707
 
        try:
4708
 
            return self._run(b, tree, dry_run, verbose, revision, force,
4709
 
                             local=local)
4710
 
        finally:
4711
 
            if tree is not None:
4712
 
                tree.unlock()
4713
 
            else:
4714
 
                b.unlock()
 
4667
            self.add_cleanup(b.unlock)
 
4668
        return self._run(b, tree, dry_run, verbose, revision, force, local=local)
4715
4669
 
4716
4670
    def _run(self, b, tree, dry_run, verbose, revision, force, local=False):
4717
4671
        from bzrlib.log import log_formatter, show_log
5256
5210
            ):
5257
5211
        branch, relpath = Branch.open_containing(directory)
5258
5212
        branch.lock_write()
5259
 
        try:
5260
 
            if delete:
5261
 
                branch.tags.delete_tag(tag_name)
5262
 
                self.outf.write('Deleted tag %s.\n' % tag_name)
 
5213
        self.add_cleanup(branch.unlock)
 
5214
        if delete:
 
5215
            branch.tags.delete_tag(tag_name)
 
5216
            self.outf.write('Deleted tag %s.\n' % tag_name)
 
5217
        else:
 
5218
            if revision:
 
5219
                if len(revision) != 1:
 
5220
                    raise errors.BzrCommandError(
 
5221
                        "Tags can only be placed on a single revision, "
 
5222
                        "not on a range")
 
5223
                revision_id = revision[0].as_revision_id(branch)
5263
5224
            else:
5264
 
                if revision:
5265
 
                    if len(revision) != 1:
5266
 
                        raise errors.BzrCommandError(
5267
 
                            "Tags can only be placed on a single revision, "
5268
 
                            "not on a range")
5269
 
                    revision_id = revision[0].as_revision_id(branch)
5270
 
                else:
5271
 
                    revision_id = branch.last_revision()
5272
 
                if (not force) and branch.tags.has_tag(tag_name):
5273
 
                    raise errors.TagAlreadyExists(tag_name)
5274
 
                branch.tags.set_tag(tag_name, revision_id)
5275
 
                self.outf.write('Created tag %s.\n' % tag_name)
5276
 
        finally:
5277
 
            branch.unlock()
 
5225
                revision_id = branch.last_revision()
 
5226
            if (not force) and branch.tags.has_tag(tag_name):
 
5227
                raise errors.TagAlreadyExists(tag_name)
 
5228
            branch.tags.set_tag(tag_name, revision_id)
 
5229
            self.outf.write('Created tag %s.\n' % tag_name)
5278
5230
 
5279
5231
 
5280
5232
class cmd_tags(Command):
5313
5265
            return
5314
5266
 
5315
5267
        branch.lock_read()
5316
 
        try:
5317
 
            if revision:
5318
 
                graph = branch.repository.get_graph()
5319
 
                rev1, rev2 = _get_revision_range(revision, branch, self.name())
5320
 
                revid1, revid2 = rev1.rev_id, rev2.rev_id
5321
 
                # only show revisions between revid1 and revid2 (inclusive)
5322
 
                tags = [(tag, revid) for tag, revid in tags if
5323
 
                    graph.is_between(revid, revid1, revid2)]
5324
 
            if sort == 'alpha':
5325
 
                tags.sort()
5326
 
            elif sort == 'time':
5327
 
                timestamps = {}
5328
 
                for tag, revid in tags:
5329
 
                    try:
5330
 
                        revobj = branch.repository.get_revision(revid)
5331
 
                    except errors.NoSuchRevision:
5332
 
                        timestamp = sys.maxint # place them at the end
5333
 
                    else:
5334
 
                        timestamp = revobj.timestamp
5335
 
                    timestamps[revid] = timestamp
5336
 
                tags.sort(key=lambda x: timestamps[x[1]])
5337
 
            if not show_ids:
5338
 
                # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
5339
 
                for index, (tag, revid) in enumerate(tags):
5340
 
                    try:
5341
 
                        revno = branch.revision_id_to_dotted_revno(revid)
5342
 
                        if isinstance(revno, tuple):
5343
 
                            revno = '.'.join(map(str, revno))
5344
 
                    except errors.NoSuchRevision:
5345
 
                        # Bad tag data/merges can lead to tagged revisions
5346
 
                        # which are not in this branch. Fail gracefully ...
5347
 
                        revno = '?'
5348
 
                    tags[index] = (tag, revno)
5349
 
        finally:
5350
 
            branch.unlock()
 
5268
        self.add_cleanup(branch.unlock)
 
5269
        if revision:
 
5270
            graph = branch.repository.get_graph()
 
5271
            rev1, rev2 = _get_revision_range(revision, branch, self.name())
 
5272
            revid1, revid2 = rev1.rev_id, rev2.rev_id
 
5273
            # only show revisions between revid1 and revid2 (inclusive)
 
5274
            tags = [(tag, revid) for tag, revid in tags if
 
5275
                graph.is_between(revid, revid1, revid2)]
 
5276
        if sort == 'alpha':
 
5277
            tags.sort()
 
5278
        elif sort == 'time':
 
5279
            timestamps = {}
 
5280
            for tag, revid in tags:
 
5281
                try:
 
5282
                    revobj = branch.repository.get_revision(revid)
 
5283
                except errors.NoSuchRevision:
 
5284
                    timestamp = sys.maxint # place them at the end
 
5285
                else:
 
5286
                    timestamp = revobj.timestamp
 
5287
                timestamps[revid] = timestamp
 
5288
            tags.sort(key=lambda x: timestamps[x[1]])
 
5289
        if not show_ids:
 
5290
            # [ (tag, revid), ... ] -> [ (tag, dotted_revno), ... ]
 
5291
            for index, (tag, revid) in enumerate(tags):
 
5292
                try:
 
5293
                    revno = branch.revision_id_to_dotted_revno(revid)
 
5294
                    if isinstance(revno, tuple):
 
5295
                        revno = '.'.join(map(str, revno))
 
5296
                except errors.NoSuchRevision:
 
5297
                    # Bad tag data/merges can lead to tagged revisions
 
5298
                    # which are not in this branch. Fail gracefully ...
 
5299
                    revno = '?'
 
5300
                tags[index] = (tag, revno)
 
5301
        self.cleanup_now()
5351
5302
        for tag, revspec in tags:
5352
5303
            self.outf.write('%-20s %s\n' % (tag, revspec))
5353
5304
 
5488
5439
            if branch is None:
5489
5440
                raise errors.BzrCommandError('cannot create branch without'
5490
5441
                                             ' source branch')
 
5442
            to_location = directory_service.directories.dereference(
 
5443
                              to_location)
5491
5444
            if '/' not in to_location and '\\' not in to_location:
5492
5445
                # This path is meant to be relative to the existing branch
5493
5446
                this_url = self._get_branch_location(control_dir)
5769
5722
    def run_for_list(self):
5770
5723
        tree = WorkingTree.open_containing('.')[0]
5771
5724
        tree.lock_read()
5772
 
        try:
5773
 
            manager = tree.get_shelf_manager()
5774
 
            shelves = manager.active_shelves()
5775
 
            if len(shelves) == 0:
5776
 
                note('No shelved changes.')
5777
 
                return 0
5778
 
            for shelf_id in reversed(shelves):
5779
 
                message = manager.get_metadata(shelf_id).get('message')
5780
 
                if message is None:
5781
 
                    message = '<no message>'
5782
 
                self.outf.write('%3d: %s\n' % (shelf_id, message))
5783
 
            return 1
5784
 
        finally:
5785
 
            tree.unlock()
 
5725
        self.add_cleanup(tree.unlock)
 
5726
        manager = tree.get_shelf_manager()
 
5727
        shelves = manager.active_shelves()
 
5728
        if len(shelves) == 0:
 
5729
            note('No shelved changes.')
 
5730
            return 0
 
5731
        for shelf_id in reversed(shelves):
 
5732
            message = manager.get_metadata(shelf_id).get('message')
 
5733
            if message is None:
 
5734
                message = '<no message>'
 
5735
            self.outf.write('%3d: %s\n' % (shelf_id, message))
 
5736
        return 1
5786
5737
 
5787
5738
 
5788
5739
class cmd_unshelve(Command):