Merge changes I5552c999,I04931606

* changes:
  config.md: Fix link to config guide
  FindOwnersBackend: Fix incompatibility of handling globs
diff --git a/java/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcher.java b/java/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcher.java
index 2db7b6d..650ae50 100644
--- a/java/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcher.java
+++ b/java/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcher.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.plugins.codeowners.backend;
 
-import com.google.common.annotations.VisibleForTesting;
-import com.google.common.flogger.FluentLogger;
 import java.nio.file.Path;
 
 /**
@@ -28,8 +26,6 @@
  * </ul>
  */
 public class FindOwnersGlobMatcher implements PathExpressionMatcher {
-  private static final FluentLogger logger = FluentLogger.forEnclosingClass();
-
   /** Singleton instance. */
   public static FindOwnersGlobMatcher INSTANCE = new FindOwnersGlobMatcher();
 
@@ -38,43 +34,7 @@
 
   @Override
   public boolean matches(String glob, Path relativePath) {
-    String adaptedGlob = replaceSingleStarWithDoubleStar(glob);
-    logger.atFine().log("adapted glob = %s", adaptedGlob);
-    return GlobMatcher.INSTANCE.matches(adaptedGlob, relativePath);
-  }
-
-  /**
-   * Replaces any single '*' in the given glob with '**'. Non-single '*'s, like '**' or '***', stay
-   * unchanged.
-   *
-   * @param glob glob in which any single '*' should be replaced by '**'
-   */
-  @VisibleForTesting
-  String replaceSingleStarWithDoubleStar(String glob) {
-    StringBuilder adaptedGlob = new StringBuilder();
-    Character previousChar = null;
-    boolean maybeSingleStar = false;
-    for (char nextCharacter : glob.toCharArray()) {
-      if (maybeSingleStar && nextCharacter != '*') {
-        // the previous character was a '*' that was not preceded by '*' (maybeSingleStar == true),
-        // since the next character is not '*', we are now sure that the previous character was a
-        // single '*' which should be replaced by '**',
-        // to do this append another '*'
-        adaptedGlob.append('*');
-      }
-      adaptedGlob.append(nextCharacter);
-
-      // the current character may be a single '*' if it's not preceded by '*'
-      maybeSingleStar =
-          nextCharacter == '*' && (previousChar == null || previousChar.charValue() != '*');
-      previousChar = nextCharacter;
-    }
-
-    if (maybeSingleStar) {
-      // the last character was a '*' that was not preceded by '*'
-      adaptedGlob.append('*');
-    }
-
-    return adaptedGlob.toString();
+    // always match files in all subdirectories
+    return GlobMatcher.INSTANCE.matches("{**/,}" + glob, relativePath);
   }
 }
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcherTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcherTest.java
index 7e0c03e..8696f19 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcherTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/FindOwnersGlobMatcherTest.java
@@ -14,8 +14,6 @@
 
 package com.google.gerrit.plugins.codeowners.backend;
 
-import static com.google.common.truth.Truth.assertThat;
-
 import org.junit.Test;
 
 /** Tests for {@link FindOwnersGlobMatcher}. */
@@ -25,67 +23,68 @@
     return FindOwnersGlobMatcher.INSTANCE;
   }
 
-  @Test
-  public void singleStarInGlobIsReplacedWithDoubleStar() throws Exception {
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("*.md"))
-        .isEqualTo("**.md");
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("foo/*.md"))
-        .isEqualTo("foo/**.md");
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("*/foo/*.md"))
-        .isEqualTo("**/foo/**.md");
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("foo/*"))
-        .isEqualTo("foo/**");
-  }
-
-  @Test
-  public void doubleStarInGlobIsNotReplaced() throws Exception {
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("**.md"))
-        .isEqualTo("**.md");
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("foo/**.md"))
-        .isEqualTo("foo/**.md");
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("**/foo/**.md"))
-        .isEqualTo("**/foo/**.md");
-  }
-
-  @Test
-  public void tripleStarInGlobIsNotReplaced() throws Exception {
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("***.md"))
-        .isEqualTo("***.md");
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("foo/***.md"))
-        .isEqualTo("foo/***.md");
-    assertThat(FindOwnersGlobMatcher.INSTANCE.replaceSingleStarWithDoubleStar("***/foo/***.md"))
-        .isEqualTo("***/foo/***.md");
-  }
-
   /**
-   * This test differs from the base class ({@link GlobMatcherTest}), since for {@link
-   * FindOwnersGlobMatcher} {@code *} also matches slashes and the test in the base class has an
-   * assertion that checks that slashes are not matched by {@code *}.
+   * This test differs from the base class ({@link GlobMatcherTest}), since {@link
+   * FindOwnersGlobMatcher} matches globs against any subdirectory and the test in the base class
+   * checks that subdirectories are not matched.
    */
   @Test
   @Override
