Merge "TrivialRebase: Aggregate approvals" into stable-2.6
diff --git a/Documentation/access-control.txt b/Documentation/access-control.txt
index 9ef6e13..5e70460 100644
--- a/Documentation/access-control.txt
+++ b/Documentation/access-control.txt
@@ -134,6 +134,17 @@
 members of `Foo` have submit rights on a project, and the members of
 `Foo-admin` typically do not need to have such rights.
 
+[[ldap_groups]]
+LDAP Groups
+-----------
+
+LDAP groups are Account Groups that are maintained inside of your
+LDAP instance. If you are using LDAP to manage your groups they will
+not appear in the Groups list. However you can use them just like
+regular Account Groups by prefixing your group with "ldap/" in the
+Access Control for a project. For example "ldap/foo-project" will
+add the LDAP "foo-project" group to the access list.
+
 
 Project Access Control Lists
 ----------------------------
diff --git a/Documentation/config-reverseproxy.txt b/Documentation/config-reverseproxy.txt
index 7161c4a..c932c0c 100644
--- a/Documentation/config-reverseproxy.txt
+++ b/Documentation/config-reverseproxy.txt
@@ -28,33 +28,34 @@
 Apache 2 Configuration
 ----------------------
 
-To run Gerrit behind an Apache server using 'mod_proxy', enable the
+To run Gerrit behind an Apache server we cannot use 'mod_proxy'
+directly, as Gerrit relies on getting unmodified escaped forward
+slashes. Depending on the setting of 'AllowEncodedSlashes',
+'mod_proxy' would either decode encoded slashes, or encode them once
+again. Hence, we resort to using 'mod_rewrite'. To enable the
 necessary Apache2 modules:
 
 ----
-  a2enmod proxy_http
+  a2enmod rewrite
   a2enmod ssl          ; # optional, needed for HTTPS / SSL
 ----
 
-Configure an Apache VirtualHost to proxy to the Gerrit daemon,
-setting the 'ProxyPass' line to use the 'http://' URL configured
-above.  Ensure the path of ProxyPass and httpd.listenUrl match,
-or links will redirect to incorrect locations.
+Configure an Apache VirtualHost to proxy to the Gerrit daemon, setting
+the 'RewriteRule' line to use the 'http://' URL configured above.
+Ensure the path of 'RewriteRule' (the part before '$1') and
+httpd.listenUrl match, or links will redirect to incorrect locations.
+
+Note that this configuration allows to pass encoded characters to the
+virtual host, which is potentially dangerous. Be sure to read up on
+this topic and that you understand the risks.
 
 ----
 	<VirtualHost *>
 	  ServerName review.example.com
 
-	  ProxyRequests Off
-	  ProxyVia Off
-	  ProxyPreserveHost On
-
-	  <Proxy *>
-		Order deny,allow
-		Allow from all
-	  </Proxy>
-
-	  ProxyPass /r/ http://127.0.0.1:8081/r/
+	  AllowEncodedSlashes NoDecode
+	  RewriteEngine On
+	  RewriteRule ^/r/(.*) http://localhost:8081/r/$1
 	</VirtualHost>
 ----
 
diff --git a/Documentation/user-changeid.txt b/Documentation/user-changeid.txt
index 0b67205..a4224bd 100644
--- a/Documentation/user-changeid.txt
+++ b/Documentation/user-changeid.txt
@@ -10,10 +10,10 @@
 Gerrit can automatically associate a new version of a change back
 to its original review, even across cherry-picks and rebases.
 
-To be picked up by Gerrit, a Change-Id line must be in the bottom
-portion (last paragraph) of a commit message, and may be mixed
-together with the Signed-off-by, Acked-by, or other such footers.
-For example:
+To be picked up by Gerrit, a Change-Id line must be in the footer
+(last paragraph) of a commit message, and may be mixed
+together with link:user-signedoffby.html[Signed-off-by], Acked-by,
+or other such lines. For example:
 
 ----
   $ git log -1
diff --git a/ReleaseNotes/ReleaseNotes-2.6.txt b/ReleaseNotes/ReleaseNotes-2.6.txt
index 4612aaa..15dc7be 100644
--- a/ReleaseNotes/ReleaseNotes-2.6.txt
+++ b/ReleaseNotes/ReleaseNotes-2.6.txt
@@ -430,6 +430,12 @@
 HTML thanks to Gson encoding HTML control characters using Unicode
 character escapes within JSON strings.
 
