mirror of
https://github.com/golang/go
synced 2024-11-22 06:04:39 -07:00
codereview: consistent indent, cut dead code
End the charade (farce?) that we are using upload.py unaltered. Cut all the unused stuff. Indent using tabs to match the rest of the file. Next: rewrite MercurialVCS to use mercurial package, to avoid overhead of forking off a new hg command multiple times for every file. And parallelize upload. R=gri CC=golang-dev https://golang.org/cl/2009046
This commit is contained in:
parent
c606b964a0
commit
3b226f9886
@ -243,7 +243,7 @@ class CL(object):
|
|||||||
form_fields.append(("issue", self.name))
|
form_fields.append(("issue", self.name))
|
||||||
vcs = None
|
vcs = None
|
||||||
if self.files:
|
if self.files:
|
||||||
vcs = GuessVCS(upload_options)
|
vcs = MercurialVCS(upload_options, repo.root)
|
||||||
data = vcs.GenerateDiff(self.files)
|
data = vcs.GenerateDiff(self.files)
|
||||||
files = vcs.GetBaseFiles(data)
|
files = vcs.GetBaseFiles(data)
|
||||||
if len(data) > MAX_UPLOAD_SIZE:
|
if len(data) > MAX_UPLOAD_SIZE:
|
||||||
@ -1789,9 +1789,7 @@ def RietveldSetup(ui, repo):
|
|||||||
rpc = None
|
rpc = None
|
||||||
|
|
||||||
#######################################################################
|
#######################################################################
|
||||||
# We keep a full copy of upload.py here to avoid import path hell.
|
# http://codereview.appspot.com/static/upload.py, heavily edited.
|
||||||
# It would be nice if hg added the hg repository root
|
|
||||||
# to the default PYTHONPATH.
|
|
||||||
|
|
||||||
#!/usr/bin/env python
|
#!/usr/bin/env python
|
||||||
#
|
#
|
||||||
@ -1861,25 +1859,13 @@ verbosity = 1
|
|||||||
# Max size of patch or base file.
|
# Max size of patch or base file.
|
||||||
MAX_UPLOAD_SIZE = 900 * 1024
|
MAX_UPLOAD_SIZE = 900 * 1024
|
||||||
|
|
||||||
# Constants for version control names. Used by GuessVCSName.
|
|
||||||
VCS_GIT = "Git"
|
|
||||||
VCS_MERCURIAL = "Mercurial"
|
|
||||||
VCS_SUBVERSION = "Subversion"
|
|
||||||
VCS_UNKNOWN = "Unknown"
|
|
||||||
|
|
||||||
# whitelist for non-binary filetypes which do not start with "text/"
|
# whitelist for non-binary filetypes which do not start with "text/"
|
||||||
# .mm (Objective-C) shows up as application/x-freemind on my Linux box.
|
# .mm (Objective-C) shows up as application/x-freemind on my Linux box.
|
||||||
TEXT_MIMETYPES = ['application/javascript', 'application/x-javascript',
|
TEXT_MIMETYPES = [
|
||||||
'application/x-freemind']
|
'application/javascript',
|
||||||
|
'application/x-javascript',
|
||||||
VCS_ABBREVIATIONS = {
|
'application/x-freemind'
|
||||||
VCS_MERCURIAL.lower(): VCS_MERCURIAL,
|
]
|
||||||
"hg": VCS_MERCURIAL,
|
|
||||||
VCS_SUBVERSION.lower(): VCS_SUBVERSION,
|
|
||||||
"svn": VCS_SUBVERSION,
|
|
||||||
VCS_GIT.lower(): VCS_GIT,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
def GetEmail(prompt):
|
def GetEmail(prompt):
|
||||||
"""Prompts the user for their email address and returns it.
|
"""Prompts the user for their email address and returns it.
|
||||||
@ -1943,8 +1929,7 @@ class ClientLoginError(urllib2.HTTPError):
|
|||||||
class AbstractRpcServer(object):
|
class AbstractRpcServer(object):
|
||||||
"""Provides a common interface for a simple RPC server."""
|
"""Provides a common interface for a simple RPC server."""
|
||||||
|
|
||||||
def __init__(self, host, auth_function, host_override=None, extra_headers={},
|
def __init__(self, host, auth_function, host_override=None, extra_headers={}, save_cookies=False):
|
||||||
save_cookies=False):
|
|
||||||
"""Creates a new HttpRpcServer.
|
"""Creates a new HttpRpcServer.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -2019,15 +2004,13 @@ class AbstractRpcServer(object):
|
|||||||
try:
|
try:
|
||||||
response = self.opener.open(req)
|
response = self.opener.open(req)
|
||||||
response_body = response.read()
|
response_body = response.read()
|
||||||
response_dict = dict(x.split("=")
|
response_dict = dict(x.split("=") for x in response_body.split("\n") if x)
|
||||||
for x in response_body.split("\n") if x)
|
|
||||||
return response_dict["Auth"]
|
return response_dict["Auth"]
|
||||||
except urllib2.HTTPError, e:
|
except urllib2.HTTPError, e:
|
||||||
if e.code == 403:
|
if e.code == 403:
|
||||||
body = e.read()
|
body = e.read()
|
||||||
response_dict = dict(x.split("=", 1) for x in body.split("\n") if x)
|
response_dict = dict(x.split("=", 1) for x in body.split("\n") if x)
|
||||||
raise ClientLoginError(req.get_full_url(), e.code, e.msg,
|
raise ClientLoginError(req.get_full_url(), e.code, e.msg, e.headers, response_dict)
|
||||||
e.headers, response_dict)
|
|
||||||
else:
|
else:
|
||||||
raise
|
raise
|
||||||
|
|
||||||
@ -2043,16 +2026,14 @@ class AbstractRpcServer(object):
|
|||||||
# This is a dummy value to allow us to identify when we're successful.
|
# This is a dummy value to allow us to identify when we're successful.
|
||||||
continue_location = "http://localhost/"
|
continue_location = "http://localhost/"
|
||||||
args = {"continue": continue_location, "auth": auth_token}
|
args = {"continue": continue_location, "auth": auth_token}
|
||||||
req = self._CreateRequest("http://%s/_ah/login?%s" %
|
req = self._CreateRequest("http://%s/_ah/login?%s" % (self.host, urllib.urlencode(args)))
|
||||||
(self.host, urllib.urlencode(args)))
|
|
||||||
try:
|
try:
|
||||||
response = self.opener.open(req)
|
response = self.opener.open(req)
|
||||||
except urllib2.HTTPError, e:
|
except urllib2.HTTPError, e:
|
||||||
response = e
|
response = e
|
||||||
if (response.code != 302 or
|
if (response.code != 302 or
|
||||||
response.info()["location"] != continue_location):
|
response.info()["location"] != continue_location):
|
||||||
raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg,
|
raise urllib2.HTTPError(req.get_full_url(), response.code, response.msg, response.headers, response.fp)
|
||||||
response.headers, response.fp)
|
|
||||||
self.authenticated = True
|
self.authenticated = True
|
||||||
|
|
||||||
def _Authenticate(self):
|
def _Authenticate(self):
|
||||||
@ -2097,8 +2078,7 @@ class AbstractRpcServer(object):
|
|||||||
print >>sys.stderr, "The user account has been disabled."
|
print >>sys.stderr, "The user account has been disabled."
|
||||||
break
|
break
|
||||||
if e.reason == "ServiceDisabled":
|
if e.reason == "ServiceDisabled":
|
||||||
print >>sys.stderr, ("The user's access to the service has been "
|
print >>sys.stderr, "The user's access to the service has been disabled."
|
||||||
"disabled.")
|
|
||||||
break
|
break
|
||||||
if e.reason == "ServiceUnavailable":
|
if e.reason == "ServiceUnavailable":
|
||||||
print >>sys.stderr, "The service is not available; try again later."
|
print >>sys.stderr, "The service is not available; try again later."
|
||||||
@ -2187,8 +2167,7 @@ class HttpRpcServer(AbstractRpcServer):
|
|||||||
try:
|
try:
|
||||||
self.cookie_jar.load()
|
self.cookie_jar.load()
|
||||||
self.authenticated = True
|
self.authenticated = True
|
||||||
StatusUpdate("Loaded authentication cookies from %s" %
|
StatusUpdate("Loaded authentication cookies from %s" % self.cookie_file)
|
||||||
self.cookie_file)
|
|
||||||
except (cookielib.LoadError, IOError):
|
except (cookielib.LoadError, IOError):
|
||||||
# Failed to load cookies - just ignore them.
|
# Failed to load cookies - just ignore them.
|
||||||
pass
|
pass
|
||||||
@ -2205,79 +2184,6 @@ class HttpRpcServer(AbstractRpcServer):
|
|||||||
return opener
|
return opener
|
||||||
|
|
||||||
|
|
||||||
parser = optparse.OptionParser(usage="%prog [options] [-- diff_options]")
|
|
||||||
parser.add_option("-y", "--assume_yes", action="store_true",
|
|
||||||
dest="assume_yes", default=False,
|
|
||||||
help="Assume that the answer to yes/no questions is 'yes'.")
|
|
||||||
# Logging
|
|
||||||
group = parser.add_option_group("Logging options")
|
|
||||||
group.add_option("-q", "--quiet", action="store_const", const=0,
|
|
||||||
dest="verbose", help="Print errors only.")
|
|
||||||
group.add_option("-v", "--verbose", action="store_const", const=2,
|
|
||||||
dest="verbose", default=1,
|
|
||||||
help="Print info level logs (default).")
|
|
||||||
group.add_option("--noisy", action="store_const", const=3,
|
|
||||||
dest="verbose", help="Print all logs.")
|
|
||||||
# Review server
|
|
||||||
group = parser.add_option_group("Review server options")
|
|
||||||
group.add_option("-s", "--server", action="store", dest="server",
|
|
||||||
default="codereview.appspot.com",
|
|
||||||
metavar="SERVER",
|
|
||||||
help=("The server to upload to. The format is host[:port]. "
|
|
||||||
"Defaults to '%default'."))
|
|
||||||
group.add_option("-e", "--email", action="store", dest="email",
|
|
||||||
metavar="EMAIL", default=None,
|
|
||||||
help="The username to use. Will prompt if omitted.")
|
|
||||||
group.add_option("-H", "--host", action="store", dest="host",
|
|
||||||
metavar="HOST", default=None,
|
|
||||||
help="Overrides the Host header sent with all RPCs.")
|
|
||||||
group.add_option("--no_cookies", action="store_false",
|
|
||||||
dest="save_cookies", default=True,
|
|
||||||
help="Do not save authentication cookies to local disk.")
|
|
||||||
# Issue
|
|
||||||
group = parser.add_option_group("Issue options")
|
|
||||||
group.add_option("-d", "--description", action="store", dest="description",
|
|
||||||
metavar="DESCRIPTION", default=None,
|
|
||||||
help="Optional description when creating an issue.")
|
|
||||||
group.add_option("-f", "--description_file", action="store",
|
|
||||||
dest="description_file", metavar="DESCRIPTION_FILE",
|
|
||||||
default=None,
|
|
||||||
help="Optional path of a file that contains "
|
|
||||||
"the description when creating an issue.")
|
|
||||||
group.add_option("-r", "--reviewers", action="store", dest="reviewers",
|
|
||||||
metavar="REVIEWERS", default=None,
|
|
||||||
help="Add reviewers (comma separated email addresses).")
|
|
||||||
group.add_option("--cc", action="store", dest="cc",
|
|
||||||
metavar="CC", default=None,
|
|
||||||
help="Add CC (comma separated email addresses).")
|
|
||||||
group.add_option("--private", action="store_true", dest="private",
|
|
||||||
default=False,
|
|
||||||
help="Make the issue restricted to reviewers and those CCed")
|
|
||||||
# Upload options
|
|
||||||
group = parser.add_option_group("Patch options")
|
|
||||||
group.add_option("-m", "--message", action="store", dest="message",
|
|
||||||
metavar="MESSAGE", default=None,
|
|
||||||
help="A message to identify the patch. "
|
|
||||||
"Will prompt if omitted.")
|
|
||||||
group.add_option("-i", "--issue", type="int", action="store",
|
|
||||||
metavar="ISSUE", default=None,
|
|
||||||
help="Issue number to which to add. Defaults to new issue.")
|
|
||||||
group.add_option("--download_base", action="store_true",
|
|
||||||
dest="download_base", default=False,
|
|
||||||
help="Base files will be downloaded by the server "
|
|
||||||
"(side-by-side diffs may not work on files with CRs).")
|
|
||||||
group.add_option("--rev", action="store", dest="revision",
|
|
||||||
metavar="REV", default=None,
|
|
||||||
help="Branch/tree/revision to diff against (used by DVCS).")
|
|
||||||
group.add_option("--send_mail", action="store_true",
|
|
||||||
dest="send_mail", default=False,
|
|
||||||
help="Send notification email to reviewers.")
|
|
||||||
group.add_option("--vcs", action="store", dest="vcs",
|
|
||||||
metavar="VCS", default=None,
|
|
||||||
help=("Version control system (optional, usually upload.py "
|
|
||||||
"already guesses the right VCS)."))
|
|
||||||
|
|
||||||
|
|
||||||
def GetRpcServer(options):
|
def GetRpcServer(options):
|
||||||
"""Returns an instance of an AbstractRpcServer.
|
"""Returns an instance of an AbstractRpcServer.
|
||||||
|
|
||||||
@ -2306,16 +2212,14 @@ def GetRpcServer(options):
|
|||||||
options.server,
|
options.server,
|
||||||
lambda: (email, "password"),
|
lambda: (email, "password"),
|
||||||
host_override=options.host,
|
host_override=options.host,
|
||||||
extra_headers={"Cookie":
|
extra_headers={"Cookie": 'dev_appserver_login="%s:False"' % email},
|
||||||
'dev_appserver_login="%s:False"' % email},
|
|
||||||
save_cookies=options.save_cookies)
|
save_cookies=options.save_cookies)
|
||||||
# Don't try to talk to ClientLogin.
|
# Don't try to talk to ClientLogin.
|
||||||
server.authenticated = True
|
server.authenticated = True
|
||||||
return server
|
return server
|
||||||
|
|
||||||
return rpc_server_class(options.server, GetUserCredentials,
|
return rpc_server_class(options.server, GetUserCredentials,
|
||||||
host_override=options.host,
|
host_override=options.host, save_cookies=options.save_cookies)
|
||||||
save_cookies=options.save_cookies)
|
|
||||||
|
|
||||||
|
|
||||||
def EncodeMultipartFormData(fields, files):
|
def EncodeMultipartFormData(fields, files):
|
||||||
@ -2347,8 +2251,7 @@ def EncodeMultipartFormData(fields, files):
|
|||||||
if type(value) == unicode:
|
if type(value) == unicode:
|
||||||
value = value.encode("utf-8")
|
value = value.encode("utf-8")
|
||||||
lines.append('--' + BOUNDARY)
|
lines.append('--' + BOUNDARY)
|
||||||
lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' %
|
lines.append('Content-Disposition: form-data; name="%s"; filename="%s"' % (key, filename))
|
||||||
(key, filename))
|
|
||||||
lines.append('Content-Type: %s' % GetContentType(filename))
|
lines.append('Content-Type: %s' % GetContentType(filename))
|
||||||
lines.append('')
|
lines.append('')
|
||||||
lines.append(value)
|
lines.append(value)
|
||||||
@ -2368,8 +2271,7 @@ def GetContentType(filename):
|
|||||||
use_shell = sys.platform.startswith("win")
|
use_shell = sys.platform.startswith("win")
|
||||||
|
|
||||||
def RunShellWithReturnCode(command, print_output=False,
|
def RunShellWithReturnCode(command, print_output=False,
|
||||||
universal_newlines=True,
|
universal_newlines=True, env=os.environ):
|
||||||
env=os.environ):
|
|
||||||
"""Executes a command and returns the output from stdout and the return code.
|
"""Executes a command and returns the output from stdout and the return code.
|
||||||
|
|
||||||
Args:
|
Args:
|
||||||
@ -2383,8 +2285,7 @@ def RunShellWithReturnCode(command, print_output=False,
|
|||||||
"""
|
"""
|
||||||
logging.info("Running %s", command)
|
logging.info("Running %s", command)
|
||||||
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
p = subprocess.Popen(command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
|
||||||
shell=use_shell, universal_newlines=universal_newlines,
|
shell=use_shell, universal_newlines=universal_newlines, env=env)
|
||||||
env=env)
|
|
||||||
if print_output:
|
if print_output:
|
||||||
output_array = []
|
output_array = []
|
||||||
while True:
|
while True:
|
||||||
@ -2407,8 +2308,7 @@ def RunShellWithReturnCode(command, print_output=False,
|
|||||||
|
|
||||||
def RunShell(command, silent_ok=False, universal_newlines=True,
|
def RunShell(command, silent_ok=False, universal_newlines=True,
|
||||||
print_output=False, env=os.environ):
|
print_output=False, env=os.environ):
|
||||||
data, retcode = RunShellWithReturnCode(command, print_output,
|
data, retcode = RunShellWithReturnCode(command, print_output, universal_newlines, env)
|
||||||
universal_newlines, env)
|
|
||||||
if retcode:
|
if retcode:
|
||||||
ErrorExit("Got error status from %s:\n%s" % (command, data))
|
ErrorExit("Got error status from %s:\n%s" % (command, data))
|
||||||
if not silent_ok and not data:
|
if not silent_ok and not data:
|
||||||
@ -2510,7 +2410,8 @@ class VersionControlSystem(object):
|
|||||||
if options.verbose > 0 and not file_too_large:
|
if options.verbose > 0 and not file_too_large:
|
||||||
print "Uploading %s file for %s" % (type, filename)
|
print "Uploading %s file for %s" % (type, filename)
|
||||||
url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id)
|
url = "/%d/upload_content/%d/%d" % (int(issue), int(patchset), file_id)
|
||||||
form_fields = [("filename", filename),
|
form_fields = [
|
||||||
|
("filename", filename),
|
||||||
("status", status),
|
("status", status),
|
||||||
("checksum", checksum),
|
("checksum", checksum),
|
||||||
("is_binary", str(is_binary)),
|
("is_binary", str(is_binary)),
|
||||||
@ -2520,10 +2421,8 @@ class VersionControlSystem(object):
|
|||||||
form_fields.append(("file_too_large", "1"))
|
form_fields.append(("file_too_large", "1"))
|
||||||
if options.email:
|
if options.email:
|
||||||
form_fields.append(("user", options.email))
|
form_fields.append(("user", options.email))
|
||||||
ctype, body = EncodeMultipartFormData(form_fields,
|
ctype, body = EncodeMultipartFormData(form_fields, [("data", filename, content)])
|
||||||
[("data", filename, content)])
|
response_body = rpc_server.Send(url, body, content_type=ctype)
|
||||||
response_body = rpc_server.Send(url, body,
|
|
||||||
content_type=ctype)
|
|
||||||
if not response_body.startswith("OK"):
|
if not response_body.startswith("OK"):
|
||||||
StatusUpdate(" --> %s" % response_body)
|
StatusUpdate(" --> %s" % response_body)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
@ -2560,379 +2459,6 @@ class VersionControlSystem(object):
|
|||||||
return not mimetype.startswith("text/")
|
return not mimetype.startswith("text/")
|
||||||
|
|
||||||
|
|
||||||
class SubversionVCS(VersionControlSystem):
|
|
||||||
"""Implementation of the VersionControlSystem interface for Subversion."""
|
|
||||||
|
|
||||||
def __init__(self, options):
|
|
||||||
super(SubversionVCS, self).__init__(options)
|
|
||||||
if self.options.revision:
|
|
||||||
match = re.match(r"(\d+)(:(\d+))?", self.options.revision)
|
|
||||||
if not match:
|
|
||||||
ErrorExit("Invalid Subversion revision %s." % self.options.revision)
|
|
||||||
self.rev_start = match.group(1)
|
|
||||||
self.rev_end = match.group(3)
|
|
||||||
else:
|
|
||||||
self.rev_start = self.rev_end = None
|
|
||||||
# Cache output from "svn list -r REVNO dirname".
|
|
||||||
# Keys: dirname, Values: 2-tuple (ouput for start rev and end rev).
|
|
||||||
self.svnls_cache = {}
|
|
||||||
# SVN base URL is required to fetch files deleted in an older revision.
|
|
||||||
# Result is cached to not guess it over and over again in GetBaseFile().
|
|
||||||
required = self.options.download_base or self.options.revision is not None
|
|
||||||
self.svn_base = self._GuessBase(required)
|
|
||||||
|
|
||||||
def GuessBase(self, required):
|
|
||||||
"""Wrapper for _GuessBase."""
|
|
||||||
return self.svn_base
|
|
||||||
|
|
||||||
def _GuessBase(self, required):
|
|
||||||
"""Returns the SVN base URL.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
required: If true, exits if the url can't be guessed, otherwise None is
|
|
||||||
returned.
|
|
||||||
"""
|
|
||||||
info = RunShell(["svn", "info"])
|
|
||||||
for line in info.splitlines():
|
|
||||||
words = line.split()
|
|
||||||
if len(words) == 2 and words[0] == "URL:":
|
|
||||||
url = words[1]
|
|
||||||
scheme, netloc, path, params, query, fragment = urlparse.urlparse(url)
|
|
||||||
username, netloc = urllib.splituser(netloc)
|
|
||||||
if username:
|
|
||||||
logging.info("Removed username from base URL")
|
|
||||||
if netloc.endswith("svn.python.org"):
|
|
||||||
if netloc == "svn.python.org":
|
|
||||||
if path.startswith("/projects/"):
|
|
||||||
path = path[9:]
|
|
||||||
elif netloc != "pythondev@svn.python.org":
|
|
||||||
ErrorExit("Unrecognized Python URL: %s" % url)
|
|
||||||
base = "http://svn.python.org/view/*checkout*%s/" % path
|
|
||||||
logging.info("Guessed Python base = %s", base)
|
|
||||||
elif netloc.endswith("svn.collab.net"):
|
|
||||||
if path.startswith("/repos/"):
|
|
||||||
path = path[6:]
|
|
||||||
base = "http://svn.collab.net/viewvc/*checkout*%s/" % path
|
|
||||||
logging.info("Guessed CollabNet base = %s", base)
|
|
||||||
elif netloc.endswith(".googlecode.com"):
|
|
||||||
path = path + "/"
|
|
||||||
base = urlparse.urlunparse(("http", netloc, path, params,
|
|
||||||
query, fragment))
|
|
||||||
logging.info("Guessed Google Code base = %s", base)
|
|
||||||
else:
|
|
||||||
path = path + "/"
|
|
||||||
base = urlparse.urlunparse((scheme, netloc, path, params,
|
|
||||||
query, fragment))
|
|
||||||
logging.info("Guessed base = %s", base)
|
|
||||||
return base
|
|
||||||
if required:
|
|
||||||
ErrorExit("Can't find URL in output from svn info")
|
|
||||||
return None
|
|
||||||
|
|
||||||
def GenerateDiff(self, args):
|
|
||||||
cmd = ["svn", "diff"]
|
|
||||||
if self.options.revision:
|
|
||||||
cmd += ["-r", self.options.revision]
|
|
||||||
cmd.extend(args)
|
|
||||||
data = RunShell(cmd)
|
|
||||||
count = 0
|
|
||||||
for line in data.splitlines():
|
|
||||||
if line.startswith("Index:") or line.startswith("Property changes on:"):
|
|
||||||
count += 1
|
|
||||||
logging.info(line)
|
|
||||||
if not count:
|
|
||||||
ErrorExit("No valid patches found in output from svn diff")
|
|
||||||
return data
|
|
||||||
|
|
||||||
def _CollapseKeywords(self, content, keyword_str):
|
|
||||||
"""Collapses SVN keywords."""
|
|
||||||
# svn cat translates keywords but svn diff doesn't. As a result of this
|
|
||||||
# behavior patching.PatchChunks() fails with a chunk mismatch error.
|
|
||||||
# This part was originally written by the Review Board development team
|
|
||||||
# who had the same problem (http://reviews.review-board.org/r/276/).
|
|
||||||
# Mapping of keywords to known aliases
|
|
||||||
svn_keywords = {
|
|
||||||
# Standard keywords
|
|
||||||
'Date': ['Date', 'LastChangedDate'],
|
|
||||||
'Revision': ['Revision', 'LastChangedRevision', 'Rev'],
|
|
||||||
'Author': ['Author', 'LastChangedBy'],
|
|
||||||
'HeadURL': ['HeadURL', 'URL'],
|
|
||||||
'Id': ['Id'],
|
|
||||||
|
|
||||||
# Aliases
|
|
||||||
'LastChangedDate': ['LastChangedDate', 'Date'],
|
|
||||||
'LastChangedRevision': ['LastChangedRevision', 'Rev', 'Revision'],
|
|
||||||
'LastChangedBy': ['LastChangedBy', 'Author'],
|
|
||||||
'URL': ['URL', 'HeadURL'],
|
|
||||||
}
|
|
||||||
|
|
||||||
def repl(m):
|
|
||||||
if m.group(2):
|
|
||||||
return "$%s::%s$" % (m.group(1), " " * len(m.group(3)))
|
|
||||||
return "$%s$" % m.group(1)
|
|
||||||
keywords = [keyword
|
|
||||||
for name in keyword_str.split(" ")
|
|
||||||
for keyword in svn_keywords.get(name, [])]
|
|
||||||
return re.sub(r"\$(%s):(:?)([^\$]+)\$" % '|'.join(keywords), repl, content)
|
|
||||||
|
|
||||||
def GetUnknownFiles(self):
|
|
||||||
status = RunShell(["svn", "status", "--ignore-externals"], silent_ok=True)
|
|
||||||
unknown_files = []
|
|
||||||
for line in status.split("\n"):
|
|
||||||
if line and line[0] == "?":
|
|
||||||
unknown_files.append(line)
|
|
||||||
return unknown_files
|
|
||||||
|
|
||||||
def ReadFile(self, filename):
|
|
||||||
"""Returns the contents of a file."""
|
|
||||||
file = open(filename, 'rb')
|
|
||||||
result = ""
|
|
||||||
try:
|
|
||||||
result = file.read()
|
|
||||||
finally:
|
|
||||||
file.close()
|
|
||||||
return result
|
|
||||||
|
|
||||||
def GetStatus(self, filename):
|
|
||||||
"""Returns the status of a file."""
|
|
||||||
if not self.options.revision:
|
|
||||||
status = RunShell(["svn", "status", "--ignore-externals", filename])
|
|
||||||
if not status:
|
|
||||||
ErrorExit("svn status returned no output for %s" % filename)
|
|
||||||
status_lines = status.splitlines()
|
|
||||||
# If file is in a cl, the output will begin with
|
|
||||||
# "\n--- Changelist 'cl_name':\n". See
|
|
||||||
# http://svn.collab.net/repos/svn/trunk/notes/changelist-design.txt
|
|
||||||
if (len(status_lines) == 3 and
|
|
||||||
not status_lines[0] and
|
|
||||||
status_lines[1].startswith("--- Changelist")):
|
|
||||||
status = status_lines[2]
|
|
||||||
else:
|
|
||||||
status = status_lines[0]
|
|
||||||
# If we have a revision to diff against we need to run "svn list"
|
|
||||||
# for the old and the new revision and compare the results to get
|
|
||||||
# the correct status for a file.
|
|
||||||
else:
|
|
||||||
dirname, relfilename = os.path.split(filename)
|
|
||||||
if dirname not in self.svnls_cache:
|
|
||||||
cmd = ["svn", "list", "-r", self.rev_start, dirname or "."]
|
|
||||||
out, returncode = RunShellWithReturnCode(cmd)
|
|
||||||
if returncode:
|
|
||||||
ErrorExit("Failed to get status for %s." % filename)
|
|
||||||
old_files = out.splitlines()
|
|
||||||
args = ["svn", "list"]
|
|
||||||
if self.rev_end:
|
|
||||||
args += ["-r", self.rev_end]
|
|
||||||
cmd = args + [dirname or "."]
|
|
||||||
out, returncode = RunShellWithReturnCode(cmd)
|
|
||||||
if returncode:
|
|
||||||
ErrorExit("Failed to run command %s" % cmd)
|
|
||||||
self.svnls_cache[dirname] = (old_files, out.splitlines())
|
|
||||||
old_files, new_files = self.svnls_cache[dirname]
|
|
||||||
if relfilename in old_files and relfilename not in new_files:
|
|
||||||
status = "D "
|
|
||||||
elif relfilename in old_files and relfilename in new_files:
|
|
||||||
status = "M "
|
|
||||||
else:
|
|
||||||
status = "A "
|
|
||||||
return status
|
|
||||||
|
|
||||||
def GetBaseFile(self, filename):
|
|
||||||
status = self.GetStatus(filename)
|
|
||||||
base_content = None
|
|
||||||
new_content = None
|
|
||||||
|
|
||||||
# If a file is copied its status will be "A +", which signifies
|
|
||||||
# "addition-with-history". See "svn st" for more information. We need to
|
|
||||||
# upload the original file or else diff parsing will fail if the file was
|
|
||||||
# edited.
|
|
||||||
if status[0] == "A" and status[3] != "+":
|
|
||||||
# We'll need to upload the new content if we're adding a binary file
|
|
||||||
# since diff's output won't contain it.
|
|
||||||
mimetype = RunShell(["svn", "propget", "svn:mime-type", filename],
|
|
||||||
silent_ok=True)
|
|
||||||
base_content = ""
|
|
||||||
is_binary = bool(mimetype) and not mimetype.startswith("text/")
|
|
||||||
if is_binary and self.IsImage(filename):
|
|
||||||
new_content = self.ReadFile(filename)
|
|
||||||
elif (status[0] in ("M", "D", "R") or
|
|
||||||
(status[0] == "A" and status[3] == "+") or # Copied file.
|
|
||||||
(status[0] == " " and status[1] == "M")): # Property change.
|
|
||||||
args = []
|
|
||||||
if self.options.revision:
|
|
||||||
url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
|
|
||||||
else:
|
|
||||||
# Don't change filename, it's needed later.
|
|
||||||
url = filename
|
|
||||||
args += ["-r", "BASE"]
|
|
||||||
cmd = ["svn"] + args + ["propget", "svn:mime-type", url]
|
|
||||||
mimetype, returncode = RunShellWithReturnCode(cmd)
|
|
||||||
if returncode:
|
|
||||||
# File does not exist in the requested revision.
|
|
||||||
# Reset mimetype, it contains an error message.
|
|
||||||
mimetype = ""
|
|
||||||
get_base = False
|
|
||||||
is_binary = bool(mimetype) and not mimetype.startswith("text/")
|
|
||||||
if status[0] == " ":
|
|
||||||
# Empty base content just to force an upload.
|
|
||||||
base_content = ""
|
|
||||||
elif is_binary:
|
|
||||||
if self.IsImage(filename):
|
|
||||||
get_base = True
|
|
||||||
if status[0] == "M":
|
|
||||||
if not self.rev_end:
|
|
||||||
new_content = self.ReadFile(filename)
|
|
||||||
else:
|
|
||||||
url = "%s/%s@%s" % (self.svn_base, filename, self.rev_end)
|
|
||||||
new_content = RunShell(["svn", "cat", url],
|
|
||||||
universal_newlines=True, silent_ok=True)
|
|
||||||
else:
|
|
||||||
base_content = ""
|
|
||||||
else:
|
|
||||||
get_base = True
|
|
||||||
|
|
||||||
if get_base:
|
|
||||||
if is_binary:
|
|
||||||
universal_newlines = False
|
|
||||||
else:
|
|
||||||
universal_newlines = True
|
|
||||||
if self.rev_start:
|
|
||||||
# "svn cat -r REV delete_file.txt" doesn't work. cat requires
|
|
||||||
# the full URL with "@REV" appended instead of using "-r" option.
|
|
||||||
url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
|
|
||||||
base_content = RunShell(["svn", "cat", url],
|
|
||||||
universal_newlines=universal_newlines,
|
|
||||||
silent_ok=True)
|
|
||||||
else:
|
|
||||||
base_content = RunShell(["svn", "cat", filename],
|
|
||||||
universal_newlines=universal_newlines,
|
|
||||||
silent_ok=True)
|
|
||||||
if not is_binary:
|
|
||||||
args = []
|
|
||||||
if self.rev_start:
|
|
||||||
url = "%s/%s@%s" % (self.svn_base, filename, self.rev_start)
|
|
||||||
else:
|
|
||||||
url = filename
|
|
||||||
args += ["-r", "BASE"]
|
|
||||||
cmd = ["svn"] + args + ["propget", "svn:keywords", url]
|
|
||||||
keywords, returncode = RunShellWithReturnCode(cmd)
|
|
||||||
if keywords and not returncode:
|
|
||||||
base_content = self._CollapseKeywords(base_content, keywords)
|
|
||||||
else:
|
|
||||||
StatusUpdate("svn status returned unexpected output: %s" % status)
|
|
||||||
sys.exit(1)
|
|
||||||
return base_content, new_content, is_binary, status[0:5]
|
|
||||||
|
|
||||||
|
|
||||||
class GitVCS(VersionControlSystem):
|
|
||||||
"""Implementation of the VersionControlSystem interface for Git."""
|
|
||||||
|
|
||||||
def __init__(self, options):
|
|
||||||
super(GitVCS, self).__init__(options)
|
|
||||||
# Map of filename -> (hash before, hash after) of base file.
|
|
||||||
# Hashes for "no such file" are represented as None.
|
|
||||||
self.hashes = {}
|
|
||||||
# Map of new filename -> old filename for renames.
|
|
||||||
self.renames = {}
|
|
||||||
|
|
||||||
def GenerateDiff(self, extra_args):
|
|
||||||
# This is more complicated than svn's GenerateDiff because we must convert
|
|
||||||
# the diff output to include an svn-style "Index:" line as well as record
|
|
||||||
# the hashes of the files, so we can upload them along with our diff.
|
|
||||||
|
|
||||||
# Special used by git to indicate "no such content".
|
|
||||||
NULL_HASH = "0"*40
|
|
||||||
|
|
||||||
extra_args = extra_args[:]
|
|
||||||
if self.options.revision:
|
|
||||||
extra_args = [self.options.revision] + extra_args
|
|
||||||
extra_args.append('-M')
|
|
||||||
|
|
||||||
# --no-ext-diff is broken in some versions of Git, so try to work around
|
|
||||||
# this by overriding the environment (but there is still a problem if the
|
|
||||||
# git config key "diff.external" is used).
|
|
||||||
env = os.environ.copy()
|
|
||||||
if 'GIT_EXTERNAL_DIFF' in env: del env['GIT_EXTERNAL_DIFF']
|
|
||||||
gitdiff = RunShell(["git", "diff", "--no-ext-diff", "--full-index"]
|
|
||||||
+ extra_args, env=env)
|
|
||||||
svndiff = []
|
|
||||||
filecount = 0
|
|
||||||
filename = None
|
|
||||||
for line in gitdiff.splitlines():
|
|
||||||
match = re.match(r"diff --git a/(.*) b/(.*)$", line)
|
|
||||||
if match:
|
|
||||||
filecount += 1
|
|
||||||
# Intentionally use the "after" filename so we can show renames.
|
|
||||||
filename = match.group(2)
|
|
||||||
svndiff.append("Index: %s\n" % filename)
|
|
||||||
if match.group(1) != match.group(2):
|
|
||||||
self.renames[match.group(2)] = match.group(1)
|
|
||||||
else:
|
|
||||||
# The "index" line in a git diff looks like this (long hashes elided):
|
|
||||||
# index 82c0d44..b2cee3f 100755
|
|
||||||
# We want to save the left hash, as that identifies the base file.
|
|
||||||
match = re.match(r"index (\w+)\.\.(\w+)", line)
|
|
||||||
if match:
|
|
||||||
before, after = (match.group(1), match.group(2))
|
|
||||||
if before == NULL_HASH:
|
|
||||||
before = None
|
|
||||||
if after == NULL_HASH:
|
|
||||||
after = None
|
|
||||||
self.hashes[filename] = (before, after)
|
|
||||||
svndiff.append(line + "\n")
|
|
||||||
if not filecount:
|
|
||||||
ErrorExit("No valid patches found in output from git diff")
|
|
||||||
return "".join(svndiff)
|
|
||||||
|
|
||||||
def GetUnknownFiles(self):
|
|
||||||
status = RunShell(["git", "ls-files", "--exclude-standard", "--others"],
|
|
||||||
silent_ok=True)
|
|
||||||
return status.splitlines()
|
|
||||||
|
|
||||||
def GetFileContent(self, file_hash, is_binary):
|
|
||||||
"""Returns the content of a file identified by its git hash."""
|
|
||||||
data, retcode = RunShellWithReturnCode(["git", "show", file_hash],
|
|
||||||
universal_newlines=not is_binary)
|
|
||||||
if retcode:
|
|
||||||
ErrorExit("Got error status from 'git show %s'" % file_hash)
|
|
||||||
return data
|
|
||||||
|
|
||||||
def GetBaseFile(self, filename):
|
|
||||||
hash_before, hash_after = self.hashes.get(filename, (None,None))
|
|
||||||
base_content = None
|
|
||||||
new_content = None
|
|
||||||
is_binary = self.IsBinary(filename)
|
|
||||||
status = None
|
|
||||||
|
|
||||||
if filename in self.renames:
|
|
||||||
status = "A +" # Match svn attribute name for renames.
|
|
||||||
if filename not in self.hashes:
|
|
||||||
# If a rename doesn't change the content, we never get a hash.
|
|
||||||
base_content = RunShell(["git", "show", filename])
|
|
||||||
elif not hash_before:
|
|
||||||
status = "A"
|
|
||||||
base_content = ""
|
|
||||||
elif not hash_after:
|
|
||||||
status = "D"
|
|
||||||
else:
|
|
||||||
status = "M"
|
|
||||||
|
|
||||||
is_image = self.IsImage(filename)
|
|
||||||
|
|
||||||
# Grab the before/after content if we need it.
|
|
||||||
# We should include file contents if it's text or it's an image.
|
|
||||||
if not is_binary or is_image:
|
|
||||||
# Grab the base content if we don't have it already.
|
|
||||||
if base_content is None and hash_before:
|
|
||||||
base_content = self.GetFileContent(hash_before, is_binary)
|
|
||||||
# Only include the "after" file if it's an image; otherwise it
|
|
||||||
# it is reconstructed from the diff.
|
|
||||||
if is_image and hash_after:
|
|
||||||
new_content = self.GetFileContent(hash_after, is_binary)
|
|
||||||
|
|
||||||
return (base_content, new_content, is_binary, status)
|
|
||||||
|
|
||||||
|
|
||||||
class MercurialVCS(VersionControlSystem):
|
class MercurialVCS(VersionControlSystem):
|
||||||
"""Implementation of the VersionControlSystem interface for Mercurial."""
|
"""Implementation of the VersionControlSystem interface for Mercurial."""
|
||||||
|
|
||||||
@ -3104,223 +2630,3 @@ def UploadSeparatePatches(issue, rpc_server, patchset, data, options):
|
|||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
rv.append([lines[1], patch[0]])
|
rv.append([lines[1], patch[0]])
|
||||||
return rv
|
return rv
|
||||||
|
|
||||||
|
|
||||||
def GuessVCSName():
|
|
||||||
"""Helper to guess the version control system.
|
|
||||||
|
|
||||||
This examines the current directory, guesses which VersionControlSystem
|
|
||||||
we're using, and returns an string indicating which VCS is detected.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A pair (vcs, output). vcs is a string indicating which VCS was detected
|
|
||||||
and is one of VCS_GIT, VCS_MERCURIAL, VCS_SUBVERSION, or VCS_UNKNOWN.
|
|
||||||
output is a string containing any interesting output from the vcs
|
|
||||||
detection routine, or None if there is nothing interesting.
|
|
||||||
"""
|
|
||||||
# Mercurial has a command to get the base directory of a repository
|
|
||||||
# Try running it, but don't die if we don't have hg installed.
|
|
||||||
# NOTE: we try Mercurial first as it can sit on top of an SVN working copy.
|
|
||||||
try:
|
|
||||||
out, returncode = RunShellWithReturnCode(["hg", "root"])
|
|
||||||
if returncode == 0:
|
|
||||||
return (VCS_MERCURIAL, out.strip())
|
|
||||||
except OSError, (errno, message):
|
|
||||||
if errno != 2: # ENOENT -- they don't have hg installed.
|
|
||||||
raise
|
|
||||||
|
|
||||||
# Subversion has a .svn in all working directories.
|
|
||||||
if os.path.isdir('.svn'):
|
|
||||||
logging.info("Guessed VCS = Subversion")
|
|
||||||
return (VCS_SUBVERSION, None)
|
|
||||||
|
|
||||||
# Git has a command to test if you're in a git tree.
|
|
||||||
# Try running it, but don't die if we don't have git installed.
|
|
||||||
try:
|
|
||||||
out, returncode = RunShellWithReturnCode(["git", "rev-parse",
|
|
||||||
"--is-inside-work-tree"])
|
|
||||||
if returncode == 0:
|
|
||||||
return (VCS_GIT, None)
|
|
||||||
except OSError, (errno, message):
|
|
||||||
if errno != 2: # ENOENT -- they don't have git installed.
|
|
||||||
raise
|
|
||||||
|
|
||||||
return (VCS_UNKNOWN, None)
|
|
||||||
|
|
||||||
|
|
||||||
def GuessVCS(options):
|
|
||||||
"""Helper to guess the version control system.
|
|
||||||
|
|
||||||
This verifies any user-specified VersionControlSystem (by command line
|
|
||||||
or environment variable). If the user didn't specify one, this examines
|
|
||||||
the current directory, guesses which VersionControlSystem we're using,
|
|
||||||
and returns an instance of the appropriate class. Exit with an error
|
|
||||||
if we can't figure it out.
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A VersionControlSystem instance. Exits if the VCS can't be guessed.
|
|
||||||
"""
|
|
||||||
vcs = options.vcs
|
|
||||||
if not vcs:
|
|
||||||
vcs = os.environ.get("CODEREVIEW_VCS")
|
|
||||||
if vcs:
|
|
||||||
v = VCS_ABBREVIATIONS.get(vcs.lower())
|
|
||||||
if v is None:
|
|
||||||
ErrorExit("Unknown version control system %r specified." % vcs)
|
|
||||||
(vcs, extra_output) = (v, None)
|
|
||||||
else:
|
|
||||||
(vcs, extra_output) = GuessVCSName()
|
|
||||||
|
|
||||||
if vcs == VCS_MERCURIAL:
|
|
||||||
if extra_output is None:
|
|
||||||
extra_output = RunShell(["hg", "root"]).strip()
|
|
||||||
return MercurialVCS(options, extra_output)
|
|
||||||
elif vcs == VCS_SUBVERSION:
|
|
||||||
return SubversionVCS(options)
|
|
||||||
elif vcs == VCS_GIT:
|
|
||||||
return GitVCS(options)
|
|
||||||
|
|
||||||
ErrorExit(("Could not guess version control system. "
|
|
||||||
"Are you in a working copy directory?"))
|
|
||||||
|
|
||||||
|
|
||||||
def RealMain(argv, data=None):
|
|
||||||
"""The real main function.
|
|
||||||
|
|
||||||
Args:
|
|
||||||
argv: Command line arguments.
|
|
||||||
data: Diff contents. If None (default) the diff is generated by
|
|
||||||
the VersionControlSystem implementation returned by GuessVCS().
|
|
||||||
|
|
||||||
Returns:
|
|
||||||
A 2-tuple (issue id, patchset id).
|
|
||||||
The patchset id is None if the base files are not uploaded by this
|
|
||||||
script (applies only to SVN checkouts).
|
|
||||||
"""
|
|
||||||
logging.basicConfig(format=("%(asctime).19s %(levelname)s %(filename)s:"
|
|
||||||
"%(lineno)s %(message)s "))
|
|
||||||
os.environ['LC_ALL'] = 'C'
|
|
||||||
options, args = parser.parse_args(argv[1:])
|
|
||||||
global verbosity
|
|
||||||
verbosity = options.verbose
|
|
||||||
if verbosity >= 3:
|
|
||||||
logging.getLogger().setLevel(logging.DEBUG)
|
|
||||||
elif verbosity >= 2:
|
|
||||||
logging.getLogger().setLevel(logging.INFO)
|
|
||||||
vcs = GuessVCS(options)
|
|
||||||
if isinstance(vcs, SubversionVCS):
|
|
||||||
# base field is only allowed for Subversion.
|
|
||||||
# Note: Fetching base files may become deprecated in future releases.
|
|
||||||
base = vcs.GuessBase(options.download_base)
|
|
||||||
else:
|
|
||||||
base = None
|
|
||||||
if not base and options.download_base:
|
|
||||||
options.download_base = True
|
|
||||||
logging.info("Enabled upload of base file")
|
|
||||||
if not options.assume_yes:
|
|
||||||
vcs.CheckForUnknownFiles()
|
|
||||||
if data is None:
|
|
||||||
data = vcs.GenerateDiff(args)
|
|
||||||
files = vcs.GetBaseFiles(data)
|
|
||||||
if verbosity >= 1:
|
|
||||||
print "Upload server:", options.server, "(change with -s/--server)"
|
|
||||||
if options.issue:
|
|
||||||
prompt = "Message describing this patch set: "
|
|
||||||
else:
|
|
||||||
prompt = "New issue subject: "
|
|
||||||
message = options.message or raw_input(prompt).strip()
|
|
||||||
if not message:
|
|
||||||
ErrorExit("A non-empty message is required")
|
|
||||||
rpc_server = GetRpcServer(options)
|
|
||||||
form_fields = [("subject", message)]
|
|
||||||
if base:
|
|
||||||
form_fields.append(("base", base))
|
|
||||||
if options.issue:
|
|
||||||
form_fields.append(("issue", str(options.issue)))
|
|
||||||
if options.email:
|
|
||||||
form_fields.append(("user", options.email))
|
|
||||||
if options.reviewers:
|
|
||||||
for reviewer in options.reviewers.split(','):
|
|
||||||
if "@" in reviewer and not reviewer.split("@")[1].count(".") == 1:
|
|
||||||
ErrorExit("Invalid email address: %s" % reviewer)
|
|
||||||
form_fields.append(("reviewers", options.reviewers))
|
|
||||||
if options.cc:
|
|
||||||
for cc in options.cc.split(','):
|
|
||||||
if "@" in cc and not cc.split("@")[1].count(".") == 1:
|
|
||||||
ErrorExit("Invalid email address: %s" % cc)
|
|
||||||
form_fields.append(("cc", options.cc))
|
|
||||||
description = options.description
|
|
||||||
if options.description_file:
|
|
||||||
if options.description:
|
|
||||||
ErrorExit("Can't specify description and description_file")
|
|
||||||
file = open(options.description_file, 'r')
|
|
||||||
description = file.read()
|
|
||||||
file.close()
|
|
||||||
if description:
|
|
||||||
form_fields.append(("description", description))
|
|
||||||
# Send a hash of all the base file so the server can determine if a copy
|
|
||||||
# already exists in an earlier patchset.
|
|
||||||
base_hashes = ""
|
|
||||||
for file, info in files.iteritems():
|
|
||||||
if not info[0] is None:
|
|
||||||
checksum = md5(info[0]).hexdigest()
|
|
||||||
if base_hashes:
|
|
||||||
base_hashes += "|"
|
|
||||||
base_hashes += checksum + ":" + file
|
|
||||||
form_fields.append(("base_hashes", base_hashes))
|
|
||||||
if options.private:
|
|
||||||
if options.issue:
|
|
||||||
print "Warning: Private flag ignored when updating an existing issue."
|
|
||||||
else:
|
|
||||||
form_fields.append(("private", "1"))
|
|
||||||
# If we're uploading base files, don't send the email before the uploads, so
|
|
||||||
# that it contains the file status.
|
|
||||||
if options.send_mail and options.download_base:
|
|
||||||
form_fields.append(("send_mail", "1"))
|
|
||||||
if not options.download_base:
|
|
||||||
form_fields.append(("content_upload", "1"))
|
|
||||||
if len(data) > MAX_UPLOAD_SIZE:
|
|
||||||
print "Patch is large, so uploading file patches separately."
|
|
||||||
uploaded_diff_file = []
|
|
||||||
form_fields.append(("separate_patches", "1"))
|
|
||||||
else:
|
|
||||||
uploaded_diff_file = [("data", "data.diff", data)]
|
|
||||||
ctype, body = EncodeMultipartFormData(form_fields, uploaded_diff_file)
|
|
||||||
response_body = rpc_server.Send("/upload", body, content_type=ctype)
|
|
||||||
patchset = None
|
|
||||||
if not options.download_base or not uploaded_diff_file:
|
|
||||||
lines = response_body.splitlines()
|
|
||||||
if len(lines) >= 2:
|
|
||||||
msg = lines[0]
|
|
||||||
patchset = lines[1].strip()
|
|
||||||
patches = [x.split(" ", 1) for x in lines[2:]]
|
|
||||||
else:
|
|
||||||
msg = response_body
|
|
||||||
else:
|
|
||||||
msg = response_body
|
|
||||||
if not response_body.startswith("Issue created.") and \
|
|
||||||
not response_body.startswith("Issue updated."):
|
|
||||||
print >>sys.stderr, msg
|
|
||||||
sys.exit(0)
|
|
||||||
issue = msg[msg.rfind("/")+1:]
|
|
||||||
|
|
||||||
if not uploaded_diff_file:
|
|
||||||
result = UploadSeparatePatches(issue, rpc_server, patchset, data, options)
|
|
||||||
if not options.download_base:
|
|
||||||
patches = result
|
|
||||||
|
|
||||||
if not options.download_base:
|
|
||||||
vcs.UploadBaseFiles(issue, rpc_server, patches, patchset, options, files)
|
|
||||||
if options.send_mail:
|
|
||||||
rpc_server.Send("/" + issue + "/mail", payload="")
|
|
||||||
return issue, patchset
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
try:
|
|
||||||
RealMain(sys.argv)
|
|
||||||
except KeyboardInterrupt:
|
|
||||||
print
|
|
||||||
StatusUpdate("Interrupted.")
|
|
||||||
sys.exit(1)
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user