summaryrefslogtreecommitdiffstats
path: root/webapp/git_upload.py
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/git_upload.py')
-rwxr-xr-xwebapp/git_upload.py253
1 files changed, 253 insertions, 0 deletions
diff --git a/webapp/git_upload.py b/webapp/git_upload.py
new file mode 100755
index 0000000000..a6fa76879d
--- /dev/null
+++ b/webapp/git_upload.py
@@ -0,0 +1,253 @@
+#!/usr/bin/env python2.5
+#
+# Copyright 2007 Google Inc.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import getpass
+import logging
+import optparse
+import os
+import subprocess
+import sys
+from tempfile import mkstemp
+
+from codereview.proto_client import HttpRpc, Proxy
+from codereview.review_pb2 import ReviewService_Stub
+from codereview.upload_bundle_pb2 import *
+
+try:
+ import readline
+except ImportError:
+ pass
+
+MAX_SEGMENT_SIZE = 1020 * 1024
+
+# The logging verbosity:
+# 0: Errors only.
+# 1: Status messages.
+# 2: Info logs.
+# 3: Debug logs.
+verbosity = 1
+
+def StatusUpdate(msg):
+ """Print a status message to stdout.
+
+ If 'verbosity' is greater than 0, print the message.
+
+ Args:
+ msg: The string to print.
+ """
+ if verbosity > 0:
+ print msg
+
+
+def ErrorExit(msg):
+ """Print an error message to stderr and exit."""
+ print >>sys.stderr, msg
+ sys.exit(1)
+
+
+def RunShell(command, args=(), silent_ok=False):
+ command = "%s %s" % (command, " ".join(args))
+ logging.info("Running %s", command)
+ stream = os.popen(command, "r")
+ data = stream.read()
+ if stream.close():
+ ErrorExit("Got error status from %s" % command)
+ if not silent_ok and not data:
+ ErrorExit("No output from %s" % command)
+ return data
+
+
+def RunGit(*args):
+ argv = ["git"]
+ argv += args
+ retcode = subprocess.call(argv)
+ if retcode != 0:
+ raise OSError, retcode
+
+def GitVal(*args):
+ data = RunShell("git", args)
+ if data.rfind("\n") == len(data) - 1:
+ return data[0 : len(data) - 1]
+ return data
+
+
+parser = optparse.OptionParser(usage="%prog [options] [-- diff_options]")
+
+# 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 'codereview.appspot.com'."))
+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.")
+
+# Git
+group = parser.add_option_group("Git options")
+group.add_option("-p", "--project", action="store", dest="dest_project",
+ metavar="PROJECT",
+ help=("Name of the Git repository to submit into."))
+group.add_option("-b", "--branch", action="store", dest="dest_branch",
+ metavar="BRANCH",
+ help=("Name of the branch the changes are proposed for."))
+group.add_option("-B", "--base", action="store", dest="base_commit",
+ default="refs/remotes/origin/master",
+ metavar="COMMIT",
+ help=("Base commit for the bundle."))
+
+def GetRpcServer(options):
+ """Returns an RpcServer.
+
+ Returns:
+ A new RpcServer, on which RPC calls can be made.
+ """
+
+ def GetUserCredentials():
+ """Prompts the user for a username and password."""
+ email = options.email
+ if email is None:
+ email = raw_input("Email: ").strip()
+ password = getpass.getpass("Password for %s: " % email)
+ return (email, password)
+
+ # If this is the dev_appserver, use fake authentication.
+ host = (options.host or options.server).lower()
+ if host == "localhost" or host.startswith("localhost:"):
+ email = options.email
+ if email is None:
+ email = "test@example.com"
+ logging.info("Using debug user %s. Override with --email" % email)
+
+ server = HttpRpc(
+ options.server,
+ lambda: (email, "password"),
+ host_override=options.host,
+ extra_headers={"Cookie":
+ 'dev_appserver_login="%s:False"' % email})
+ # Don't try to talk to ClientLogin.
+ server.authenticated = True
+ return server
+
+ if options.save_cookies:
+ cookie_file = ".gerrit_cookies"
+ else:
+ cookie_file = None
+
+ return HttpRpc(options.server, GetUserCredentials,
+ host_override=options.host,
+ cookie_file=cookie_file)
+
+
+def RealMain(argv, data=None):
+ 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)
+
+ srv = GetRpcServer(options)
+ review = Proxy(ReviewService_Stub(srv))
+
+ git_dir = GitVal("rev-parse","--git-dir")
+
+ revlist = GitVal("rev-list",
+ "^" + options.base_commit,
+ "HEAD").split("\n")
+
+ tmp_fd, tmp_bundle = mkstemp(".bundle", ".gpq", git_dir)
+ os.close(tmp_fd)
+
+ try:
+ RunGit("bundle", "create",
+ tmp_bundle,
+ "^" + options.base_commit,
+ "HEAD")
+ fd = open(tmp_bundle, "rb")
+
+ bundle_id = None
+ segment_id = 0
+ next_data = fd.read(MAX_SEGMENT_SIZE)
+
+ while len(next_data) > 0:
+ this_data = next_data
+ next_data = fd.read(MAX_SEGMENT_SIZE)
+ segment_id += 1
+
+ if bundle_id is None:
+ req = UploadBundleRequest()
+ req.dest_project = options.dest_project
+ req.dest_branch = options.dest_branch
+ for c in revlist:
+ req.contained_object.append(c)
+ else:
+ req = UploadBundleContinue()
+ req.bundle_id = bundle_id
+ req.segment_id = segment_id
+
+ req.bundle_data = this_data
+ if len(next_data) > 0:
+ req.partial_upload = True
+ else:
+ req.partial_upload = False
+
+ if bundle_id is None:
+ rsp = review.UploadBundle(req)
+ else:
+ rsp = review.ContinueBundle(req)
+
+ if rsp.status_code == UploadBundleResponse.CONTINUE:
+ bundle_id = rsp.bundle_id
+ else:
+ print rsp
+ break
+ finally:
+ os.unlink(tmp_bundle)
+
+def main():
+ try:
+ RealMain(sys.argv)
+ except KeyboardInterrupt:
+ print
+ StatusUpdate("Interrupted.")
+ sys.exit(1)
+
+
+if __name__ == "__main__":
+ main()