Merge "Add REST endpoints for draft changes"
diff --git a/.buckconfig b/.buckconfig
index e60c7cd5..171acb5 100644
--- a/.buckconfig
+++ b/.buckconfig
@@ -15,3 +15,6 @@
 
 [java]
   src_roots = java, resources
+
+[project]
+  ignore = .git
diff --git a/ReleaseNotes/ReleaseNotes-2.6.2.txt b/ReleaseNotes/ReleaseNotes-2.6.2.txt
index 2a7a7b3..af00a71 100644
--- a/ReleaseNotes/ReleaseNotes-2.6.2.txt
+++ b/ReleaseNotes/ReleaseNotes-2.6.2.txt
@@ -21,10 +21,6 @@
 If the title is not specified, the path of the dashboard config file
 is used as title.
 
-* link:https://code.google.com/p/gerrit/issues/detail?id=2010[Issue 2010]:
-Fix null-pointer exception when searching for changes with the query
-`owner:self`.
-
 * Properly handle double-click on external group in GroupTable.
 +
 Double-clicking on an external group opens the group's URL (if it
@@ -41,11 +37,15 @@
 * link:https://code.google.com/p/gerrit/issues/detail?id=1966[Issue 1966]:
 Fix Gerrit plugins under Tomcat by avoiding Guice static filter.
 
-* link:https://code.google.com/p/gerrit/issues/detail?id=2054[Issue 2054]:
-Expand capabilities of `ldap.groupMemberPattern`.
+* link:https://code.google.com/p/gerrit/issues/detail?id=2010[Issue 2010]:
+Fix null-pointer exception when searching for changes with the query
+`owner:self`.
 
 * link:https://code.google.com/p/gerrit/issues/detail?id=2039[Issue 2039]:
-Fix browser NPE when ChangeCache is incomplete.
+Fix browser null-pointer exception when ChangeCache is incomplete.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2054[Issue 2054]:
+Expand capabilities of `ldap.groupMemberPattern`.
 
 * link:https://code.google.com/p/gerrit/issues/detail?id=2056[Issue 2056]:
 Display custom NoOp label score for open changes.
@@ -56,5 +56,12 @@
 * link:https://code.google.com/p/gerrit/issues/detail?id=2098[Issue 2098]:
 Fix re-enabling of disabled plugins.
 
+* link:https://code.google.com/p/gerrit/issues/detail?id=2127[Issue 2127]:
+Remove hard-coded documentation links from the admin page.
+
+* link:https://code.google.com/p/gerrit/issues/detail?id=2128[Issue 2128]:
+Fix null-pointer exception when deleting draft patch set when previous
+draft was already deleted.
+
 No other changes since 2.6.1.
 
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
index 3a95fc3..adb346f 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/AbstractDaemonTest.java
@@ -14,20 +14,51 @@
 
 package com.google.gerrit.acceptance;
 
