blob: d3788f466d357a21e0c02b4f28b74bd482e3c472 [file] [log] [blame]
# ex:ts=4:sw=4:sts=4:et
# -*- tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*-
#
# patchtestrepo: PatchTestRepo class used mainly to control a git repo from patchtest
#
# Copyright (C) 2016 Intel Corporation
#
# SPDX-License-Identifier: GPL-2.0-only
#
import os
import utils
import logging
from patch import PatchTestPatch
logger = logging.getLogger('patchtest')
info=logger.info
class PatchTestRepo(object):
# prefixes used for temporal branches/stashes
prefix = 'patchtest'
def __init__(self, patch, repodir, commit=None, branch=None):
self._repodir = repodir
self._patch = PatchTestPatch(patch)
self._current_branch = self._get_current_branch()
# targeted branch defined on the patch may be invalid, so make sure there
# is a corresponding remote branch
valid_patch_branch = None
if self._patch.branch in self.upstream_branches():
valid_patch_branch = self._patch.branch
# Target Branch
# Priority (top has highest priority):
# 1. branch given at cmd line
# 2. branch given at the patch
# 3. current branch
self._branch = branch or valid_patch_branch or self._current_branch
# Target Commit
# Priority (top has highest priority):
# 1. commit given at cmd line
# 2. branch given at cmd line
# 3. branch given at the patch
# 3. current HEAD
self._commit = self._get_commitid(commit) or \
self._get_commitid(branch) or \
self._get_commitid(valid_patch_branch) or \
self._get_commitid('HEAD')
self._workingbranch = "%s_%s" % (PatchTestRepo.prefix, os.getpid())
# create working branch
self._exec({'cmd': ['git', 'checkout', '-b', self._workingbranch, self._commit]})
self._patchmerged = False
# Check if patch can be merged using git-am
self._patchcanbemerged = True
try:
self._exec({'cmd': ['git', 'am', '--keep-cr'], 'input': self._patch.contents})
except utils.CmdException as ce:
self._exec({'cmd': ['git', 'am', '--abort']})
self._patchcanbemerged = False
finally:
# if patch was applied, remove it
if self._patchcanbemerged:
self._exec({'cmd':['git', 'reset', '--hard', self._commit]})
# for debugging purposes, print all repo parameters
logger.debug("Parameters")
logger.debug("\tRepository : %s" % self._repodir)
logger.debug("\tTarget Commit : %s" % self._commit)
logger.debug("\tTarget Branch : %s" % self._branch)
logger.debug("\tWorking branch : %s" % self._workingbranch)
logger.debug("\tPatch : %s" % self._patch)
@property
def patch(self):
return self._patch.path
@property
def branch(self):
return self._branch
@property
def commit(self):
return self._commit
@property
def ismerged(self):
return self._patchmerged
@property
def canbemerged(self):
return self._patchcanbemerged
def _exec(self, cmds):
_cmds = []
if isinstance(cmds, dict):
_cmds.append(cmds)
elif isinstance(cmds, list):
_cmds = cmds
else:
raise utils.CmdException({'cmd':str(cmds)})
results = []
cmdfailure = False
try:
results = utils.exec_cmds(_cmds, self._repodir)
except utils.CmdException as ce:
cmdfailure = True
raise ce
finally:
if cmdfailure:
for cmd in _cmds:
logger.debug("CMD: %s" % ' '.join(cmd['cmd']))
else:
for result in results:
cmd, rc, stdout, stderr = ' '.join(result['cmd']), result['returncode'], result['stdout'], result['stderr']
logger.debug("CMD: %s RCODE: %s STDOUT: %s STDERR: %s" % (cmd, rc, stdout, stderr))
return results
def _get_current_branch(self, commit='HEAD'):
cmd = {'cmd':['git', 'rev-parse', '--abbrev-ref', commit]}
cb = self._exec(cmd)[0]['stdout']
if cb == commit:
logger.warning('You may be detached so patchtest will checkout to master after execution')
cb = 'master'
return cb
def _get_commitid(self, commit):
if not commit:
return None
try:
cmd = {'cmd':['git', 'rev-parse', '--short', commit]}
return self._exec(cmd)[0]['stdout']
except utils.CmdException as ce:
# try getting the commit under any remotes
cmd = {'cmd':['git', 'remote']}
remotes = self._exec(cmd)[0]['stdout']
for remote in remotes.splitlines():
cmd = {'cmd':['git', 'rev-parse', '--short', '%s/%s' % (remote, commit)]}
try:
return self._exec(cmd)[0]['stdout']
except utils.CmdException:
pass
return None
def upstream_branches(self):
cmd = {'cmd':['git', 'branch', '--remotes']}
remote_branches = self._exec(cmd)[0]['stdout']
# just get the names, without the remote name
branches = set(branch.split('/')[-1] for branch in remote_branches.splitlines())
return branches
def merge(self):
if self._patchcanbemerged:
self._exec({'cmd': ['git', 'am', '--keep-cr'],
'input': self._patch.contents,
'updateenv': {'PTRESOURCE':self._patch.path}})
self._patchmerged = True
def clean(self):
self._exec({'cmd':['git', 'checkout', '%s' % self._current_branch]})
self._exec({'cmd':['git', 'branch', '-D', self._workingbranch]})
self._patchmerged = False