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 |