| OLD | NEW |
| (Empty) |
| 1 # Copyright (C) 2010 Google Inc. All rights reserved. | |
| 2 # | |
| 3 # Redistribution and use in source and binary forms, with or without | |
| 4 # modification, are permitted provided that the following conditions are | |
| 5 # met: | |
| 6 # | |
| 7 # * Redistributions of source code must retain the above copyright | |
| 8 # notice, this list of conditions and the following disclaimer. | |
| 9 # * Redistributions in binary form must reproduce the above | |
| 10 # copyright notice, this list of conditions and the following disclaimer | |
| 11 # in the documentation and/or other materials provided with the | |
| 12 # distribution. | |
| 13 # * Neither the name of Google Inc. nor the names of its | |
| 14 # contributors may be used to endorse or promote products derived from | |
| 15 # this software without specific prior written permission. | |
| 16 # | |
| 17 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |
| 18 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |
| 19 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |
| 20 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |
| 21 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |
| 22 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |
| 23 # LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |
| 24 # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |
| 25 # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |
| 26 # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |
| 27 # OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |
| 28 | |
| 29 import cStringIO as StringIO | |
| 30 import logging | |
| 31 import sys | |
| 32 import re | |
| 33 import tempfile | |
| 34 | |
| 35 from webkitpy.tool.steps.abstractstep import AbstractStep | |
| 36 from webkitpy.common.system.executive import Executive, ScriptError | |
| 37 from webkitpy.common.checkout import diff_parser | |
| 38 | |
| 39 from webkitpy.tool.steps import confirmdiff | |
| 40 | |
| 41 _log = logging.getLogger(__name__) | |
| 42 | |
| 43 | |
| 44 class HasLanded(confirmdiff.ConfirmDiff): | |
| 45 | |
| 46 @classmethod | |
| 47 def convert_to_svn(cls, diff): | |
| 48 lines = StringIO.StringIO(diff).readlines() | |
| 49 convert = diff_parser.get_diff_converter(lines) | |
| 50 return "".join(convert(x) for x in lines) | |
| 51 | |
| 52 @classmethod | |
| 53 def strip_change_log(cls, diff): | |
| 54 output = [] | |
| 55 skipping = False | |
| 56 for line in StringIO.StringIO(diff).readlines(): | |
| 57 indexline = re.match("^Index: ([^\\n]*/)?([^/\\n]*)$", line) | |
| 58 if skipping and indexline: | |
| 59 skipping = False | |
| 60 if indexline and indexline.group(2) == "ChangeLog": | |
| 61 skipping = True | |
| 62 if not skipping: | |
| 63 output.append(line) | |
| 64 return "".join(output) | |
| 65 | |
| 66 @classmethod | |
| 67 def diff_diff(cls, diff1, diff2, diff1_suffix, diff2_suffix, executive=None)
: | |
| 68 # Now this is where it gets complicated, we need to compare our diff to
the diff at landed_revision. | |
| 69 diff1_patch = tempfile.NamedTemporaryFile(suffix=diff1_suffix + '.patch'
) | |
| 70 diff1_patch.write(diff1) | |
| 71 diff1_patch.flush() | |
| 72 | |
| 73 # Check if there are any differences in the patch that don't happen | |
| 74 diff2_patch = tempfile.NamedTemporaryFile(suffix=diff2_suffix + '.patch'
) | |
| 75 diff2_patch.write(diff2) | |
| 76 diff2_patch.flush() | |
| 77 | |
| 78 # Diff the two diff's together... | |
| 79 if not executive: | |
| 80 executive = Executive() | |
| 81 | |
| 82 try: | |
| 83 return executive.run_command( | |
| 84 ["interdiff", diff1_patch.name, diff2_patch.name], decode_output
=False) | |
| 85 except ScriptError, e: | |
| 86 _log.warning("Unable to find interdiff util (part of GNU difftools p
ackage) which is required.") | |
| 87 raise | |
| 88 | |
| 89 def run(self, state): | |
| 90 # Check if there are changes first | |
| 91 if not self._tool.scm().local_changes_exist(): | |
| 92 _log.warn("No local changes found, exiting.") | |
| 93 return True | |
| 94 | |
| 95 # Check if there is a SVN revision in the bug from the commit queue | |
| 96 landed_revision = self.cached_lookup(state, "bug").commit_revision() | |
| 97 if not landed_revision: | |
| 98 raise ScriptError("Unable to find landed message in associated bug."
) | |
| 99 | |
| 100 # Now this is there it gets complicated, we need to compare our diff to
the diff at landed_revision. | |
| 101 landed_diff_bin = self._tool.scm().diff_for_revision(landed_revision) | |
| 102 landed_diff_trimmed = self.strip_change_log(self.convert_to_svn(landed_d
iff_bin)) | |
| 103 | |
| 104 # Check if there are any differences in the patch that don't happen | |
| 105 local_diff_bin = self._tool.scm().create_patch() | |
| 106 local_diff_trimmed = self.strip_change_log(self.convert_to_svn(local_dif
f_bin)) | |
| 107 | |
| 108 # Diff the two diff's together... | |
| 109 diff_diff = self.diff_diff(landed_diff_trimmed, local_diff_trimmed, | |
| 110 '-landed', '-local', | |
| 111 executive=self._tool.executive) | |
| 112 | |
| 113 with self._show_pretty_diff(diff_diff) as pretty_diff_file: | |
| 114 if not pretty_diff_file: | |
| 115 self._tool.user.page(diff_diff) | |
| 116 | |
| 117 if self._tool.user.confirm("May I discard local changes?"): | |
| 118 # Discard changes if the user confirmed we should | |
| 119 _log.warn("Discarding changes as requested.") | |
| 120 self._tool.scm().discard_local_changes() | |
| OLD | NEW |