lib/oe/patch: auto-commit when falling back from git am

When PATCHTOOL = "git", if we're not able to use "git am" to apply a
patch and fall back to "git apply" or "patch", it is desirable to
actually commit the changes, attempting to preserve (and interpret) the
patch header as part of the commit message if present. As a bonus, the
code for extracting the commit message is callable externally in case it
is useful elsewhere.

(From OE-Core rev: 8c522846093809a8deb866079e73fa317266c80e)

Signed-off-by: Paul Eggleton <paul.eggleton@linux.intel.com>
Signed-off-by: Richard Purdie <richard.purdie@linuxfoundation.org>
This commit is contained in:
Paul Eggleton 2014-12-19 11:41:46 +00:00 committed by Richard Purdie
parent e03c04758b
commit 400933b050
1 changed files with 86 additions and 0 deletions

View File

@ -202,6 +202,78 @@ class GitApplyTree(PatchTree):
def __init__(self, dir, d):
PatchTree.__init__(self, dir, d)
@staticmethod
def extractPatchHeader(patchfile):
"""
Extract just the header lines from the top of a patch file
"""
lines = []
with open(patchfile, 'r') as f:
for line in f.readlines():
if line.startswith('Index: ') or line.startswith('diff -') or line.startswith('---'):
break
lines.append(line)
return lines
@staticmethod
def prepareCommit(patchfile):
"""
Prepare a git commit command line based on the header from a patch file
(typically this is useful for patches that cannot be applied with "git am" due to formatting)
"""
import tempfile
import re
author_re = re.compile('[\S ]+ <\S+@\S+\.\S+>')
# Process patch header and extract useful information
lines = GitApplyTree.extractPatchHeader(patchfile)
outlines = []
author = None
date = None
for line in lines:
if line.startswith('Subject: '):
subject = line.split(':', 1)[1]
# Remove any [PATCH][oe-core] etc.
subject = re.sub(r'\[.+?\]\s*', '', subject)
outlines.insert(0, '%s\n\n' % subject.strip())
continue
if line.startswith('From: ') or line.startswith('Author: '):
authorval = line.split(':', 1)[1].strip().replace('"', '')
# git is fussy about author formatting i.e. it must be Name <email@domain>
if author_re.match(authorval):
author = authorval
continue
if line.startswith('Date: '):
if date is None:
dateval = line.split(':', 1)[1].strip()
# Very crude check for date format, since git will blow up if it's not in the right
# format. Without e.g. a python-dateutils dependency we can't do a whole lot more
if len(dateval) > 12:
date = dateval
continue
if line.startswith('Signed-off-by: '):
authorval = line.split(':', 1)[1].strip().replace('"', '')
# git is fussy about author formatting i.e. it must be Name <email@domain>
if author_re.match(authorval):
author = authorval
outlines.append(line)
# Add a pointer to the original patch file name
if outlines and outlines[-1].strip():
outlines.append('\n')
outlines.append('(from original patch: %s)\n' % os.path.basename(patchfile))
# Write out commit message to a file
with tempfile.NamedTemporaryFile('w', delete=False) as tf:
tmpfile = tf.name
for line in outlines:
tf.write(line)
# Prepare git command
cmd = ["git", "commit", "-F", tmpfile]
# git doesn't like plain email addresses as authors
if author and '<' in author:
cmd.append('--author="%s"' % author)
if date:
cmd.append('--date="%s"' % date)
return (tmpfile, cmd)
def _applypatch(self, patch, force = False, reverse = False, run = True):
def _applypatchhelper(shellcmd, patch, force = False, reverse = False, run = True):
if reverse:
@ -218,11 +290,25 @@ class GitApplyTree(PatchTree):
shellcmd = ["git", "--work-tree=.", "am", "-3", "-p%s" % patch['strippath']]
return _applypatchhelper(shellcmd, patch, force, reverse, run)
except CmdError:
# Fall back to git apply
shellcmd = ["git", "--git-dir=.", "apply", "-p%s" % patch['strippath']]
try:
output = _applypatchhelper(shellcmd, patch, force, reverse, run)
except CmdError:
# Fall back to patch
output = PatchTree._applypatch(self, patch, force, reverse, run)
# Add all files
shellcmd = ["git", "add", "-f", "."]
output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
# Exclude the patches directory
shellcmd = ["git", "reset", "HEAD", self.patchdir]
output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
# Commit the result
(tmpfile, shellcmd) = self.prepareCommit(patch['file'])
try:
output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
finally:
os.remove(tmpfile)
return output