/brz/remove-bazaar

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

« back to all changes in this revision

Viewing changes to bzrlib/graph.py

  • Committer: Martin Pool
  • Date: 2009-06-05 23:21:51 UTC
  • mfrom: (4415 +trunk)
  • mto: This revision was merged to the branch mainline in revision 4416.
  • Revision ID: mbp@sourcefrog.net-20090605232151-luwmyyl95siraqyz
merge trunk

Show diffs side-by-side

added added

removed removed

Lines of Context:
12
12
#
13
13
# You should have received a copy of the GNU General Public License
14
14
# along with this program; if not, write to the Free Software
15
 
# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
17
17
import time
18
18
 
121
121
            self._get_parent_map = self._real_provider.get_parent_map
122
122
        else:
123
123
            self._get_parent_map = get_parent_map
124
 
        self._cache = {}
125
 
        self._cache_misses = True
 
124
        self._cache = None
 
125
        self.enable_cache(True)
126
126
 
127
127
    def __repr__(self):
128
128
        return "%s(%r)" % (self.__class__.__name__, self._real_provider)
133
133
            raise AssertionError('Cache enabled when already enabled.')
134
134
        self._cache = {}
135
135
        self._cache_misses = cache_misses
 
136
        self.missing_keys = set()
136
137
 
137
138
    def disable_cache(self):
138
139
        """Disable and clear the cache."""
139
140
        self._cache = None
 
141
        self._cache_misses = None
 
142
        self.missing_keys = set()
140
143
 
141
144
    def get_cached_map(self):
142
145
        """Return any cached get_parent_map values."""
143
146
        if self._cache is None:
144
147
            return None
145
 
        return dict((k, v) for k, v in self._cache.items()
146
 
                    if v is not None)
 
148
        return dict(self._cache)
147
149
 
148
150
    def get_parent_map(self, keys):
149
151
        """See _StackedParentsProvider.get_parent_map."""
150
 
        # Hack to build up the caching logic.
151
 
        ancestry = self._cache
152
 
        if ancestry is None:
153
 
            # Caching is disabled.
154
 
            missing_revisions = set(keys)
155
 
            ancestry = {}
 
152
        cache = self._cache
 
153
        if cache is None:
 
154
            cache = self._get_parent_map(keys)
156
155
        else:
157
 
            missing_revisions = set(key for key in keys if key not in ancestry)
158
 
        if missing_revisions:
159
 
            parent_map = self._get_parent_map(missing_revisions)
160
 
            ancestry.update(parent_map)
161
 
            if self._cache_misses:
162
 
                # None is never a valid parents list, so it can be used to
163
 
                # record misses.
164
 
                ancestry.update(dict((k, None) for k in missing_revisions
165
 
                                     if k not in parent_map))
166
 
        present_keys = [k for k in keys if ancestry.get(k) is not None]
167
 
        return dict((k, ancestry[k]) for k in present_keys)
 
156
            needed_revisions = set(key for key in keys if key not in cache)
 
157
            # Do not ask for negatively cached keys
 
158
            needed_revisions.difference_update(self.missing_keys)
 
159
            if needed_revisions:
 
160
                parent_map = self._get_parent_map(needed_revisions)
 
161
                cache.update(parent_map)
 
162
                if self._cache_misses:
 
163
                    for key in needed_revisions:
 
164
                        if key not in parent_map:
 
165
                            self.note_missing_key(key)
 
166
        result = {}
 
167
        for key in keys:
 
168
            value = cache.get(key)
 
169
            if value is not None:
 
170
                result[key] = value
 
171
        return result
 
172
 
 
173
    def note_missing_key(self, key):
 
174
        """Note that key is a missing key."""
 
175
        if self._cache_misses:
 
176
            self.missing_keys.add(key)
168
177
 
169
178
 
170
179
class Graph(object):
600
609
                                 all_unique_searcher._iterations)
601
610
            unique_tip_searchers = next_unique_searchers
602
611
 
603
 
    @symbol_versioning.deprecated_method(symbol_versioning.one_one)
604
 
    def get_parents(self, revisions):
605
 
        """Find revision ids of the parents of a list of revisions
606
 
 
607
 
        A list is returned of the same length as the input.  Each entry
608
 
        is a list of parent ids for the corresponding input revision.
609
 
 
610
 
        [NULL_REVISION] is used as the parent of the first user-committed
611
 
        revision.  Its parent list is empty.
612
 
 
613
 
        If the revision is not present (i.e. a ghost), None is used in place
614
 
        of the list of parents.
615
 
 
616
 
        Deprecated in bzr 1.2 - please see get_parent_map.
617
 
        """
618
 
        parents = self.get_parent_map(revisions)
619
 
        return [parents.get(r, None) for r in revisions]
620
 
 
621
612
    def get_parent_map(self, revisions):
622
613
        """Get a map of key:parent_list for revisions.
623
614
 
1461
1452
            a SearchResult from a smart server, in which case the keys list is
1462
1453
            not necessarily immediately available.
1463
1454
        """
1464
 
        self._recipe = (start_keys, exclude_keys, key_count)
 
1455
        self._recipe = ('search', start_keys, exclude_keys, key_count)
1465
1456
        self._keys = frozenset(keys)
1466
1457
 
1467
1458
    def get_recipe(self):
1469
1460
 
