Index: rietveld.py |
diff --git a/rietveld.py b/rietveld.py |
index c2045ac3311ea7375b496cd7473c4fd2bf1cedfb..df529ad6efb02bef5d3cbf9ee017d8edfee103ed 100644 |
--- a/rietveld.py |
+++ b/rietveld.py |
@@ -15,6 +15,7 @@ The following hypothesis are made: |
import logging |
import os |
+import re |
import sys |
import time |
import urllib2 |
@@ -131,15 +132,13 @@ class Rietveld(object): |
# Ignore the diff. |
out.append(patch.FilePatchDelete(filename, state['is_binary'])) |
elif status in ('A', 'M'): |
- # TODO(maruel): Rietveld support is still weird, add this line once it's |
- # safe to use. |
- # props = state.get('property_changes', '').splitlines() or [] |
- props = [] |
+ svn_props = self.parse_svn_properties( |
+ state.get('property_changes', ''), filename) |
if state['is_binary']: |
out.append(patch.FilePatchBinary( |
filename, |
self.get_file_content(issue, patchset, state['id']), |
- props, |
+ svn_props, |
is_new=(status[0] == 'A'))) |
else: |
# Ignores num_chunks since it may only contain an header. |
@@ -149,24 +148,68 @@ class Rietveld(object): |
if e.code == 404: |
raise patch.UnsupportedPatchFormat( |
filename, 'File doesn\'t have a diff.') |
- out.append(patch.FilePatchDiff(filename, diff, props)) |
+ out.append(patch.FilePatchDiff(filename, diff, svn_props)) |
if status[0] == 'A': |
# It won't be set for empty file. |
out[-1].is_new = True |
else: |
- # Line too long (N/80) |
- # pylint: disable=C0301 |
- # TODO: Add support for MM, A+, etc. Rietveld removes the svn properties |
- # from the diff. |
- # Example of mergeinfo across branches: |
- # http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmlcatalog_dummy.cc |
- # svn:eol-style property that is lost in the diff |
- # http://codereview.chromium.org/202046/diff/1/third_party/libxml/xmllint_dummy.cc |
- # Change with no diff, only svn property change: |
- # http://codereview.chromium.org/6462019/ |
+ # TODO(maruel): Add support for MM, A+, etc. Rietveld removes the svn |
+ # properties from the diff. |
raise patch.UnsupportedPatchFormat(filename, status) |
return patch.PatchSet(out) |
+ @staticmethod |
+ def parse_svn_properties(rietveld_svn_props, filename): |
+ """Returns a list of tuple [('property', 'newvalue')]. |
+ |
+ rietveld_svn_props is the exact format from 'svn diff'. |
+ """ |
+ rietveld_svn_props = rietveld_svn_props.splitlines() |
+ svn_props = [] |
+ if not rietveld_svn_props: |
+ return svn_props |
+ # 1. Ignore svn:mergeinfo. |
+ # 2. Accept svn:eol-style and svn:executable. |
+ # 3. Refuse any other. |
+ # \n |
+ # Added: svn:ignore\n |
+ # + LF\n |
+ while rietveld_svn_props: |
+ spacer = rietveld_svn_props.pop(0) |
+ if spacer or not rietveld_svn_props: |
+ # svn diff always put a spacer between the unified diff and property |
+ # diff |
+ raise patch.UnsupportedPatchFormat( |
+ filename, 'Failed to parse svn properties.') |
+ |
+ # Something like 'Added: svn:eol-style'. Note the action is localized. |
+ # *sigh*. |
+ action = rietveld_svn_props.pop(0) |
+ match = re.match(r'^(\w+): (.+)$', action) |
+ if not match or not rietveld_svn_props: |
+ raise patch.UnsupportedPatchFormat( |
+ filename, 'Failed to parse svn properties.') |
+ |
+ if match.group(2) == 'svn:mergeinfo': |
+ # Silently ignore the content. |
+ rietveld_svn_props.pop(0) |
+ continue |
+ |
+ if match.group(1) not in ('Added', 'Modified'): |
+ # Will fail for our French friends. |
+ raise patch.UnsupportedPatchFormat( |
+ filename, 'Unsupported svn property operation.') |
+ |
+ if match.group(2) in ('svn:eol-style', 'svn:executable'): |
+ # ' + foo' where foo is the new value. That's fragile. |
+ content = rietveld_svn_props.pop(0) |
+ match2 = re.match(r'^ \+ (.*)$', content) |
+ if not match2: |
+ raise patch.UnsupportedPatchFormat( |
+ filename, 'Unsupported svn property format.') |
+ svn_props.append((match.group(2), match2.group(1))) |
+ return svn_props |
+ |
def update_description(self, issue, description): |
"""Sets the description for an issue on Rietveld.""" |
logging.info('new description for issue %s' % issue) |