summaryrefslogtreecommitdiffstats
path: root/meta/lib/oe/patch.py
diff options
context:
space:
mode:
authorPaul Eggleton <paul.eggleton@linux.intel.com>2014-12-19 11:41:46 +0000
committerRichard Purdie <richard.purdie@linuxfoundation.org>2014-12-21 12:37:56 +0000
commit400933b0501b651f3c67d93570eb929da5ed02b4 (patch)
treed4981f083389c0fde267f42c636005ed77fc2d33 /meta/lib/oe/patch.py
parente03c04758b5599e2ac7a3eb20954b81fcb4cf3d3 (diff)
downloadpoky-400933b0501b651f3c67d93570eb929da5ed02b4.tar.gz
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>
Diffstat (limited to 'meta/lib/oe/patch.py')
-rw-r--r--meta/lib/oe/patch.py86
1 files changed, 86 insertions, 0 deletions
diff --git a/meta/lib/oe/patch.py b/meta/lib/oe/patch.py
index 788f465bd9..2d56ba404e 100644
--- a/meta/lib/oe/patch.py
+++ b/meta/lib/oe/patch.py
@@ -202,6 +202,78 @@ class GitApplyTree(PatchTree):
202 def __init__(self, dir, d): 202 def __init__(self, dir, d):
203 PatchTree.__init__(self, dir, d) 203 PatchTree.__init__(self, dir, d)
204 204
205 @staticmethod
206 def extractPatchHeader(patchfile):
207 """
208 Extract just the header lines from the top of a patch file
209 """
210 lines = []
211 with open(patchfile, 'r') as f:
212 for line in f.readlines():
213 if line.startswith('Index: ') or line.startswith('diff -') or line.startswith('---'):
214 break
215 lines.append(line)
216 return lines
217
218 @staticmethod
219 def prepareCommit(patchfile):
220 """
221 Prepare a git commit command line based on the header from a patch file
222 (typically this is useful for patches that cannot be applied with "git am" due to formatting)
223 """
224 import tempfile
225 import re
226 author_re = re.compile('[\S ]+ <\S+@\S+\.\S+>')
227 # Process patch header and extract useful information
228 lines = GitApplyTree.extractPatchHeader(patchfile)
229 outlines = []
230 author = None
231 date = None
232 for line in lines:
233 if line.startswith('Subject: '):
234 subject = line.split(':', 1)[1]
235 # Remove any [PATCH][oe-core] etc.
236 subject = re.sub(r'\[.+?\]\s*', '', subject)
237 outlines.insert(0, '%s\n\n' % subject.strip())
238 continue
239 if line.startswith('From: ') or line.startswith('Author: '):
240 authorval = line.split(':', 1)[1].strip().replace('"', '')
241 # git is fussy about author formatting i.e. it must be Name <email@domain>
242 if author_re.match(authorval):
243 author = authorval
244 continue
245 if line.startswith('Date: '):
246 if date is None:
247 dateval = line.split(':', 1)[1].strip()
248 # Very crude check for date format, since git will blow up if it's not in the right
249 # format. Without e.g. a python-dateutils dependency we can't do a whole lot more
250 if len(dateval) > 12:
251 date = dateval
252 continue
253 if line.startswith('Signed-off-by: '):
254 authorval = line.split(':', 1)[1].strip().replace('"', '')
255 # git is fussy about author formatting i.e. it must be Name <email@domain>
256 if author_re.match(authorval):
257 author = authorval
258 outlines.append(line)
259 # Add a pointer to the original patch file name
260 if outlines and outlines[-1].strip():
261 outlines.append('\n')
262 outlines.append('(from original patch: %s)\n' % os.path.basename(patchfile))
263 # Write out commit message to a file
264 with tempfile.NamedTemporaryFile('w', delete=False) as tf:
265 tmpfile = tf.name
266 for line in outlines:
267 tf.write(line)
268 # Prepare git command
269 cmd = ["git", "commit", "-F", tmpfile]
270 # git doesn't like plain email addresses as authors
271 if author and '<' in author:
272 cmd.append('--author="%s"' % author)
273 if date:
274 cmd.append('--date="%s"' % date)
275 return (tmpfile, cmd)
276
205 def _applypatch(self, patch, force = False, reverse = False, run = True): 277 def _applypatch(self, patch, force = False, reverse = False, run = True):
206 def _applypatchhelper(shellcmd, patch, force = False, reverse = False, run = True): 278 def _applypatchhelper(shellcmd, patch, force = False, reverse = False, run = True):
207 if reverse: 279 if reverse:
@@ -218,11 +290,25 @@ class GitApplyTree(PatchTree):
218 shellcmd = ["git", "--work-tree=.", "am", "-3", "-p%s" % patch['strippath']] 290 shellcmd = ["git", "--work-tree=.", "am", "-3", "-p%s" % patch['strippath']]
219 return _applypatchhelper(shellcmd, patch, force, reverse, run) 291 return _applypatchhelper(shellcmd, patch, force, reverse, run)
220 except CmdError: 292 except CmdError:
293 # Fall back to git apply
221 shellcmd = ["git", "--git-dir=.", "apply", "-p%s" % patch['strippath']] 294 shellcmd = ["git", "--git-dir=.", "apply", "-p%s" % patch['strippath']]
222 try: 295 try:
223 output = _applypatchhelper(shellcmd, patch, force, reverse, run) 296 output = _applypatchhelper(shellcmd, patch, force, reverse, run)
224 except CmdError: 297 except CmdError:
298 # Fall back to patch
225 output = PatchTree._applypatch(self, patch, force, reverse, run) 299 output = PatchTree._applypatch(self, patch, force, reverse, run)
300 # Add all files
301 shellcmd = ["git", "add", "-f", "."]
302 output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
303 # Exclude the patches directory
304 shellcmd = ["git", "reset", "HEAD", self.patchdir]
305 output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
306 # Commit the result
307 (tmpfile, shellcmd) = self.prepareCommit(patch['file'])
308 try:
309 output += runcmd(["sh", "-c", " ".join(shellcmd)], self.dir)
310 finally:
311 os.remove(tmpfile)
226 return output 312 return output
227 313
228 314