def extract()

in eden/scm/edenscm/mercurial/patch.py [0:0]


def extract(ui, fileobj):
    """extract patch from data read from fileobj.

    patch can be a normal patch or contained in an email message.

    return a dictionary. Standard keys are:
      - filename,
      - message,
      - user,
      - date,
      - branch,
      - node,
      - p1,
      - p2.
    Any item can be missing from the dictionary. If filename is missing,
    fileobj did not contain a patch. Caller must unlink filename when done."""

    # attempt to detect the start of a patch
    # (this heuristic is borrowed from quilt)
    diffre = re.compile(
        br"^(?:Index:[ \t]|diff[ \t]-|RCS file: |"
        br"retrieving revision [0-9]+(\.[0-9]+)*$|"
        br"---[ \t].*?^\+\+\+[ \t]|"
        br"\*\*\*[ \t].*?^---[ \t])",
        re.MULTILINE | re.DOTALL,
    )

    data = {}
    fd, tmpname = tempfile.mkstemp(prefix="hg-patch-")
    tmpfp = util.fdopen(fd, "wb")
    try:
        msg = pycompat.parse_email(fileobj)

        subject = msg["Subject"] and mail.headdecode(msg["Subject"])
        data["user"] = msg["From"] and mail.headdecode(msg["From"])
        if not subject and not data["user"]:
            # Not an email, restore parsed headers if any
            subject = "\n".join(": ".join(h) for h in msg.items()) + "\n"

        # should try to parse msg['Date']
        parents = []

        if subject:
            if subject.startswith("[PATCH"):
                pend = subject.find("]")
                if pend >= 0:
                    subject = subject[pend + 1 :].lstrip()
            subject = re.sub(r"\n[ \t]+", " ", subject)
            ui.debug("Subject: %s\n" % subject)
        if data["user"]:
            ui.debug("From: %s\n" % data["user"])
        diffs_seen = 0
        ok_types = ("text/plain", "text/x-diff", "text/x-patch")
        message = b""
        for part in msg.walk():
            content_type = part.get_content_type()
            ui.debug("Content-Type: %s\n" % content_type)
            if content_type not in ok_types:
                continue
            payload = part.get_payload(decode=True)
            m = diffre.search(payload)
            if m:
                hgpatch = False
                hgpatchheader = False
                ignoretext = False

                ui.debug("found patch at byte %d\n" % m.start(0))
                diffs_seen += 1
                cfp = stringio()
                for line in payload[: m.start(0)].splitlines():
                    if line.startswith(b"# HG changeset patch") and not hgpatch:
                        ui.debug("patch generated by hg export\n")
                        hgpatch = True
                        hgpatchheader = True
                        # drop earlier commit message content
                        cfp.seek(0)
                        cfp.truncate()
                        subject = None
                    elif hgpatchheader:
                        if line.startswith(b"# User "):
                            data["user"] = pycompat.decodeutf8(line[7:])
                            ui.debug("From: %s\n" % data["user"])
                        elif line.startswith(b"# Parent "):
                            parents.append(pycompat.decodeutf8(line[9:].lstrip()))
                        elif line.startswith(b"# "):
                            for header, key in patchheadermap:
                                prefix = b"# %s " % header
                                if line.startswith(prefix):
                                    data[key] = pycompat.decodeutf8(line[len(prefix) :])
                        else:
                            hgpatchheader = False
                    elif line == b"---":
                        ignoretext = True
                    if not hgpatchheader and not ignoretext:
                        cfp.write(line)
                        cfp.write(b"\n")
                message = cfp.getvalue()
                if tmpfp:
                    tmpfp.write(payload)
                    if not payload.endswith(b"\n"):
                        tmpfp.write(b"\n")
            elif not diffs_seen and message and content_type == "text/plain":
                message += b"\n" + payload
    except:  # re-raises
        tmpfp.close()
        os.unlink(tmpname)
        raise

    message = pycompat.decodeutf8(message)
    if subject and not message.startswith(subject):
        message = "%s\n%s" % (subject, message)
    data["message"] = message
    tmpfp.close()
    if parents:
        data["p1"] = parents.pop(0)
        if parents:
            data["p2"] = parents.pop(0)

    if diffs_seen:
        data["filename"] = tmpname
    else:
        os.unlink(tmpname)
    return data