mercurial/context.py
changeset 36917 7affcabf561e
parent 36781 ffa3026d4196
child 36918 5d3abd6a5b25
equal deleted inserted replaced
36916:1b9f6440506b 36917:7affcabf561e
    24     short,
    24     short,
    25     wdirid,
    25     wdirid,
    26     wdirnodes,
    26     wdirnodes,
    27     wdirrev,
    27     wdirrev,
    28 )
    28 )
    29 from .thirdparty import (
       
    30     attr,
       
    31 )
       
    32 from . import (
    29 from . import (
       
    30     dagop,
    33     encoding,
    31     encoding,
    34     error,
    32     error,
    35     fileset,
    33     fileset,
    36     match as matchmod,
    34     match as matchmod,
    37     mdiff,
    35     mdiff,
   976         in the file, where ctx is the filectx of the node where
   974         in the file, where ctx is the filectx of the node where
   977         that line was last changed; if linenumber parameter is true, number is
   975         that line was last changed; if linenumber parameter is true, number is
   978         the line number at the first appearance in the managed file, otherwise,
   976         the line number at the first appearance in the managed file, otherwise,
   979         number has a fixed value of False.
   977         number has a fixed value of False.
   980         '''
   978         '''
       
   979         annotateline = dagop.annotateline
       
   980         _annotatepair = dagop._annotatepair
   981 
   981 
   982         def lines(text):
   982         def lines(text):
   983             if text.endswith("\n"):
   983             if text.endswith("\n"):
   984                 return text.count("\n")
   984                 return text.count("\n")
   985             return text.count("\n") + int(bool(text))
   985             return text.count("\n") + int(bool(text))
  1102         """Returns `data()` after running repository decoding filters.
  1102         """Returns `data()` after running repository decoding filters.
  1103 
  1103 
  1104         This is often equivalent to how the data would be expressed on disk.
  1104         This is often equivalent to how the data would be expressed on disk.
  1105         """
  1105         """
  1106         return self._repo.wwritedata(self.path(), self.data())
  1106         return self._repo.wwritedata(self.path(), self.data())
  1107 
       
  1108 @attr.s(slots=True, frozen=True)
       
  1109 class annotateline(object):
       
  1110     fctx = attr.ib()
       
  1111     lineno = attr.ib(default=False)
       
  1112     # Whether this annotation was the result of a skip-annotate.
       
  1113     skip = attr.ib(default=False)
       
  1114 
       
  1115 def _annotatepair(parents, childfctx, child, skipchild, diffopts):
       
  1116     r'''
       
  1117     Given parent and child fctxes and annotate data for parents, for all lines
       
  1118     in either parent that match the child, annotate the child with the parent's
       
  1119     data.
       
  1120 
       
  1121     Additionally, if `skipchild` is True, replace all other lines with parent
       
  1122     annotate data as well such that child is never blamed for any lines.
       
  1123 
       
  1124     See test-annotate.py for unit tests.
       
  1125     '''
       
  1126     pblocks = [(parent, mdiff.allblocks(parent[1], child[1], opts=diffopts))
       
  1127                for parent in parents]
       
  1128 
       
  1129     if skipchild:
       
  1130         # Need to iterate over the blocks twice -- make it a list
       
  1131         pblocks = [(p, list(blocks)) for (p, blocks) in pblocks]
       
  1132     # Mercurial currently prefers p2 over p1 for annotate.
       
  1133     # TODO: change this?
       
  1134     for parent, blocks in pblocks:
       
  1135         for (a1, a2, b1, b2), t in blocks:
       
  1136             # Changed blocks ('!') or blocks made only of blank lines ('~')
       
  1137             # belong to the child.
       
  1138             if t == '=':
       
  1139                 child[0][b1:b2] = parent[0][a1:a2]
       
  1140 
       
  1141     if skipchild:
       
  1142         # Now try and match up anything that couldn't be matched,
       
  1143         # Reversing pblocks maintains bias towards p2, matching above
       
  1144         # behavior.
       
  1145         pblocks.reverse()
       
  1146 
       
  1147         # The heuristics are:
       
  1148         # * Work on blocks of changed lines (effectively diff hunks with -U0).
       
  1149         # This could potentially be smarter but works well enough.
       
  1150         # * For a non-matching section, do a best-effort fit. Match lines in
       
  1151         #   diff hunks 1:1, dropping lines as necessary.
       
  1152         # * Repeat the last line as a last resort.
       
  1153 
       
  1154         # First, replace as much as possible without repeating the last line.
       
  1155         remaining = [(parent, []) for parent, _blocks in pblocks]
       
  1156         for idx, (parent, blocks) in enumerate(pblocks):
       
  1157             for (a1, a2, b1, b2), _t in blocks:
       
  1158                 if a2 - a1 >= b2 - b1:
       
  1159                     for bk in xrange(b1, b2):
       
  1160                         if child[0][bk].fctx == childfctx:
       
  1161                             ak = min(a1 + (bk - b1), a2 - 1)
       
  1162                             child[0][bk] = attr.evolve(parent[0][ak], skip=True)
       
  1163                 else:
       
  1164                     remaining[idx][1].append((a1, a2, b1, b2))
       
  1165 
       
  1166         # Then, look at anything left, which might involve repeating the last
       
  1167         # line.
       
  1168         for parent, blocks in remaining:
       
  1169             for a1, a2, b1, b2 in blocks:
       
  1170                 for bk in xrange(b1, b2):
       
  1171                     if child[0][bk].fctx == childfctx:
       
  1172                         ak = min(a1 + (bk - b1), a2 - 1)
       
  1173                         child[0][bk] = attr.evolve(parent[0][ak], skip=True)
       
  1174     return child
       
  1175 
  1107 
  1176 class filectx(basefilectx):
  1108 class filectx(basefilectx):
  1177     """A filecontext object makes access to data related to a particular
  1109     """A filecontext object makes access to data related to a particular
  1178        filerevision convenient."""
  1110        filerevision convenient."""
  1179     def __init__(self, repo, path, changeid=None, fileid=None,
  1111     def __init__(self, repo, path, changeid=None, fileid=None,