| """Search for MRs and issues related to a list of commits.""" | |
| import argparse | |
| import json | |
| import sys | |
| import subprocess | |
| import re | |
| def find_cherry_pick_source(commit_hash: str): | |
| """ | |
| For a given commit hash, find the original commit it was cherry-picked from. | |
| Args: | |
| commit_hash: The commit hash to inspect. | |
| Returns: | |
| The full hash of the original commit if found, otherwise None. | |
| """ | |
| try: | |
| # Use 'git show' to get the full commit message for the given hash. | |
| # The '-s' flag suppresses the diff output. | |
| # The '--format=%B' flag prints only the raw commit body/message. | |
| commit_message = subprocess.check_output( | |
| ["git", "show", "-s", "--format=%B", commit_hash.strip()], | |
| text=True, | |
| stderr=subprocess.PIPE, | |
| ).strip() | |
| # This regex looks for the specific line Git adds during a cherry-pick. | |
| # It captures the full 40-character SHA-1 hash. | |
| cherry_pick_pattern = re.compile( | |
| r"\(cherry picked from commit ([a-f0-9]{40})\)" | |
| ) | |
| # Search the entire commit message for the pattern. | |
| match = cherry_pick_pattern.search(commit_message) | |
| if match: | |
| # If a match is found, return the captured group (the original commit hash). | |
| return match.group(1) | |
| else: | |
| return None | |
| except subprocess.CalledProcessError as e: | |
| # This error occurs if the git command fails, e.g., for an invalid hash. | |
| print( | |
| f"Error processing commit '{commit_hash.strip()}': {e.stderr.strip()}", | |
| file=sys.stderr, | |
| ) | |
| return None | |
| except FileNotFoundError: | |
| # This error occurs if the 'git' command itself isn't found. | |
| print( | |
| "Error: 'git' command not found. Please ensure Git is installed and in your PATH.", | |
| file=sys.stderr, | |
| ) | |
| sys.exit(1) | |
| def main(): | |
| """ | |
| Main function to read commit hashes from stdin and process them. | |
| """ | |
| parser = argparse.ArgumentParser( | |
| description="A script to download all MRs from GitLab matching specified criteria." | |
| ) | |
| parser.add_argument( | |
| "--merge_requests_file", | |
| type=str, | |
| required=True, | |
| help="JSON file containing all the merge request information extracted via the GitLab API.", | |
| ) | |
| # E.g. git log --pretty=%H 3e819d83bf52abda16bb53565f6801df40d071f1..3.4.1 | |
| parser.add_argument( | |
| "--commits", | |
| required=True, | |
| help="List of commits, '-' for stdin.", | |
| ) | |
| args = parser.parse_args() | |
| mrs = [] | |
| with open(args.merge_requests_file, "r") as file: | |
| mrs = json.load(file) | |
| mrs_by_commit = {} | |
| if args.commits == "-": | |
| commit_hashes = sys.stdin.readlines() | |
| else: | |
| with open(args.commits, "r") as file: | |
| commit_hashes = file.readlines() | |
| # Arrange commits by SHA. | |
| for mr in mrs: | |
| for key in ["sha", "merge_commit_sha", "squash_commit_sha"]: | |
| sha = mr[key] | |
| if sha: | |
| mrs_by_commit[sha] = mr | |
| # Find the MRs and issues related to each commit. | |
| info = {} | |
| for sha in commit_hashes: | |
| sha = sha.strip() | |
| if not sha: | |
| continue | |
| # If a cherry-pick, extract the original hash. | |
| sha = find_cherry_pick_source(sha) or sha | |
| mr = mrs_by_commit.get(sha) | |
| commit_info = {} | |
| if mr: | |
| commit_info["merge_request"] = mr["iid"] | |
| commit_info["related_issues"] = [ | |
| issue["iid"] for issue in mr["related_issues"] | |
| ] | |
| commit_info["closes_issues"] = [ | |
| issue["iid"] for issue in mr["closes_issues"] | |
| ] | |
| info[sha] = commit_info | |
| print(json.dumps(info, indent=2)) | |
| if __name__ == "__main__": | |
| main() |