Created
March 10, 2025 15:33
-
-
Save graingert-coef/62f49452f7ea8eef78e3479ca0bc1b67 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import argparse | |
import git | |
import requirements | |
def get_version(req): | |
"""Extracts version from requirement or raises an error if not specified.""" | |
if req.specs: | |
return req.specs[0][1] | |
else: | |
raise ValueError(f"Package {req.name} does not specify a version with '=='.") | |
def parse_requirements(file_content): | |
"""Parses content from a requirements.txt file and returns a dictionary of packages and their versions.""" | |
packages = { | |
req.name: get_version(req) | |
for req in requirements.parse(file_content) | |
} | |
return packages | |
def compare_requirements(before_io, after_io): | |
"""Compares two requirements files from IO streams and prints a list of version changes or 'no changes'.""" | |
before_requirements = parse_requirements(before_io) | |
after_requirements = parse_requirements(after_io) | |
changes = False | |
for package in after_requirements: | |
before_version = before_requirements.get(package, None) | |
after_version = after_requirements.get(package) | |
if before_version != after_version: | |
changes = True | |
print(f"{package} {before_version} -> {after_version}") | |
if not changes: | |
print("no changes") | |
def get_file_at_commit(repo, commit_hash, file_path): | |
"""Retrieves file content from a specific commit in a Git repository.""" | |
try: | |
content = repo.commit(commit_hash).tree[file_path].data_stream.read().decode('utf-8') | |
except KeyError: | |
content = "" # File does not exist at this commit | |
return content | |
def main(): | |
parser = argparse.ArgumentParser(description='Compare requirements.txt file between two Git commits.') | |
parser.add_argument('commit_range', nargs='?', default='HEAD', help='The commit range to compare, defaults to HEAD. Formats accepted: <commit> or <start_commit>...<end_commit>') | |
parser.add_argument('file_path', nargs='?', default='requirements.txt', help='Path to the requirements.txt file within the repository, defaults to "requirements.txt"') | |
args = parser.parse_args() | |
repo = git.Repo() # Assumes the script is run within a git repository | |
if '...' in args.commit_range: | |
commits = list(repo.iter_commits(args.commit_range)) | |
if not commits: | |
raise ValueError("No commits found in the specified range.") | |
start_commit = commits[-1] if len(commits) > 1 else commits[0].parents[0] | |
end_commit = commits[0] | |
else: | |
commit = repo.commit(args.commit_range) | |
start_commit = commit.parents[0] if commit.parents else None # Use the parent commit if available | |
end_commit = commit | |
start_content = get_file_at_commit(repo, start_commit, args.file_path) if start_commit else "" | |
end_content = get_file_at_commit(repo, end_commit, args.file_path) | |
compare_requirements(start_content, end_content) | |
if __name__ == "__main__": | |
main() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment