1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4import argparse
5import re
6import subprocess
7
8
9# This script extracts commits that touch third party webrtc code so they can
10# be imported into Git. It filters out commits that are not part of upstream
11# code and rewrites the paths to match upstream. Finally, the commits are
12# combined into a mailbox file that can be applied with `git am`.
13LIBWEBRTC_DIR = "third_party/libwebrtc/webrtc"
14
15
16def build_commit_list(revset):
17    """Build commit list from the specified revset.
18
19    The revset can be a single revision, e.g. 52bb9bb94661, or a range,
20    e.g. 8c08a5bb8a99::52bb9bb94661, or any other valid revset
21    (check hg help revset). Only commits that touch libwebrtc are included.
22    """
23    res = subprocess.run(
24        ["hg", "log", "-r", revset, "-M", "--template", "{node}\n", LIBWEBRTC_DIR],
25        capture_output=True,
26        text=True,
27        cwd="../../../../",
28    )
29    return [line.strip() for line in res.stdout.strip().split("\n")]
30
31
32def extract_author_date(sha1):
33    res = subprocess.run(
34        ["hg", "log", "-r", sha1, "--template", "{author}|{date|isodate}"],
35        capture_output=True,
36        text=True,
37    )
38    return res.stdout.split("|")
39
40
41def extract_description(sha1):
42    res = subprocess.run(
43        ["hg", "log", "-r", sha1, "--template", "{desc}"],
44        capture_output=True,
45        text=True,
46    )
47    return res.stdout
48
49
50def extract_commit(sha1):
51    res = subprocess.run(
52        ["hg", "log", "-r", sha1, "-pg", "--template", "\n"],
53        capture_output=True,
54        text=True,
55    )
56    return res.stdout
57
58
59def filter_nonwebrtc(commit):
60    filtered = []
61    skipping = False
62    for line in commit.split("\n"):
63        # Extract only patches affecting libwebrtc, but avoid commits that
64        # touch build, which is tracked by a separate repo, or that affect
65        # moz.build files which are code generated.
66        if (
67            line.startswith("diff --git a/" + LIBWEBRTC_DIR)
68            and not line.startswith("diff --git a/" + LIBWEBRTC_DIR + "/build")
69            and not line.endswith("moz.build")
70        ):
71            skipping = False
72        elif line.startswith("diff --git"):
73            skipping = True
74        if not skipping:
75            filtered.append(line)
76    return "\n".join(filtered)
77
78
79def fixup_paths(commit):
80    return re.sub("third_party/libwebrtc/webrtc/", "", commit)
81
82
83def write_as_mbox(sha1, author, date, description, commit, ofile):
84    # Use same magic date as git format-patch
85    ofile.write("From {} Mon Sep 17 00:00:00 2001\n".format(sha1))
86    ofile.write("From: {}\n".format(author))
87    ofile.write("Date: {}\n".format(date))
88    description = description.split("\n")
89    ofile.write("Subject: {}\n".format(description[0]))
90    ofile.write("\n".join(description[1:]))
91    ofile.write(
92        "\nMercurial Revision: https://hg.mozilla.org/mozilla-central/rev/{}\n".format(
93            sha1
94        )
95    )
96    ofile.write(commit)
97    ofile.write("\n")
98    ofile.write("\n")
99
100
101if __name__ == "__main__":
102    commits = []
103    parser = argparse.ArgumentParser(
104        description="Format commits for upstream libwebrtc"
105    )
106    parser.add_argument(
107        "revsets", metavar="revset", type=str, nargs="+", help="A revset to process"
108    )
109    args = parser.parse_args()
110
111    for revset in args.revsets:
112        commits.extend(build_commit_list(revset))
113
114    with open("mailbox.patch", "w") as ofile:
115        for sha1 in commits:
116            author, date = extract_author_date(sha1)
117            description = extract_description(sha1)
118            filtered_commit = filter_nonwebrtc(extract_commit(sha1))
119            if len(filtered_commit) == 0:
120                continue
121            fixedup_commit = fixup_paths(filtered_commit)
122            write_as_mbox(sha1, author, date, description, fixedup_commit, ofile)
123