~loggerhead-team/loggerhead/trunk-rich

« back to all changes in this revision

Viewing changes to loggerhead/history.py

  • Committer: Robey Pointer
  • Date: 2007-03-12 17:51:19 UTC
  • Revision ID: robey@lag.net-20070312175119-xms19elalw5ax1zc
revert the change that stopped converting spaces to ' '.
in other words, go back to doing the nbsp conversion.  bug report from
kent gibson reveals that at least in firefox, the spaces aren't handled
the way i want.

Show diffs side-by-side

added added

removed removed

Lines of Context:
47
47
import bzrlib.annotate
48
48
import bzrlib.branch
49
49
import bzrlib.bundle.serializer
 
50
import bzrlib.decorators
50
51
import bzrlib.diff
51
52
import bzrlib.errors
52
53
import bzrlib.progress
82
83
bzrlib.ui.ui_factory = ThreadSafeUIFactory()
83
84
 
84
85
 
 
86
def _process_side_by_side_buffers(line_list, delete_list, insert_list):
 
87
    while len(delete_list) < len(insert_list):
 
88
        delete_list.append((None, '', 'context'))
 
89
    while len(insert_list) < len(delete_list):
 
90
        insert_list.append((None, '', 'context'))
 
91
    while len(delete_list) > 0:
 
92
        d = delete_list.pop(0)
 
93
        i = insert_list.pop(0)
 
94
        line_list.append(util.Container(old_lineno=d[0], new_lineno=i[0],
 
95
                                        old_line=d[1], new_line=i[1],
 
96
                                        old_type=d[2], new_type=i[2]))
 
97
 
 
98
 
 
99
def _make_side_by_side(chunk_list):
 
100
    """
 
101
    turn a normal unified-style diff (post-processed by parse_delta) into a
 
102
    side-by-side diff structure.  the new structure is::
 
103
    
 
104
        chunks: list(
 
105
            diff: list(
 
106
                old_lineno: int,
 
107
                new_lineno: int,
 
108
                old_line: str,
 
109
                new_line: str,
 
110
                type: str('context' or 'changed'),
 
111
            )
 
112
        )
 
113
    """
 
114
    out_chunk_list = []
 
115
    for chunk in chunk_list:
 
116
        line_list = []
 
117
        delete_list, insert_list = [], []
 
118
        for line in chunk.diff:
 
119
            if line.type == 'context':
 
120
                if len(delete_list) or len(insert_list):
 
121
                    _process_side_by_side_buffers(line_list, delete_list, insert_list)
 
122
                    delete_list, insert_list = [], []
 
123
                line_list.append(util.Container(old_lineno=line.old_lineno, new_lineno=line.new_lineno,
 
124
                                                old_line=line.line, new_line=line.line,
 
125
                                                old_type=line.type, new_type=line.type))
 
126
            elif line.type == 'delete':
 
127
                delete_list.append((line.old_lineno, line.line, line.type))
 
128
            elif line.type == 'insert':
 
129
                insert_list.append((line.new_lineno, line.line, line.type))
 
130
        if len(delete_list) or len(insert_list):
 
131
            _process_side_by_side_buffers(line_list, delete_list, insert_list)
 
132
        out_chunk_list.append(util.Container(diff=line_list))
 
133
    return out_chunk_list
 
134
 
 
135
 
 
136
def is_branch(folder):
 
137
    try:
 
138
        bzrlib.branch.Branch.open(folder)
 
139
        return True
 
140
    except:
 
141
        return False
 
142
 
 
143
 
 
144
def clean_message(message):
 
145
    # clean up a commit message and return it and a short (1-line) version
 
146
    message = message.splitlines()
 
147
    if len(message) == 1:
 
148
        # robey-style 1-line long message
 
149
        message = textwrap.wrap(message[0])
 
150
        
 
151
    # make short form of commit message
 
152
    short_message = message[0]
 
153
    if len(short_message) > 60:
 
154
        short_message = short_message[:60] + '...'
 
155
    
 
156
    return message, short_message
 
157
 
 
158
 
85
159
# from bzrlib
86
160
class _RevListToTimestamps(object):
87
161
    """This takes a list of revisions, and allows you to bisect by date"""
168
242
        # branch history has changed.  we need to immediately close and stop
169
243
        # using our caches, because a new history object will be created to
170
244
        # replace us, using the same cache files.
 
245
        # (may also be called during server shutdown.)
171
246
        if self._change_cache is not None:
172
247
            self._change_cache.close()
173
248
            self._change_cache = None
191
266
    count = property(lambda self: self._count, None, None)
192
267
 
193
268
    @with_branch_lock
 
269
    def get_config(self):
 
270
        return self._branch.get_config()
 
271
    
 
272
    @with_branch_lock
194
273
    def get_revision(self, revid):
195
274
        return self._branch.repository.get_revision(revid)
196
275
    
497
576
        p_changes = self.get_changes(list(fetch_set))
498
577
        p_change_dict = dict([(c.revid, c) for c in p_changes])
499
578
        for change in changes:
 
