191
191
class History (object):
193
193
def __init__(self):
194
self._change_cache = None
195
194
self._file_change_cache = None
197
195
self._lock = threading.RLock()
200
def from_branch(cls, branch, name=None):
198
def from_branch(cls, branch):
203
201
self._branch = branch
204
202
self._last_revid = self._branch.last_revision()
207
name = self._branch.nick
209
self.log = logging.getLogger('loggerhead.%s' % (name,))
204
self.log = logging.getLogger('loggerhead.%s' % (self._branch.nick,))
211
206
graph = branch.repository.get_graph()
212
207
parent_map = dict(((key, value) for key, value in
257
252
return revision_graph
260
def from_folder(cls, path, name=None):
255
def from_folder(cls, path):
261
256
b = bzrlib.branch.Branch.open(path)
264
return cls.from_branch(b, name)
259
return cls.from_branch(b)
278
273
return self._branch.last_revision() != self._last_revid
280
def use_cache(self, cache):
281
self._change_cache = cache
283
275
def use_file_cache(self, cache):
284
276
self._file_change_cache = cache
286
def use_search_index(self, index):
290
279
def has_revisions(self):
291
280
return not bzrlib.revision.is_null(self.last_revid)
295
# called when a new history object needs to be created, because the
296
# branch history has changed. we need to immediately close and stop
297
# using our caches, because a new history object will be created to
298
# replace us, using the same cache files.
299
# (may also be called during server shutdown.)
300
if self._change_cache is not None:
301
self._change_cache.close()
302
self._change_cache = None
303
if self._index is not None:
307
def flush_cache(self):
308
if self._change_cache is None:
310
self._change_cache.flush()
312
def check_rebuild(self):
313
if self._change_cache is not None:
314
self._change_cache.check_rebuild()
315
#if self._index is not None:
316
# self._index.check_rebuild()
318
282
last_revid = property(lambda self: self._last_revid, None, None)
320
284
@with_branch_lock
321
285
def get_config(self):
322
286
return self._branch.get_config()
325
288
def get_revno(self, revid):
326
289
if revid not in self._revision_info:
385
348
return revid_list[index:]
387
350
@with_branch_lock
388
def get_revision_history_matching(self, revid_list, text):
389
self.log.debug('searching %d revisions for %r', len(revid_list), text)
391
# this is going to be painfully slow. :(
394
for revid in revid_list:
395
change = self.get_changes([ revid ])[0]
396
if text in change.comment.lower():
398
self.log.debug('searched %d revisions for %r in %r secs', len(revid_list), text, time.time() - z)
401
def get_revision_history_matching_indexed(self, revid_list, text):
402
self.log.debug('searching %d revisions for %r', len(revid_list), text)
404
if self._index is None:
405
return self.get_revision_history_matching(revid_list, text)
406
out = self._index.find(text, revid_list)
407
self.log.debug('searched %d revisions for %r in %r secs: %d results', len(revid_list), text, time.time() - z, len(out))
408
# put them in some coherent order :)
409
out = [r for r in self._full_history if r in out]
413
351
def get_search_revid_list(self, query, revid_list):
415
353
given a "quick-search" query, try a few obvious possible meanings:
451
389
revid_list = list(self.get_revids_from(None, self._last_revid))
452
390
return self.get_revision_history_since(revid_list, date)
454
# check comment fields.
455
if revid_list is None:
456
revid_list = self._full_history
457
return self.get_revision_history_matching_indexed(revid_list, query)
459
392
revno_re = re.compile(r'^[\d\.]+$')
460
393
# the date regex are without a final '$' so that queries like
461
394
# "2006-11-30 12:15" still mostly work. (i think it's better to give
540
473
revid_list = None
542
475
revid_list = self.get_search_revid_list(query, revid_list)
543
if len(revid_list) > 0:
476
if revid_list and len(revid_list) > 0:
544
477
if revid not in revid_list:
545
478
revid = revid_list[0]
546
479
return revid, start_revid, revid_list
549
481
return None, None, []
551
483
@with_branch_lock
648
579
Revisions not present and NULL_REVISION will be ignored.
650
if self._change_cache is None:
651
changes = self.get_changes_uncached(revid_list)
653
changes = self._change_cache.get_changes(revid_list)
581
changes = self.get_changes_uncached(revid_list)
654
582
if len(changes) == 0:
660
588
merge_revids = self.simplify_merge_point_list(self.get_merge_point_list(change.revid))
661
589
change.merge_points = [util.Container(revid=r, revno=self.get_revno(r)) for r in merge_revids]
662
590
if len(change.parents) > 0:
663
if isinstance(change.parents[0], util.Container):
664
# old cache stored a potentially-bogus revno
665
change.parents = [util.Container(revid=p.revid, revno=self.get_revno(p.revid)) for p in change.parents]
667
change.parents = [util.Container(revid=r, revno=self.get_revno(r)) for r in change.parents]
591
change.parents = [util.Container(revid=r,
592
revno=self.get_revno(r)) for r in change.parents]
668
593
change.revno = self.get_revno(change.revid)
677
# alright, let's profile this sucka. (FIXME remove this eventually...)
678
def _get_changes_profiled(self, revid_list):
679
from loggerhead.lsprof import profile
681
ret, stats = profile(self.get_changes_uncached, revid_list)
684
cPickle.dump(stats, open('lsprof.stats', 'w'), 2)
685
self.log.info('lsprof complete!')
688
602
@with_branch_lock
689
603
@with_bzrlib_read_lock
690
604
def get_changes_uncached(self, revid_list):
605
# FIXME: deprecated method in getting a null revision
691
606
revid_list = filter(lambda revid: not bzrlib.revision.is_null(revid),
693
repo = self._branch.repository
694
parent_map = repo.get_graph().get_parent_map(revid_list)
608
parent_map = self._branch.repository.get_graph().get_parent_map(revid_list)
695
609
# We need to return the answer in the same order as the input,
696
610
# less any ghosts.
697
611
present_revids = [revid for revid in revid_list
698
612
if revid in parent_map]
699
rev_list = repo.get_revisions(present_revids)
613
rev_list = self._branch.repository.get_revisions(present_revids)
701
615
return [self._change_from_revision(rev) for rev in rev_list]