You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
90 lines
3.6 KiB
Python
90 lines
3.6 KiB
Python
#
|
|
# Copyright (c) Contributors to the Open 3D Engine Project.
|
|
# For complete copyright and license terms please see the LICENSE at the root of this distribution.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0 OR MIT
|
|
#
|
|
#
|
|
|
|
import subprocess
|
|
import git
|
|
import pathlib
|
|
|
|
# Basic representation of a git repository
|
|
class Repo:
|
|
def __init__(self, repo_path: str):
|
|
self._repo = git.Repo(repo_path)
|
|
self._remote_url = self._repo.remotes[0].config_reader.get("url")
|
|
|
|
# Returns the current branch
|
|
@property
|
|
def current_branch(self):
|
|
branch = self._repo.active_branch
|
|
return branch.name
|
|
|
|
# Returns the remote URL
|
|
@property
|
|
def remote_url(self):
|
|
return self._remote_url
|
|
|
|
def create_diff_file(self, src_commit_hash: str, dst_commit_hash: str, output_path: pathlib.Path, multi_branch: bool):
|
|
"""
|
|
Attempts to create a diff from the src and dst commits and write to the specified output file.
|
|
|
|
@param src_commit_hash: The hash for the source commit.
|
|
@param dst_commit_hash: The hash for the destination commit.
|
|
@param multi_branch: The two commits are on different branches so view the changes on the
|
|
branch containing and up to dst_commit, starting at a common ancestor of both.
|
|
@param output_path: The path to the file to write the diff to.
|
|
"""
|
|
|
|
try:
|
|
# Remove the existing file (if any) and create the parent directory
|
|
# output_path.unlink(missing_ok=True) # missing_ok is only available in Python 3.8+
|
|
if output_path.is_file():
|
|
output_path.unlink()
|
|
output_path.parent.mkdir(exist_ok=True)
|
|
except EnvironmentError as e:
|
|
raise RuntimeError(f"Could not create path for output file '{output_path}'")
|
|
|
|
args = ["git", "diff", "--name-status", f"--output={output_path}"]
|
|
if multi_branch:
|
|
args.append(f"{src_commit_hash}...{dst_commit_hash}")
|
|
else:
|
|
args.append(src_commit_hash)
|
|
args.append(dst_commit_hash)
|
|
|
|
# git diff will only write to the output file if both commit hashes are valid
|
|
subprocess.run(args)
|
|
if not output_path.is_file():
|
|
raise RuntimeError(f"Source commit '{src_commit_hash}' and/or destination commit '{dst_commit_hash}' are invalid")
|
|
|
|
def is_descendent(self, src_commit_hash: str, dst_commit_hash: str):
|
|
"""
|
|
Determines whether or not dst_commit is a descendent of src_commit.
|
|
|
|
@param src_commit_hash: The hash for the source commit.
|
|
@param dst_commit_hash: The hash for the destination commit.
|
|
@return: True if the dst commit descends from the src commit, otherwise False.
|
|
"""
|
|
|
|
if not src_commit_hash and not dst_commit_hash:
|
|
return False
|
|
result = subprocess.run(["git", "merge-base", "--is-ancestor", src_commit_hash, dst_commit_hash])
|
|
return result.returncode == 0
|
|
|
|
# Returns the distance between two commits
|
|
def commit_distance(self, src_commit_hash: str, dst_commit_hash: str):
|
|
"""
|
|
Determines the number of commits between src_commit and dst_commit.
|
|
|
|
@param src_commit_hash: The hash for the source commit.
|
|
@param dst_commit_hash: The hash for the destination commit.
|
|
@return: The distance between src_commit and dst_commit (if both are valid commits), otherwise None.
|
|
"""
|
|
|
|
if not src_commit_hash and not dst_commit_hash:
|
|
return None
|
|
commits = self._repo.iter_commits(src_commit_hash + '..' + dst_commit_hash)
|
|
return len(list(commits))
|