/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/log.py

  • Committer: Richard Wilbur
  • Date: 2016-02-04 19:07:28 UTC
  • mto: This revision was merged to the branch mainline in revision 6618.
  • Revision ID: richard.wilbur@gmail.com-20160204190728-p0zvfii6zase0fw7
Update COPYING.txt from the original http://www.gnu.org/licenses/gpl-2.0.txt  (Only differences were in whitespace.)  Thanks to Petr Stodulka for pointing out the discrepancy.

Show diffs side-by-side

added added

removed removed

Lines of Context:
50
50
from __future__ import absolute_import
51
51
 
52
52
import codecs
53
 
import itertools
 
53
from cStringIO import StringIO
 
54
from itertools import (
 
55
    chain,
 
56
    izip,
 
57
    )
54
58
import re
55
59
import sys
56
60
from warnings import (
57
61
    warn,
58
62
    )
59
63
 
60
 
from .lazy_import import lazy_import
 
64
from bzrlib.lazy_import import lazy_import
61
65
lazy_import(globals(), """
62
66
 
63
 
from breezy import (
 
67
from bzrlib import (
64
68
    config,
65
69
    controldir,
66
70
    diff,
 
71
    errors,
67
72
    foreign,
68
73
    repository as _mod_repository,
69
74
    revision as _mod_revision,
 
75
    revisionspec,
70
76
    tsort,
71
77
    )
72
 
from breezy.i18n import gettext, ngettext
 
78
from bzrlib.i18n import gettext, ngettext
73
79
""")
74
80
 
75
 
from . import (
76
 
    errors,
 
81
from bzrlib import (
77
82
    lazy_regex,
78
83
    registry,
79
 
    revisionspec,
80
84
    )
81
 
from .osutils import (
 
85
from bzrlib.osutils import (
82
86
    format_date,
83
87
    format_date_with_offset_in_original_timezone,
84
88
    get_diff_header_encoding,
85
89
    get_terminal_encoding,
86
90
    terminal_width,
87
91
    )
88
 
from .sixish import (
89
 
    BytesIO,
90
 
    range,
91
 
    zip,
92
 
    )
93
 
from .tree import find_previous_path
94
 
 
95
 
 
96
 
def find_touching_revisions(repository, last_revision, last_tree, last_path):
 
92
 
 
93
 
 
94
def find_touching_revisions(branch, file_id):
97
95
    """Yield a description of revisions which affect the file_id.
98
96
 
99
97
    Each returned element is (revno, revision_id, description)
104
102
    TODO: Perhaps some way to limit this to only particular revisions,
105
103
    or to traverse a non-mainline set of revisions?
106
104
    """
107
 
    last_verifier = last_tree.get_file_verifier(last_path)
108
 
    graph = repository.get_graph()
109
 
    history = list(graph.iter_lefthand_ancestry(last_revision, []))
110
 
    revno = len(history)
111
 
    for revision_id in history:
112
 
        this_tree = repository.revision_tree(revision_id)
113
 
        this_path = find_previous_path(last_tree, this_tree, last_path)
 
105
    last_ie = None
 
106
    last_path = None
 
107
    revno = 1
 
108
    graph = branch.repository.get_graph()
 
109
    history = list(graph.iter_lefthand_ancestry(branch.last_revision(),
 
110
        [_mod_revision.NULL_REVISION]))
 
111
    for revision_id in reversed(history):
 
112
        this_inv = branch.repository.get_inventory(revision_id)
 
113
        if this_inv.has_id(file_id):
 
114
            this_ie = this_inv[file_id]
 
115
            this_path = this_inv.id2path(file_id)
 
116
        else:
 
117
            this_ie = this_path = None
114
118
 
115
119
        # now we know how it was last time, and how it is in this revision.
116
120
        # are those two states effectively the same or not?
117
 
        if this_path is not None and last_path is None:
118
 
            yield revno, revision_id, "deleted " + this_path
119
 
            this_verifier = this_tree.get_file_verifier(this_path)
120
 
        elif this_path is None and last_path is not None:
121
 
            yield revno, revision_id, "added " + last_path
 
121
 
 
122
        if not this_ie and not last_ie:
 
123
            # not present in either
 
124
            pass
 
125
        elif this_ie and not last_ie:
 
126
            yield revno, revision_id, "added " + this_path
 
127
        elif not this_ie and last_ie:
 
128
            # deleted here
 
129
            yield revno, revision_id, "deleted " + last_path
122
130
        elif this_path != last_path:
123
 
            yield revno, revision_id, ("renamed %s => %s" % (this_path, last_path))
124
 
            this_verifier = this_tree.get_file_verifier(this_path)
125
 
        else:
126
 
            this_verifier = this_tree.get_file_verifier(this_path)
127
 
            if (this_verifier != last_verifier):
128
 
                yield revno, revision_id, "modified " + this_path
 
131
            yield revno, revision_id, ("renamed %s => %s" % (last_path, this_path))
 
132
        elif (this_ie.text_size != last_ie.text_size
 
133
              or this_ie.text_sha1 != last_ie.text_sha1):
 
134
            yield revno, revision_id, "modified " + this_path
129
135
 
130
 
        last_verifier = this_verifier
 
136
        last_ie = this_ie
131
137
        last_path = this_path
132
 
        last_tree = this_tree
133
 
        if last_path is None:
134
 
            return
135
 
        revno -= 1
 
138
        revno += 1
136
139
 
137
140
 
138
141
def show_log(branch,
267
270
 
268
271
    :param _match_using_deltas: a private parameter controlling the
269
272
      algorithm used for matching specific_fileids. This parameter
270
 
      may be removed in the future so breezy client code should NOT
 
273
      may be removed in the future so bzrlib client code should NOT
271
274
      use it.
272
275
 
273
276
    :param exclude_common_ancestry: Whether -rX..Y should be interpreted as a
319
322
    return result
320
323
 
321
324
 
322
 
def format_signature_validity(rev_id, branch):
 
325
def format_signature_validity(rev_id, repo):
323
326
    """get the signature validity
324
327
 
325
328
    :param rev_id: revision id to validate
326
 
    :param branch: branch of revision
 
329
    :param repo: repository of revision
327
330
    :return: human readable string to print to log
328
331
    """
329
 
    from breezy import gpg
 
332
    from bzrlib import gpg
330
333
 
331
 
    gpg_strategy = gpg.GPGStrategy(branch.get_config_stack())
332
 
    result = branch.repository.verify_revision_signature(rev_id, gpg_strategy)
 
334
    gpg_strategy = gpg.GPGStrategy(None)
 
335
    result = repo.verify_revision_signature(rev_id, gpg_strategy)
333
336
    if result[0] == gpg.SIGNATURE_VALID:
334
337
        return u"valid signature from {0}".format(result[1])
335
338
    if result[0] == gpg.SIGNATURE_KEY_MISSING:
405
408
 
406
409
        # Find and print the interesting revisions
407
410
        generator = self._generator_factory(self.branch, rqst)
408
 
        try:
409
 
            for lr in generator.iter_log_revisions():
410
 
                lf.log_revision(lr)
411
 
        except errors.GhostRevisionUnusableHere:
412
 
            raise errors.BzrCommandError(
413
 
                    gettext('Further revision history missing.'))
 
411
        for lr in generator.iter_log_revisions():
 
412
            lf.log_revision(lr)
414
413
        lf.show_advice()
415
414
 
416
415
    def _generator_factory(self, branch, rqst):
456
455
                    continue
457
456
                if omit_merges and len(rev.parent_ids) > 1:
458
457
                    continue
459
 
                if rev is None:
460
 
                    raise errors.GhostRevisionUnusableHere(rev_id)
461
458
                if diff_type is None:
462
459
                    diff = None
463
460
                else:
464
461
                    diff = self._format_diff(rev, rev_id, diff_type)
465
462
                if show_signature:
466
 
                    signature = format_signature_validity(rev_id, self.branch)
 
463
                    signature = format_signature_validity(rev_id,
 
464
                                                self.branch.repository)
467
465
                else:
468
466
                    signature = None
469
467
                yield LogRevision(rev, revno, merge_depth, delta,
486
484
            specific_files = [tree_2.id2path(id) for id in file_ids]
487
485
        else:
488
486
            specific_files = None
489
 
        s = BytesIO()
 
487
        s = StringIO()
490
488
        path_encoding = get_diff_header_encoding()
491
489
        diff.show_diff_trees(tree_1, tree_2, s, specific_files, old_label='',
492
490
            new_label='', path_encoding=path_encoding)
663
661
    # shown naturally, i.e. just like it is for linear logging. We can easily
664
662
    # make forward the exact opposite display, but showing the merge revisions
665
663
    # indented at the end seems slightly nicer in that case.
666
 
    view_revisions = itertools.chain(iter(initial_revisions),
 
664
    view_revisions = chain(iter(initial_revisions),
667
665
        _graph_view_revisions(branch, start_rev_id, end_rev_id,
668
666
                              rebase_initial_depths=(direction == 'reverse'),
669
667
                              exclude_common_ancestry=exclude_common_ancestry))
724
722
    :param exclude_common_ancestry: Whether the start_rev_id should be part of
725
723
        the iterated revisions.
726
724
    :return: An iterator of (revision_id, dotted_revno, merge_depth) tuples.
727
 
        dotted_revno will be None for ghosts
728
725
    :raises _StartNotLinearAncestor: if a start_rev_id is specified but
729
726
        is not found walking the left-hand history
730
727
    """
733
730
    graph = repo.get_graph()
734
731
    if start_rev_id is None and end_rev_id is None:
735
732
        cur_revno = br_revno
736
 
        graph_iter = graph.iter_lefthand_ancestry(br_rev_id,
737
 
            (_mod_revision.NULL_REVISION,))
738
 
        while True:
739
 
            try:
740
 
                revision_id = next(graph_iter)
741
 
            except errors.RevisionNotPresent as e:
742
 
                # Oops, a ghost.
743
 
                yield e.revision_id, None, None
744
 
                break
745
 
            else:
746
 
                yield revision_id, str(cur_revno), 0
747
 
                cur_revno -= 1
 
733
        for revision_id in graph.iter_lefthand_ancestry(br_rev_id,
 
734
            (_mod_revision.NULL_REVISION,)):
 
735
            yield revision_id, str(cur_revno), 0
 
736
            cur_revno -= 1
748
737
    else:
749
738
        if end_rev_id is None:
750
739
            end_rev_id = br_rev_id
751
740
        found_start = start_rev_id is None
752
 
        graph_iter = graph.iter_lefthand_ancestry(end_rev_id,
753
 
            (_mod_revision.NULL_REVISION,))
754
 
        while True:
755
 
            try:
756
 
                revision_id = next(graph_iter)
757
 
            except StopIteration:
758
 
                break
759
 
            except errors.RevisionNotPresent as e:
760
 
                # Oops, a ghost.
761
 
                yield e.revision_id, None, None
762
 
                break
763
 
            else:
764
 
                revno_str = _compute_revno_str(branch, revision_id)
765
 
                if not found_start and revision_id == start_rev_id:
766
 
                    if not exclude_common_ancestry:
767
 
                        yield revision_id, revno_str, 0
768
 
                    found_start = True
769
 
                    break
770
 
                else:
 
741
        for revision_id in graph.iter_lefthand_ancestry(end_rev_id,
 
742
                (_mod_revision.NULL_REVISION,)):
 
743
            revno_str = _compute_revno_str(branch, revision_id)
 
744
            if not found_start and revision_id == start_rev_id:
 
745
                if not exclude_common_ancestry:
771
746
                    yield revision_id, revno_str, 0
772
 
        if not found_start:
773
 
            raise _StartNotLinearAncestor()
 
747
                found_start = True
 
748
                break
 
749
            else:
 
750
                yield revision_id, revno_str, 0
 
751
        else:
 
752
            if not found_start:
 
753
                raise _StartNotLinearAncestor()
774
754
 
775
755
 
776
756
def _graph_view_revisions(branch, start_rev_id, end_rev_id,
821
801
    """Adjust depths upwards so the top level is 0."""
822
802
    # If either the first or last revision have a merge_depth of 0, we're done
823
803
    if view_revisions and view_revisions[0][2] and view_revisions[-1][2]:
824
 
        min_depth = min([d for r, n, d in view_revisions])
 
804
        min_depth = min([d for r,n,d in view_revisions])
825
805
        if min_depth != 0:
826
 
            view_revisions = [(r, n, d-min_depth) for r, n, d in view_revisions]
 
806
            view_revisions = [(r,n,d-min_depth) for r,n,d in view_revisions]
827
807
    return view_revisions
828
808
 
829
809
 
844
824
    """
845
825
    # Convert view_revisions into (view, None, None) groups to fit with
846
826
    # the standard interface here.
847
 
    if isinstance(view_revisions, list):
 
827
    if type(view_revisions) == list:
848
828
        # A single batch conversion is faster than many incremental ones.
849
829
        # As we have all the data, do a batch conversion.
850
830
        nones = [None] * len(view_revisions)
851
 
        log_rev_iterator = iter([list(zip(view_revisions, nones, nones))])
 
831
        log_rev_iterator = iter([zip(view_revisions, nones, nones)])
852
832
    else:
853
833
        def _convert():
854
834
            for view in view_revisions:
880
860
    :return: An iterator over lists of ((rev_id, revno, merge_depth), rev,
881
861
        delta).
882
862
    """
883
 
    if not match:
 
863
    if match is None:
884
864
        return log_rev_iterator
885
865
    searchRE = [(k, [re.compile(x, re.IGNORECASE) for x in v])
886
 
                for k, v in match.items()]
 
866
                for (k,v) in match.iteritems()]
887
867
    return _filter_re(searchRE, log_rev_iterator)
888
868
 
889
869
 
900
880
               'author': (rev.get_apparent_authors()),
901
881
               'bugs': list(rev.iter_bugs())
902
882
               }
903
 
    strings[''] = [item for inner_list in strings.values()
 
883
    strings[''] = [item for inner_list in strings.itervalues()
904
884
                   for item in inner_list]
905
 
    for (k, v) in searchRE:
 
885
    for (k,v) in searchRE:
906
886
        if k in strings and not _match_any_filter(strings[k], v):
907
887
            return False
908
888
    return True
909
889
 
910
890
def _match_any_filter(strings, res):
911
 
    return any(re.search(s) for re in res for s in strings)
 
891
    return any([filter(None, map(re.search, strings)) for re in res])
912
892
 
913
893
def _make_delta_filter(branch, generate_delta, search, log_rev_iterator,
914
894
    fileids=None, direction='reverse'):
959
939
        new_revs = []
960
940
        if delta_type == 'full' and not check_fileids:
961
941
            deltas = repository.get_deltas_for_revisions(revisions)
962
 
            for rev, delta in zip(revs, deltas):
 
942
            for rev, delta in izip(revs, deltas):
963
943
                new_revs.append((rev[0], rev[1], delta))
964
944
        else:
965
945
            deltas = repository.get_deltas_for_revisions(revisions, fileid_set)
966
 
            for rev, delta in zip(revs, deltas):
 
946
            for rev, delta in izip(revs, deltas):
967
947
                if check_fileids:
968
948
                    if delta is None or not delta.has_changed():
969
949
                        continue
1017
997
    for revs in log_rev_iterator:
1018
998
        # r = revision_id, n = revno, d = merge depth
1019
999
        revision_ids = [view[0] for view, _, _ in revs]
1020
 
        revisions = dict(repository.iter_revisions(revision_ids))
1021
 
        yield [(rev[0], revisions[rev[0][0]], rev[2]) for rev in revs]
 
1000
        revisions = repository.get_revisions(revision_ids)
 
1001
        revs = [(rev[0], revision, rev[2]) for rev, revision in
 
1002
            izip(revs, revisions)]
 
1003
        yield revs
1022
1004
 
1023
1005
 
1024
1006
def _make_batch_filter(branch, generate_delta, search, log_rev_iterator):
1230
1212
    #       rate). This particular access is clustered with a low success rate.
1231
1213
    modified_text_revisions = set()
1232
1214
    chunk_size = 1000
1233
 
    for start in range(0, len(text_keys), chunk_size):
 
1215
    for start in xrange(0, len(text_keys), chunk_size):
1234
1216
        next_keys = text_keys[start:start + chunk_size]
1235
1217
        # Only keep the revision_id portion of the key
1236
1218
        modified_text_revisions.update(
1251
1233
 
1252
1234
        if rev_id in modified_text_revisions:
1253
1235
            # This needs to be logged, along with the extra revisions
1254
 
            for idx in range(len(current_merge_stack)):
 
1236
            for idx in xrange(len(current_merge_stack)):
1255
1237
                node = current_merge_stack[idx]
1256
1238
                if node is not None:
1257
1239
                    if include_merges or node[2] == 0:
1603
1585
        to_file.write("%s%s\n" % (indent, ('\n' + indent).join(lines)))
1604
1586
        if revision.delta is not None:
1605
1587
            # Use the standard status output to display changes
1606
 
            from breezy.delta import report_delta
 
1588
            from bzrlib.delta import report_delta
1607
1589
            report_delta(to_file, revision.delta, short_status=False,
1608
1590
                         show_ids=self.show_ids, indent=indent)
1609
1591
        if revision.diff is not None:
1675
1657
 
1676
1658
        if revision.delta is not None:
1677
1659
            # Use the standard status output to display changes
1678
 
            from breezy.delta import report_delta
 
1660
            from bzrlib.delta import report_delta
1679
1661
            report_delta(to_file, revision.delta,
1680
1662
                         short_status=self.delta_format==1,
1681
1663
                         show_ids=self.show_ids, indent=indent + offset)
1765
1747
                               show_offset=False)
1766
1748
        committer_str = self.authors(revision.rev, 'first', sep=', ')
1767
1749
        committer_str = committer_str.replace(' <', '  <')
1768
 
        to_file.write('%s  %s\n\n' % (date_str, committer_str))
 
1750
        to_file.write('%s  %s\n\n' % (date_str,committer_str))
1769
1751
 
1770
1752
        if revision.delta is not None and revision.delta.has_changed():
1771
1753
            for c in revision.delta.added + revision.delta.removed + revision.delta.modified:
1772
1754
                path, = c[:1]
1773
1755
                to_file.write('\t* %s:\n' % (path,))
1774
1756
            for c in revision.delta.renamed:
1775
 
                oldpath, newpath = c[:2]
 
1757
                oldpath,newpath = c[:2]
1776
1758
                # For renamed files, show both the old and the new path
1777
 
                to_file.write('\t* %s:\n\t* %s:\n' % (oldpath, newpath))
 
1759
                to_file.write('\t* %s:\n\t* %s:\n' % (oldpath,newpath))
1778
1760
            to_file.write('\n')
1779
1761
 
1780
1762
        if not revision.rev.message:
1884
1866
    # This is the first index which is different between
1885
1867
    # old and new
1886
1868
    base_idx = None
1887
 
    for i in range(max(len(new_rh), len(old_rh))):
 
1869
    for i in xrange(max(len(new_rh),
 
1870
                        len(old_rh))):
1888
1871
        if (len(new_rh) <= i
1889
1872
            or len(old_rh) <= i
1890
1873
            or new_rh[i] != old_rh[i]):
1940
1923
    while do_new or do_old:
1941
1924
        if do_new:
1942
1925
            try:
1943
 
                new_revision = next(new_iter)
 
1926
                new_revision = new_iter.next()
1944
1927
            except StopIteration:
1945
1928
                do_new = False
1946
1929
            else:
1951
1934
                    break
1952
1935
        if do_old:
1953
1936
            try:
1954
 
                old_revision = next(old_iter)
 
1937
                old_revision = old_iter.next()
1955
1938
            except StopIteration:
1956
1939
                do_old = False
1957
1940
            else:
2031
2014
      kind is one of values 'directory', 'file', 'symlink', 'tree-reference'.
2032
2015
      branch will be read-locked.
2033
2016
    """
2034
 
    from breezy.builtins import _get_revision_range
 
2017
    from bzrlib.builtins import _get_revision_range
2035
2018
    tree, b, path = controldir.ControlDir.open_containing_tree_or_branch(
2036
2019
        file_list[0])
2037
2020
    add_cleanup(b.lock_read().unlock)
2058
2041
        tree1 = None
2059
2042
        for fp in relpaths:
2060
2043
            file_id = tree.path2id(fp)
2061
 
            kind = _get_kind_for_file_id(tree, fp, file_id)
 
2044
            kind = _get_kind_for_file_id(tree, file_id)
2062
2045
            if file_id is None:
2063
2046
                # go back to when time began
2064
2047
                if tree1 is None:
2072
2055
                        tree1 = b.repository.revision_tree(rev1)
2073
2056
                if tree1:
2074
2057
                    file_id = tree1.path2id(fp)
2075
 
                    kind = _get_kind_for_file_id(tree1, fp, file_id)
 
2058
                    kind = _get_kind_for_file_id(tree1, file_id)
2076
2059
            info_list.append((fp, file_id, kind))
2077
2060
 
2078
2061
    elif start_rev_info == end_rev_info:
2080
2063
        tree = b.repository.revision_tree(end_rev_info.rev_id)
2081
2064
        for fp in relpaths:
2082
2065
            file_id = tree.path2id(fp)
2083
 
            kind = _get_kind_for_file_id(tree, fp, file_id)
 
2066
            kind = _get_kind_for_file_id(tree, file_id)
2084
2067
            info_list.append((fp, file_id, kind))
2085
2068
 
2086
2069
    else:
2094
2077
        tree1 = None
2095
2078
        for fp in relpaths:
2096
2079
            file_id = tree.path2id(fp)
2097
 
            kind = _get_kind_for_file_id(tree, fp, file_id)
 
2080
            kind = _get_kind_for_file_id(tree, file_id)
2098
2081
            if file_id is None:
2099
2082
                if tree1 is None:
2100
2083
                    rev_id = start_rev_info.rev_id
2104
2087
                    else:
2105
2088
                        tree1 = b.repository.revision_tree(rev_id)
2106
2089
                file_id = tree1.path2id(fp)
2107
 
                kind = _get_kind_for_file_id(tree1, fp, file_id)
 
2090
                kind = _get_kind_for_file_id(tree1, file_id)
2108
2091
            info_list.append((fp, file_id, kind))
2109
2092
    return b, info_list, start_rev_info, end_rev_info
2110
2093
 
2111
2094
 
2112
 
def _get_kind_for_file_id(tree, path, file_id):
 
2095
def _get_kind_for_file_id(tree, file_id):
2113
2096
    """Return the kind of a file-id or None if it doesn't exist."""
2114
2097
    if file_id is not None:
2115
 
        return tree.kind(path, file_id)
 
2098
        return tree.kind(file_id)
2116
2099
    else:
2117
2100
        return None
2118
2101
 
2121
2104
 
2122
2105
# Use the properties handlers to print out bug information if available
2123
2106
def _bugs_properties_handler(revision):
2124
 
    if 'bugs' in revision.properties:
 
2107
    if revision.properties.has_key('bugs'):
2125
2108
        bug_lines = revision.properties['bugs'].split('\n')
2126
2109
        bug_rows = [line.split(' ', 1) for line in bug_lines]
2127
2110
        fixed_bug_urls = [row[0] for row in bug_rows if