38
from breezy import tag
39
37
import breezy.branch
40
38
import breezy.delta
41
39
import breezy.errors
42
40
import breezy.foreign
43
42
import breezy.revision
45
from loggerhead import search
46
from loggerhead import util
47
from loggerhead.wholehistory import compute_whole_history_data
43
from breezy import tag
46
from breezy.transport import NoSuchFile
48
from breezy.errors import NoSuchFile
50
from . import search, util
51
from .wholehistory import compute_whole_history_data
50
54
def is_branch(folder):
52
56
breezy.branch.Branch.open(folder)
58
except breezy.errors.NotBranchError:
117
122
self.old_tree = old_tree
118
123
self.new_tree = new_tree
120
def revid(self, tree, file_id):
125
def revid(self, tree, path):
127
return breezy.revision.NULL_REVISION
122
path = tree.id2path(file_id)
123
return tree.get_file_revision(path, file_id)
124
except breezy.errors.NoSuchId:
129
return tree.get_file_revision(path)
131
return breezy.revision.NULL_REVISION
127
def report(self, file_id, paths, versioned, renamed, modified,
133
def report(self, paths, versioned, renamed, copied, modified,
128
134
exe_change, kind):
136
paths, versioned, renamed, copied,
137
modified, exe_change, kind)
139
def _report(self, paths, versioned, renamed, copied, modified,
129
141
if modified not in ('unchanged', 'kind changed'):
130
142
if versioned == 'removed':
131
143
filename = rich_filename(paths[0], kind[0])
133
145
filename = rich_filename(paths[1], kind[1])
134
146
self.text_changes.append(util.Container(
135
filename=filename, file_id=file_id,
136
old_revision=self.revid(self.old_tree, file_id),
137
new_revision=self.revid(self.new_tree, file_id)))
148
old_revision=self.revid(self.old_tree, paths[0]),
149
new_revision=self.revid(self.new_tree, paths[1])))
138
150
if versioned == 'added':
139
151
self.added.append(util.Container(
140
152
filename=rich_filename(paths[1], kind), kind=kind[1]))
151
163
filename=rich_filename(paths[1], kind),
152
164
text_modified=modified == 'modified', exe_change=exe_change))
154
167
# The lru_cache is not thread-safe, so we need a lock around it for
156
169
rev_info_memory_cache_lock = threading.RLock()
158
172
class RevInfoMemoryCache(object):
159
173
"""A store that validates values against the revids they were stored with.
200
214
rev_info_memory_cache_lock.release()
202
# Used to store locks that prevent multiple threads from building a
216
# Used to store locks that prevent multiple threads from building a
203
217
# revision graph for the same branch at the same time, because that can
204
218
# cause severe performance issues that are so bad that the system seems
358
372
def get_short_revision_history_by_fileid(self, file_id):
359
373
# FIXME: would be awesome if we could get, for a folder, the list of
360
374
# revisions where items within that folder changed.i
375
# TODO(jelmer): Avoid versionedfile-specific texts
361
376
possible_keys = [(file_id, revid) for revid in self._rev_indices]
362
377
get_parent_map = self._branch.repository.texts.get_parent_map
363
378
# We chunk the requests as this works better with GraphIndex.
365
380
# for more information.
367
382
chunk_size = 1000
368
for start in xrange(0, len(possible_keys), chunk_size):
383
for start in range(0, len(possible_keys), chunk_size):
369
384
next_keys = possible_keys[start:start + chunk_size]
370
385
revids += [k[1] for k in get_parent_map(next_keys)]
371
386
del possible_keys, next_keys
406
421
# ignore the passed-in revid_list
407
422
revid = self.fix_revid(query)
408
423
if revid is not None:
409
if isinstance(revid, unicode):
410
revid = revid.encode('utf-8')
411
424
changes = self.get_changes([revid])
412
425
if (changes is not None) and (len(changes) > 0):
456
471
revid = self._revno_revid[revid]
458
473
raise breezy.errors.NoSuchRevision(self._branch_nick, revid)
474
if not isinstance(revid, bytes):
475
revid = revid.encode('utf-8')
461
def get_file_view(self, revid, file_id):
463
Given a revid and optional path, return a (revlist, revid) for
464
navigation through the current scope: from the revid (or the latest
465
revision) back to the original revision.
467
If file_id is None, the entire revision history is the list scope.
470
revid = self.last_revid
471
if file_id is not None:
473
self.get_short_revision_history_by_fileid(file_id))
474
revlist = self.get_revids_from(revlist, revid)
476
revlist = self.get_revids_from(None, revid)
480
479
def _iterate_sufficiently(iterable, stop_at, extra_rev_count):
481
480
"""Return a list of iterable.
505
def get_view(self, revid, start_revid, file_id, query=None,
504
def _get_file_view(self, revid, file_id):
506
Given a revid and optional path, return a (revlist, revid) for
507
navigation through the current scope: from the revid (or the latest
508
revision) back to the original revision.
510
If file_id is None, the entire revision history is the list scope.
513
revid = self.last_revid
514
if file_id is not None:
516
self.get_short_revision_history_by_fileid(file_id))
517
revlist = self.get_revids_from(revlist, revid)
519
revlist = self.get_revids_from(None, revid)
522
def get_view(self, revid, start_revid, path, query=None,
506
523
extra_rev_count=None):
508
use the URL parameters (revid, start_revid, file_id, and query) to
509
determine the revision list we're viewing (start_revid, file_id, query)
525
use the URL parameters (revid, start_revid, path, and query) to
526
determine the revision list we're viewing (start_revid, path, query)
510
527
and where we are in it (revid).
512
529
- if a query is given, we're viewing query results.
513
- if a file_id is given, we're viewing revisions for a specific
530
- if a path is given, we're viewing revisions for a specific
515
532
- if a start_revid is given, we're viewing the branch from a
516
533
specific revision up the tree.
528
545
- start_revid: starting revision of this view
529
546
- revid_list: list of revision ids for this view
531
file_id and query are never changed so aren't returned, but they may
548
path and query are never changed so aren't returned, but they may
532
549
contain vital context for future url navigation.
534
551
if start_revid is None:
535
552
start_revid = self.last_revid
537
554
if query is None:
538
revid_list = self.get_file_view(start_revid, file_id)
555
repo = self._branch.repository
557
file_id = repo.revision_tree(start_revid).path2id(path)
560
revid_list = self._get_file_view(start_revid, file_id)
539
561
revid_list = self._iterate_sufficiently(revid_list, revid,
541
563
if revid is None:
543
565
if revid not in revid_list:
544
566
# if the given revid is not in the revlist, use a revlist that
545
567
# starts at the given revid.
546
revid_list = self.get_file_view(revid, file_id)
568
revid_list = self._get_file_view(revid, file_id)
547
569
revid_list = self._iterate_sufficiently(revid_list, revid,
549
571
start_revid = revid
550
572
return revid, start_revid, revid_list
552
576
# potentially limit the search
553
577
if file_id is not None:
554
revid_list = self.get_file_view(start_revid, file_id)
578
revid_list = self._get_file_view(start_revid, file_id)
556
580
revid_list = None
557
581
revid_list = search.search_revisions(self._branch, query)
566
590
return None, None, []
568
def get_inventory(self, revid):
569
if revid not in self._inventory_cache:
570
self._inventory_cache[revid] = (
571
self._branch.repository.get_inventory(revid))
572
return self._inventory_cache[revid]
574
def get_path(self, revid, file_id):
575
if (file_id is None) or (file_id == ''):
577
path = self.get_inventory(revid).id2path(file_id)
578
if (len(path) > 0) and not path.startswith('/'):
582
def get_file_id(self, revid, path):
583
if (len(path) > 0) and not path.startswith('/'):
585
return self.get_inventory(revid).path2id(path)
592
def revision_tree(self, revid):
593
return self._branch.repository.revision_tree(revid)
595
def file_exists(self, revid, path):
596
if (len(path) > 0) and not path.startswith('/'):
599
return self.revision_tree(revid).has_filename(path)
600
except breezy.errors.NoSuchRevision:
587
603
def get_merge_point_list(self, revid):
620
636
d[revnos] = (revnolast, revid)
622
return [revid for (_, revid) in d.itervalues()]
638
return [revid for (_, revid) in d.values()]
624
640
def add_branch_nicks(self, change):
632
648
for p in change.merge_points:
633
649
fetch_set.add(p.revid)
634
650
p_changes = self.get_changes(list(fetch_set))
635
p_change_dict = dict([(c.revid, c) for c in p_changes])
651
p_change_dict = {c.revid: c for c in p_changes}
636
652
for p in change.parents:
637
653
if p.revid in p_change_dict:
638
654
p.branch_nick = p_change_dict[p.revid].branch_nick
676
695
def get_changes_uncached(self, revid_list):
677
696
# FIXME: deprecated method in getting a null revision
678
revid_list = filter(lambda revid: not breezy.revision.is_null(revid),
697
revid_list = list(filter(lambda revid: not breezy.revision.is_null(revid),
680
699
parent_map = self._branch.repository.get_graph().get_parent_map(
682
701
# We need to return the answer in the same order as the input,
713
732
'revid': revision.revision_id,
714
733
'date': datetime.datetime.fromtimestamp(revision.timestamp),
715
734
'utc_date': datetime.datetime.utcfromtimestamp(revision.timestamp),
735
'timestamp': revision.timestamp,
716
736
'committer': revision.committer,
717
737
'authors': revision.get_apparent_authors(),
718
738
'branch_nick': revision.properties.get('branch-nick', None),
726
746
if isinstance(revision, breezy.foreign.ForeignRevision):
727
747
foreign_revid, mapping = (
728
748
revision.foreign_revid, revision.mapping)
729
elif ":" in revision.revision_id:
749
elif b":" in revision.revision_id:
731
751
foreign_revid, mapping = \
732
752
breezy.foreign.foreign_vcs_registry.parse_revision_id(
752
772
changes = self.get_file_changes(entry)
753
773
entry.changes = changes
755
def get_file(self, file_id, revid):
775
def get_file(self, path, revid):
756
776
"""Returns (path, filename, file contents)"""
757
inv = self.get_inventory(revid)
758
inv_entry = inv[file_id]
759
rev_tree = self._branch.repository.revision_tree(inv_entry.revision)
760
path = inv.id2path(file_id)
761
if not path.startswith('/'):
777
if not isinstance(path, str):
778
raise TypeError(path)
779
if not isinstance(revid, bytes):
780
raise TypeError(revid)
781
rev_tree = self._branch.repository.revision_tree(revid)
783
if not display_path.startswith('/'):
762
784
path = '/' + path
763
return path, inv_entry.name, rev_tree.get_file_text(file_id)
785
return (display_path, breezy.osutils.basename(path),
786
rev_tree.get_file_text(path))
765
788
def file_changes_for_revision_ids(self, old_revid, new_revid):
767
790
Return a nested data structure containing the changes in a delta::
769
added: list((filename, file_id)),
770
renamed: list((old_filename, new_filename, file_id)),
771
deleted: list((filename, file_id)),
776
text_changes: list((filename, file_id)),
792
added: list((filename)),
793
renamed: list((old_filename, new_filename)),
794
deleted: list((filename)),
795
modified: list((filename)),
796
text_changes: list((filename)),
778
798
repo = self._branch.repository
779
799
if (breezy.revision.is_null(old_revid) or
780
breezy.revision.is_null(new_revid) or
781
old_revid == new_revid):
800
breezy.revision.is_null(new_revid) or
801
old_revid == new_revid):
782
802
old_tree, new_tree = map(
783
803
repo.revision_tree, [old_revid, new_revid])