-import org.junit.After;
-import org.junit.Before;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Rule;
+import org.junit.rules.TestRule;
+import org.junit.runner.Description;
+import org.junit.runners.model.Statement;
 
 public abstract class AbstractDaemonTest {
   protected GerritServer server;
 
-  @Before
-  public final void beforeTest() throws Exception {
-    server = GerritServer.start();
+  @Rule
+  public TestRule testRunner = new TestRule() {
+    @Override
+    public Statement apply(final Statement base, final Description description) {
+      return new Statement() {
+        @Override
+        public void evaluate() throws Throwable {
+          beforeTest(config(description));
+          base.evaluate();
+          afterTest();
+        }
+      };
+    }
+  };
+
+  private static Config config(Description description) {
+    GerritConfigs cfgs = description.getAnnotation(GerritConfigs.class);
+    GerritConfig cfg = description.getAnnotation(GerritConfig.class);
+    if (cfgs != null && cfg != null) {
+      throw new IllegalStateException("Use either @GerritConfigs or @GerritConfig not both");
+    }
+    if (cfgs != null) {
+      return ConfigAnnotationParser.parse(cfgs);
+    } else if (cfg != null) {
+      return ConfigAnnotationParser.parse(cfg);
+    } else {
+      return null;
+    }
+  }
+
+  private void beforeTest(Config cfg) throws Exception {
+    server = GerritServer.start(cfg);
     server.getTestInjector().injectMembers(this);
   }
 
-  @After
-  public final void afterTest() throws Exception {
+  private void afterTest() throws Exception {
     server.stop();
   }
 }
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
new file mode 100644
index 0000000..cf60fb4
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/ConfigAnnotationParser.java
@@ -0,0 +1,58 @@
+// Copyright (C) 2013 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.
+
+package com.google.gerrit.acceptance;
+
+import com.google.common.base.Splitter;
+import com.google.common.collect.Lists;
+
+import org.eclipse.jgit.lib.Config;
+
+import java.util.ArrayList;
+
+class ConfigAnnotationParser {
+
+  private static Splitter splitter = Splitter.on(".").trimResults();
+
+  static Config parse(GerritConfigs annotation) {
+    if (annotation == null) {
+      return null;
+    }
+
+    Config cfg = new Config();
+    for (GerritConfig c : annotation.value()) {
+      parse(cfg, c);
+    }
+    return cfg;
+  }
+
+  static Config parse(GerritConfig annotation) {
+    Config cfg = new Config();
+    parse(cfg, annotation);
+    return cfg;
+  }
+
+  static private void parse(Config cfg, GerritConfig c) {
+    ArrayList<String> l = Lists.newArrayList(splitter.split(c.name()));
+    if (l.size() == 2) {
+      cfg.setString(l.get(0), null, l.get(1), c.value());
+    } else if (l.size() == 3) {
+      cfg.setString(l.get(0), l.get(1), l.get(2), c.value());
+    } else {
+      throw new IllegalArgumentException(
+          "GerritConfig.name must be of the format"
+              + " section.subsection.name or section.name");
+    }
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
new file mode 100644
index 0000000..5cb1229
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfig.java
@@ -0,0 +1,28 @@
+// Copyright (C) 2013 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.
+
+package com.google.gerrit.acceptance;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Target({METHOD})
+@Retention(RUNTIME)
+public @interface GerritConfig {
+  String name();
+  String value();
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfigs.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfigs.java
new file mode 100644
index 0000000..58bb9f2
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritConfigs.java
@@ -0,0 +1,27 @@
+// Copyright (C) 2013 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.
+
+package com.google.gerrit.acceptance;
+
+import static java.lang.annotation.ElementType.METHOD;
+import static java.lang.annotation.RetentionPolicy.RUNTIME;
+
+import java.lang.annotation.Retention;
+import java.lang.annotation.Target;
+
+@Target({METHOD})
+@Retention(RUNTIME)
+public @interface GerritConfigs {
+  public GerritConfig[] value();
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
index de70796..f97d2b9 100644
--- a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/GerritServer.java
@@ -23,6 +23,7 @@
 import com.google.inject.Module;
 
 import org.eclipse.jgit.errors.ConfigInvalidException;
+import org.eclipse.jgit.lib.Config;
 import org.eclipse.jgit.lib.RepositoryCache;
 import org.eclipse.jgit.storage.file.FileBasedConfig;
 import org.eclipse.jgit.util.FS;
@@ -42,11 +43,11 @@
 import java.util.concurrent.Executors;
 import java.util.concurrent.TimeUnit;
 
-class GerritServer {
+public class GerritServer {
 
   /** Returns fully started Gerrit server */
-  static GerritServer start() throws Exception {
-    final File site = initSite();
+  static GerritServer start(Config base) throws Exception {
+    final File site = initSite(base);
     final CyclicBarrier serverStarted = new CyclicBarrier(2);
     final Daemon daemon = new Daemon(new Runnable() {
       public void run() {
@@ -80,7 +81,7 @@
     return new GerritServer(site, i, daemon, daemonService);
   }
 
-  private static File initSite() throws Exception {
+  private static File initSite(Config base) throws Exception {
     File tmp = TempFileUtil.createTempDirectory();
     Init init = new Init();
     int rc = init.main(new String[] {
@@ -93,10 +94,11 @@
     InetSocketAddress http = newPort();
     InetSocketAddress sshd = newPort();
     String url = "http://" + format(http) + "/";
-    FileBasedConfig cfg = new FileBasedConfig(
+    MergeableFileBasedConfig cfg = new MergeableFileBasedConfig(
         new File(new File(tmp, "etc"), "gerrit.config"),
         FS.DETECTED);
     cfg.load();
+    cfg.merge(base);
     cfg.setString("gerrit", null, "canonicalWebUrl", url);
     cfg.setString("httpd", null, "listenUrl", url);
     cfg.setString("sshd", null, "listenAddress", format(sshd));
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
new file mode 100644
index 0000000..f1baa9d
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/MergeableFileBasedConfig.java
@@ -0,0 +1,60 @@
+// Copyright (C) 2013 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.
+
+package com.google.gerrit.acceptance;
+
+import com.google.common.collect.Lists;
+
+import org.eclipse.jgit.lib.Config;
+import org.eclipse.jgit.storage.file.FileBasedConfig;
+import org.eclipse.jgit.util.FS;
+
+import java.io.File;
+
+/**
+ * A file based Config that can merge another Config instance.
+ */
+public class MergeableFileBasedConfig extends FileBasedConfig {
+  public MergeableFileBasedConfig(File cfgLocation, FS fs) {
+    super(cfgLocation, fs);
+  }
+
+  /**
+   * Merge another Config into this Config.
+   *
+   * In case a configuration parameter exists both in this instance and in the
+   * merged instance then the value in this instance will simply replaced by
+   * the value from the merged instance.
+   *
+   * @param s Config to merge into this instance
+   */
+  public void merge(Config s) {
+    if (s == null) {
+      return;
+    }
+    for (String section : s.getSections()) {
+      for (String subsection : s.getSubsections(section)) {
+        for (String name : s.getNames(section, subsection)) {
+          setStringList(section, subsection, name, Lists.newArrayList(s
+              .getStringList(section, subsection, name)));
+        }
+      }
+
+      for (String name : s.getNames(section)) {
+        setStringList(section, null, name,
+            Lists.newArrayList(s.getStringList(section, null, name)));
+      }
+    }
+  }
+}
diff --git a/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseGerritConfigAnnotationTest.java b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseGerritConfigAnnotationTest.java
new file mode 100644
index 0000000..0931e12
--- /dev/null
+++ b/gerrit-acceptance-tests/src/test/java/com/google/gerrit/acceptance/UseGerritConfigAnnotationTest.java
@@ -0,0 +1,46 @@
+// Copyright (C) 2013 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.
+
+package com.google.gerrit.acceptance;
+
+import static org.junit.Assert.assertEquals;
+
+import com.google.gerrit.server.config.GerritServerConfig;
+import com.google.inject.Inject;
+
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+
+public class UseGerritConfigAnnotationTest extends AbstractDaemonTest {
+
+  @Inject
+  @GerritServerConfig
+  Config serverConfig;
+
+  @Test
+  @GerritConfig(name="x.y", value="z")
+  public void testOne() {
+    assertEquals("z", serverConfig.getString("x", null, "y"));
+  }
+
+  @Test
+  @GerritConfigs({
+      @GerritConfig(name="x.y", value="z"),
+      @GerritConfig(name="a.b", value="c"),
+  })
+  public void testMultiple() {
+    assertEquals("z", serverConfig.getString("x", null, "y"));
+    assertEquals("c", serverConfig.getString("a", null, "b"));
+  }
+}
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/DeleteDraftPatchSet.java b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/DeleteDraftPatchSet.java
index adec292..d20b0f6 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/DeleteDraftPatchSet.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/changedetail/DeleteDraftPatchSet.java
@@ -112,9 +112,7 @@
       }
       if (change.currentPatchSetId().equals(patchSetId)) {
         try {
-          PatchSet.Id id =
-              new PatchSet.Id(patchSetId.getParentKey(), patchSetId.get() - 1);
-          change.setCurrentPatchSet(patchSetInfoFactory.get(db, id));
+          change.setCurrentPatchSet(patchSetInfoFactory.get(db, highestId));
         } catch (PatchSetInfoNotAvailableException e) {
           throw new NoSuchChangeException(changeId);
         }
diff --git a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
index 76d873a..24186b8 100644
--- a/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
+++ b/gerrit-server/src/main/java/com/google/gerrit/server/query/change/QueryChanges.java
@@ -160,6 +160,7 @@
         } else {
           changes = changes.subList(0, imp.getLimit());
         }
+        data.set(n, changes);
         more.set(n, true);
       }
     }