+* Apache reverse proxies need `AllowEncodedSlashes NoDecode`
++
+When Apache is used as a reverse proxy the NoDecode option
+must be set for AllowEncodedSlashes to prevent Apache from
+mangling Gerrit REST API URLs.
+
 Project Dashboards
 ~~~~~~~~~~~~~~~~~~
 * link:http://gerrit-documentation.googlecode.com/svn/Documentation/2.6/user-dashboards.html#project-dashboards[
diff --git a/contrib/trivial_rebase.py b/contrib/trivial_rebase.py
index 0a0e510..6254c69 100755
--- a/contrib/trivial_rebase.py
+++ b/contrib/trivial_rebase.py
@@ -159,7 +159,8 @@
     git_show_process = subprocess.Popen(git_show_cmd, stdout=subprocess.PIPE)
     patch_id_process = subprocess.Popen(patch_id_cmd, stdout=subprocess.PIPE,
                                         stdin=git_show_process.stdout)
-    return patch_id_process.communicate()[0]
+    res = patch_id_process.communicate()[0] or '0'
+    return res.split()[0]
 
   def SuExec(self, as_user, cmd):
     suexec_cmd = [self.ssh, '-l', "Gerrit Code Review", self.ssh_port_flag, self.port, self.server]
@@ -187,14 +188,10 @@
     assert prev_revision, "Previous revision not found"
     prev_patch_id = self.GetPatchId(prev_revision)
     cur_patch_id = self.GetPatchId(self.commit)
-    if not (prev_patch_id and cur_patch_id):
-      # Merge commit
-      if not prev_patch_id:
-        print "GetPatchId failed for commit %s" % (prev_revision)
-      if not cur_patch_id:
-        print "GetPatchId failed for commit %s" % (self.commit)
+    if prev_patch_id == '0' and cur_patch_id == '0':
+      print "commits %s and %s are both empty or merge commits" % (prev_revision, self.commit)
       return
