/loggerhead/trunk

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/loggerhead/trunk

« back to all changes in this revision

Viewing changes to loggerhead/history.py

  • Committer: Ubuntu One Auto Copilot
  • Author(s): Jelmer Vernooij
  • Date: 2023-02-01 10:38:17 UTC
  • mfrom: (543.2.1 lp:loggerhead)
  • Revision ID: otto-copilot@canonical.com-20230201103817-h68q8zmdvm7u1vv4
Sort Python import definitions

Merged from https://code.launchpad.net/~jelmer/loggerhead/isort/+merge/436635

Show diffs side-by-side

added added

removed removed

Lines of Context:
33
33
import re
34
34
import textwrap
35
35
import threading
36
 
import tarfile
37
36
 
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
 
41
import breezy.osutils
43
42
import breezy.revision
44
 
 
45
 
from loggerhead import search
46
 
from loggerhead import util
47
 
from loggerhead.wholehistory import compute_whole_history_data
 
43
from breezy import tag
 
44
 
 
45
try:
 
46
    from breezy.transport import NoSuchFile
 
47
except ImportError:
 
48
    from breezy.errors import NoSuchFile
 
49
 
 
50
from . import search, util
 
51
from .wholehistory import compute_whole_history_data
48
52
 
49
53
 
50
54
def is_branch(folder):
51
55
    try:
52
56
        breezy.branch.Branch.open(folder)
53
57
        return True
54
 
    except:
 
58
    except breezy.errors.NotBranchError:
55
59
        return False
56
60
 
57
61
 
106
110
    def __len__(self):
107
111
        return len(self.revid_list)
108
112
 
 
113
 
109
114
class FileChangeReporter(object):
110
115
 
111
116
    def __init__(self, old_tree, new_tree):
117
122
        self.old_tree = old_tree
118
123
        self.new_tree = new_tree
119
124
 
120
 
    def revid(self, tree, file_id):
 
125
    def revid(self, tree, path):
 
126
        if path is None:
 
127
            return breezy.revision.NULL_REVISION
121
128
        try:
122
 
            path = tree.id2path(file_id)
123
 
            return tree.get_file_revision(path, file_id)
124
 
        except breezy.errors.NoSuchId:
125
 
            return 'null:'
 
129
            return tree.get_file_revision(path)
 
130
        except NoSuchFile:
 
131
            return breezy.revision.NULL_REVISION
126
132
 
127
 
    def report(self, file_id, paths, versioned, renamed, modified,
 
133
    def report(self, paths, versioned, renamed, copied, modified,
128
134
               exe_change, kind):
 
135
        return self._report(
 
136
                paths, versioned, renamed, copied,
 
137
                modified, exe_change, kind)
 
138
 
 
139
    def _report(self, paths, versioned, renamed, copied, modified,
 
140
                exe_change, kind):
129
141
        if modified not in ('unchanged', 'kind changed'):
130
142
            if versioned == 'removed':
131
143
                filename = rich_filename(paths[0], kind[0])
132
144
            else:
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)))
 
147
                filename=filename,
 
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))
153
165
 
 
166
 
154
167
# The lru_cache is not thread-safe, so we need a lock around it for
155
168
# all threads.
156
169
rev_info_memory_cache_lock = threading.RLock()
157
170
 
 
171
 
158
172
class RevInfoMemoryCache(object):
159
173
    """A store that validates values against the revids they were stored with.
160
174
 
199
213
        finally:
200
214
            rev_info_memory_cache_lock.release()
201
215
 
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
205
219
# to hang.
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.
366
381
        revids = []
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):
413
426
                return [revid]
449
462
        # if a "revid" is actually a dotted revno, convert it to a revid
450
463
        if revid is None:
451
464
            return revid
 
465
        if not isinstance(revid, str):
 
466
            raise TypeError(revid)
452
467
        if revid == 'head:':
453
468
            return self.last_revid
454
469
        try:
456
471
                revid = self._revno_revid[revid]
457
472
        except KeyError:
458
473
            raise breezy.errors.NoSuchRevision(self._branch_nick, revid)
 
474
        if not isinstance(revid, bytes):
 
475
            revid = revid.encode('utf-8')
459
476
        return revid
460
477
 
461
 
    def get_file_view(self, revid, file_id):
462
 
        """
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.
466
 
 
467
 
        If file_id is None, the entire revision history is the list scope.
468
 
        """
469
 
        if revid is None:
470
 
            revid = self.last_revid
471
 
        if file_id is not None:
472
 
            revlist = list(
473
 
                self.get_short_revision_history_by_fileid(file_id))
474
 
            revlist = self.get_revids_from(revlist, revid)
475
 
        else:
476
 
            revlist = self.get_revids_from(None, revid)
477
 
        return revlist
478
 
 
479
478
    @staticmethod
480
479
    def _iterate_sufficiently(iterable, stop_at, extra_rev_count):
481
480
        """Return a list of iterable.
