~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/repository.py

(jameinel) Allow 'bzr serve' to interpret SIGHUP as a graceful shutdown.
 (bug #795025) (John A Meinel)

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
 
from __future__ import absolute_import
18
 
 
19
17
from bzrlib.lazy_import import lazy_import
20
18
lazy_import(globals(), """
21
19
import itertools
22
20
import time
23
21
 
24
22
from bzrlib import (
 
23
    bzrdir,
25
24
    config,
26
25
    controldir,
27
26
    debug,
40
39
""")
41
40
 
42
41
from bzrlib import (
43
 
    bzrdir,
44
42
    errors,
45
43
    registry,
46
44
    symbol_versioning,
76
74
    record_root_entry = True
77
75
    # whether this commit builder supports the record_entry_contents interface
78
76
    supports_record_entry_contents = False
79
 
    # whether this commit builder will automatically update the branch that is
80
 
    # being committed to
81
 
    updates_branch = False
82
77
 
83
 
    def __init__(self, repository, parents, config_stack, timestamp=None,
 
78
    def __init__(self, repository, parents, config, timestamp=None,
84
79
                 timezone=None, committer=None, revprops=None,
85
80
                 revision_id=None, lossy=False):
86
81
        """Initiate a CommitBuilder.
95
90
        :param lossy: Whether to discard data that can not be natively
96
91
            represented, when pushing to a foreign VCS 
97
92
        """
98
 
        self._config_stack = config_stack
 
93
        self._config = config
99
94
        self._lossy = lossy
100
95
 
101
96
        if committer is None:
102
 
            self._committer = self._config_stack.get('email')
 
97
            self._committer = self._config.username()
103
98
        elif not isinstance(committer, unicode):
104
99
            self._committer = committer.decode() # throw if non-ascii
105
100
        else:
347
342
        self.control_files.break_lock()
348
343
 
349
344
    @staticmethod
350
 
    def create(controldir):
351
 
        """Construct the current default format repository in controldir."""
352
 
        return RepositoryFormat.get_default_format().initialize(controldir)
 
345
    def create(a_bzrdir):
 
346
        """Construct the current default format repository in a_bzrdir."""
 
347
        return RepositoryFormat.get_default_format().initialize(a_bzrdir)
353
348
 
354
 
    def __init__(self, _format, controldir, control_files):
 
349
    def __init__(self, _format, a_bzrdir, control_files):
355
350
        """instantiate a Repository.
356
351
 
357
352
        :param _format: The format of the repository on disk.
358
 
        :param controldir: The ControlDir of the repository.
 
353
        :param a_bzrdir: The BzrDir of the repository.
359
354
        :param control_files: Control files to use for locking, etc.
360
355
        """
361
356
        # In the future we will have a single api for all stores for
364
359
        super(Repository, self).__init__()
365
360
        self._format = _format
366
361
        # the following are part of the public API for Repository:
367
 
        self.bzrdir = controldir
 
362
        self.bzrdir = a_bzrdir
368
363
        self.control_files = control_files
369
364
        # for tests
370
365
        self._write_group = None
552
547
            def __init__(self):
553
548
                self.first_call = True
554
549
 
555
 
            def __call__(self, controldir):
556
 
                # On the first call, the parameter is always the controldir
 
550
            def __call__(self, bzrdir):
 
551
                # On the first call, the parameter is always the bzrdir
557
552
                # containing the current repo.
558
553
                if not self.first_call:
559
554
                    try:
560
 
                        repository = controldir.open_repository()
 
555
                        repository = bzrdir.open_repository()
561
556
                    except errors.NoRepositoryPresent:
562
557
                        pass
563
558
                    else:
564
559
                        return False, ([], repository)
565
560
                self.first_call = False
566
 
                value = (controldir.list_branches(), None)
 
561
                value = (bzrdir.list_branches(), None)
567
562
                return True, value
568
563
 
569
564
        ret = []
570
 
        for branches, repository in controldir.ControlDir.find_bzrdirs(
 
565
        for branches, repository in bzrdir.BzrDir.find_bzrdirs(
571
566
                self.user_transport, evaluate=Evaluator()):
572
567
            if branches is not None:
573
568
                ret.extend(branches)
607
602
        For instance, if the repository is at URL/.bzr/repository,
608
603
        Repository.open(URL) -> a Repository instance.
609
604
        """
610
 
        control = controldir.ControlDir.open(base)
 
605
        control = bzrdir.BzrDir.open(base)
611
606
        return control.open_repository()
612
607
 
613
608
    def copy_content_into(self, destination, revision_id=None):
644
639
        """
645
640
 
646
641
    def suspend_write_group(self):
647
 
        """Suspend a write group.
648
 
 
649
 
        :raise UnsuspendableWriteGroup: If the write group can not be
650
 
            suspended.
651
 
        :return: List of tokens
652
 
        """
653
642
        raise errors.UnsuspendableWriteGroup(self)
654
643
 
655
644
    def refresh_data(self):
677
666
    def _resume_write_group(self, tokens):
678
667
        raise errors.UnsuspendableWriteGroup(self)
679
668
 
680
 
    def fetch(self, source, revision_id=None, find_ghosts=False):
 
669
    def fetch(self, source, revision_id=None, find_ghosts=False,
 
670
            fetch_spec=None):
681
671
        """Fetch the content required to construct revision_id from source.
682
672
 
683
 
        If revision_id is None, then all content is copied.
 
673
        If revision_id is None and fetch_spec is None, then all content is
 
674
        copied.
684
675
 
685
676
        fetch() may not be used when the repository is in a write group -
686
677
        either finish the current write group before using fetch, or use
692
683
        :param revision_id: If specified, all the content needed for this
693
684
            revision ID will be copied to the target.  Fetch will determine for
694
685
            itself which content needs to be copied.
 
686
        :param fetch_spec: If specified, a SearchResult or
 
687
            PendingAncestryResult that describes which revisions to copy.  This
 
688
            allows copying multiple heads at once.  Mutually exclusive with
 
689
            revision_id.
695
690
        """
 
691
        if fetch_spec is not None and revision_id is not None:
 
692
            raise AssertionError(
 
693
                "fetch_spec and revision_id are mutually exclusive.")
696
694
        if self.is_in_write_group():
697
695
            raise errors.InternalBzrError(
698
696
                "May not fetch while in a write group.")
700
698
        # TODO: lift out to somewhere common with RemoteRepository
701
699
        # <https://bugs.launchpad.net/bzr/+bug/401646>
702
700
        if (self.has_same_location(source)
 
701
            and fetch_spec is None
703
702
            and self._has_same_fallbacks(source)):
704
703
            # check that last_revision is in 'from' and then return a
705
704
            # no-operation.
708
707
                self.get_revision(revision_id)
709
708
            return 0, []
710
709
        inter = InterRepository.get(source, self)
711
 
        return inter.fetch(revision_id=revision_id, find_ghosts=find_ghosts)
 
710
        return inter.fetch(revision_id=revision_id,
 
711
            find_ghosts=find_ghosts, fetch_spec=fetch_spec)
712
712
 
713
713
    def create_bundle(self, target, base, fileobj, format=None):
714
714
        return serializer.write_bundle(self, target, base, fileobj, format)
715
715
 
716
 
    def get_commit_builder(self, branch, parents, config_stack, timestamp=None,
 
716
    def get_commit_builder(self, branch, parents, config, timestamp=None,
717
717
                           timezone=None, committer=None, revprops=None,
718
718
                           revision_id=None, lossy=False):
719
719
        """Obtain a CommitBuilder for this repository.
720
720
 
721
721
        :param branch: Branch to commit to.
722
722
        :param parents: Revision ids of the parents of the new revision.
723
 
        :param config_stack: Configuration stack to use.
 
723
        :param config: Configuration to use.
724
724
        :param timestamp: Optional timestamp recorded for commit.
725
725
        :param timezone: Optional timezone for timestamp.
726
726
        :param committer: Optional committer to set for commit.
746
746
                repo.unlock()
747
747
 
748
748
    @needs_read_lock
749
 
    def clone(self, controldir, revision_id=None):
750
 
        """Clone this repository into controldir using the current format.
 
749
    def clone(self, a_bzrdir, revision_id=None):
 
750
        """Clone this repository into a_bzrdir using the current format.
751
751
 
752
752
        Currently no check is made that the format of this repository and
753
753
        the bzrdir format are compatible. FIXME RBC 20060201.
756
756
        """
757
757
        # TODO: deprecate after 0.16; cloning this with all its settings is
758
758
        # probably not very useful -- mbp 20070423
759
 
        dest_repo = self._create_sprouting_repo(
760
 
            controldir, shared=self.is_shared())
 
759
        dest_repo = self._create_sprouting_repo(a_bzrdir, shared=self.is_shared())
761
760
        self.copy_content_into(dest_repo, revision_id)
762
761
        return dest_repo
763
762
 
930
929
        parent_ids.discard(_mod_revision.NULL_REVISION)
931
930
        return parent_ids
932
931
 
 
932
    def fileids_altered_by_revision_ids(self, revision_ids):
 
933
        """Find the file ids and versions affected by revisions.
 
934
 
 
935
        :param revisions: an iterable containing revision ids.
 
936
        :return: a dictionary mapping altered file-ids to an iterable of
 
937
            revision_ids. Each altered file-ids has the exact revision_ids
 
938
            that altered it listed explicitly.
 
939
        """
 
940
        raise NotImplementedError(self.fileids_altered_by_revision_ids)
 
941
 
933
942
    def iter_files_bytes(self, desired_files):
934
943
        """Iterate through file versions.
935
944
 
981
990
            raise AssertionError('_iter_for_revno returned too much history')
982
991
        return (True, partial_history[-1])
983
992
 
 
993
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
994
    def iter_reverse_revision_history(self, revision_id):
 
995
        """Iterate backwards through revision ids in the lefthand history
 
996
 
 
997
        :param revision_id: The revision id to start with.  All its lefthand
 
998
            ancestors will be traversed.
 
999
        """
 
1000
        graph = self.get_graph()
 
1001
        stop_revisions = (None, _mod_revision.NULL_REVISION)
 
1002
        return graph.iter_lefthand_ancestry(revision_id, stop_revisions)
 
1003
 
984
1004
    def is_shared(self):
985
1005
        """Return True if this repository is flagged as a shared repository."""
986
1006
        raise NotImplementedError(self.is_shared)
1023
1043
        """
1024
1044
        raise NotImplementedError(self.revision_trees)
1025
1045
 
 
1046
    @needs_read_lock
 
1047
    @symbol_versioning.deprecated_method(
 
1048
        symbol_versioning.deprecated_in((2, 4, 0)))
 
1049
    def get_ancestry(self, revision_id, topo_sorted=True):
 
1050
        """Return a list of revision-ids integrated by a revision.
 
1051
 
 
1052
        The first element of the list is always None, indicating the origin
 
1053
        revision.  This might change when we have history horizons, or
 
1054
        perhaps we should have a new API.
 
1055
 
 
1056
        This is topologically sorted.
 
1057
        """
 
1058
        if 'evil' in debug.debug_flags:
 
1059
            mutter_callsite(2, "get_ancestry is linear with history.")
 
1060
        if _mod_revision.is_null(revision_id):
 
1061
            return [None]
 
1062
        if not self.has_revision(revision_id):
 
1063
            raise errors.NoSuchRevision(self, revision_id)
 
1064
        graph = self.get_graph()
 
1065
        keys = set()
 
1066
        search = graph._make_breadth_first_searcher([revision_id])
 
1067
        while True:
 
1068
            try:
 
1069
                found, ghosts = search.next_with_ghosts()
 
1070
            except StopIteration:
 
1071
                break
 
1072
            keys.update(found)
 
1073
        if _mod_revision.NULL_REVISION in keys:
 
1074
            keys.remove(_mod_revision.NULL_REVISION)
 
1075
        if topo_sorted:
 
1076
            parent_map = graph.get_parent_map(keys)
 
1077
            keys = tsort.topo_sort(parent_map)
 
1078
        return [None] + list(keys)
 
1079
 
1026
1080
    def pack(self, hint=None, clean_obsolete_packs=False):
1027
1081
        """Compress the data within the repository.
1028
1082
 
1104
1158
                [parents_provider, other_repository._make_parents_provider()])
1105
1159
        return graph.Graph(parents_provider)
1106
1160
 
 
1161
    def revision_ids_to_search_result(self, result_set):
 
1162
        """Convert a set of revision ids to a graph SearchResult."""
 
1163
        result_parents = set()
 
1164
        for parents in self.get_graph().get_parent_map(
 
1165
            result_set).itervalues():
 
1166
            result_parents.update(parents)
 
1167
        included_keys = result_set.intersection(result_parents)
 
1168
        start_keys = result_set.difference(included_keys)
 
1169
        exclude_keys = result_parents.difference(result_set)
 
1170
        result = graph.SearchResult(start_keys, exclude_keys,
 
1171
            len(result_set), result_set)
 
1172
        return result
 
1173
 
1107
1174
    @needs_write_lock
1108
1175
    def set_make_working_trees(self, new_value):
1109
1176
        """Set the policy flag for making working trees when creating branches.
1127
1194
        self.store_revision_signature(gpg_strategy, plaintext, revision_id)
1128
1195
 
1129
1196
    @needs_read_lock
1130
 
    def verify_revision_signature(self, revision_id, gpg_strategy):
 
1197
    def verify_revision(self, revision_id, gpg_strategy):
1131
1198
        """Verify the signature on a revision.
1132
 
 
 
1199
        
1133
1200
        :param revision_id: the revision to verify
1134
1201
        :gpg_strategy: the GPGStrategy object to used
1135
 
 
 
1202
        
1136
1203
        :return: gpg.SIGNATURE_VALID or a failed SIGNATURE_ value
1137
1204
        """
1138
1205
        if not self.has_signature_for_revision_id(revision_id):
1144
1211
 
1145
1212
        return gpg_strategy.verify(signature, plaintext)
1146
1213
 
1147
 
    @needs_read_lock
1148
 
    def verify_revision_signatures(self, revision_ids, gpg_strategy):
1149
 
        """Verify revision signatures for a number of revisions.
1150
 
 
1151
 
        :param revision_id: the revision to verify
1152
 
        :gpg_strategy: the GPGStrategy object to used
1153
 
        :return: Iterator over tuples with revision id, result and keys
1154
 
        """
1155
 
        for revid in revision_ids:
1156
 
            (result, key) = self.verify_revision_signature(revid, gpg_strategy)
1157
 
            yield revid, result, key
1158
 
 
1159
1214
    def has_signature_for_revision_id(self, revision_id):
1160
1215
        """Query for a revision signature for revision_id in the repository."""
1161
1216
        raise NotImplementedError(self.has_signature_for_revision_id)
1191
1246
            return
1192
1247
        try:
1193
1248
            if branch is None:
1194
 
                conf = config.GlobalStack()
 
1249
                conf = config.GlobalConfig()
1195
1250
            else:
1196
 
                conf = branch.get_config_stack()
1197
 
            if 'format_deprecation' in conf.get('suppress_warnings'):
 
1251
                conf = branch.get_config()
 
1252
            if conf.suppress_warning('format_deprecation'):
1198
1253
                return
1199
1254
            warning("Format %s for %s is deprecated -"
1200
1255
                    " please use 'bzr upgrade' to get better performance"
1260
1315
        """Returns the policy for making working trees on new branches."""
1261
1316
        return not self._transport.has('no-working-trees')
1262
1317
 
1263
 
    @needs_write_lock
1264
 
    def update_feature_flags(self, updated_flags):
1265
 
        """Update the feature flags for this branch.
1266
 
 
1267
 
        :param updated_flags: Dictionary mapping feature names to necessities
1268
 
            A necessity can be None to indicate the feature should be removed
1269
 
        """
1270
 
        self._format._update_feature_flags(updated_flags)
1271
 
        self.control_transport.put_bytes('format', self._format.as_string())
1272
 
 
1273
1318
 
1274
1319
class RepositoryFormatRegistry(controldir.ControlComponentFormatRegistry):
1275
1320
    """Repository format registry."""
1276
1321
 
1277
1322
    def get_default(self):
1278
1323
        """Return the current default format."""
1279
 
        return controldir.format_registry.make_bzrdir('default').repository_format
 
1324
        from bzrlib import bzrdir
 
1325
        return bzrdir.format_registry.make_bzrdir('default').repository_format
1280
1326
 
1281
1327
 
1282
1328
network_format_registry = registry.FormatRegistry()
1325
1371
    created.
1326
1372
 
1327
1373
    Common instance attributes:
1328
 
    _matchingbzrdir - the controldir format that the repository format was
 
1374
    _matchingbzrdir - the bzrdir format that the repository format was
1329
1375
    originally written to work with. This can be used if manually
1330
1376
    constructing a bzrdir and repository, or more commonly for test suite
1331
1377
    parameterization.
1373
1419
    supports_versioned_directories = None
1374
1420
    # Can other repositories be nested into one of this format?
1375
1421
    supports_nesting_repositories = None
1376
 
    # Is it possible for revisions to be present without being referenced
1377
 
    # somewhere ?
1378
 
    supports_unreferenced_revisions = None
1379
1422
 
1380
1423
    def __repr__(self):
1381
1424
        return "%s()" % self.__class__.__name__
1387
1430
    def __ne__(self, other):
1388
1431
        return not self == other
1389
1432
 
 
1433
    @classmethod
 
1434
    def find_format(klass, a_bzrdir):
 
1435
        """Return the format for the repository object in a_bzrdir.
 
1436
 
 
1437
        This is used by bzr native formats that have a "format" file in
 
1438
        the repository.  Other methods may be used by different types of
 
1439
        control directory.
 
1440
        """
 
1441
        try:
 
1442
            transport = a_bzrdir.get_repository_transport(None)
 
1443
            format_string = transport.get_bytes("format")
 
1444
            return format_registry.get(format_string)
 
1445
        except errors.NoSuchFile:
 
1446
            raise errors.NoRepositoryPresent(a_bzrdir)
 
1447
        except KeyError:
 
1448
            raise errors.UnknownFormatError(format=format_string,
 
1449
                                            kind='repository')
 
1450
 
 
1451
    @classmethod
 
1452
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
1453
    def register_format(klass, format):
 
1454
        format_registry.register(format)
 
1455
 
 
1456
    @classmethod
 
1457
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
1458
    def unregister_format(klass, format):
 
1459
        format_registry.remove(format)
 
1460
 
 
1461
    @classmethod
 
1462
    @symbol_versioning.deprecated_method(symbol_versioning.deprecated_in((2, 4, 0)))
 
1463
    def get_default_format(klass):
 
1464
        """Return the current default format."""
 
1465
        return format_registry.get_default()
 
1466
 
 
1467
    def get_format_string(self):
 
1468
        """Return the ASCII format string that identifies this format.
 
1469
 
 
1470
        Note that in pre format ?? repositories the format string is
 
1471
        not permitted nor written to disk.
 
1472
        """
 
1473
        raise NotImplementedError(self.get_format_string)
 
1474
 
1390
1475
    def get_format_description(self):
1391
1476
        """Return the short description for this format."""
1392
1477
        raise NotImplementedError(self.get_format_description)
1393
1478
 
1394
 
    def initialize(self, controldir, shared=False):
1395
 
        """Initialize a repository of this format in controldir.
 
1479
    def initialize(self, a_bzrdir, shared=False):
 
1480
        """Initialize a repository of this format in a_bzrdir.
1396
1481
 
1397
 
        :param controldir: The controldir to put the new repository in it.
 
1482
        :param a_bzrdir: The bzrdir to put the new repository in it.
1398
1483
        :param shared: The repository should be initialized as a sharable one.
1399
1484
        :returns: The new repository object.
1400
1485
 
1401
1486
        This may raise UninitializableFormat if shared repository are not
1402
 
        compatible the controldir.
 
1487
        compatible the a_bzrdir.
1403
1488
        """
1404
1489
        raise NotImplementedError(self.initialize)
1405
1490
 
1441
1526
                'Does not support nested trees', target_format,
1442
1527
                from_format=self)
1443
1528
 
1444
 
    def open(self, controldir, _found=False):
1445
 
        """Return an instance of this format for a controldir.
 
1529
    def open(self, a_bzrdir, _found=False):
 
1530
        """Return an instance of this format for the bzrdir a_bzrdir.
1446
1531
 
1447
1532
        _found is a private parameter, do not use it.
1448
1533
        """
1449
1534
        raise NotImplementedError(self.open)
1450
1535
 
1451
 
    def _run_post_repo_init_hooks(self, repository, controldir, shared):
1452
 
        from bzrlib.controldir import ControlDir, RepoInitHookParams
1453
 
        hooks = ControlDir.hooks['post_repo_init']
 
1536
    def _run_post_repo_init_hooks(self, repository, a_bzrdir, shared):
 
1537
        from bzrlib.bzrdir import BzrDir, RepoInitHookParams
 
1538
        hooks = BzrDir.hooks['post_repo_init']
1454
1539
        if not hooks:
1455
1540
            return
1456
 
        params = RepoInitHookParams(repository, self, controldir, shared)
 
1541
        params = RepoInitHookParams(repository, self, a_bzrdir, shared)
1457
1542
        for hook in hooks:
1458
1543
            hook(params)
1459
1544
 
1460
1545
 
1461
 
class RepositoryFormatMetaDir(bzrdir.BzrFormat, RepositoryFormat):
 
1546
class MetaDirRepositoryFormat(RepositoryFormat):
1462
1547
    """Common base class for the new repositories using the metadir layout."""
1463
1548
 
1464
1549
    rich_root_data = False
1474
1559
        return matching
1475
1560
 
1476
1561
    def __init__(self):
1477
 
        RepositoryFormat.__init__(self)
1478
 
        bzrdir.BzrFormat.__init__(self)
 
1562
        super(MetaDirRepositoryFormat, self).__init__()
1479
1563
 
1480
1564
    def _create_control_files(self, a_bzrdir):
1481
1565
        """Create the required files and the initial control_files object."""
1505
1589
        finally:
1506
1590
            control_files.unlock()
1507
1591
 
1508
 
    @classmethod
1509
 
    def find_format(klass, a_bzrdir):
1510
 
        """Return the format for the repository object in a_bzrdir.
1511
 
 
1512
 
        This is used by bzr native formats that have a "format" file in
1513
 
        the repository.  Other methods may be used by different types of
1514
 
        control directory.
1515
 
        """
1516
 
        try:
1517
 
            transport = a_bzrdir.get_repository_transport(None)
1518
 
            format_string = transport.get_bytes("format")
1519
 
        except errors.NoSuchFile:
1520
 
            raise errors.NoRepositoryPresent(a_bzrdir)
1521
 
        return klass._find_format(format_registry, 'repository', format_string)
1522
 
 
1523
 
    def check_support_status(self, allow_unsupported, recommend_upgrade=True,
1524
 
            basedir=None):
1525
 
        RepositoryFormat.check_support_status(self,
1526
 
            allow_unsupported=allow_unsupported, recommend_upgrade=recommend_upgrade,
1527
 
            basedir=basedir)
1528
 
        bzrdir.BzrFormat.check_support_status(self, allow_unsupported=allow_unsupported,
1529
 
            recommend_upgrade=recommend_upgrade, basedir=basedir)
 
1592
    def network_name(self):
 
1593
        """Metadir formats have matching disk and network format strings."""
 
1594
        return self.get_format_string()
1530
1595
 
1531
1596
 
1532
1597
# formats which have no format string are not discoverable or independently
1533
1598
# creatable on disk, so are not registered in format_registry.  They're
1534
1599
# all in bzrlib.repofmt.knitreponow.  When an instance of one of these is
1535
 
# needed, it's constructed directly by the ControlDir.  Non-native formats where
 
1600
# needed, it's constructed directly by the BzrDir.  Non-native formats where
1536
1601
# the repository is not separately opened are similar.
1537
1602
 
1538
1603
format_registry.register_lazy(
1649
1714
        self.target.fetch(self.source, revision_id=revision_id)
1650
1715
 
1651
1716
    @needs_write_lock
1652
 
    def fetch(self, revision_id=None, find_ghosts=False):
 
1717
    def fetch(self, revision_id=None, find_ghosts=False,
 
1718
            fetch_spec=None):
1653
1719
        """Fetch the content required to construct revision_id.
1654
1720
 
1655
1721
        The content is copied from self.source to self.target.