Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 1 | #!/usr/bin/env python3 |
| 2 | # -*- coding: utf-8 -*-" |
| 3 | # |
| 4 | # Copyright 2020 The Chromium OS Authors. All rights reserved. |
| 5 | # Use of this source code is governed by a BSD-style license that can be |
| 6 | # found in the LICENSE file. |
| 7 | |
| 8 | """Module containing methods interfacing with git |
| 9 | |
| 10 | i.e Parsing git logs for change-id, full commit sha's, etc. |
| 11 | """ |
| 12 | |
| 13 | from __future__ import print_function |
Guenter Roeck | 85c10bb | 2020-04-16 15:36:47 -0700 | [diff] [blame] | 14 | import logging |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 15 | import re |
| 16 | import subprocess |
| 17 | import common |
| 18 | |
| 19 | |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 20 | def _git_check_output(path, command): |
| 21 | git_cmd = ['git', '-C', path] + command |
| 22 | return subprocess.check_output(git_cmd, encoding='utf-8', errors='ignore', |
| 23 | stderr=subprocess.DEVNULL) |
| 24 | |
| 25 | |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 26 | def get_upstream_fullsha(abbrev_sha): |
| 27 | """Returns the full upstream sha for an abbreviated 12 digit sha using git cli""" |
| 28 | upstream_absolute_path = common.get_kernel_absolute_path(common.UPSTREAM_PATH) |
| 29 | try: |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 30 | cmd = ['rev-parse', abbrev_sha] |
| 31 | full_sha = _git_check_output(upstream_absolute_path, cmd) |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 32 | return full_sha.rstrip() |
| 33 | except subprocess.CalledProcessError as e: |
| 34 | raise type(e)('Could not find full upstream sha for %s' % abbrev_sha, e.cmd) from e |
| 35 | |
| 36 | |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 37 | def _get_commit_message(kernel_path, sha): |
Hirthanan Subenderan | d6922c3 | 2020-03-23 14:17:40 -0700 | [diff] [blame] | 38 | """Returns the commit message for a sha in a given local path to kernel.""" |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 39 | try: |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 40 | cmd = ['log', '--format=%B', '-n', '1', sha] |
| 41 | commit_message = _git_check_output(kernel_path, cmd) |
Hirthanan Subenderan | 3450f51 | 2020-04-09 22:36:50 -0700 | [diff] [blame] | 42 | |
| 43 | # Single newline following commit message |
| 44 | return commit_message.rstrip() + '\n' |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 45 | except subprocess.CalledProcessError as e: |
Hirthanan Subenderan | d6922c3 | 2020-03-23 14:17:40 -0700 | [diff] [blame] | 46 | raise type(e)('Couldnt retrieve commit in kernel path %s for sha %s' |
| 47 | % (kernel_path, sha), e.cmd) from e |
| 48 | |
| 49 | |
| 50 | def get_upstream_commit_message(upstream_sha): |
| 51 | """Returns the commit message for a given upstream sha using git cli.""" |
| 52 | upstream_absolute_path = common.get_kernel_absolute_path(common.UPSTREAM_PATH) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 53 | return _get_commit_message(upstream_absolute_path, upstream_sha) |
Hirthanan Subenderan | d6922c3 | 2020-03-23 14:17:40 -0700 | [diff] [blame] | 54 | |
| 55 | |
| 56 | def get_chrome_commit_message(chrome_sha): |
| 57 | """Returns the commit message for a given chrome sha using git cli.""" |
| 58 | chrome_absolute_path = common.get_kernel_absolute_path(common.CHROMEOS_PATH) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 59 | return _get_commit_message(chrome_absolute_path, chrome_sha) |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 60 | |
| 61 | |
Guenter Roeck | bfda124 | 2020-12-29 15:24:18 -0800 | [diff] [blame] | 62 | def get_merge_sha(branch, sha): |
| 63 | """Returns SHA of merge commit for provided SHA if available""" |
| 64 | |
| 65 | chrome_absolute_path = common.get_kernel_absolute_path(common.CHROMEOS_PATH) |
| 66 | |
| 67 | try: |
| 68 | # Get list of merges in <branch> since <sha> |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 69 | cmd = ['log', '--format=%h', '--abbrev=12', '--ancestry-path', '--merges', |
| 70 | '%s..%s' % (sha, branch)] |
| 71 | sha_list = _git_check_output(chrome_absolute_path, cmd) |
Guenter Roeck | bfda124 | 2020-12-29 15:24:18 -0800 | [diff] [blame] | 72 | if not sha_list: |
| 73 | logging.info('No merge commit for sha %s in branch %s', sha, branch) |
| 74 | return None |
| 75 | # merge_sha is our presumed merge commit |
| 76 | merge_sha = sha_list.splitlines()[-1] |
| 77 | # Verify if <sha> is indeed part of the merge |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 78 | cmd = ['log', '--format=%h', '--abbrev=12', '%s~1..%s' % (merge_sha, merge_sha)] |
| 79 | sha_list = _git_check_output(chrome_absolute_path, cmd) |
Guenter Roeck | bfda124 | 2020-12-29 15:24:18 -0800 | [diff] [blame] | 80 | if sha_list and sha in sha_list.splitlines(): |
| 81 | return merge_sha |
| 82 | logging.info('Merge commit for sha %s found as %s, but sha is missing in merge', |
| 83 | sha, merge_sha) |
| 84 | |
| 85 | except subprocess.CalledProcessError as e: |
| 86 | logging.info('Error "%s" while trying to find merge commit for sha %s in branch %s', |
| 87 | e, sha, branch) |
| 88 | |
| 89 | return None |
| 90 | |
| 91 | |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 92 | def get_commit_changeid_linux_chrome(kernel_sha): |
Hirthanan Subenderan | d6922c3 | 2020-03-23 14:17:40 -0700 | [diff] [blame] | 93 | """Returns the changeid of the kernel_sha commit by parsing linux_chrome git log. |
| 94 | |
| 95 | kernel_sha will be one of linux_stable or linux_chrome commits. |
| 96 | """ |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 97 | chrome_absolute_path = common.get_kernel_absolute_path(common.CHROMEOS_PATH) |
| 98 | try: |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 99 | cmd = ['log', '--format=%B', '-n', '1', kernel_sha] |
| 100 | commit_message = _git_check_output(chrome_absolute_path, cmd) |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 101 | |
| 102 | m = re.findall('^Change-Id: (I[a-z0-9]{40})$', commit_message, re.M) |
| 103 | |
| 104 | # Get last change-id in case chrome sha cherry-picked/reverted into new commit |
| 105 | return m[-1] |
| 106 | except subprocess.CalledProcessError as e: |
Hirthanan Subenderan | d6922c3 | 2020-03-23 14:17:40 -0700 | [diff] [blame] | 107 | raise type(e)('Couldnt retrieve changeid for commit %s' % kernel_sha, e.cmd) from e |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 108 | except IndexError as e: |
Hirthanan Subenderan | d6922c3 | 2020-03-23 14:17:40 -0700 | [diff] [blame] | 109 | # linux_stable kernel_sha's do not have an associated ChangeID |
| 110 | return None |
Hirthanan Subenderan | b186655 | 2020-03-20 14:01:14 -0700 | [diff] [blame] | 111 | |
| 112 | |
Guenter Roeck | 949d05b | 2020-05-12 12:35:36 -0700 | [diff] [blame] | 113 | def get_tag_emails_linux_chrome(sha): |
| 114 | """Returns unique list of chromium.org or google.com e-mails. |
| 115 | |
| 116 | The returned lust of e-mails is associated with tags found after |
| 117 | the last 'cherry picked from commit' message in the commit identified |
| 118 | by sha. Tags and e-mails are found by parsing the commit log. |
| 119 | |
| 120 | sha is expected to be be a commit in linux_stable or in linux_chrome. |
| 121 | """ |
| 122 | absolute_path = common.get_kernel_absolute_path(common.CHROMEOS_PATH) |
| 123 | try: |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 124 | cmd = ['log', '--format=%B', '-n', '1', sha] |
| 125 | commit_message = _git_check_output(absolute_path, cmd) |
Guenter Roeck | 949d05b | 2020-05-12 12:35:36 -0700 | [diff] [blame] | 126 | # If the commit has been cherry-picked, use subsequent tags to create |
| 127 | # list of reviewers. Otherwise, use all tags. Either case, only return |
| 128 | # e-mail addresses from Google domains. |
| 129 | s = commit_message.split('cherry picked from commit') |
| 130 | tags = 'Signed-off-by|Reviewed-by|Tested-by|Commit-Queue' |
| 131 | domains = 'chromium.org|google.com' |
| 132 | m = '^(?:%s): .* <(.*@(?:%s))>$' % (tags, domains) |
| 133 | emails = re.findall(m, s[-1], re.M) |
| 134 | if not emails: |
| 135 | # Final fallback: In some situations, "cherry picked from" |
| 136 | # is at the very end of the commit description, with no |
| 137 | # subsequent tags. If that happens, look for tags in the |
| 138 | # entire description. |
| 139 | emails = re.findall(m, commit_message, re.M) |
| 140 | return list(set(emails)) |
| 141 | except subprocess.CalledProcessError as e: |
| 142 | raise type(e)('Could not retrieve tag e-mails for commit %s' % sha, e.cmd) from e |
| 143 | except IndexError: |
| 144 | # sha does do not have a recognized tag |
| 145 | return None |
| 146 | |
| 147 | |
Guenter Roeck | d9b6c6c | 2020-04-16 10:06:46 -0700 | [diff] [blame] | 148 | # match "vX.Y[.Z][.rcN]" |
| 149 | version = re.compile(r'(v[0-9]+(?:\.[0-9]+)+(?:-rc[0-9]+)?)\s*') |
| 150 | |
| 151 | def get_integrated_tag(sha): |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 152 | """For a given SHA, find the first upstream tag that includes it.""" |
Guenter Roeck | d9b6c6c | 2020-04-16 10:06:46 -0700 | [diff] [blame] | 153 | |
| 154 | try: |
| 155 | path = common.get_kernel_absolute_path(common.UPSTREAM_PATH) |
Guenter Roeck | abea3d2 | 2021-01-21 07:14:46 -0800 | [diff] [blame^] | 156 | cmd = ['describe', '--match', 'v*', '--contains', sha] |
| 157 | tag = _git_check_output(path, cmd) |
Guenter Roeck | d9b6c6c | 2020-04-16 10:06:46 -0700 | [diff] [blame] | 158 | return version.match(tag).group() |
| 159 | except AttributeError: |
| 160 | return None |
| 161 | except subprocess.CalledProcessError: |
| 162 | return None |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 163 | |
| 164 | |
| 165 | class commitHandler: |
| 166 | """Class to control active accesses on a git repository""" |
| 167 | |
| 168 | def __init__(self, kernel, branch=None): |
| 169 | self.metadata = common.get_kernel_metadata(kernel) |
| 170 | if not branch: |
| 171 | branch = self.metadata.branches[0] |
| 172 | self.branch = branch |
| 173 | self.merge_base = self.metadata.tag_template % branch |
| 174 | self.branchname = self.metadata.get_kernel_branch(branch) |
| 175 | self.path = common.get_kernel_absolute_path(self.metadata.path) |
| 176 | self.status = 'unknown' |
| 177 | self.commit_list = { } # indexed by merge_base |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 178 | |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 179 | current_branch_cmd = ['symbolic-ref', '-q', '--short', 'HEAD'] |
| 180 | self.current_branch = self.__git_check_output(current_branch_cmd).rstrip() |
| 181 | |
| 182 | def __git_command(self, command): |
| 183 | return ['git', '-C', self.path] + command |
| 184 | |
| 185 | def __git_check_output(self, command): |
| 186 | cmd = self.__git_command(command) |
| 187 | return subprocess.check_output(cmd, encoding='utf-8', errors='ignore') |
| 188 | |
| 189 | def __git_run(self, command): |
| 190 | cmd = self.__git_command(command) |
| 191 | subprocess.run(cmd, check=True) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 192 | |
| 193 | def __set_branch(self, branch): |
| 194 | """Set the active branch""" |
| 195 | if branch != self.branch: |
| 196 | self.branch = branch |
| 197 | self.merge_base = self.metadata.tag_template % branch |
| 198 | self.branchname = self.metadata.get_kernel_branch(branch) |
| 199 | self.status = 'unknown' |
| 200 | |
Guenter Roeck | 4a39042 | 2021-01-20 12:33:04 -0800 | [diff] [blame] | 201 | def __reset_hard_ref(self, reference): |
| 202 | """Force reset to provided reference""" |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 203 | reset_cmd = ['reset', '-q', '--hard', reference] |
| 204 | self.__git_run(reset_cmd) |
Guenter Roeck | 4a39042 | 2021-01-20 12:33:04 -0800 | [diff] [blame] | 205 | |
| 206 | def __reset_hard_head(self): |
| 207 | """Force hard reset to git head in checked out branch""" |
| 208 | self.__reset_hard_ref('HEAD') |
| 209 | |
| 210 | def __reset_hard_origin(self): |
| 211 | """Force hard reset to head of remote branch""" |
| 212 | self.__reset_hard_ref('origin/%s' % self.branchname) |
| 213 | |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 214 | def __checkout_and_clean(self): |
| 215 | """Clean up uncommitted files in branch and checkout to be up to date with origin.""" |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 216 | clean_untracked = ['clean', '-d', '-x', '-f', '-q'] |
| 217 | checkout = ['checkout', '-q', self.branchname] |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 218 | |
Guenter Roeck | 4a39042 | 2021-01-20 12:33:04 -0800 | [diff] [blame] | 219 | self.__reset_hard_head() |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 220 | self.__git_run(clean_untracked) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 221 | |
| 222 | if self.current_branch != self.branchname: |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 223 | self.__git_run(checkout) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 224 | self.current_branch = self.branchname |
| 225 | |
Guenter Roeck | 4a39042 | 2021-01-20 12:33:04 -0800 | [diff] [blame] | 226 | self.__reset_hard_origin() |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 227 | |
| 228 | def __setup(self): |
| 229 | """Local setup function, to be called for each access""" |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 230 | if self.status == 'unknown': |
| 231 | self.__checkout_and_clean() |
| 232 | elif self.status == 'changed': |
Guenter Roeck | 4a39042 | 2021-01-20 12:33:04 -0800 | [diff] [blame] | 233 | self.__reset_hard_origin() |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 234 | |
| 235 | self.status = 'clean' |
| 236 | |
| 237 | def __search_subject(self, sha): |
| 238 | """Check if subject associated with 'sha' exists in the current branch""" |
| 239 | |
| 240 | try: |
| 241 | # Retrieve subject line of provided SHA |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 242 | cmd = ['log', '--pretty=format:%s', '-n', '1', sha] |
| 243 | subject = self.__git_check_output(cmd) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 244 | except subprocess.CalledProcessError: |
| 245 | logging.error('Failed to get subject for sha %s', sha) |
| 246 | return False |
| 247 | |
| 248 | if self.branch not in self.commit_list: |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 249 | cmd = ['log', '--no-merges', '--format=%s', |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 250 | '%s..%s' % (self.merge_base, self.branchname)] |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 251 | subjects = self.__git_check_output(cmd) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 252 | self.commit_list[self.branch] = subjects.splitlines() |
| 253 | |
| 254 | # The following is a raw search which will match, for example, a revert of a commit. |
| 255 | # A better method to check if commits have been applied would be desirable. |
| 256 | subjects = self.commit_list[self.branch] |
| 257 | return any(subject in s for s in subjects) |
| 258 | |
| 259 | def __get_git_push_cmd(self, reviewers): |
| 260 | """Generates git push command with added reviewers and autogenerated tag. |
| 261 | |
| 262 | Read more about gerrit tags here: |
| 263 | https://gerrit-review.googlesource.com/Documentation/cmd-receive-pack.html |
| 264 | """ |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 265 | reviewers_tag = ['r=%s'% r for r in reviewers] |
| 266 | autogenerated_tag = ['t=autogenerated'] |
| 267 | tags = ','.join(reviewers_tag + autogenerated_tag) |
Guenter Roeck | 61e294f | 2021-01-20 14:44:46 -0800 | [diff] [blame] | 268 | head = 'HEAD:refs/for/%s%%%s' % (self.branchname, tags) |
| 269 | return ['push', 'origin', head] |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 270 | |
Guenter Roeck | 76b864c | 2021-01-20 15:24:16 -0800 | [diff] [blame] | 271 | def fetch(self, remote=None): |
| 272 | """Fetch changes from provided remote or from origin""" |
| 273 | if not remote: |
| 274 | remote = 'origin' |
| 275 | self.__setup() |
| 276 | fetch_cmd = ['fetch', '-q', remote] |
| 277 | self.__git_run(fetch_cmd) |
| 278 | self.status = 'changed' |
| 279 | |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 280 | def pull(self, branch=None): |
| 281 | """Pull changes from remote repository into provided or default branch""" |
| 282 | if branch: |
| 283 | self.__set_branch(branch) |
| 284 | self.__setup() |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 285 | pull_cmd = ['pull', '-q'] |
| 286 | self.__git_run(pull_cmd) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 287 | |
| 288 | def cherry_pick_and_push(self, fixer_upstream_sha, fixer_changeid, fix_commit_message, |
| 289 | reviewers): |
| 290 | """Cherry picks upstream commit into chrome repo. |
| 291 | |
| 292 | Adds reviewers and autogenerated tag with the pushed commit. |
| 293 | """ |
| 294 | |
| 295 | self.__setup() |
| 296 | try: |
| 297 | self.status = 'changed' |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 298 | self.__git_run(['cherry-pick', '-n', fixer_upstream_sha]) |
| 299 | self.__git_run(['commit', '-s', '-m', fix_commit_message]) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 300 | |
| 301 | # commit has been cherry-picked and committed locally, precommit hook |
| 302 | # in git repository adds changeid to the commit message. Pick it unless |
| 303 | # we already have one passed as parameter. |
| 304 | if not fixer_changeid: |
| 305 | fixer_changeid = get_commit_changeid_linux_chrome('HEAD') |
| 306 | |
| 307 | # Sometimes the commit hook doesn't attach the Change-Id to the last |
| 308 | # paragraph in the commit message. This seems to happen if the commit |
| 309 | # message includes '---' which would normally identify the start of |
| 310 | # comments. If the Change-Id is not in the last paragraph, uploading |
| 311 | # the patch is rejected by Gerrit. Force-move the Change-Id to the end |
| 312 | # of the commit message to solve the problem. This conveniently also |
| 313 | # replaces the auto-generated Change-Id with the optional Change-Id |
| 314 | # passed as parameter. |
| 315 | commit_message = get_chrome_commit_message('HEAD') |
| 316 | commit_message = re.sub(r'Change-Id:.*\n?', '', commit_message) |
| 317 | commit_message = commit_message.rstrip() |
| 318 | commit_message += '\nChange-Id: %s' % fixer_changeid |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 319 | self.__git_run(['commit', '--amend', '-m', commit_message]) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 320 | |
| 321 | git_push_cmd = self.__get_git_push_cmd(reviewers) |
Guenter Roeck | 61e294f | 2021-01-20 14:44:46 -0800 | [diff] [blame] | 322 | self.__git_run(git_push_cmd) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 323 | |
| 324 | return fixer_changeid |
| 325 | except subprocess.CalledProcessError as e: |
| 326 | raise ValueError('Failed to cherrypick and push upstream fix %s on branch %s' |
| 327 | % (fixer_upstream_sha, self.branchname)) from e |
| 328 | finally: |
Guenter Roeck | 4a39042 | 2021-01-20 12:33:04 -0800 | [diff] [blame] | 329 | self.__reset_hard_head() |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 330 | self.status = 'changed' |
| 331 | |
| 332 | def cherrypick_status(self, sha, branch=None, apply=True): |
| 333 | """cherry-pick provided sha into repository and branch identified by this class instance |
| 334 | |
| 335 | Return Status Enum: |
| 336 | MERGED if the patch has already been applied, |
| 337 | OPEN if the patch is missing and applies cleanly, |
| 338 | CONFLICT if the patch is missing and fails to apply. |
| 339 | """ |
| 340 | |
| 341 | if branch: |
| 342 | self.__set_branch(branch) |
| 343 | |
| 344 | self.__setup() |
| 345 | |
| 346 | ret = None |
| 347 | try: |
| 348 | applied = self.__search_subject(sha) |
| 349 | if applied: |
| 350 | ret = common.Status.MERGED |
| 351 | raise ValueError |
| 352 | |
| 353 | if not apply: |
| 354 | raise ValueError |
| 355 | |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 356 | result = subprocess.call(self.__git_command(['cherry-pick', '-n', sha]), |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 357 | stdout=subprocess.DEVNULL, |
| 358 | stderr=subprocess.DEVNULL) |
| 359 | if result: |
| 360 | ret = common.Status.CONFLICT |
| 361 | raise ValueError |
| 362 | |
Guenter Roeck | 19a4be8 | 2021-01-20 14:01:53 -0800 | [diff] [blame] | 363 | diff = self.__git_check_output(['diff', 'HEAD']) |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 364 | if diff: |
| 365 | ret = common.Status.OPEN |
| 366 | raise ValueError |
| 367 | |
| 368 | ret = common.Status.MERGED |
| 369 | |
| 370 | except ValueError: |
| 371 | pass |
| 372 | |
| 373 | except subprocess.CalledProcessError: |
| 374 | ret = common.Status.CONFLICT |
| 375 | |
| 376 | finally: |
Guenter Roeck | 4a39042 | 2021-01-20 12:33:04 -0800 | [diff] [blame] | 377 | self.__reset_hard_head() |
Guenter Roeck | 32b23dc | 2021-01-17 13:29:06 -0800 | [diff] [blame] | 378 | self.status = 'clean' |
| 379 | |
| 380 | return ret |