OLD | NEW |
| 1 # coding=utf8 |
1 # Copyright (c) 2010 The Chromium Authors. All rights reserved. | 2 # Copyright (c) 2010 The Chromium Authors. All rights reserved. |
2 # Use of this source code is governed by a BSD-style license that can be | 3 # Use of this source code is governed by a BSD-style license that can be |
3 # found in the LICENSE file. | 4 # found in the LICENSE file. |
4 """Utility functions to handle patches.""" | 5 """Utility functions to handle patches.""" |
5 | 6 |
| 7 import logging |
6 import re | 8 import re |
7 import subprocess2 | 9 import subprocess2 |
8 | 10 |
9 | 11 |
10 def auto_mangle_git_patch(patch): | 12 def auto_mangle_git_patch(patch): |
11 """Mangles a patch and automatically strip out git decoration.""" | 13 """Mangles a patch and automatically strip out git decoration.""" |
12 # Git patches have a/ at the beginning of source paths. We strip that out | 14 # Git patches have a/ at the beginning of source paths. We strip that out |
13 # with a regexp rather than the -p flag to patch so we can feed either Git | 15 # with a regexp rather than the -p flag to patch so we can feed either Git |
14 # or svn-style patches into the same apply command. re.sub() should be used | 16 # or svn-style patches into the same apply command. re.sub() should be used |
15 # but flags=re.MULTILINE is only in python 2.7. | 17 # but flags=re.MULTILINE is only in python 2.7. |
16 out = [] | 18 out = [] |
17 for line in patch.splitlines(True): | 19 for line in patch.splitlines(True): |
18 # TODO: It should just process the header lines. | 20 # TODO: It should just process the header lines. |
19 out.append(re.sub(r'^--- a/', r'--- ', | 21 out.append(re.sub(r'^--- a/', r'--- ', |
20 re.sub(r'^\+\+\+ b/', r'+++ ', line))) | 22 re.sub(r'^\+\+\+ b/', r'+++ ', line))) |
21 return ''.join(out) | 23 return ''.join(out) |
22 | 24 |
23 | 25 |
24 def apply_patch(location, patch): | 26 def apply_patch(location, patch): |
25 """Applies a svn patch, manually applying svn meta data.""" | 27 """Applies a svn patch, manually applying svn meta data.""" |
26 # TODO: Do not shell out patch to enable svn cp/mv/ren and friends | 28 # TODO: Do not shell out patch to enable svn cp/mv/ren and friends |
27 # TODO: Add binary support | 29 # TODO: Add binary support |
28 # TODO: Reuse some webkit svn-apply stuff? | 30 # TODO: Reuse some webkit svn-apply stuff? |
29 cmd = ['patch', '-p0', '--forward', '--force'] | 31 cmd = ['patch', '-p0', '--forward', '--force'] |
30 subprocess2.check_call(cmd, stdin=patch, cwd=location) | 32 subprocess2.check_call(cmd, stdin=patch, cwd=location) |
31 | 33 |
| 34 |
| 35 def verify_patch(patch_data): |
| 36 """Verifies if a patch can be applied. |
| 37 |
| 38 Returns a string if the patch can't be applied explaining the issue. |
| 39 """ |
| 40 lines = patch_data.splitlines(False) |
| 41 svn_separator = ( |
| 42 '___________________________________________________________________') |
| 43 |
| 44 index = 0 |
| 45 |
| 46 def next_line(i): |
| 47 if index+i >= len(lines): |
| 48 return '' |
| 49 return lines[index+i] |
| 50 |
| 51 for index, line in enumerate(lines): |
| 52 if line == svn_separator: |
| 53 # Look for the next line. |
| 54 # TODO: If we ever hit non English patches, we need to |
| 55 # use something like re.match(ur'^\w+$', u'Modifié', re.UNICODE) |
| 56 m = re.match(r'^(\w+)\s*\:\s*svn\:([\w\-]+)$', next_line(1)) |
| 57 if not m: |
| 58 # The patch confused the script; |
| 59 continue |
| 60 prop = m.group(2) |
| 61 if prop in ('mergeinfo', 'executable', 'ignore'): |
| 62 # In practice, executable should be treated like eol-style, assuming we |
| 63 # lookup auto-props. |
| 64 return 'Can\'t apply svn property svn:%s' % prop |
| 65 elif prop in ('eol-style', 'mime-type'): |
| 66 # Silently ignore it, assume that the auto-props are ok. |
| 67 # TODO: Verify the auto-props would trigger. |
| 68 continue |
| 69 else: |
| 70 # Unknown property |
| 71 logging.error('Unknown property %s' % prop) |
| 72 continue |
| 73 m = re.match(r'^Index\:\s*(.+)$', line) |
| 74 if m: |
| 75 # We need to find --- on the next 3 lines otherwise discard the patch. |
| 76 if next_line(1).startswith('--- '): |
| 77 continue |
| 78 if not next_line(1): |
| 79 return 'Can\'t patch empty file (svn)' |
| 80 if next_line(2).startswith('--- '): |
| 81 continue |
| 82 if not next_line(2): |
| 83 return 'Can\'t patch empty file (svn)' |
| 84 if next_line(3).startswith('--- '): |
| 85 continue |
| 86 if not next_line(3): |
| 87 return 'Can\'t patch empty file (svn)' |
| 88 if next_line(4).startswith('--- '): |
| 89 # See testSupported3() for a real-world example. |
| 90 continue |
| 91 return 'Can\'t patch empty file (svn)' |
| 92 m = re.match(r'diff \-\-git (.*)$', line) |
| 93 if m: |
| 94 if not next_line(1): |
| 95 return 'Can\'t patch empty file (git)' |
| 96 if next_line(1).startswith('old mode '): |
| 97 return 'Unsupported git file mode change' |
| 98 return None |
OLD | NEW |