-  public void matchFileTypeInCurrentFolder() throws Exception {
-    String pathExpression = "*.md";
-    assertMatch(pathExpression, "README.md", "config.md");
-    assertNoMatch(pathExpression, "README", "README.md5");
-  }
-
-  @Test
-  public void matchFileTypeInCurrentFolderAndAllSubfoldersBySingleStar() throws Exception {
+  public void matchFileType() throws Exception {
     String pathExpression = "*.md";
     assertMatch(pathExpression, "README.md", "config.md", "foo/README.md", "foo/bar/README.md");
     assertNoMatch(pathExpression, "README", "README.md5");
   }
 
+  /**
+   * This test differs from the base class ({@link GlobMatcherTest}), since {@link
+   * FindOwnersGlobMatcher} matches globs against any subdirectory and the test in the base class
+   * checks that subdirectories are not matched.
+   */
   @Test
-  public void matchAllFilesInSubfolderBySingleStar() throws Exception {
-    String pathExpression = "foo/*";
+  @Override
+  public void matchConcreteFile() throws Exception {
+    String pathExpression = "BUILD";
+    assertMatch(pathExpression, "BUILD", "foo/BUILD", "foo/bar/BUILD");
+    assertNoMatch(pathExpression, "README", "BUILD2");
+  }
+
+  /**
+   * This test differs from the base class ({@link GlobMatcherTest}), since {@link
+   * FindOwnersGlobMatcher} matches globs against any subdirectory and the test in the base class
+   * checks that subdirectories are not matched.
+   */
+  @Test
+  @Override
+  public void matchAllFilesInSubfolder() throws Exception {
+    String pathExpression = "foo/**";
     assertMatch(
         pathExpression,
         "foo/README.md",
         "foo/config.txt",
         "foo/bar/README.md",
-        "foo/bar/baz/README.md");
-    assertNoMatch(pathExpression, "README", "foo2/README");
+        "foo/bar/baz/README.md",
+        "bar/foo/README.md",
+        "bar/foo/config.txt",
+        "bar/foo/bar/README.md",
+        "bar/foo/bar/baz/README.md");
+    assertNoMatch(pathExpression, "README", "foo2/README", "bar/README", "bar/foo2/README");
+  }
+
+  /**
+   * This test differs from the base class ({@link GlobMatcherTest}), since {@link
+   * FindOwnersGlobMatcher} matches globs against any subdirectory and the test in the base class
+   * checks that subdirectories are not matched.
+   */
+  @Test
+  @Override
+  public void matchPattern() throws Exception {
+    String pathExpression = "{**/,}foo-[1-4].txt";
+    assertMatch(pathExpression, "foo-1.txt", "foo-2.txt", "sub/foo-3.txt", "sub/sub/foo-4.txt");
+    assertNoMatch(pathExpression, "foo-5.txt", "foo-11.txt");
+
+    String pathExpression2 = "foo-[1-4].txt";
+    assertMatch(pathExpression2, "foo-1.txt", "foo-2.txt", "sub/foo-3.txt", "sub/sub/foo-4.txt");
+    assertNoMatch(pathExpression2, "foo-5.txt", "foo-11.txt");
   }
 }
diff --git a/javatests/com/google/gerrit/plugins/codeowners/backend/GlobMatcherTest.java b/javatests/com/google/gerrit/plugins/codeowners/backend/GlobMatcherTest.java
index 75affd9..5291bb7 100644
--- a/javatests/com/google/gerrit/plugins/codeowners/backend/GlobMatcherTest.java
+++ b/javatests/com/google/gerrit/plugins/codeowners/backend/GlobMatcherTest.java
@@ -24,17 +24,17 @@
   }
 
   @Test
-  public void matchConcreteFileInCurrentFolder() throws Exception {
+  public void matchConcreteFile() throws Exception {
     String pathExpression = "BUILD";
     assertMatch(pathExpression, "BUILD");
-    assertNoMatch(pathExpression, "README", "BUILD2", "foo/BUILD");
+    assertNoMatch(pathExpression, "README", "BUILD2", "foo/BUILD", "foo/bar/BUILD");
   }
 
   @Test
-  public void matchFileTypeInCurrentFolder() throws Exception {
+  public void matchFileType() throws Exception {
     String pathExpression = "*.md";
     assertMatch(pathExpression, "README.md", "config.md");
-    assertNoMatch(pathExpression, "README", "README.md5", "foo/README.md");
+    assertNoMatch(pathExpression, "README", "README.md5", "foo/README.md", "foo/bar/README.md");
   }
 
   @Test
@@ -60,7 +60,16 @@
         "foo/config.txt",
         "foo/bar/README.md",
         "foo/bar/baz/README.md");
-    assertNoMatch(pathExpression, "README", "foo2/README");
+    assertNoMatch(
+        pathExpression,
+        "README",
+        "foo2/README",
+        "bar/README",
+        "bar/foo2/README",
+        "bar/foo/README.md",
+        "bar/foo/config.txt",
+        "bar/foo/bar/README.md",
+        "bar/foo/bar/baz/README.md");
   }
 
   @Test