1470
1461
        The recipe allows reconstruction of the same results at a later date
1471
1462
        without knowing all the found keys. The essential elements are a list
1472
 
        of keys to start and and to stop at. In order to give reproducible
 
1463
        of keys to start and to stop at. In order to give reproducible
1473
1464
        results when ghosts are encountered by a search they are automatically
1474
1465
        added to the exclude list (or else ghost filling may alter the
1475
1466
        results).
1476
1467
 
1477
 
        :return: A tuple (start_keys_set, exclude_keys_set, revision_count). To
1478
 
            recreate the results of this search, create a breadth first
1479
 
            searcher on the same graph starting at start_keys. Then call next()
1480
 
            (or next_with_ghosts()) repeatedly, and on every result, call
1481
 
            stop_searching_any on any keys from the exclude_keys set. The
1482
 
            revision_count value acts as a trivial cross-check - the found
1483
 
            revisions of the new search should have as many elements as
 
1468
        :return: A tuple ('search', start_keys_set, exclude_keys_set,
 
1469
            revision_count). To recreate the results of this search, create a
 
1470
            breadth first searcher on the same graph starting at start_keys.
 
1471
            Then call next() (or next_with_ghosts()) repeatedly, and on every
 
1472
            result, call stop_searching_any on any keys from the exclude_keys
 
1473
            set. The revision_count value acts as a trivial cross-check - the
 
1474
            found revisions of the new search should have as many elements as
1484
1475
            revision_count. If it does not, then additional revisions have been
1485
1476
            ghosted since the search was executed the first time and the second
1486
1477
            time.
1494
1485
        """
1495
1486
        return self._keys
1496
1487
 
 
1488
    def is_empty(self):
 
1489
        """Return false if the search lists 1 or more revisions."""
 
1490
        return self._recipe[3] == 0
 
1491
 
 
1492
    def refine(self, seen, referenced):
 
1493
        """Create a new search by refining this search.
 
1494
 
 
1495
        :param seen: Revisions that have been satisfied.
 
1496
        :param referenced: Revision references observed while satisfying some
 
1497
            of this search.
 
1498
        """
 
1499
        start = self._recipe[1]
 
1500
        exclude = self._recipe[2]
 
1501
        count = self._recipe[3]
 
1502
        keys = self.get_keys()
 
1503
        # New heads = referenced + old heads - seen things - exclude
 
1504
        pending_refs = set(referenced)
 
1505
        pending_refs.update(start)
 
1506
        pending_refs.difference_update(seen)
 
1507
        pending_refs.difference_update(exclude)
 
1508
        # New exclude = old exclude + satisfied heads
 
1509
        seen_heads = start.intersection(seen)
 
1510
        exclude.update(seen_heads)
 
1511
        # keys gets seen removed
 
1512
        keys = keys - seen
 
1513
        # length is reduced by len(seen)
 
1514
        count -= len(seen)
 
1515
        return SearchResult(pending_refs, exclude, count, keys)
 
1516
 
1497
1517
 
1498
1518
class PendingAncestryResult(object):
1499
1519
    """A search result that will reconstruct the ancestry for some graph heads.
1509
1529
        :param repo: a repository to use to generate the ancestry for the given
1510
1530
            heads.
1511
1531
        """
1512
 
        self.heads = heads
 
1532
        self.heads = frozenset(heads)
1513
1533
        self.repo = repo
1514
1534
 
1515
1535
    def get_recipe(self):
1516
 
        raise NotImplementedError(self.get_recipe)
 
1536
        """Return a recipe that can be used to replay this search.
 
1537
 
 
1538
        The recipe allows reconstruction of the same results at a later date.
 
1539
 
 
1540
        :seealso SearchResult.get_recipe:
 
1541
 
 
1542
        :return: A tuple ('proxy-search', start_keys_set, set(), -1)
 
1543
            To recreate this result, create a PendingAncestryResult with the
 
1544
            start_keys_set.
 
1545
        """
 
1546
        return ('proxy-search', self.heads, set(), -1)
1517
1547
 
1518
1548
    def get_keys(self):
1519
1549
        """See SearchResult.get_keys.
1526
1556
    def _get_keys(self, graph):
1527
1557
        NULL_REVISION = revision.NULL_REVISION
1528
1558
        keys = [key for (key, parents) in graph.iter_ancestry(self.heads)
1529
 
                if key != NULL_REVISION]
 
1559
                if key != NULL_REVISION and parents is not None]
1530
1560
        return keys
1531
1561
 
 
1562
    def is_empty(self):
 
1563
        """Return false if the search lists 1 or more revisions."""
 
1564
        if revision.NULL_REVISION in self.heads:
 
1565
            return len(self.heads) == 1
 
1566
        else:
 
1567
            return len(self.heads) == 0
 
1568
 
 
1569
    def refine(self, seen, referenced):
 
1570
        """Create a new search by refining this search.
 
1571
 
 
1572
        :param seen: Revisions that have been satisfied.
 
1573
        :param referenced: Revision references observed while satisfying some
 
1574
            of this search.
 
1575
        """
 
1576
        referenced = self.heads.union(referenced)
 
1577
        return PendingAncestryResult(referenced - seen, self.repo)
 
1578
 
1532
1579
 
1533
1580
def collapse_linear_regions(parent_map):
1534
1581
    """Collapse regions of the graph that are 'linear'.