1f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#!/usr/bin/env python
2f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# Copyright 2014 the V8 project authors. All rights reserved.
3f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# Redistribution and use in source and binary forms, with or without
4f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# modification, are permitted provided that the following conditions are
5f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# met:
6f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#
7f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#     * Redistributions of source code must retain the above copyright
8f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#       notice, this list of conditions and the following disclaimer.
9f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#     * Redistributions in binary form must reproduce the above
10f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#       copyright notice, this list of conditions and the following
11f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#       disclaimer in the documentation and/or other materials provided
12f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#       with the distribution.
13f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#     * Neither the name of Google Inc. nor the names of its
14f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#       contributors may be used to endorse or promote products derived
15f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#       from this software without specific prior written permission.
16f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org#
17f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
18f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
19f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
20f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
21f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
22f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
23f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
24f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
25f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
27f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
29f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.orgimport re
30f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
316474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.orgSHA1_RE = re.compile('^[a-fA-F0-9]{40}$')
329aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.orgROLL_DEPS_GIT_SVN_ID_RE = re.compile('^git-svn-id: .*@([0-9]+) .*$')
339aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
349aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org# Regular expression that matches a single commit footer line.
359aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.orgCOMMIT_FOOTER_ENTRY_RE = re.compile(r'([^:]+):\s+(.+)')
369aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
379aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org# Footer metadata key for commit position.
389aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.orgCOMMIT_POSITION_FOOTER_KEY = 'Cr-Commit-Position'
399aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
409aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org# Regular expression to parse a commit position
419aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.orgCOMMIT_POSITION_RE = re.compile(r'(.+)@\{#(\d+)\}')
429aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
439aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org# Key for the 'git-svn' ID metadata commit footer entry.
449aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.orgGIT_SVN_ID_FOOTER_KEY = 'git-svn-id'
459aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
469aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org# e.g., git-svn-id: https://v8.googlecode.com/svn/trunk@23117
479aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org#     ce2b1a6d-e550-0410-aec6-3dcde31c8c00
489aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.orgGIT_SVN_ID_RE = re.compile(r'((?:\w+)://[^@]+)@(\d+)\s+(?:[a-zA-Z0-9\-]+)')
499aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
509aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
519aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org# Copied from bot_update.py.
529aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.orgdef GetCommitMessageFooterMap(message):
539aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  """Returns: (dict) A dictionary of commit message footer entries.
549aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  """
559aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  footers = {}
569aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
579aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  # Extract the lines in the footer block.
589aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  lines = []
599aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  for line in message.strip().splitlines():
609aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    line = line.strip()
619aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    if len(line) == 0:
629aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      del(lines[:])
639aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      continue
649aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    lines.append(line)
659aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
669aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  # Parse the footer
679aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  for line in lines:
689aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    m = COMMIT_FOOTER_ENTRY_RE.match(line)
699aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    if not m:
709aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      # If any single line isn't valid, the entire footer is invalid.
719aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      footers.clear()
729aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      return footers
739aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    footers[m.group(1)] = m.group(2).strip()
749aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  return footers
756474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org
765b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
775b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.orgclass GitFailedException(Exception):
785b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org  pass
795b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
805b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
81f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.orgdef Strip(f):
82f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  def new_f(*args, **kwargs):
83f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    return f(*args, **kwargs).strip()
84f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  return new_f
85f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
86f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
87f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.orgdef MakeArgs(l):
88f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  """['-a', '', 'abc', ''] -> '-a abc'"""
89f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  return " ".join(filter(None, l))
90f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
91f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
92f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.orgdef Quoted(s):
93f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  return "\"%s\"" % s
94f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
95f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
96f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.orgclass GitRecipesMixin(object):
9742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitIsWorkdirClean(self, **kwargs):
9842ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git("status -s -uno", **kwargs).strip() == ""
99f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
100f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  @Strip
10142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitBranch(self, **kwargs):
10242ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git("branch", **kwargs)
103f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
10442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitCreateBranch(self, name, branch="", **kwargs):
105f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert name
10642ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(["checkout -b", name, branch]), **kwargs)
107f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
10842ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitDeleteBranch(self, name, **kwargs):
109f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert name
11042ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(["branch -D", name]), **kwargs)
111f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
11242ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitReset(self, name, **kwargs):
1135b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org    assert name
11442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(["reset --hard", name]), **kwargs)
1155b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
11642ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitStash(self, **kwargs):
11742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(["stash"]), **kwargs)
1184c3ce7c3fd2802da8f91c6516a9c9aea3cd93f1emachenbach@chromium.org
11942ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitRemotes(self, **kwargs):
12042ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return map(str.strip,
12142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org               self.Git(MakeArgs(["branch -r"]), **kwargs).splitlines())
1225b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
12342ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitCheckout(self, name, **kwargs):
124f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert name
12542ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(["checkout -f", name]), **kwargs)
126f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
12742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitCheckoutFile(self, name, branch_or_hash, **kwargs):
12800a4ad70bb8883074ba7630edd7140762bcb86a0machenbach@chromium.org    assert name
1295697144afb43181fed170b81c194fe1cc0fce3b6machenbach@chromium.org    assert branch_or_hash
13042ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(["checkout -f", branch_or_hash, "--", name]), **kwargs)
13100a4ad70bb8883074ba7630edd7140762bcb86a0machenbach@chromium.org
13242ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitCheckoutFileSafe(self, name, branch_or_hash, **kwargs):
1335b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org    try:
13442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org      self.GitCheckoutFile(name, branch_or_hash, **kwargs)
1355b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org    except GitFailedException:  # pragma: no cover
1365b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org      # The file doesn't exist in that revision.
1375b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org      return False
1385b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org    return True
1395b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
14042ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitChangedFiles(self, git_hash, **kwargs):
1415b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org    assert git_hash
1425b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org    try:
1435b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org      files = self.Git(MakeArgs(["diff --name-only",
1445b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org                                 git_hash,
14542ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org                                 "%s^" % git_hash]), **kwargs)
1465b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org      return map(str.strip, files.splitlines())
1475b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org    except GitFailedException:  # pragma: no cover
1485b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org      # Git fails using "^" at branch roots.
1495b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org      return []
1505b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
1515b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org
152f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  @Strip
15342ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitCurrentBranch(self, **kwargs):
15442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    for line in self.Git("status -s -b -uno", **kwargs).strip().splitlines():
155f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      match = re.match(r"^## (.+)", line)
156f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      if match: return match.group(1)
157486536df718553960f9700559e80e5b10b0d5994dslomov@chromium.org    raise Exception("Couldn't find curent branch.")  # pragma: no cover
158f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
159f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  @Strip
160f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  def GitLog(self, n=0, format="", grep="", git_hash="", parent_hash="",
16142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org             branch="", reverse=False, **kwargs):
162f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert not (git_hash and parent_hash)
163f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    args = ["log"]
164f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if n > 0:
165f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args.append("-%d" % n)
166f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if format:
167f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args.append("--format=%s" % format)
168f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if grep:
169255043f8054e713a64509c707cfabadd42344683machenbach@chromium.org      args.append("--grep=\"%s\"" % grep.replace("\"", "\\\""))
170f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if reverse:
171f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args.append("--reverse")
172f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if git_hash:
173f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args.append(git_hash)
174f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if parent_hash:
175f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args.append("%s^" % parent_hash)
176f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    args.append(branch)
17742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git(MakeArgs(args), **kwargs)
178f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
17942ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitGetPatch(self, git_hash, **kwargs):
180f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert git_hash
18142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git(MakeArgs(["log", "-1", "-p", git_hash]), **kwargs)
182f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
1835b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org  # TODO(machenbach): Unused? Remove.
18442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitAdd(self, name, **kwargs):
185f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert name
18642ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(["add", Quoted(name)]), **kwargs)
187f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
18842ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitApplyPatch(self, patch_file, reverse=False, **kwargs):
189f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert patch_file
190f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    args = ["apply --index --reject"]
191f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if reverse:
192f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args.append("--reverse")
193f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    args.append(Quoted(patch_file))
19442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(args), **kwargs)
195f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
1969d1a7a8cdb664730cf5703185e85a4716748c564machenbach@chromium.org  def GitUpload(self, reviewer="", author="", force=False, cq=False,
19742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org                bypass_hooks=False, **kwargs):
198f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    args = ["cl upload --send-mail"]
199f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if author:
200f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args += ["--email", Quoted(author)]
201f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if reviewer:
202f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args += ["-r", Quoted(reviewer)]
203f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if force:
204f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args.append("-f")
205c3564d8de4ebfc4fa3dc009fc9f6f18968ffcbd7machenbach@chromium.org    if cq:
206c3564d8de4ebfc4fa3dc009fc9f6f18968ffcbd7machenbach@chromium.org      args.append("--use-commit-queue")
2079d1a7a8cdb664730cf5703185e85a4716748c564machenbach@chromium.org    if bypass_hooks:
2089d1a7a8cdb664730cf5703185e85a4716748c564machenbach@chromium.org      args.append("--bypass-hooks")
209f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    # TODO(machenbach): Check output in forced mode. Verify that all required
210f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    # base files were uploaded, if not retry.
21142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(args), pipe=False, **kwargs)
212f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
21342ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitCommit(self, message="", file_name="", author=None, **kwargs):
214f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert message or file_name
215f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    args = ["commit"]
216f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if file_name:
217f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args += ["-aF", Quoted(file_name)]
218f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    if message:
219f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org      args += ["-am", Quoted(message)]
2209aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    if author:
2219aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      args += ["--author", "\"%s <%s>\"" % (author, author)]
22242ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(MakeArgs(args), **kwargs)
223f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
22442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitPresubmit(self, **kwargs):
22542ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git("cl presubmit", "PRESUBMIT_TREE_CHECK=\"skip\"", **kwargs)
226f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
22742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitDCommit(self, **kwargs):
22842ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git(
22942ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org        "cl dcommit -f --bypass-hooks", retry_on=lambda x: x is None, **kwargs)
230f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
23142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitDiff(self, loc1, loc2, **kwargs):
23242ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git(MakeArgs(["diff", loc1, loc2]), **kwargs)
233f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
23442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitPull(self, **kwargs):
23542ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git("pull", **kwargs)
236f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
23742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitFetchOrigin(self, **kwargs):
23842ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git("fetch origin", **kwargs)
2396474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org
24042ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitConvertToSVNRevision(self, git_hash, **kwargs):
24142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    result = self.Git(MakeArgs(["rev-list", "-n", "1", git_hash]), **kwargs)
2426474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org    if not result or not SHA1_RE.match(result):
2436474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org      raise GitFailedException("Git hash %s is unknown." % git_hash)
24442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    log = self.GitLog(n=1, format="%B", git_hash=git_hash, **kwargs)
2456474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org    for line in reversed(log.splitlines()):
2469aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      match = ROLL_DEPS_GIT_SVN_ID_RE.match(line.strip())
2476474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org      if match:
2486474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org        return match.group(1)
2496474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org    raise GitFailedException("Couldn't convert %s to SVN." % git_hash)
2506474a1cfee1cdad45de5cc96960085e1c7daf11cmachenbach@chromium.org
2519aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  @Strip
2529aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  # Copied from bot_update.py and modified for svn-like numbers only.
25342ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GetCommitPositionNumber(self, git_hash, **kwargs):
2549aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    """Dumps the 'git' log for a specific revision and parses out the commit
2559aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    position number.
2569aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
2579aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    If a commit position metadata key is found, its number will be returned.
2589aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
2599aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    Otherwise, we will search for a 'git-svn' metadata entry. If one is found,
2609aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    its SVN revision value is returned.
2619aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    """
26242ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    git_log = self.GitLog(format='%B', n=1, git_hash=git_hash, **kwargs)
2639aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    footer_map = GetCommitMessageFooterMap(git_log)
2649aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
2659aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    # Search for commit position metadata
2669aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    value = footer_map.get(COMMIT_POSITION_FOOTER_KEY)
2679aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    if value:
2689aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      match = COMMIT_POSITION_RE.match(value)
2699aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      if match:
2709aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org        return match.group(2)
2719aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
2729aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    # Extract the svn revision from 'git-svn' metadata
2739aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    value = footer_map.get(GIT_SVN_ID_FOOTER_KEY)
2749aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    if value:
2759aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      match = GIT_SVN_ID_RE.match(value)
2769aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org      if match:
2779aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org        return match.group(2)
2789aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org    return None
2799aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
2809aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org  ### Git svn stuff
2819aaa825cf89e1bcfece269a453300ebf4a26d64dmachenbach@chromium.org
28242ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitSVNFetch(self, **kwargs):
28342ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git("svn fetch", **kwargs)
284f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
28542ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitSVNRebase(self, **kwargs):
28642ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    self.Git("svn rebase", **kwargs)
287474e8b19cf12dc057572a8543864dd6586ee0a65machenbach@chromium.org
2885b080567cf135f6dbaf23973ba6b6fa1d6af83b3machenbach@chromium.org  # TODO(machenbach): Unused? Remove.
289f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  @Strip
29042ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitSVNLog(self, **kwargs):
29142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git("svn log -1 --oneline", **kwargs)
292f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
293f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  @Strip
29442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitSVNFindGitHash(self, revision, branch="", **kwargs):
295f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    assert revision
29642ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git(
29742ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org        MakeArgs(["svn find-rev", "r%s" % revision, branch]), **kwargs)
298f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
299f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org  @Strip
30042ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitSVNFindSVNRev(self, git_hash, branch="", **kwargs):
30142ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git(MakeArgs(["svn find-rev", git_hash, branch]), **kwargs)
302f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
30342ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitSVNDCommit(self, **kwargs):
30442ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org    return self.Git("svn dcommit 2>&1", retry_on=lambda x: x is None, **kwargs)
305f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org
30642ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org  def GitSVNTag(self, version, **kwargs):
307f5a24546072ecdbbd6372c85c42157e01e913561titzer@chromium.org    self.Git(("svn tag %s -m \"Tagging version %s\"" % (version, version)),
30842ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org             retry_on=lambda x: x is None,
30942ed2fc449e83fab2ccbf1b769a5e83715c9d783machenbach@chromium.org             **kwargs)
310