コード例 #1
0
ファイル: __init__.py プロジェクト: ahornby/eden
def overlaydiffcontext(ctx, chunks):
    """(ctx, [crecord.uihunk]) -> memctx

    return a memctx with some [1] patches (chunks) applied to ctx.
    [1]: modifications are handled. renames, mode changes, etc. are ignored.
    """
    # sadly the applying-patch logic is hardly reusable, and messy:
    # 1. the core logic "_applydiff" is too heavy - it writes .rej files, it
    #    needs a file stream of a patch and will re-parse it, while we have
    #    structured hunk objects at hand.
    # 2. a lot of different implementations about "chunk" (patch.hunk,
    #    patch.recordhunk, crecord.uihunk)
    # as we only care about applying changes to modified files, no mode
    # change, no binary diff, and no renames, it's probably okay to
    # re-invent the logic using much simpler code here.
    memworkingcopy = {}  # {path: content}
    patchmap = defaultdict(lambda: [])  # {path: [(a1, a2, [bline])]}
    for path, info in map(_parsechunk, chunks):
        if not path or not info:
            continue
        patchmap[path].append(info)
    for path, patches in pycompat.iteritems(patchmap):
        if path not in ctx or not patches:
            continue
        patches.sort(reverse=True)
        lines = mdiff.splitnewlines(ctx[path].data())
        for a1, a2, blines in patches:
            lines[a1:a2] = blines
        memworkingcopy[path] = b"".join(lines)
    return overlaycontext(memworkingcopy, ctx)
コード例 #2
0
ファイル: __init__.py プロジェクト: ahornby/eden
    def diffwith(self, targetfctx, fm=None):
        """calculate fixups needed by examining the differences between
        self.fctxs[-1] and targetfctx, chunk by chunk.

        targetfctx is the target state we move towards. we may or may not be
        able to get there because not all modified chunks can be amended into
        a non-public fctx unambiguously.

        call this only once, before apply().

        update self.fixups, self.chunkstats, and self.targetlines.
        """
        a = self.contents[-1]
        alines = self.contentlines[-1]
        b = targetfctx.data()
        blines = mdiff.splitnewlines(b)
        self.targetlines = blines

        self.linelog.annotate(self.linelog.maxrev)
        annotated = self.linelog.annotateresult  # [(linelog rev, linenum)]
        assert len(annotated) == len(alines)
        # add a dummy end line to make insertion at the end easier
        if annotated:
            dummyendline = (annotated[-1][0], annotated[-1][1] + 1)
            annotated.append(dummyendline)

        # analyse diff blocks
        for chunk in self._alldiffchunks(a, b, alines, blines):
            newfixups = self._analysediffchunk(chunk, annotated)
            self.chunkstats[0] += bool(newfixups)  # 1 or 0
            self.chunkstats[1] += 1
            self.fixups += newfixups
            if fm is not None:
                self._showchanges(fm, alines, blines, chunk, newfixups)
コード例 #3
0
ファイル: __init__.py プロジェクト: jsoref/eden
 def _checkoutlinelogwithedits(self):
     """() -> [str]. prompt all lines for edit"""
     alllines = self.linelog.getalllines()
     # header
     editortext = (_('HG: editing %s\nHG: "y" means the line to the right '
                     "exists in the changeset to the top\nHG:\n") %
                   self.fctxs[-1].path())
     # [(idx, fctx)]. hide the dummy emptyfilecontext
     visiblefctxs = [(i, f) for i, f in enumerate(self.fctxs)
                     if not isinstance(f, emptyfilecontext)]
     for i, (j, f) in enumerate(visiblefctxs):
         editortext += _("HG: %s/%s %s %s\n") % (
             "|" * i,
             "-" * (len(visiblefctxs) - i + 1),
             node.short(f.node()),
             f.description().split("\n", 1)[0],
         )
     editortext += _("HG: %s\n") % ("|" * len(visiblefctxs))
     # figure out the lifetime of a line, this is relatively inefficient,
     # but probably fine
     lineset = defaultdict(lambda: set())  # {(llrev, linenum): {llrev}}
     for i, f in visiblefctxs:
         self.linelog.annotate((i + 1) * 2)
         for l in self.linelog.annotateresult:
             lineset[l].add(i)
     # append lines
     for l in alllines:
         editortext += "    %s : %s" % (
             "".join([("y" if i in lineset[l] else " ")
                      for i, _f in visiblefctxs]),
             self._getline(l),
         )
     # run editor
     editedtext = self.ui.edit(editortext, "", action="absorb")
     if not editedtext:
         raise error.Abort(_("empty editor text"))
     # parse edited result
     contents = ["" for i in self.fctxs]
     leftpadpos = 4
     colonpos = leftpadpos + len(visiblefctxs) + 1
     for l in mdiff.splitnewlines(editedtext):
         if l.startswith("HG:"):
             continue
         if l[colonpos - 1:colonpos + 2] != " : ":
             raise error.Abort(_("malformed line: %s") % l)
         linecontent = l[colonpos + 2:]
         for i, ch in enumerate(l[leftpadpos:colonpos - 1]):
             if ch == "y":
                 contents[visiblefctxs[i][0]] += linecontent
     # chunkstats is hard to calculate if anything changes, therefore
     # set them to just a simple value (1, 1).
     if editedtext != editortext:
         self.chunkstats = [1, 1]
     return contents
