diff options
author | Paul Eggleton <paul.eggleton@linux.intel.com> | 2014-12-19 11:41:46 +0000 |
---|---|---|
committer | Richard Purdie <richard.purdie@linuxfoundation.org> | 2014-12-21 12:37:56 +0000 |
commit | 400933b0501b651f3c67d93570eb929da5ed02b4 (patch) | |
tree | d4981f083389c0fde267f42c636005ed77fc2d33 | |
parent | e03c04758b5599e2ac7a3eb20954b81fcb4cf3d3 (diff) | |
download | poky-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>
-rw-r--r-- | meta/lib/oe/patch.py | 86 |
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 | ||