502
501
                result.append(n)
503
502
        return result
504
503
 
505
 
    def get_view(self, revid, start_revid, file_id, query=None,
 
504
    def _get_file_view(self, revid, file_id):
 
505
        """
 
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.
 
509
 
 
510
        If file_id is None, the entire revision history is the list scope.
 
511
        """
 
512
        if revid is None:
 
513
            revid = self.last_revid
 
514
        if file_id is not None:
 
515
            revlist = list(
 
516
                self.get_short_revision_history_by_fileid(file_id))
 
517
            revlist = self.get_revids_from(revlist, revid)
 
518
        else:
 
519
            revlist = self.get_revids_from(None, revid)
 
520
        return revlist
 
521
 
 
522
    def get_view(self, revid, start_revid, path, query=None,
506
523
                 extra_rev_count=None):
507
524
        """
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).
511
528
 
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
514
531
              file.
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
530
547
 
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.
533
550
        """
534
551
        if start_revid is None:
535
552
            start_revid = self.last_revid
536
553
 
537
554
        if query is None:
538
 
            revid_list = self.get_file_view(start_revid, file_id)
 
555
            repo = self._branch.repository
 
556
            if path is not None:
 
557
                file_id = repo.revision_tree(start_revid).path2id(path)
 
558
            else:
 
559
                file_id = None
 
560
            revid_list = self._get_file_view(start_revid, file_id)
539
561
            revid_list = self._iterate_sufficiently(revid_list, revid,
540
562
                                                    extra_rev_count)
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,
548
570
                                                        extra_rev_count)
549
571
                start_revid = revid
550
572
            return revid, start_revid, revid_list
 
573
        else:
 
574
            file_id = None
551
575
 
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)
555
579
        else:
556
580
            revid_list = None
557
581
        revid_list = search.search_revisions(self._branch, query)
565
589
            # search index.
566
590
            return None, None, []
567
591
 
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]
573
 
 
574
 
    def get_path(self, revid, file_id):
575
 
        if (file_id is None) or (file_id == ''):
576
 
            return ''
577
 
        path = self.get_inventory(revid).id2path(file_id)
578
 
        if (len(path) > 0) and not path.startswith('/'):
579
 
            path = '/' + path
580
 
        return path
581
 
 
582
 
    def get_file_id(self, revid, path):
583
 
        if (len(path) > 0) and not path.startswith('/'):
584
 
            path = '/' + path
585
 
        return self.get_inventory(revid).path2id(path)
 
592
    def revision_tree(self, revid):
 
593
        return self._branch.repository.revision_tree(revid)
 
594
 
 
595
    def file_exists(self, revid, path):
 
596
        if (len(path) > 0) and not path.startswith('/'):
 
597
            path = '/' + path
 
598
        try:
 
599
            return self.revision_tree(revid).has_filename(path)
 
600
        except breezy.errors.NoSuchRevision:
 
601
            return False
586
602
 
587
603
    def get_merge_point_list(self, revid):
588
604
        """
619
635
            else:
620
636
                d[revnos] = (revnolast, revid)
621
637
 
622
 
        return [revid for (_, revid) in d.itervalues()]
 
638
        return [revid for (_, revid) in d.values()]
623
639
 
624
640
    def add_branch_nicks(self, change):
625
641
        """
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
649
665
 
650
666
        Revisions not present and NULL_REVISION will be ignored.
651
667
        """
 
668
        for revid in revid_list:
 
669
            if not isinstance(revid, bytes):
 
670
                raise TypeError(revid_list)
652
671
        changes = self.get_changes_uncached(revid_list)
653
672
        if len(changes) == 0:
654
673
            return changes
675
694
 
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),
679
 
                            revid_list)
 
697
        revid_list = list(filter(lambda revid: not breezy.revision.is_null(revid),
 
698
                            revid_list))
680
699
        parent_map = self._branch.repository.get_graph().get_parent_map(
681
700
                         revid_list)
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:
730
750
            try:
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
754
774
 
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)
 
782
        display_path = path
 
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))
764
787
 
765
788
    def file_changes_for_revision_ids(self, old_revid, new_revid):
766
789
        """
767
790
        Return a nested data structure containing the changes in a delta::
768
791
 
769
 
            added: list((filename, file_id)),
770
 
            renamed: list((old_filename, new_filename, file_id)),
771
 
            deleted: list((filename, file_id)),
772
 
            modified: list(
773
 
                filename: str,
774
 
                file_id: str,
775
 
            ),
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)),
777
797
        """
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])
784
804
        else: