summaryrefslogtreecommitdiffstats
path: root/webapp/codereview/internal/merge_service.py
diff options
context:
space:
mode:
Diffstat (limited to 'webapp/codereview/internal/merge_service.py')
-rw-r--r--webapp/codereview/internal/merge_service.py161
1 files changed, 161 insertions, 0 deletions
diff --git a/webapp/codereview/internal/merge_service.py b/webapp/codereview/internal/merge_service.py
new file mode 100644
index 0000000000..bf9c721f29
--- /dev/null
+++ b/webapp/codereview/internal/merge_service.py
@@ -0,0 +1,161 @@
+# Copyright 2008 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 logging
+
+from google.appengine.ext import db
+
+from codereview import models
+from codereview.models import gql
+
+from merge_pb2 import MergeService
+from pending_merge_pb2 import *
+from post_merge_result_pb2 import *
+from post_branch_update_pb2 import *
+from util import InternalAPI, automatic_retry
+
+from codereview import email
+
+def _send_clean_merge_email(http_request, change):
+ if not change.emailed_clean_merge:
+ email.send_change_message(http_request, change,
+ "mails/clean_merge.txt", None)
+ change.emailed_clean_merge = True
+
+def _send_missing_dependency_merge_email(http_request, change):
+ if not change.emailed_clean_merge:
+ email.send_change_message(http_request, change,
+ "mails/missing_dependency.txt", None)
+ change.emailed_missing_dependency = True
+
+def _send_path_conflict_email(http_request, change):
+ if not change.emailed_clean_merge:
+ email.send_change_message(http_request, change,
+ "mails/path_conflict.txt", None)
+ change.emailed_path_conflict = True
+
+
+class InvalidBranchStatusError(Exception):
+ """The branch cannot be updated in this way at this time."""
+
+class MergeServiceImp(MergeService, InternalAPI):
+ @automatic_retry
+ def NextPendingMerge(self, rpc_controller, req, done):
+
+ patchsets = []
+ while not patchsets:
+ branch = gql(models.Branch,
+ "WHERE status = 'NEEDS_MERGE'"
+ " ORDER BY merge_submitted").get()
+ if branch is None:
+ break
+ patchsets = branch.begin_merge()
+
+ rsp = PendingMergeResponse()
+ if patchsets:
+ first = patchsets[0].change
+ rsp.status_code = PendingMergeResponse.MERGE_READY
+ rsp.dest_project_name = str(first.dest_project.name)
+ rsp.dest_project_key = str(first.dest_project.key())
+ rsp.dest_branch_name = str(first.dest_branch.name)
+ rsp.dest_branch_key = str(first.dest_branch.key())
+ for ps in patchsets:
+ pmi = rsp.change.add()
+ pmi.patchset_key = str(ps.key())
+ pmi.revision_id = str(ps.revision.id)
+ else:
+ rsp.status_code = PendingMergeResponse.QUEUE_EMPTY
+ done(rsp)
+
+ @automatic_retry
+ def PostMergeResult(self, rpc_controller, req, done):
+ rsp = PostMergeResultResponse()
+
+ success = []
+ fail = []
+ defer = []
+
+ for ri in req.change:
+ sc = ri.status_code
+ ps = db.get(db.Key(ri.patchset_key))
+
+ if ps.change.merged:
+ success.append(ps)
+ continue
+
+ def chg_trans(key):
+ change = db.get(key)
+ if change.merge_patchset.key() != ps.key():
+ return False
+
+ if sc == MergeResultItem.CLEAN_MERGE:
+ pass
+
+ elif sc == MergeResultItem.ALREADY_MERGED:
+ change.merged = True
+ change.closed = True
+ change.put()
+
+ elif sc == MergeResultItem.MISSING_DEPENDENCY:
+ pass
+
+ elif sc == MergeResultItem.PATH_CONFLICT:
+ change.unsubmit_merge()
+ change.put()
+
+ return True
+ if db.run_in_transaction(chg_trans, ps.change.key()):
+ if sc == MergeResultItem.CLEAN_MERGE:
+ _send_clean_merge_email(self.http_request, ps.change)
+ ps.change.put()
+
+ elif sc == MergeResultItem.ALREADY_MERGED:
+ success.append(ps)
+
+ elif sc == MergeResultItem.MISSING_DEPENDENCY:
+ _send_missing_dependency_merge_email(self.http_request, ps.change)
+ ps.change.put()
+ defer.append(ps)
+
+ elif sc == MergeResultItem.PATH_CONFLICT:
+ _send_path_conflict_email(self.http_request, ps.change)
+ ps.change.put()
+ fail.append(ps)
+ else:
+ fail.append(ps)
+
+ branch = db.get(db.Key(req.dest_branch_key))
+ branch.finish_merge(success, fail, defer)
+
+ done(rsp)
+
+ @automatic_retry
+ def PostBranchUpdate(self, rpc_controller, req, done):
+ rsp = PostBranchUpdateResponse()
+
+ branch = db.get(db.Key(req.branch_key))
+ merged = [db.get(db.Key(c_key)) for c_key in req.new_change]
+
+ branch.merged(merged)
+
+ for ps in merged:
+ def trans(key):
+ change = db.get(key)
+ if change.merge_patchset.key() == ps.key():
+ change.merged = True
+ change.closed = True
+ change.put()
+ db.run_in_transaction(trans, ps.change.key())
+
+ done(rsp)