Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / v8 / tools / push-to-trunk / git_recipes.py
1 #!/usr/bin/env python
2 # Copyright 2014 the V8 project authors. All rights reserved.
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
11 #       disclaimer in the documentation and/or other materials provided
12 #       with the distribution.
13 #     * Neither the name of Google Inc. nor the names of its
14 #       contributors may be used to endorse or promote products derived
15 #       from 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 re
30
31 SHA1_RE = re.compile('^[a-fA-F0-9]{40}$')
32 ROLL_DEPS_GIT_SVN_ID_RE = re.compile('^git-svn-id: .*@([0-9]+) .*$')
33
34 # Regular expression that matches a single commit footer line.
35 COMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)')
36
37 # Footer metadata key for commit position.
38 COMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position'
39
40 # Regular expression to parse a commit position
41 COMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}')
42
43 # Key for the 'git-svn' ID metadata commit footer entry.
44 GIT_SVN_ID_FOOTER_KEY = 'git-svn-id'
45
46 # e.g., git-svn-id: https://v8.googlecode.com/svn/trunk@23117
47 #     ce2b1a6d-e550-0410-aec6-3dcde31c8c00
48 GIT_SVN_ID_RE = re.compile(r'((?:\w+)://[^@]+)@(\d+)\s+(?:[a-zA-Z0-9\-]+)')
49
50
51 # Copied from bot_update.py.
52 def GetCommitMessageFooterMap(message):
53   """Returns: (dict) A dictionary of commit message footer entries.
54   """
55   footers = {}
56
57   # Extract the lines in the footer block.
58   lines = []
59   for line in message.strip().splitlines():
60     line = line.strip()
61     if len(line) == 0:
62       del(lines[:])
63       continue
64     lines.append(line)
65
66   # Parse the footer
67   for line in lines:
68     m = COMMIT_FOOTER_ENTRY_RE.match(line)
69     if not m:
70       # If any single line isn't valid, the entire footer is invalid.
71       footers.clear()
72       return footers
73     footers[m.group(1)] = m.group(2).strip()
74   return footers
75
76
77 class GitFailedException(Exception):
78   pass
79
80
81 def Strip(f):
82   def new_f(*args, **kwargs):
83     result = f(*args, **kwargs)
84     if result is None:
85       return result
86     else:
87       return result.strip()
88   return new_f
89
90
91 def MakeArgs(l):
92   """['-a', '', 'abc', ''] -> '-a abc'"""
93   return " ".join(filter(None, l))
94
95
96 def Quoted(s):
97   return "\"%s\"" % s
98
99
100 class GitRecipesMixin(object):
101   def GitIsWorkdirClean(self, **kwargs):
102     return self.Git("status -s -uno", **kwargs).strip() == ""
103
104   @Strip
105   def GitBranch(self, **kwargs):
106     return self.Git("branch", **kwargs)
107
108   def GitCreateBranch(self, name, remote="", **kwargs):
109     assert name
110     remote_args = ["--upstream", remote] if remote else []
111     self.Git(MakeArgs(["new-branch", name] + remote_args), **kwargs)
112
113   def GitDeleteBranch(self, name, **kwargs):
114     assert name
115     self.Git(MakeArgs(["branch -D", name]), **kwargs)
116
117   def GitReset(self, name, **kwargs):
118     assert name
119     self.Git(MakeArgs(["reset --hard", name]), **kwargs)
120
121   def GitStash(self, **kwargs):
122     self.Git(MakeArgs(["stash"]), **kwargs)
123
124   def GitRemotes(self, **kwargs):
125     return map(str.strip,
126                self.Git(MakeArgs(["branch -r"]), **kwargs).splitlines())
127
128   def GitCheckout(self, name, **kwargs):
129     assert name
130     self.Git(MakeArgs(["checkout -f", name]), **kwargs)
131
132   def GitCheckoutFile(self, name, branch_or_hash, **kwargs):
133     assert name
134     assert branch_or_hash
135     self.Git(MakeArgs(["checkout -f", branch_or_hash, "--", name]), **kwargs)
136
137   def GitCheckoutFileSafe(self, name, branch_or_hash, **kwargs):
138     try:
139       self.GitCheckoutFile(name, branch_or_hash, **kwargs)
140     except GitFailedException:  # pragma: no cover
141       # The file doesn't exist in that revision.
142       return False
143     return True
144
145   def GitChangedFiles(self, git_hash, **kwargs):
146     assert git_hash
147     try:
148       files = self.Git(MakeArgs(["diff --name-only",
149                                  git_hash,
150                                  "%s^" % git_hash]), **kwargs)
151       return map(str.strip, files.splitlines())
152     except GitFailedException:  # pragma: no cover
153       # Git fails using "^" at branch roots.
154       return []
155
156
157   @Strip
158   def GitCurrentBranch(self, **kwargs):
159     for line in self.Git("status -s -b -uno", **kwargs).strip().splitlines():
160       match = re.match(r"^## (.+)", line)
161       if match: return match.group(1)
162     raise Exception("Couldn't find curent branch.")  # pragma: no cover
163
164   @Strip
165   def GitLog(self, n=0, format="", grep="", git_hash="", parent_hash="",
166              branch="", reverse=False, **kwargs):
167     assert not (git_hash and parent_hash)
168     args = ["log"]
169     if n > 0:
170       args.append("-%d" % n)
171     if format:
172       args.append("--format=%s" % format)
173     if grep:
174       args.append("--grep=\"%s\"" % grep.replace("\"", "\\\""))
175     if reverse:
176       args.append("--reverse")
177     if git_hash:
178       args.append(git_hash)
179     if parent_hash:
180       args.append("%s^" % parent_hash)
181     args.append(branch)
182     return self.Git(MakeArgs(args), **kwargs)
183
184   def GitGetPatch(self, git_hash, **kwargs):
185     assert git_hash
186     return self.Git(MakeArgs(["log", "-1", "-p", git_hash]), **kwargs)
187
188   # TODO(machenbach): Unused? Remove.
189   def GitAdd(self, name, **kwargs):
190     assert name
191     self.Git(MakeArgs(["add", Quoted(name)]), **kwargs)
192
193   def GitApplyPatch(self, patch_file, reverse=False, **kwargs):
194     assert patch_file
195     args = ["apply --index --reject"]
196     if reverse:
197       args.append("--reverse")
198     args.append(Quoted(patch_file))
199     self.Git(MakeArgs(args), **kwargs)
200
201   def GitUpload(self, reviewer="", author="", force=False, cq=False,
202                 bypass_hooks=False, cc="", **kwargs):
203     args = ["cl upload --send-mail"]
204     if author:
205       args += ["--email", Quoted(author)]
206     if reviewer:
207       args += ["-r", Quoted(reviewer)]
208     if force:
209       args.append("-f")
210     if cq:
211       args.append("--use-commit-queue")
212     if bypass_hooks:
213       args.append("--bypass-hooks")
214     if cc:
215       args += ["--cc", Quoted(cc)]
216     # TODO(machenbach): Check output in forced mode. Verify that all required
217     # base files were uploaded, if not retry.
218     self.Git(MakeArgs(args), pipe=False, **kwargs)
219
220   def GitCommit(self, message="", file_name="", author=None, **kwargs):
221     assert message or file_name
222     args = ["commit"]
223     if file_name:
224       args += ["-aF", Quoted(file_name)]
225     if message:
226       args += ["-am", Quoted(message)]
227     if author:
228       args += ["--author", "\"%s <%s>\"" % (author, author)]
229     self.Git(MakeArgs(args), **kwargs)
230
231   def GitPresubmit(self, **kwargs):
232     self.Git("cl presubmit", "PRESUBMIT_TREE_CHECK=\"skip\"", **kwargs)
233
234   def GitDCommit(self, **kwargs):
235     self.Git(
236         "cl dcommit -f --bypass-hooks", retry_on=lambda x: x is None, **kwargs)
237
238   def GitCLLand(self, **kwargs):
239     self.Git(
240         "cl land -f --bypass-hooks", retry_on=lambda x: x is None, **kwargs)
241
242   def GitDiff(self, loc1, loc2, **kwargs):
243     return self.Git(MakeArgs(["diff", loc1, loc2]), **kwargs)
244
245   def GitPull(self, **kwargs):
246     self.Git("pull", **kwargs)
247
248   def GitFetchOrigin(self, **kwargs):
249     self.Git("fetch origin", **kwargs)
250
251   def GitConvertToSVNRevision(self, git_hash, **kwargs):
252     result = self.Git(MakeArgs(["rev-list", "-n", "1", git_hash]), **kwargs)
253     if not result or not SHA1_RE.match(result):
254       raise GitFailedException("Git hash %s is unknown." % git_hash)
255     log = self.GitLog(n=1, format="%B", git_hash=git_hash, **kwargs)
256     for line in reversed(log.splitlines()):
257       match = ROLL_DEPS_GIT_SVN_ID_RE.match(line.strip())
258       if match:
259         return match.group(1)
260     raise GitFailedException("Couldn't convert %s to SVN." % git_hash)
261
262   @Strip
263   # Copied from bot_update.py and modified for svn-like numbers only.
264   def GetCommitPositionNumber(self, git_hash, **kwargs):
265     """Dumps the 'git' log for a specific revision and parses out the commit
266     position number.
267
268     If a commit position metadata key is found, its number will be returned.
269
270     Otherwise, we will search for a 'git-svn' metadata entry. If one is found,
271     its SVN revision value is returned.
272     """
273     git_log = self.GitLog(format='%B', n=1, git_hash=git_hash, **kwargs)
274     footer_map = GetCommitMessageFooterMap(git_log)
275
276     # Search for commit position metadata
277     value = footer_map.get(COMMIT_POSITION_FOOTER_KEY)
278     if value:
279       match = COMMIT_POSITION_RE.match(value)
280       if match:
281         return match.group(2)
282
283     # Extract the svn revision from 'git-svn' metadata
284     value = footer_map.get(GIT_SVN_ID_FOOTER_KEY)
285     if value:
286       match = GIT_SVN_ID_RE.match(value)
287       if match:
288         return match.group(2)
289     return None
290
291   ### Git svn stuff
292
293   def GitSVNFetch(self, **kwargs):
294     self.Git("svn fetch", **kwargs)
295
296   def GitSVNRebase(self, **kwargs):
297     self.Git("svn rebase", **kwargs)
298
299   # TODO(machenbach): Unused? Remove.
300   @Strip
301   def GitSVNLog(self, **kwargs):
302     return self.Git("svn log -1 --oneline", **kwargs)
303
304   @Strip
305   def GitSVNFindGitHash(self, revision, branch="", **kwargs):
306     assert revision
307     args = MakeArgs(["svn find-rev", "r%s" % revision, branch])
308
309     # Pick the last line if multiple lines are available. The first lines might
310     # print information about rebuilding the svn-git mapping.
311     return self.Git(args, **kwargs).splitlines()[-1]
312
313   @Strip
314   def GitSVNFindSVNRev(self, git_hash, branch="", **kwargs):
315     return self.Git(MakeArgs(["svn find-rev", git_hash, branch]), **kwargs)
316
317   def GitSVNDCommit(self, **kwargs):
318     return self.Git("svn dcommit 2>&1", retry_on=lambda x: x is None, **kwargs)
319
320   def GitSVNTag(self, version, **kwargs):
321     self.Git(("svn tag %s -m \"Tagging version %s\"" % (version, version)),
322              retry_on=lambda x: x is None,
323              **kwargs)