579
            # arch-converted branches may not have merged branch info :(
500
580
            for p in change.parents:
501
 
                p.branch_nick = p_change_dict[p.revid].branch_nick
 
581
                if p.revid in p_change_dict:
 
582
                    p.branch_nick = p_change_dict[p.revid].branch_nick
 
583
                else:
 
584
                    p.branch_nick = '(missing)'
502
585
            for p in change.merge_points:
503
 
                p.branch_nick = p_change_dict[p.revid].branch_nick
 
586
                if p.revid in p_change_dict:
 
587
                    p.branch_nick = p_change_dict[p.revid].branch_nick
 
588
                else:
 
589
                    p.branch_nick = '(missing)'
504
590
    
505
591
    @with_branch_lock
506
592
    def get_changes(self, revid_list, get_diffs=False):
529
615
        stats.sort()
530
616
        stats.freeze()
531
617
        cPickle.dump(stats, open('lsprof.stats', 'w'), 2)
 
618
        self.log.info('lsprof complete!')
532
619
        return ret
533
620
 
 
621
    def _get_deltas_for_revisions_with_trees(self, revisions):
 
622
        """Produce a generator of revision deltas.
 
623
        
 
624
        Note that the input is a sequence of REVISIONS, not revision_ids.
 
625
        Trees will be held in memory until the generator exits.
 
626
        Each delta is relative to the revision's lefthand predecessor.
 
627
        """
 
628
        required_trees = set()
 
629
        for revision in revisions:
 
630
            required_trees.add(revision.revision_id)
 
631
            required_trees.update(revision.parent_ids[:1])
 
632
        trees = dict((t.get_revision_id(), t) for 
 
633
                     t in self._branch.repository.revision_trees(required_trees))
 
634
        ret = []
 
635
        self._branch.repository.lock_read()
 
636
        try:
 
637
            for revision in revisions:
 
638
                if not revision.parent_ids:
 
639
                    old_tree = self._branch.repository.revision_tree(None)
 
640
                else:
 
641
                    old_tree = trees[revision.parent_ids[0]]
 
642
                tree = trees[revision.revision_id]
 
643
                ret.append((tree, old_tree, tree.changes_from(old_tree)))
 
644
            return ret
 
645
        finally:
 
646
            self._branch.repository.unlock()
 
647
    
 
648
    def entry_from_revision(self, revision):
 
649
        commit_time = datetime.datetime.fromtimestamp(revision.timestamp)
 
650
        
 
651
        parents = [util.Container(revid=r, revno=self.get_revno(r)) for r in revision.parent_ids]
 
652
 
 
653
        if len(parents) == 0:
 
654
            left_parent = None
 
655
        else:
 
656
            left_parent = revision.parent_ids[0]
 
657
        
 
658
        message, short_message = clean_message(revision.message)
 
659
 
 
660
        entry = {
 
661
            'revid': revision.revision_id,
 
662
            'revno': self.get_revno(revision.revision_id),
 
663
            'date': commit_time,
 
664
            'author': revision.committer,
 
665
            'branch_nick': revision.properties.get('branch-nick', None),
 
666
            'short_comment': short_message,
 
667
            'comment': revision.message,
 
668
            'comment_clean': [util.html_clean(s) for s in message],
 
669
            'parents': parents,
 
670
        }
 
671
        return util.Container(entry)
 
672
 
534
673
    @with_branch_lock
535
674
    @with_bzrlib_read_lock
536
675
    def get_changes_uncached(self, revid_list, get_diffs=False):
537
 
        try:
538
 
            rev_list = self._branch.repository.get_revisions(revid_list)
539
 
        except (KeyError, bzrlib.errors.NoSuchRevision):
540
 
            return None
 
676
        done = False
 
677
        while not done:
 
678
            try:
 
679
                rev_list = self._branch.repository.get_revisions(revid_list)
 
680
                done = True
 
681
            except (KeyError, bzrlib.errors.NoSuchRevision), e:
 
682
                # this sometimes happens with arch-converted branches.
 
683
                # i don't know why. :(
 
684
                self.log.debug('No such revision (skipping): %s', e)
 
685
                revid_list.remove(e.revision)
541
686
        
542
 
        delta_list = self._branch.repository.get_deltas_for_revisions(rev_list)
 
687
        delta_list = self._get_deltas_for_revisions_with_trees(rev_list)
543
688
        combined_list = zip(rev_list, delta_list)
544
689
        
545
 
        tree_map = {}
546
 
        if get_diffs:
547
 
            # lookup the trees for each revision, so we can calculate diffs
548
 
            lookup_set = set()
549
 
            for rev in rev_list:
550
 
                lookup_set.add(rev.revision_id)
551
 
                if len(rev.parent_ids) > 0:
552
 
                    lookup_set.add(rev.parent_ids[0])
553
 
            tree_map = dict((t.get_revision_id(), t) for t in self._branch.repository.revision_trees(lookup_set))
554
 
            # also the root tree, in case we hit the origin:
555
 
            tree_map[None] = self._branch.repository.revision_tree(None)