-    if cur_patch_id.split()[0] != prev_patch_id.split()[0]:
+    if cur_patch_id != prev_patch_id:
       # patch-ids don't match
       return
     # Patch ids match. This is a trivial rebase.
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
index bc40097..4f4e8a7 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/FormatUtil.java
@@ -125,7 +125,7 @@
   }
 
   /**
-   * Formats an account as an name and an email address.
+   * Formats an account as a name and an email address.
    * <p>
    * Example output:
    * <ul>
@@ -137,7 +137,7 @@
    */
   public static String nameEmail(AccountInfo info) {
     String name = info.name();
-    if (name == null) {
+    if (name == null || name.trim().isEmpty()) {
       name = Gerrit.getConfig().getAnonymousCowardName();
     }
 
@@ -172,7 +172,7 @@
    * returns a longer form that includes the email address.
    */
   public static String name(AccountInfo ai) {
-    if (ai.name() != null) {
+    if (ai.name() != null && !ai.name().trim().isEmpty()) {
       return ai.name();
     }
     String email = ai.email();
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
index cba2f0b..f35bd4b 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/account/ContactPanelShort.java
@@ -348,7 +348,7 @@
 
   void doSave(final AsyncCallback<Account> onSave) {
     String newName = canEditFullName() ? nameTxt.getText() : null;
-    if ("".equals(newName)) {
+    if (newName != null && newName.trim().isEmpty()) {
       newName = null;
     }
 
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
index 07f25f4..dac0b6a 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/GroupListScreen.java
@@ -18,7 +18,7 @@
 
 import com.google.gerrit.client.Gerrit;
 import com.google.gerrit.client.groups.GroupMap;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.ui.AccountScreen;
 import com.google.gerrit.client.ui.FilteredUserInterface;
 import com.google.gerrit.client.ui.IgnoreOutdatedFilterResultsCallbackWrapper;
@@ -54,6 +54,7 @@
   @Override
   protected void onLoad() {
     super.onLoad();
+    display();
     refresh();
   }
 
@@ -62,9 +63,9 @@
         : ADMIN_GROUPS + "?filter=" + URL.encodeQueryString(subname));
     GroupMap.match(subname,
         new IgnoreOutdatedFilterResultsCallbackWrapper<GroupMap>(this,
-            new ScreenLoadCallback<GroupMap>(this) {
+            new GerritCallback<GroupMap>() {
               @Override
-              protected void preDisplay(final GroupMap result) {
+              public void onSuccess(GroupMap result) {
                 groups.display(result, subname);
                 groups.finishDisplay();
               }
diff --git a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
index 6af437c..ee58420 100644
--- a/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
+++ b/gerrit-gwtui/src/main/java/com/google/gerrit/client/admin/ProjectListScreen.java
@@ -21,7 +21,7 @@
 import com.google.gerrit.client.GitwebLink;
 import com.google.gerrit.client.projects.ProjectInfo;
 import com.google.gerrit.client.projects.ProjectMap;
-import com.google.gerrit.client.rpc.ScreenLoadCallback;
+import com.google.gerrit.client.rpc.GerritCallback;
 import com.google.gerrit.client.ui.FilteredUserInterface;
 import com.google.gerrit.client.ui.HighlightingInlineHyperlink;
 import com.google.gerrit.client.ui.IgnoreOutdatedFilterResultsCallbackWrapper;
@@ -63,6 +63,7 @@
   @Override
   protected void onLoad() {
     super.onLoad();
+    display();
     refresh();
   }
 
@@ -71,9 +72,9 @@
         : ADMIN_PROJECTS + "?filter=" + URL.encodeQueryString(subname));
     ProjectMap.match(subname,
         new IgnoreOutdatedFilterResultsCallbackWrapper<ProjectMap>(this,
-            new ScreenLoadCallback<ProjectMap>(this) {
+            new GerritCallback<ProjectMap>() {
               @Override
-              protected void preDisplay(final ProjectMap result) {
+              public void onSuccess(ProjectMap result) {
                 projects.display(result);
               }
             }));
diff --git a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/container/ConfigurationError.html b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/container/ConfigurationError.html
index 7294012..a7cd489 100644
--- a/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/container/ConfigurationError.html
+++ b/gerrit-httpd/src/main/resources/com/google/gerrit/httpd/auth/container/ConfigurationError.html
@@ -49,22 +49,16 @@
 &lt;VirtualHost <span class='ServerName'>review.example.com</span><span class='ServerPort'>:80</span>&gt;
     ServerName <span class='ServerName'>review.example.com</span>
 
-    ProxyRequests Off
-    ProxyVia Off
-    ProxyPreserveHost On
-
-    &lt;Proxy *&gt;
-          Order deny,allow
-          Allow from all
-    &lt;/Proxy&gt;
-
 <div class='apache_auth'>    &lt;Location <span class='ContextPath'>/r</span>/login/&gt;
       AuthType Basic
       AuthName "Gerrit Code Review"
       Require valid-user
       ...
     &lt;/Location&gt;</div>
-    ProxyPass <span class='ContextPath'>/r</span>/ http://...<span class='ContextPath'>/r</span>/
+
+    AllowEncodedSlashes NoDecode
+    RewriteEngine On
+    RewriteRule ^<span class='ContextPath'>/r</span>/(.*) http://...<span class='ContextPath'>/r</span>/$1
 &lt;/VirtualHost&gt;
     </pre>
   </body>
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java
index 97af5ac..8abe501 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/git/validators/CommitValidators.java
@@ -486,7 +486,8 @@
         sb.append('\n');
         sb.append(changeId).append(" I").append(c.name());
         sb.append('\n');
-        sb.append("Hint: A potential Change-Id was found, but it was not in the footer of the commit message.");
+        sb.append("Hint: A potential Change-Id was found, but it was not in the ");
+        sb.append("footer (last paragraph) of the commit message.");
       }
     }
     sb.append('\n');
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
index 244028c..ab70395 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ListProjectsCommand.java
@@ -21,6 +21,8 @@
 
 import org.apache.sshd.server.Environment;
 
+import java.util.List;
+
 @CommandMetaData(name = "ls-projects", descr = "List projects visible to the caller")
 final class ListProjectsCommand extends BaseCommand {
   @Inject
@@ -33,7 +35,8 @@
       public void run() throws Exception {
         parseCommandLine(impl);
         if (!impl.getFormat().isJson()) {
-          if (impl.isShowTree() && (impl.getShowBranch() != null)) {
+          List<String> showBranch = impl.getShowBranch();
+          if (impl.isShowTree() && (showBranch != null) && !showBranch.isEmpty()) {
             throw new UnloggedFailure(1, "fatal: --tree and --show-branch options are not compatible.");
           }
           if (impl.isShowTree() && impl.isShowDescription()) {
diff --git a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java
index 09c25ff..987380f 100644
--- a/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java
+++ b/gerrit-sshd/src/main/java/com/google/gerrit/sshd/commands/ScpCommand.java
@@ -92,6 +92,7 @@
 
   private void runImp() {
     try {
+      readAck();
       if (error != null) {
         throw error;
       }