Sending mail when merge failed due to path conflict, missing dependency or other reason
diff --git a/src/main/java/com/google/gerrit/git/MergeOp.java b/src/main/java/com/google/gerrit/git/MergeOp.java
index ebff14a..3a25af3 100644
--- a/src/main/java/com/google/gerrit/git/MergeOp.java
+++ b/src/main/java/com/google/gerrit/git/MergeOp.java
@@ -678,9 +678,10 @@
         }
 
         case MISSING_DEPENDENCY: {
+          ChangeMessage msg = null;
           try {
             final String txt =
-                "Change could not be merged because of a missing dependency.  As soon as its dependencies are submitted, the change will be submitted.";
+                "Change could not be merged because of a missing dependency. As soon as its dependencies are submitted, the change will be submitted.";
             final List<ChangeMessage> msgList =
                 schema.changeMessages().byChange(c.getId()).toList();
             if (msgList.size() > 0) {
@@ -694,10 +695,25 @@
               }
             }
 
-            schema.changeMessages().insert(
-                Collections.singleton(message(c, txt)));
+            msg = message(c, txt);
+            schema.changeMessages().insert(Collections.singleton(msg));
           } catch (OrmException e) {
           }
+
+          try {
+            final ChangeMail cm = new ChangeMail(server, c);
+            cm.setFrom(getSubmitter(c));
+            cm.setReviewDb(schema);
+            cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()), schema
+                .patchSetInfo().get(c.currentPatchSetId()));
+            cm.setChangeMessage(msg);
+            cm.sendMergeFailed();
+          } catch (OrmException e) {
+            log.error("Cannot submit patch set for Change " + c.getId() + " due to a missing dependency.", e);
+          } catch (MessagingException e) {
+            log.error("Cannot submit patch set for Change " + c.getId() + " due to a missing dependency.", e);
+          }
+
           break;
         }
 
@@ -721,6 +737,25 @@
     return m;
   }
 
+  private Account.Id getSubmitter(Change c) {
+    ChangeApproval submitter = null;
+    try {
+      final List<ChangeApproval> approvals =
+          schema.changeApprovals().byChange(c.getId()).toList();
+      for (ChangeApproval a : approvals) {
+        if (a.getValue() > 0
+            && ApprovalCategory.SUBMIT.equals(a.getCategoryId())) {
+          if (submitter == null
+              || a.getGranted().compareTo(submitter.getGranted()) > 0) {
+            submitter = a;
+          }
+        }
+      }
+    } catch (OrmException e) {
+    }
+    return submitter != null ? submitter.getAccountId() : null;
+  }
+
   private void setMerged(Change c, ChangeMessage msg) {
     final PatchSet.Id merged = c.currentPatchSetId();
     ChangeApproval submitter = null;
@@ -818,5 +853,19 @@
         }
       }
     }
+
+    try {
+      final ChangeMail cm = new ChangeMail(server, c);
+      cm.setFrom(getSubmitter(c));
+      cm.setReviewDb(schema);
+      cm.setPatchSet(schema.patchSets().get(c.currentPatchSetId()), schema
+          .patchSetInfo().get(c.currentPatchSetId()));
+      cm.setChangeMessage(msg);
+      cm.sendMergeFailed();
+    } catch (OrmException e) {
+      log.error("Cannot submit patch set for Change " + c.getId() + " due to a path conflict.", e);
+    } catch (MessagingException e) {
+      log.error("Cannot submit patch set for Change " + c.getId() + " due to a path conflict.", e);
+    }
   }
 }
diff --git a/src/main/java/com/google/gerrit/server/ChangeMail.java b/src/main/java/com/google/gerrit/server/ChangeMail.java
index 47a4bc3..2fcd52d 100644
--- a/src/main/java/com/google/gerrit/server/ChangeMail.java
+++ b/src/main/java/com/google/gerrit/server/ChangeMail.java
@@ -370,6 +370,42 @@
     }
   }
 
+  public void sendMergeFailed() throws MessagingException {
+    if (begin("comment")) {
+      body.append("Change ");
+      body.append(change.getChangeId());
+      if (patchSetInfo != null && patchSetInfo.getAuthor() != null
+          && patchSetInfo.getAuthor().getName() != null) {
+        body.append(" by ");
+        body.append(patchSetInfo.getAuthor().getName());
+      }
+      body.append(" FAILED to submit to ");
+      body.append(change.getDest().getShortName());
+      body.append(".\n\n");
+
+      if (message != null) {
+        body.append("Error message:\n");
+        body.append("....................................................\n");
+        body.append(message.getMessage().trim());
+        if (body.length() > 0) {
+          body.append("\n\n");
+        }
+      }
+
+      if (changeUrl() != null) {
+        openFooter();
+        body.append("To view visit ");
+        body.append(changeUrl());
+        body.append("\n");
+      }
+
+      initInReplyToChange();
+      submittedTo();
+      starredTo();
+      send();
+    }
+  }
+
   public void sendAbandoned() throws MessagingException {
     if (begin("abandon")) {
       final Account a = Common.getAccountCache().get(fromId);