-
Notifications
You must be signed in to change notification settings - Fork 1
ciq-cherry-pick.sh: Some improvements #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: mainline
Are you sure you want to change the base?
Changes from 1 commit
4ad0f47
bca8c65
f4a2b42
42b21f6
6431a45
62f096e
7a8f7f8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -8,38 +8,33 @@ | |||||||||
| import textwrap | ||||||||||
| from typing import Optional | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def run_git(repo, args): | ||||||||||
| """Run a git command in the given repository and return its output as a string.""" | ||||||||||
| result = subprocess.run(["git", "-C", repo] + args, text=True, capture_output=True, check=False) | ||||||||||
| if result.returncode != 0: | ||||||||||
| raise RuntimeError(f"Git command failed: {' '.join(args)}\n{result.stderr}") | ||||||||||
| return result.stdout | ||||||||||
| from ciq_helpers import ( | ||||||||||
| CIQ_commit_exists_in_branch, | ||||||||||
| CIQ_extract_fixes_references_from_commit_body_lines, | ||||||||||
| CIQ_get_commit_body, | ||||||||||
| CIQ_hash_exists_in_ref, | ||||||||||
| CIQ_run_git, | ||||||||||
| ) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def ref_exists(repo, ref): | ||||||||||
| """Return True if the given ref exists in the repository, False otherwise.""" | ||||||||||
| try: | ||||||||||
| run_git(repo, ["rev-parse", "--verify", "--quiet", ref]) | ||||||||||
| CIQ_run_git(repo, ["rev-parse", "--verify", "--quiet", ref]) | ||||||||||
| return True | ||||||||||
| except RuntimeError: | ||||||||||
| return False | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def get_pr_commits(repo, pr_branch, base_branch): | ||||||||||
| """Get a list of commit SHAs that are in the PR branch but not in the base branch.""" | ||||||||||
| output = run_git(repo, ["rev-list", f"{base_branch}..{pr_branch}"]) | ||||||||||
| output = CIQ_run_git(repo, ["rev-list", f"{base_branch}..{pr_branch}"]) | ||||||||||
| return output.strip().splitlines() | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def get_commit_message(repo, sha): | ||||||||||
| """Get the commit message for a given commit SHA.""" | ||||||||||
| return run_git(repo, ["log", "-n", "1", "--format=%B", sha]) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def get_short_hash_and_subject(repo, sha): | ||||||||||
| """Get the abbreviated commit hash and subject for a given commit SHA.""" | ||||||||||
| output = run_git(repo, ["log", "-n", "1", "--format=%h%x00%s", sha]).strip() | ||||||||||
| output = CIQ_run_git(repo, ["log", "-n", "1", "--format=%h%x00%s", sha]).strip() | ||||||||||
| short_hash, subject = output.split("\x00", 1) | ||||||||||
| return short_hash, subject | ||||||||||
|
|
||||||||||
|
|
@@ -48,61 +43,56 @@ def hash_exists_in_mainline(repo, upstream_ref, hash_): | |||||||||
| """ | ||||||||||
| Return True if hash_ is reachable from upstream_ref (i.e., is an ancestor of it). | ||||||||||
| """ | ||||||||||
| try: | ||||||||||
| run_git(repo, ["merge-base", "--is-ancestor", hash_, upstream_ref]) | ||||||||||
| return True | ||||||||||
| except RuntimeError: | ||||||||||
| return False | ||||||||||
|
|
||||||||||
| return CIQ_hash_exists_in_ref(repo, upstream_ref, hash_) | ||||||||||
|
|
||||||||||
|
|
||||||||||
| def find_fixes_in_mainline(repo, pr_branch, upstream_ref, hash_): | ||||||||||
| """ | ||||||||||
| Return unique commits in upstream_ref that have Fixes: <N chars of hash_> in their message, case-insensitive. | ||||||||||
| Return unique commits in upstream_ref that have Fixes: <N chars of hash_> in their message, case-insensitive, | ||||||||||
| if they have not been committed in the pr_branch. | ||||||||||
| Start from 12 chars and work down to 6, but do not include duplicates if already found at a longer length. | ||||||||||
| Returns a list of tuples: (full_hash, display_string) | ||||||||||
| """ | ||||||||||
| results = [] | ||||||||||
|
|
||||||||||
| # Prepare hash prefixes from 12 down to 6 | ||||||||||
| hash_prefixes = [hash_[:index] for index in range(12, 5, -1)] | ||||||||||
roxanan1996 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||||||||||
|
|
||||||||||
| # Get all commits with 'Fixes:' in the message | ||||||||||
| output = run_git(repo, ["log", upstream_ref, "--grep", "Fixes:", "-i", "--format=%H %h %s (%an)%x0a%B%x00"]).strip() | ||||||||||
| output = CIQ_run_git( | ||||||||||
| repo, | ||||||||||
| [ | ||||||||||
| "log", | ||||||||||
| upstream_ref, | ||||||||||
| "--grep", | ||||||||||
| "Fixes:", | ||||||||||
| "-i", | ||||||||||
| "--format=%H %h %s (%an)%x0a%B%x00", | ||||||||||
| ], | ||||||||||
| ).strip() | ||||||||||
| if not output: | ||||||||||
| return [] | ||||||||||
|
|
||||||||||
| # Each commit is separated by a NUL character and a newline | ||||||||||
| commits = output.split("\x00\x0a") | ||||||||||
| # Prepare hash prefixes from 12 down to 6 | ||||||||||
| hash_prefixes = [hash_[:index] for index in range(12, 5, -1)] | ||||||||||
| for commit in commits: | ||||||||||
| if not commit.strip(): | ||||||||||
| continue | ||||||||||
| # The first line is the summary, the rest is the body | ||||||||||
|
|
||||||||||
| lines = commit.splitlines() | ||||||||||
| if not lines: | ||||||||||
| continue | ||||||||||
| # The first line is the summary, the rest is the body | ||||||||||
| header = lines[0] | ||||||||||
| full_hash = header.split()[0] | ||||||||||
| # Search for Fixes: lines in the commit message | ||||||||||
| for line in lines[1:]: | ||||||||||
| m = re.match(r"^\s*Fixes:\s*([0-9a-fA-F]{6,40})", line, re.IGNORECASE) | ||||||||||
| if m: | ||||||||||
| for prefix in hash_prefixes: | ||||||||||
| if m.group(1).lower().startswith(prefix.lower()): | ||||||||||
| if not commit_exists_in_branch(repo, pr_branch, full_hash): | ||||||||||
| results.append((full_hash, " ".join(header.split()[1:]))) | ||||||||||
| break | ||||||||||
| else: | ||||||||||
| continue | ||||||||||
| return results | ||||||||||
| full_hash, display_string = (lambda h: (h[0], " ".join(h[1:])))(header.split()) | ||||||||||
|
||||||||||
| full_hash, display_string = (lambda h: (h[0], " ".join(h[1:])))(header.split()) | |
| parts = header.split() | |
| full_hash = parts[0] | |
| display_string = " ".join(parts[1:]) |
roxanan1996 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
roxanan1996 marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -169,6 +169,88 @@ def CIQ_original_commit_author_to_tag_string(repo_path, sha): | |
| return "commit-author " + git_auth_res.stdout.decode("utf-8").replace('"', "").strip() | ||
|
|
||
|
|
||
| def CIQ_run_git(repo_path, args): | ||
| """ | ||
| Run a git command in the given repository and return its output as a string. | ||
| """ | ||
| result = subprocess.run(["git", "-C", repo_path] + args, text=True, capture_output=True, check=False) | ||
| if result.returncode != 0: | ||
| raise RuntimeError(f"Git command failed: {' '.join(args)}\n{result.stderr}") | ||
|
|
||
| return result.stdout | ||
|
|
||
|
|
||
| def CIQ_get_commit_body(repo_path, sha): | ||
| return CIQ_run_git(repo_path, ["show", "-s", sha, "--format=%B"]) | ||
PlaidCat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
|
|
||
| def CIQ_extract_fixes_references_from_commit_body_lines(lines): | ||
| fixes = [] | ||
| for line in lines: | ||
| m = re.match(r"^\s*Fixes:\s*([0-9a-fA-F]{6,40})", line, re.IGNORECASE) | ||
| if not m: | ||
| continue | ||
|
|
||
| fixes.append(m.group(1)) | ||
|
|
||
| return fixes | ||
|
|
||
|
|
||
| def CIQ_fixes_references(repo_path, sha): | ||
| """ | ||
| If commit message of sha contains lines like | ||
| Fixes: <short_fixed>, this returns a list of <short_fixed>, otherwise an empty list | ||
| """ | ||
|
|
||
| commit_body = CIQ_get_commit_body(repo_path, sha) | ||
| return CIQ_extract_fixes_references_from_commit_body_lines(lines=commit_body.splitlines()) | ||
|
|
||
|
|
||
| def CIQ_get_full_hash(repo, short_hash): | ||
PlaidCat marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return CIQ_run_git(repo, ["show", "-s", "--pretty=%H", short_hash]).strip() | ||
|
|
||
|
|
||
| def CIQ_get_current_branch(repo): | ||
| return CIQ_run_git(repo, ["branch", "--show-current"]).strip() | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no try catch here, in the even that the call out fails for what ever magical reason. |
||
|
|
||
|
|
||
| def CIQ_hash_exists_in_ref(repo, pr_ref, hash_): | ||
| """ | ||
| Return True if hash_ is reachable from pr_ref | ||
| """ | ||
|
|
||
| try: | ||
| CIQ_run_git(repo, ["merge-base", "--is-ancestor", hash_, pr_ref]) | ||
| return True | ||
| except RuntimeError: | ||
| return False | ||
|
|
||
|
|
||
| def CIQ_commit_exists_in_branch(repo, pr_branch, upstream_hash_): | ||
| """ | ||
| Return True if upstream_hash_ has been backported and it exists in the pr branch | ||
| """ | ||
|
|
||
| # First check if the commit has been backported by CIQ | ||
| output = CIQ_run_git(repo, ["log", pr_branch, "--grep", "^commit " + upstream_hash_]) | ||
roxanan1996 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| if output: | ||
roxanan1996 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
| return True | ||
|
|
||
| # If it was not backported by CIQ, maybe it came from upstream as it is | ||
| return CIQ_hash_exists_in_ref(repo, pr_branch, upstream_hash_) | ||
|
|
||
|
|
||
| def CIQ_commit_exists_in_current_branch(repo, upstream_hash_): | ||
| """ | ||
| Return True if upstream_hash_ has been backported and it exists in the current branch | ||
| """ | ||
|
|
||
| current_branch = CIQ_get_current_branch(repo) | ||
| full_upstream_hash = CIQ_get_full_hash(repo, upstream_hash_) | ||
|
|
||
| return CIQ_commit_exists_in_branch(repo, current_branch, full_upstream_hash) | ||
|
|
||
|
|
||
| def repo_init(repo): | ||
| """Initialize a git repo object. | ||
|
|
||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.