repopick: Avoid repeatedly performing the same check

Jobs such as check for already picked changes only need to be done once
for each git repository, but it was lunched every time picking a commit.

Change-Id: I87b3fea101dbcedb38502015fe9a9af5f25b397c
This commit is contained in:
dianlujitao 2024-01-13 21:27:00 +08:00 committed by Bruno Martins
parent 6682b3f35a
commit 0b48b9b0c1

View file

@ -29,9 +29,13 @@ import sys
import textwrap
import urllib.parse
import urllib.request
from collections import defaultdict
from functools import cmp_to_key
from xml.etree import ElementTree
# Default to LineageOS Gerrit
DEFAULT_GERRIT = "https://review.lineageos.org"
# cmp() is not available in Python 3, define it manually
# See https://docs.python.org/3.0/whatsnew/3.0.html#ordering-comparisons
@ -114,7 +118,6 @@ def fetch_query_via_ssh(remote_url, query):
reviews.append(review)
except:
pass
args.quiet or print("Found {0} reviews".format(len(reviews)))
return reviews
@ -179,10 +182,11 @@ def fetch_query(remote_url, query):
)
if __name__ == "__main__":
# Default to LineageOS Gerrit
default_gerrit = "https://review.lineageos.org"
def is_closed(status):
return status not in ("OPEN", "NEW", "DRAFT")
def main():
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=textwrap.dedent(
@ -269,7 +273,7 @@ if __name__ == "__main__":
parser.add_argument(
"-g",
"--gerrit",
default=default_gerrit,
default=DEFAULT_GERRIT,
metavar="",
help="Gerrit Instance to use. Form proto://[user@]host[:port]",
)
@ -373,10 +377,6 @@ if __name__ == "__main__":
revision = revision.split("refs/heads/")[-1]
project_name_to_data[name][revision] = path
# get data on requested changes
reviews = []
change_numbers = []
def cmp_reviews(review_a, review_b):
current_a = review_a["current_revision"]
parents_a = [
@ -393,18 +393,20 @@ if __name__ == "__main__":
else:
return cmp(review_a["number"], review_b["number"])
# get data on requested changes
if args.topic:
reviews = fetch_query(args.gerrit, "topic:{0}".format(args.topic))
change_numbers = [
str(r["number"]) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))
]
if args.query:
elif args.query:
reviews = fetch_query(args.gerrit, args.query)
change_numbers = [
str(r["number"]) for r in sorted(reviews, key=cmp_to_key(cmp_reviews))
]
if args.change_number:
else:
change_url_re = re.compile(r"https?://.+?/([0-9]+(?:/[0-9]+)?)/?")
change_numbers = []
for c in args.change_number:
change_number = change_url_re.findall(c)
if change_number:
@ -421,7 +423,7 @@ if __name__ == "__main__":
)
# make list of things to actually merge
mergables = []
mergables = defaultdict(list)
# If --exclude is given, create the list of commits to ignore
exclude = []
@ -446,32 +448,71 @@ if __name__ == "__main__":
print("Change %d not found, skipping" % change)
continue
mergables.append(
{
# Check if change is open and exit if it's not, unless -f is specified
if is_closed(review["status"]) and not args.force:
print(
"Change {} status is {}. Skipping the cherry pick.\nUse -f to force this pick.".format(
change, review["status"]
)
)
continue
# Convert the project name to a project path
# - check that the project path exists
if (
review["project"] in project_name_to_data
and review["branch"] in project_name_to_data[review["project"]]
):
project_path = project_name_to_data[review["project"]][review["branch"]]
elif args.path:
project_path = args.path
elif (
review["project"] in project_name_to_data
and len(project_name_to_data[review["project"]]) == 1
):
local_branch = list(project_name_to_data[review["project"]])[0]
project_path = project_name_to_data[review["project"]][local_branch]
print(
'WARNING: Project {0} has a different branch ("{1}" != "{2}")'.format(
project_path, local_branch, review["branch"]
)
)
elif args.ignore_missing:
print(
"WARNING: Skipping {0} since there is no project directory for: {1}\n".format(
review["id"], review["project"]
)
)
continue
else:
sys.stderr.write(
"ERROR: For {0}, could not determine the project path for project {1}\n".format(
review["id"], review["project"]
)
)
sys.exit(1)
item = {
"subject": review["subject"],
"project": review["project"],
"project_path": project_path,
"branch": review["branch"],
"change_id": review["change_id"],
"change_number": review["number"],
"status": review["status"],
"fetch": None,
"patchset": review["revisions"][review["current_revision"]]["_number"],
"fetch": review["revisions"][review["current_revision"]]["fetch"],
"id": change,
}
)
mergables[-1]["fetch"] = review["revisions"][review["current_revision"]][
"fetch"
]
mergables[-1]["id"] = change
if patchset:
try:
mergables[-1]["fetch"] = [
item["fetch"] = [
review["revisions"][x]["fetch"]
for x in review["revisions"]
if review["revisions"][x]["_number"] == patchset
][0]
mergables[-1]["id"] = "{0}/{1}".format(change, patchset)
mergables[-1]["patchset"] = patchset
item["id"] = "{0}/{1}".format(change, patchset)
item["patchset"] = patchset
except (IndexError, ValueError):
args.quiet or print(
"ERROR: The patch set {0}/{1} could not be found, using CURRENT_REVISION instead.".format(
@ -479,61 +520,9 @@ if __name__ == "__main__":
)
)
for item in mergables:
args.quiet or print("Applying change number {0}...".format(item["id"]))
# Check if change is open and exit if it's not, unless -f is specified
if (
item["status"] != "OPEN"
and item["status"] != "NEW"
and item["status"] != "DRAFT"
):
if args.force:
print("!! Force-picking a closed change !!\n")
else:
print(
"Change status is "
+ item["status"]
+ ". Skipping the cherry pick.\nUse -f to force this pick."
)
continue
# Convert the project name to a project path
# - check that the project path exists
project_path = None
if (
item["project"] in project_name_to_data
and item["branch"] in project_name_to_data[item["project"]]
):
project_path = project_name_to_data[item["project"]][item["branch"]]
elif args.path:
project_path = args.path
elif (
item["project"] in project_name_to_data
and len(project_name_to_data[item["project"]]) == 1
):
local_branch = list(project_name_to_data[item["project"]])[0]
project_path = project_name_to_data[item["project"]][local_branch]
print(
'WARNING: Project {0} has a different branch ("{1}" != "{2}")'.format(
project_path, local_branch, item["branch"]
)
)
elif args.ignore_missing:
print(
"WARNING: Skipping {0} since there is no project directory for: {1}\n".format(
item["id"], item["project"]
)
)
continue
else:
sys.stderr.write(
"ERROR: For {0}, could not determine the project path for project {1}\n".format(
item["id"], item["project"]
)
)
sys.exit(1)
mergables[project_path].append(item)
for project_path, per_path_mergables in mergables.items():
# If --start-branch is given, create the branch (more than once per path is okay; repo ignores gracefully)
if args.start_branch:
subprocess.run(["repo", "start", args.start_branch[0], project_path])
@ -557,9 +546,8 @@ if __name__ == "__main__":
if branch_commits_count <= check_picked_count:
check_picked_count = branch_commits_count - 1
# Check if change is already picked to HEAD...HEAD~check_picked_count
found_change = False
for i in range(0, check_picked_count):
picked_change_ids = []
for i in range(check_picked_count):
if subprocess.call(
["git", "cat-file", "-e", "HEAD~{0}".format(i)],
cwd=project_path,
@ -571,22 +559,32 @@ if __name__ == "__main__":
)
output = output.split()
if "Change-Id:" in output:
head_change_id = ""
for j, t in enumerate(reversed(output)):
if t == "Change-Id:":
head_change_id = output[len(output) - j]
picked_change_ids.append(head_change_id.strip())
break
if head_change_id.strip() == item["change_id"]:
for item in per_path_mergables:
# Check if change is already picked to HEAD...HEAD~check_picked_count
if item["change_id"] in picked_change_ids:
print(
"Skipping {0} - already picked in {1} as HEAD~{2}".format(
item["id"], project_path, i
"Skipping {0} - already picked in {1}".format(
item["id"], project_path
)
)
found_change = True
break
if found_change:
continue
apply_change(args, item)
def apply_change(args, item):
args.quiet or print("Applying change number {0}...".format(item["id"]))
if is_closed(item["status"]):
print("!! Force-picking a closed change !!\n")
project_path = item["project_path"]
# Print out some useful info
if not args.quiet:
print('--> Subject: "{0}"'.format(item["subject"]))
@ -611,7 +609,7 @@ if __name__ == "__main__":
cmd.extend(["", item["fetch"][method]["ref"]])
# Try fetching from GitHub first if using default gerrit
if args.gerrit == default_gerrit:
if args.gerrit == DEFAULT_GERRIT:
if args.verbose:
print("Trying to fetch the change from GitHub")
@ -624,10 +622,10 @@ if __name__ == "__main__":
print("ERROR: git command failed")
sys.exit(result)
# Check if it worked
if args.gerrit != default_gerrit or os.stat(FETCH_HEAD).st_size == 0:
if args.gerrit != DEFAULT_GERRIT or os.stat(FETCH_HEAD).st_size == 0:
# If not using the default gerrit or github failed, fetch from gerrit.
if args.verbose:
if args.gerrit == default_gerrit:
if args.gerrit == DEFAULT_GERRIT:
print(
"Fetching from GitHub didn't work, trying to fetch the change from Gerrit"
)
@ -684,3 +682,7 @@ if __name__ == "__main__":
sys.exit(result)
if not args.quiet:
print("")
if __name__ == "__main__":
main()