1*4882a593Smuzhiyun# 2*4882a593Smuzhiyun# Copyright (C) 2016 Intel Corporation 3*4882a593Smuzhiyun# 4*4882a593Smuzhiyun# SPDX-License-Identifier: MIT 5*4882a593Smuzhiyun# 6*4882a593Smuzhiyun"""Git repository interactions""" 7*4882a593Smuzhiyunimport os 8*4882a593Smuzhiyun 9*4882a593Smuzhiyunfrom oeqa.utils.commands import runCmd 10*4882a593Smuzhiyun 11*4882a593Smuzhiyun 12*4882a593Smuzhiyunclass GitError(Exception): 13*4882a593Smuzhiyun """Git error handling""" 14*4882a593Smuzhiyun pass 15*4882a593Smuzhiyun 16*4882a593Smuzhiyunclass GitRepo(object): 17*4882a593Smuzhiyun """Class representing a Git repository clone""" 18*4882a593Smuzhiyun def __init__(self, path, is_topdir=False): 19*4882a593Smuzhiyun git_dir = self._run_git_cmd_at(['rev-parse', '--git-dir'], path) 20*4882a593Smuzhiyun git_dir = git_dir if os.path.isabs(git_dir) else os.path.join(path, git_dir) 21*4882a593Smuzhiyun self.git_dir = os.path.realpath(git_dir) 22*4882a593Smuzhiyun 23*4882a593Smuzhiyun if self._run_git_cmd_at(['rev-parse', '--is-bare-repository'], path) == 'true': 24*4882a593Smuzhiyun self.bare = True 25*4882a593Smuzhiyun self.top_dir = self.git_dir 26*4882a593Smuzhiyun else: 27*4882a593Smuzhiyun self.bare = False 28*4882a593Smuzhiyun self.top_dir = self._run_git_cmd_at(['rev-parse', '--show-toplevel'], 29*4882a593Smuzhiyun path) 30*4882a593Smuzhiyun realpath = os.path.realpath(path) 31*4882a593Smuzhiyun if is_topdir and realpath != self.top_dir: 32*4882a593Smuzhiyun raise GitError("{} is not a Git top directory".format(realpath)) 33*4882a593Smuzhiyun 34*4882a593Smuzhiyun @staticmethod 35*4882a593Smuzhiyun def _run_git_cmd_at(git_args, cwd, **kwargs): 36*4882a593Smuzhiyun """Run git command at a specified directory""" 37*4882a593Smuzhiyun git_cmd = 'git ' if isinstance(git_args, str) else ['git'] 38*4882a593Smuzhiyun git_cmd += git_args 39*4882a593Smuzhiyun ret = runCmd(git_cmd, ignore_status=True, cwd=cwd, **kwargs) 40*4882a593Smuzhiyun if ret.status: 41*4882a593Smuzhiyun cmd_str = git_cmd if isinstance(git_cmd, str) \ 42*4882a593Smuzhiyun else ' '.join(git_cmd) 43*4882a593Smuzhiyun raise GitError("'{}' failed with exit code {}: {}".format( 44*4882a593Smuzhiyun cmd_str, ret.status, ret.output)) 45*4882a593Smuzhiyun return ret.output.strip() 46*4882a593Smuzhiyun 47*4882a593Smuzhiyun @staticmethod 48*4882a593Smuzhiyun def init(path, bare=False): 49*4882a593Smuzhiyun """Initialize a new Git repository""" 50*4882a593Smuzhiyun cmd = ['init'] 51*4882a593Smuzhiyun if bare: 52*4882a593Smuzhiyun cmd.append('--bare') 53*4882a593Smuzhiyun GitRepo._run_git_cmd_at(cmd, cwd=path) 54*4882a593Smuzhiyun return GitRepo(path, is_topdir=True) 55*4882a593Smuzhiyun 56*4882a593Smuzhiyun def run_cmd(self, git_args, env_update=None): 57*4882a593Smuzhiyun """Run Git command""" 58*4882a593Smuzhiyun env = None 59*4882a593Smuzhiyun if env_update: 60*4882a593Smuzhiyun env = os.environ.copy() 61*4882a593Smuzhiyun env.update(env_update) 62*4882a593Smuzhiyun return self._run_git_cmd_at(git_args, self.top_dir, env=env) 63*4882a593Smuzhiyun 64*4882a593Smuzhiyun def rev_parse(self, revision): 65*4882a593Smuzhiyun """Do git rev-parse""" 66*4882a593Smuzhiyun try: 67*4882a593Smuzhiyun return self.run_cmd(['rev-parse', '--verify', revision]) 68*4882a593Smuzhiyun except GitError: 69*4882a593Smuzhiyun # Revision does not exist 70*4882a593Smuzhiyun return None 71*4882a593Smuzhiyun 72*4882a593Smuzhiyun def get_current_branch(self): 73*4882a593Smuzhiyun """Get current branch""" 74*4882a593Smuzhiyun try: 75*4882a593Smuzhiyun # Strip 11 chars, i.e. 'refs/heads' from the beginning 76*4882a593Smuzhiyun return self.run_cmd(['symbolic-ref', 'HEAD'])[11:] 77*4882a593Smuzhiyun except GitError: 78*4882a593Smuzhiyun return None 79*4882a593Smuzhiyun 80*4882a593Smuzhiyun 81