コード例 #4
0
ファイル: __init__.py プロジェクト: ahornby/eden
def _parsechunk(hunk):
    """(crecord.uihunk or patch.recordhunk) -> (path, (a1, a2, [bline]))"""
    if type(hunk) not in (crecord.uihunk, patch.recordhunk):
        return None, None
    path = hunk.header.filename()
    a1 = hunk.fromline + len(hunk.before) - 1
    # remove before and after context
    hunk.before = hunk.after = []
    buf = util.stringio()
    hunk.write(buf)
    patchlines = mdiff.splitnewlines(buf.getvalue())
    # hunk.prettystr() will update hunk.removed
    a2 = a1 + hunk.removed
    blines = [l[1:] for l in patchlines[1:] if l[0:1] != b"-"]
    return path, (a1, a2, blines)
コード例 #5
0
ファイル: context.py プロジェクト: simpkins/eden
 def _refineannotateresult(self, result, f, showpath, showlines):
     """add the missing path or line contents, they can be expensive.
     f could be either node or fctx.
     """
     if showpath:
         result = self._addpathtoresult(result)
     if showlines:
         if isinstance(f, bytes):  # f: node or fctx
             llrev = self.revmap.hsh2rev(f)
             fctx = self._resolvefctx(f, self.revmap.rev2path(llrev))
         else:
             fctx = f
         lines = mdiff.splitnewlines(fctx.data())
         if len(lines) != len(result):  # linelog is probably corrupted
             raise faerror.CorruptedFileError()
         result = (result, lines)
     return result
コード例 #6
0
ファイル: context.py プロジェクト: simpkins/eden
    def _resolvelines(self, annotateresult, revmap, linelog):
        """(annotateresult) -> [line]. designed for annotatealllines.
        this is probably the most inefficient code in the whole fastannotate
        directory. but we have made a decision that the linelog does not
        store line contents. so getting them requires random accesses to
        the revlog data, since they can be many, it can be very slow.
        """
        # [llrev]
        revs = [revmap.hsh2rev(l[0]) for l in annotateresult]
        result = [None] * len(annotateresult)
        # {(rev, linenum): [lineindex]}
        key2idxs = collections.defaultdict(list)
        for i in range(len(result)):
            key2idxs[(revs[i], annotateresult[i][1])].append(i)
        while key2idxs:
            # find an unresolved line and its linelog rev to annotate
            hsh = None
            try:
                for (rev, _linenum), idxs in pycompat.iteritems(key2idxs):
                    if revmap.rev2flag(rev) & revmapmod.sidebranchflag:
                        continue
                    hsh = annotateresult[idxs[0]][0]
                    break
            except StopIteration:  # no more unresolved lines
                return result
            if hsh is None:
                # the remaining key2idxs are not in main branch, resolving them
                # using the hard way...
                revlines = {}
                for (rev, linenum), idxs in pycompat.iteritems(key2idxs):
                    if rev not in revlines:
                        hsh = annotateresult[idxs[0]][0]
                        if self.ui.debugflag:
                            self.ui.debug(
                                "fastannotate: reading %s line #%d "
                                "to resolve lines %r\n"
                                % (node.short(hsh), linenum, idxs)
                            )
                        fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
                        lines = mdiff.splitnewlines(fctx.data())
                        revlines[rev] = lines
                    for idx in idxs:
                        result[idx] = revlines[rev][linenum]
                assert all(x is not None for x in result)
                return result

            # run the annotate and the lines should match to the file content
            self.ui.debug(
                "fastannotate: annotate %s to resolve lines\n" % node.short(hsh)
            )
            linelog.annotate(rev)
            fctx = self._resolvefctx(hsh, revmap.rev2path(rev))
            annotated = linelog.annotateresult
            lines = mdiff.splitnewlines(fctx.data())
            if len(lines) != len(annotated):
                raise faerror.CorruptedFileError("unexpected annotated lines")
            # resolve lines from the annotate result
            for i, line in enumerate(lines):
                k = annotated[i]
                if k in key2idxs:
                    for idx in key2idxs[k]:
                        result[idx] = line
                    del key2idxs[k]
        return result