~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/builtins.py

  • Committer: Andrew Bennetts
  • Date: 2010-01-13 23:16:20 UTC
  • mfrom: (4957 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4960.
  • Revision ID: andrew.bennetts@canonical.com-20100113231620-n6in2yjib2v6z03g
MergeĀ lp:bzr.

Show diffs side-by-side

added added

removed removed

Lines of Context:
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
5283
5210
            ):
5284
5211
        branch, relpath = Branch.open_containing(directory)
5285
5212
        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)
 
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)
5290
5224
            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()
 
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)
5305
5230
 
5306
5231
 
5307
5232
class cmd_tags(Command):
5340
5265
            return
5341
5266
 
5342
5267
        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()
 
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()
5378
5302
        for tag, revspec in tags:
5379
5303
            self.outf.write('%-20s %s\n' % (tag, revspec))
5380
5304
 
5798
5722
    def run_for_list(self):
5799
5723
        tree = WorkingTree.open_containing('.')[0]
5800
5724
        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()
 
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
5815
5737
 
5816
5738
 
5817
5739
class cmd_unshelve(Command):
5829
5751
            enum_switch=False, value_switches=True,
5830
5752
            apply="Apply changes and remove from the shelf.",
5831
5753
            dry_run="Show changes, but do not apply or remove them.",
 
5754
            preview="Instead of unshelving the changes, show the diff that "
 
5755
                    "would result from unshelving.",
5832
5756
            delete_only="Delete changes without applying them.",
5833
5757
            keep="Apply changes but don't delete them.",
5834
5758
        )