556
 
        
557
690
        entries = []
558
 
        for rev, delta in combined_list:
559
 
            commit_time = datetime.datetime.fromtimestamp(rev.timestamp)
560
 
            
561
 
            parents = [util.Container(revid=r, revno=self.get_revno(r)) for r in rev.parent_ids]
562
 
    
563
 
            if len(parents) == 0:
564
 
                left_parent = None
565
 
            else:
566
 
                left_parent = rev.parent_ids[0]
567
 
            
568
 
            message = rev.message.splitlines()
569
 
            if len(message) == 1:
570
 
                # robey-style 1-line long message
571
 
                message = textwrap.wrap(message[0])
572
 
            
573
 
            # make short form of commit message
574
 
            short_message = message[0]
575
 
            if len(short_message) > 60:
576
 
                short_message = short_message[:60] + '...'
577
 
    
578
 
            old_tree, new_tree = None, None
579
 
            if get_diffs:
580
 
                new_tree = tree_map[rev.revision_id]
581
 
                old_tree = tree_map[left_parent]
582
 
 
583
 
            entry = {
584
 
                'revid': rev.revision_id,
585
 
                'revno': self.get_revno(rev.revision_id),
586
 
                'date': commit_time,
587
 
                'author': rev.committer,
588
 
                'branch_nick': rev.properties.get('branch-nick', None),
589
 
                'short_comment': short_message,
590
 
                'comment': rev.message,
591
 
                'comment_clean': [util.html_clean(s) for s in message],
592
 
                'parents': parents,
593
 
                'changes': self.parse_delta(delta, get_diffs, old_tree, new_tree),
594
 
            }
595
 
            entries.append(util.Container(entry))
 
691
        for rev, (new_tree, old_tree, delta) in combined_list:
 
692
            entry = self.entry_from_revision(rev)
 
693
            entry.changes = self.parse_delta(delta, get_diffs, old_tree, new_tree)
 
694
            entries.append(entry)
596
695
        
597
696
        return entries
598
697
 
 
698
    @with_bzrlib_read_lock
 
699
    def _get_diff(self, revid1, revid2):
 
700
        rev_tree1 = self._branch.repository.revision_tree(revid1)
 
701
        rev_tree2 = self._branch.repository.revision_tree(revid2)
 
702
        delta = rev_tree2.changes_from(rev_tree1)
 
703
        return rev_tree1, rev_tree2, delta
 
704
    
 
705
    def get_diff(self, revid1, revid2):
 
706
        rev_tree1, rev_tree2, delta = self._get_diff(revid1, revid2)
 
707
        entry = self.get_changes([ revid2 ], False)[0]
 
708
        entry.changes = self.parse_delta(delta, True, rev_tree1, rev_tree2)
 
709
        return entry
 
710
    
599
711
    @with_branch_lock
600
712
    def get_file(self, file_id, revid):
601
 
        "returns (filename, data)"
602
 
        inv_entry = self.get_inventory(revid)[file_id]
 
713
        "returns (path, filename, data)"
 
714
        inv = self.get_inventory(revid)
 
715
        inv_entry = inv[file_id]
603
716
        rev_tree = self._branch.repository.revision_tree(inv_entry.revision)
604
 
        return inv_entry.name, rev_tree.get_file_text(file_id)
 
717
        path = inv.id2path(file_id)
 
718
        if not path.startswith('/'):
 
719
            path = '/' + path
 
720
        return path, inv_entry.name, rev_tree.get_file_text(file_id)
605
721
    
606
722
    @with_branch_lock
607
723
    def parse_delta(self, delta, get_diffs=True, old_tree=None, new_tree=None):
702
818
        
703
819
        return util.Container(added=added, renamed=renamed, removed=removed, modified=modified)
704
820
 
 
821
    @staticmethod
 
822
    def add_side_by_side(changes):
 
823
        # FIXME: this is a rotten API.
 
824
        for change in changes:
 
825
            for m in change.changes.modified:
 
826
                m.sbs_chunks = _make_side_by_side(m.chunks)
 
827
    
705
828
    @with_branch_lock
706
829
    def get_filelist(self, inv, path, sort_type=None):
707
830
        """
802
925
 
803
926
    @with_branch_lock
804
927
    @with_bzrlib_read_lock
805
 
    def get_bundle(self, revid):
806
 
        parents = self._revision_graph[revid]
807
 
        if len(parents) > 0:
808
 
            parent_revid = parents[0]
809
 
        else:
810
 
            parent_revid = None
 
928
    def get_bundle(self, revid, compare_revid=None):
 
929
        if compare_revid is None:
 
930
            parents = self._revision_graph[revid]
 
931
            if len(parents) > 0:
 
932
                compare_revid = parents[0]
 
933
            else:
 
934
                compare_revid = None
811
935
        s = StringIO()
812
 
        bzrlib.bundle.serializer.write_bundle(self._branch.repository, revid, parent_revid, s)
 
936
        bzrlib.bundle.serializer.write_bundle(self._branch.repository, revid, compare_revid, s)
813
937
        return s.getvalue()
814
938