@@ -68,5 +77,9 @@
     String pathExpression = "{**/,}foo-[1-4].txt";
     assertMatch(pathExpression, "foo-1.txt", "foo-2.txt", "sub/foo-3.txt", "sub/sub/foo-4.txt");
     assertNoMatch(pathExpression, "foo-5.txt", "foo-11.txt");
+
+    String pathExpression2 = "foo-[1-4].txt";
+    assertMatch(pathExpression2, "foo-1.txt", "foo-2.txt");
+    assertNoMatch(pathExpression2, "foo-5.txt", "foo-11.txt", "sub/foo-3.txt", "sub/sub/foo-4.txt");
   }
 }
diff --git a/resources/Documentation/config.md b/resources/Documentation/config.md
index aa9c064..9c99988 100644
--- a/resources/Documentation/config.md
+++ b/resources/Documentation/config.md
@@ -4,7 +4,7 @@
 file in the `plugin.@PLUGIN@` subsection.
 
 This page describes all available configuration parameters. For configuration
-recommendations please consult the [config guide](#config-guide.html).
+recommendations please consult the [config guide](config-guide.html).
 
 ## <a id="projectLevelConfigFile">
 In addition some configuration can be done on the project level in
diff --git a/resources/Documentation/path-expressions.md b/resources/Documentation/path-expressions.md
index 322d757..e5c2dd8 100644
--- a/resources/Documentation/path-expressions.md
+++ b/resources/Documentation/path-expressions.md
@@ -7,15 +7,18 @@
 
 Which syntax is used depends on the used code owner backend:
 
-* [find-owners](backend-find-owners.html) backend: uses [globs](#globs)
-* [proto](backend-proto.html) backend: uses
-  [simple path expressions](#simplePathExpressions)
+* [find-owners](backend-find-owners.html) backend:
+  uses [globs](#globs), but each glob is automatically prefixed with `{**/,}`
+  so that subfolders are always matched (e.g. `*.md` matches all md files in all
+  subfolders, rather then only md files in the current folder)
+* [proto](backend-proto.html) backend:
+  uses [simple path expressions](#simplePathExpressions)
 
 ## <a id="globs">Globs
 
 Globs support the following wildcards:
 
-* `*`/`**`: matches any string, including slashes
+* `*`: matches any string, including slashes
 * `?`: matches any character
 * `[abc]`: matches one character given in the bracket
 * `[a-c]`: matches one character from the range given in the bracket
@@ -34,15 +37,15 @@
 
 ## <a id="examples">Examples
 
-| To Match | Glob | Simple Path Expression |
-| -------- | ---- | ---------------------- |
-| Concrete file in current folder | `BUILD` | `BUILD` |
-| File type in current folder | not possible | `*.md` |
-| Concrete file in the current folder and in all subfolders | `{**/,}BUILD` | needs 2 expressions: `BUILD` + `.../BUILD` |
-| File type in the current folder and in all subfolders | `**.md` or `*.md` | `....md` |
-| All files in a subfolder | `my-folder/**` | `my-folder/...` |
-| All “foo-<1-digit-number>.txt” files in all subfolders | `{**/,}foo-[0-9].txt` | not possible |
-| All “foo-<n-digit-number>.txt” files in all subfolders | not possible | not possible |
+| To Match | Glob | find-owners | Simple Path Expression |
+| -------- | ---- | ----------- | ---------------------- |
+| Concrete file in current folder | `BUILD` | not possible | `BUILD` |
+| File type in current folder | `*.md` | not possible | `*.md` |
+| Concrete file in the current folder and in all subfolders | `{**/,}BUILD` | `BUILD` | needs 2 expressions: `BUILD` + `.../BUILD` |
+| File type in the current folder and in all subfolders | `**.md` | `*.md` or `**.md` | `....md` |
+| All files in a subfolder | `my-folder/**` | not possible, but you can add a `my-folder/OWNERS` file instead of using a glob | `my-folder/...` |
+| All “foo-<1-digit-number>.txt” files in all subfolders | `{**/,}foo-[0-9].txt` | `foo-[0-9].txt` |not possible |
+| All “foo-<n-digit-number>.txt” files in all subfolders | not possible | not possible | not possible
 
 ---