#! /usr/bin/env python
"""This module acts as an interface for acting on git commits"""
import git
from git_wrapper import exceptions
from git_wrapper.utils.decorators import reference_exists
[docs]class GitCommit(object):
def __init__(self, git_repo, logger):
"""Constructor for GitCommit object
:param repo.GitRepo git_repo: An already constructed GitRepo object to use
:param logging.Logger logger: A pre-configured Python Logger object
"""
self.git_repo = git_repo
self.logger = logger
[docs] @reference_exists('sha')
def describe(self, sha):
"""Return tag and commit info for a given sha
:param str sha: The SHA1 of the commit to describe
:return dict: A dict with tag and patch data
"""
ret_data = {'tag': '', 'patch': ''}
try:
output = self.git_repo.git.describe('--all', sha).split('-g')
except git.CommandError as ex:
msg = f"Error while running describe command on sha {sha}: {ex}"
raise exceptions.DescribeException(msg) from ex
if output:
tag = output[0]
# Lightweight tags have a tag/ prefix when returned
if tag.startswith('tag/'):
tag = tag[4:]
ret_data['tag'] = tag
if len(output) > 1:
ret_data['patch'] = output[1]
return ret_data
[docs] def commit(self, message, signoff=False):
"""Create a commit for changes to tracked files in the repo.
Equivalent to `git commit -a -m <message>`.
:param str message: The commit message
:param bool signoff: Whether to add signed-off-by to commit message
"""
if not message or not isinstance(message, str):
self.logger.debug("Cannot create commit without commit message.")
raise exceptions.CommitMessageMissingException('No commit message text provided.')
# Add tracked files to the index
self.git_repo.git.add(update=True)
changes = self.git_repo.git.diff(name_only=True, staged=True)
if not changes:
self.logger.info("No changes to commit.")
return False
# Commit the changes
msg = f"Preparing to commit changes to the following files: {changes}"
self.logger.debug(msg)
commit = self.git_repo.git.commit(message=message, all=True, signoff=signoff)
self.logger.info(f"Committed changes as commit {commit}")
[docs] @reference_exists('hash_')
def revert(self, hash_, message=None):
"""Revert a specified commit.
:param str hash_: The commit hash or reference to rebase to
:param str message: Extra info to be included in commit message
"""
try:
self.git_repo.git.revert(hash_, no_edit=True)
except git.GitCommandError as ex:
msg = f"Revert failed for hash {hash_}. Error: {ex}"
raise exceptions.RevertException(msg) from ex
if message:
commit = git.repo.fun.name_to_object(self.git_repo.repo, hash_)
self.git_repo.git.commit(
"--amend",
"-m", f"Revert '{commit.summary}'",
"-m", f"This reverts commit {commit.hexsha}.",
"-m", message
)
[docs] @reference_exists('reference_B')
@reference_exists('reference_A')
def same(self, reference_A, reference_B):
"""Determine whether two references refer to the same commit.
:param str reference_A: A commit ref (sha, tag, branch name, ...)
:param str reference_B: A commit ref (sha, tag, branch name, ...)
:return bool: True if the references point to the same commit,
False if not
"""
commitA = git.repo.fun.name_to_object(self.git_repo.repo, reference_A)
commitB = git.repo.fun.name_to_object(self.git_repo.repo, reference_B)
if commitA.hexsha == commitB.hexsha:
return True
else:
return False
[docs] @reference_exists('branch_name')
@reference_exists('sha')
def cherrypick(self, sha, branch_name):
"""Apply given sha on given branch
:param str sha: The SHA1 of the commit to cherry-pick
:param str branch_name: The branch to apply it to
"""
if self.git_repo.repo.is_dirty():
working_dir = self.git_repo.repo.working_dir
msg = (f"Repository {working_dir} is dirty. Please clean "
"workspace before proceeding.")
raise exceptions.DirtyRepositoryException(msg)
# Checkout
try:
self.git_repo.git.checkout(branch_name)
except git.GitCommandError as ex:
msg = f"Could not checkout branch {branch_name}. Error: {ex}"
raise exceptions.CheckoutException(msg) from ex
# Cherry-pick
try:
self.git_repo.git.cherry_pick(sha)
except git.GitCommandError as ex:
msg = (f"Could not cherry-pick commit {sha} on {branch_name}. "
f"Error: {ex}")
raise exceptions.ChangeNotAppliedException(msg) from ex
msg = f"Successfully cherry-picked commit {sha} on {branch_name}"
self.logger.debug(msg)
[docs] def abort_cherrypick(self):
"""Aborts a cherrypick."""
try:
self.git_repo.git.cherry_pick('--abort')
except git.GitCommandError as ex:
msg = f"Cherrypick abort command failed. Error: {ex}"
raise exceptions.AbortException(msg) from ex
[docs] @reference_exists('ref')
def to_hexsha(self, ref):
"""Return the commit hex sha for a given commit reference
:param str ref: The tag, branch, etc referring to a commit
:return str: The commit's hex sha for the given reference
"""
return git.repo.fun.name_to_object(self.git_repo.repo, ref).hexsha