Initial Contribution
diff --git a/gerrit_upload.py b/gerrit_upload.py
new file mode 100755
index 0000000..a49fb23
--- /dev/null
+++ b/gerrit_upload.py
@@ -0,0 +1,156 @@
+#
+# Copyright (C) 2008 The Android Open Source Project
+#
+# 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 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 *
+from git_command import GitCommand
+from error import UploadError
+
+try:
+  import readline
+except ImportError:
+  pass
+
+MAX_SEGMENT_SIZE = 1020 * 1024
+
+def _GetRpcServer(email, server, save_cookies):
+  """Returns an RpcServer.
+
+  Returns:
+    A new RpcServer, on which RPC calls can be made.
+  """
+
+  def GetUserCredentials():
+    """Prompts the user for a username and password."""
+    e = email
+    if e is None:
+      e = raw_input("Email: ").strip()
+    password = getpass.getpass("Password for %s: " % e)
+    return (e, password)
+
+  # If this is the dev_appserver, use fake authentication.
+  lc_server = server.lower()
+  if lc_server == "localhost" or lc_server.startswith("localhost:"):
+    if email is None:
+      email = "test@example.com"
+    server = HttpRpc(
+        server,
+        lambda: (email, "password"),
+        extra_headers={"Cookie":
+                       'dev_appserver_login="%s:False"' % email})
+    # Don't try to talk to ClientLogin.
+    server.authenticated = True
+    return server
+
+  if save_cookies:
+    cookie_file = ".gerrit_cookies"
+  else:
+    cookie_file = None
+
+  return HttpRpc(server, GetUserCredentials,
+                 cookie_file=cookie_file)
+
+def UploadBundle(project,
+                 server,
+                 email,
+                 dest_project,
+                 dest_branch,
+                 src_branch,
+                 bases,
+                 save_cookies=True):
+
+  srv = _GetRpcServer(email, server, save_cookies)
+  review = Proxy(ReviewService_Stub(srv))
+  tmp_fd, tmp_bundle = mkstemp(".bundle", ".gpq")
+  os.close(tmp_fd)
+
+  srcid = project.bare_git.rev_parse(src_branch)
+  revlist = project._revlist(src_branch, *bases)
+
+  if srcid not in revlist:
+    # This can happen if src_branch is an annotated tag
+    #
+    revlist.append(srcid)
+  revlist_size = len(revlist) * 42
+
+  try:
+    cmd = ['bundle', 'create', tmp_bundle, src_branch]
+    cmd.extend(bases)
+    if GitCommand(project, cmd).Wait() != 0:
+      raise UploadError('cannot create bundle')
+    fd = open(tmp_bundle, "rb")
+
+    bundle_id = None
+    segment_id = 0
+    next_data = fd.read(MAX_SEGMENT_SIZE - revlist_size)
+
+    while True:
+      this_data = next_data
+      next_data = fd.read(MAX_SEGMENT_SIZE)
+      segment_id += 1
+
+      if bundle_id is None:
+        req = UploadBundleRequest()
+        req.dest_project = str(dest_project)
+        req.dest_branch = str(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
+      elif rsp.status_code == UploadBundleResponse.RECEIVED:
+        bundle_id = rsp.bundle_id
+        return bundle_id
+      else:
+        if rsp.status_code == UploadBundleResponse.UNKNOWN_PROJECT:
+          reason = 'unknown project "%s"' % dest_project
+        elif rsp.status_code == UploadBundleResponse.UNKNOWN_BRANCH:
+          reason = 'unknown branch "%s"' % dest_branch
+        elif rsp.status_code == UploadBundleResponse.UNKNOWN_BUNDLE:
+          reason = 'unknown bundle'
+        elif rsp.status_code == UploadBundleResponse.NOT_BUNDLE_OWNER:
+          reason = 'not bundle owner'
+        elif rsp.status_code == UploadBundleResponse.BUNDLE_CLOSED:
+          reason = 'bundle closed'
+        elif rsp.status_code == UploadBundleResponse.UNAUTHORIZED_USER:
+          reason = ('Unauthorized user.  Visit http://%s/hello to sign up.'
+                    % server)
+        else:
+          reason = 'unknown error ' + str(rsp.status_code)
+        raise UploadError(reason)
+  finally:
+    os.unlink(tmp_bundle)