Merge "Cache sorted ref lists in CommitData.getRefsById"
diff --git a/Documentation/config.md b/Documentation/config.md
index 29f328f..c9c7448 100644
--- a/Documentation/config.md
+++ b/Documentation/config.md
@@ -186,6 +186,10 @@
* `blocknote`: Gitiles style note/promo/aside blocks to raise
awareness to important content. Default false.
+* `frontmatter`: strip a leading YAML front matter block (delimited
+ by `---` lines) from the rendered output instead of treating it as
+ document content. Default false.
+
* `ghthematicbreak`: accept `--` for `<hr>`, like GitHub Flavor
Markdown. Default follows `githubFlavor`.
diff --git a/Documentation/markdown.md b/Documentation/markdown.md
index 4c76388..83f14d7 100644
--- a/Documentation/markdown.md
+++ b/Documentation/markdown.md
@@ -474,6 +474,26 @@
Gitiles includes additional extensions to the Markdown language that
make documentation writing for the web easier without using raw HTML.
+### Front matter
+
+Requires `markdown.frontmatter` to be true.
+
+Files authored for static site generators (such as Jekyll or Hugo)
+often begin with a YAML *front matter* block delimited by `---` lines:
+
+```
+---
+title: My page
+layout: post
+---
+
+# My page
+```
+
+When enabled, Gitiles strips this leading block from the rendered
+output instead of displaying the raw `key: value` metadata as
+document content. The block must begin on the first line of the file.
+
### Table of contents
Requires `markdown.toc` to be true.
diff --git a/external_deps.lock.json b/external_deps.lock.json
index 3410784..36df3f9 100755
--- a/external_deps.lock.json
+++ b/external_deps.lock.json
@@ -23,8 +23,9 @@
"com.jcraft:jzlib": 864660349,
"commons-codec:commons-codec": -1724312797,
"commons-io:commons-io": -1271816170,
- "commons-logging:commons-logging": -1374531806,
+ "commons-logging:commons-logging": -916690979,
"io.github.java-diff-utils:java-diff-utils": 1296992014,
+ "jakarta.servlet:jakarta.servlet-api": -3550902,
"javax.servlet:javax.servlet-api": 669233360,
"junit:junit": -744267592,
"net.bytebuddy:byte-buddy": 1769080559,
@@ -49,6 +50,8 @@
"org.commonmark:commonmark-ext-autolink": -1853742120,
"org.commonmark:commonmark-ext-gfm-strikethrough": 350394231,
"org.commonmark:commonmark-ext-gfm-tables": 1881582931,
+ "org.commonmark:commonmark-ext-yaml-front-matter": -1519651186,
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet": 835495267,
"org.eclipse.jetty.ee8:jetty-ee8-nested": 1867536598,
"org.eclipse.jetty.ee8:jetty-ee8-security": -374913491,
"org.eclipse.jetty.ee8:jetty-ee8-servlet": 894276934,
@@ -134,12 +137,14 @@
"commons-codec:commons-codec:jar:sources": -499809306,
"commons-io:commons-io": 1539755249,
"commons-io:commons-io:jar:sources": 614234247,
- "commons-logging:commons-logging": -102005530,
- "commons-logging:commons-logging:jar:sources": 147282524,
+ "commons-logging:commons-logging": -2039356713,
+ "commons-logging:commons-logging:jar:sources": 494659498,
"io.github.java-diff-utils:java-diff-utils": -1180802694,
"io.github.java-diff-utils:java-diff-utils:jar:sources": 1551226070,
"jakarta.inject:jakarta.inject-api": 188991469,
"jakarta.inject:jakarta.inject-api:jar:sources": -1584622703,
+ "jakarta.servlet:jakarta.servlet-api": -1421863120,
+ "jakarta.servlet:jakarta.servlet-api:jar:sources": -2084357823,
"javax.activation:activation": -1387662613,
"javax.activation:activation:jar:sources": -848464236,
"javax.annotation:jsr250-api": 847275234,
@@ -173,7 +178,7 @@
"org.apache.commons:commons-math3:jar:sources": -2132756896,
"org.apache.commons:commons-text": 1458185425,
"org.apache.commons:commons-text:jar:sources": -596520490,
- "org.apache.httpcomponents:httpclient": 726829008,
+ "org.apache.httpcomponents:httpclient": -569179945,
"org.apache.httpcomponents:httpclient:jar:sources": -779528763,
"org.apache.httpcomponents:httpcore": 67453319,
"org.apache.httpcomponents:httpcore:jar:sources": 2134002822,
@@ -204,7 +209,11 @@
"org.commonmark:commonmark-ext-gfm-strikethrough:jar:sources": 992870423,
"org.commonmark:commonmark-ext-gfm-tables": -1205584749,
"org.commonmark:commonmark-ext-gfm-tables:jar:sources": 1341057091,
+ "org.commonmark:commonmark-ext-yaml-front-matter": 1918089254,
+ "org.commonmark:commonmark-ext-yaml-front-matter:jar:sources": -902462472,
"org.commonmark:commonmark:jar:sources": -1511261547,
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet": 349394374,
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet:jar:sources": 501419503,
"org.eclipse.jetty.ee8:jetty-ee8-nested": -65830655,
"org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources": -2141029892,
"org.eclipse.jetty.ee8:jetty-ee8-security": -1787377969,
@@ -474,10 +483,10 @@
},
"commons-logging:commons-logging": {
"shasums": {
- "jar": "f8ead8943401081dea0aa824b5b1ba40a0e4ed297a572a0f02258150a0b62357",
- "sources": "6e821e03cfc64e509cc162d428af84697b6c4188eb14c0b137c75b69649976ef"
+ "jar": "d175dbd751dd782a63bde28c7a039520e971f25e84b79c19b8435edc3603e0dc",
+ "sources": "28104e5057a11c6e9cda72e973d767b3a0185e65363fede91bc848bc6a309634"
},
- "version": "1.3.6"
+ "version": "1.4.0"
},
"io.github.java-diff-utils:java-diff-utils": {
"shasums": {
@@ -493,6 +502,13 @@
},
"version": "2.0.1"
},
+ "jakarta.servlet:jakarta.servlet-api": {
+ "shasums": {
+ "jar": "8a31f465f3593bf2351531a5c952014eb839da96a605b5825b93dd54714c48c4",
+ "sources": "6eb958543e0548bb93d2519e40224d13c8003b10cc615b5652bfd9899350bfb4"
+ },
+ "version": "6.1.0"
+ },
"javax.activation:activation": {
"shasums": {
"jar": "2881c79c9d6ef01c58e62beea13e9d1ac8b8baa16f2fc198ad6e6776defdcdd3",
@@ -723,6 +739,20 @@
},
"version": "0.24.0"
},
+ "org.commonmark:commonmark-ext-yaml-front-matter": {
+ "shasums": {
+ "jar": "a845baba681ccbf385695fbaa6d58eb40d5ecc68f3edb968c42f074f630e8fec",
+ "sources": "5cee64663842f1e7128af9c3e62ef183ea2cf205436d27a08b7cb5be0b20edb6"
+ },
+ "version": "0.24.0"
+ },
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet": {
+ "shasums": {
+ "jar": "05cccc74bbe1478ceb765e967b1cb1a3c8d9b51e99acf586190887e467ffd87d",
+ "sources": "51a734b4bc04235b24ccd759515dd2947e5b675c227c556f02262b765bce47eb"
+ },
+ "version": "12.1.10"
+ },
"org.eclipse.jetty.ee8:jetty-ee8-nested": {
"shasums": {
"jar": "240df6fb5bb28545183cbac87f191c0d21074b17a2c9d4562581a68a8d79c347",
@@ -1068,6 +1098,16 @@
"org.commonmark:commonmark-ext-gfm-tables": [
"org.commonmark:commonmark"
],
+ "org.commonmark:commonmark-ext-yaml-front-matter": [
+ "org.commonmark:commonmark"
+ ],
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet": [
+ "jakarta.servlet:jakarta.servlet-api",
+ "org.eclipse.jetty:jetty-security",
+ "org.eclipse.jetty:jetty-server",
+ "org.eclipse.jetty:jetty-session",
+ "org.slf4j:slf4j-api"
+ ],
"org.eclipse.jetty.ee8:jetty-ee8-nested": [
"org.eclipse.jetty.toolchain:jetty-servlet-api",
"org.eclipse.jetty:jetty-http",
@@ -1414,7 +1454,8 @@
],
"commons-logging:commons-logging": [
"org.apache.commons.logging",
- "org.apache.commons.logging.impl"
+ "org.apache.commons.logging.impl",
+ "org.apache.commons.logging.jakarta"
],
"io.github.java-diff-utils:java-diff-utils": [
"com.github.difflib",
@@ -1428,6 +1469,12 @@
"jakarta.inject:jakarta.inject-api": [
"jakarta.inject"
],
+ "jakarta.servlet:jakarta.servlet-api": [
+ "jakarta.servlet",
+ "jakarta.servlet.annotation",
+ "jakarta.servlet.descriptor",
+ "jakarta.servlet.http"
+ ],
"javax.activation:activation": [
"com.sun.activation.registries",
"com.sun.activation.viewers",
@@ -2413,6 +2460,19 @@
"org.commonmark.ext.gfm.tables",
"org.commonmark.ext.gfm.tables.internal"
],
+ "org.commonmark:commonmark-ext-yaml-front-matter": [
+ "org.commonmark.ext.front.matter",
+ "org.commonmark.ext.front.matter.internal"
+ ],
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet": [
+ "org.eclipse.jetty.ee10.servlet",
+ "org.eclipse.jetty.ee10.servlet.internal",
+ "org.eclipse.jetty.ee10.servlet.jmx",
+ "org.eclipse.jetty.ee10.servlet.listener",
+ "org.eclipse.jetty.ee10.servlet.security",
+ "org.eclipse.jetty.ee10.servlet.security.authentication",
+ "org.eclipse.jetty.ee10.servlet.util"
+ ],
"org.eclipse.jetty.ee8:jetty-ee8-nested": [
"org.eclipse.jetty.ee8.nested",
"org.eclipse.jetty.ee8.nested.jmx"
@@ -2711,6 +2771,8 @@
"io.github.java-diff-utils:java-diff-utils:jar:sources",
"jakarta.inject:jakarta.inject-api",
"jakarta.inject:jakarta.inject-api:jar:sources",
+ "jakarta.servlet:jakarta.servlet-api",
+ "jakarta.servlet:jakarta.servlet-api:jar:sources",
"javax.activation:activation",
"javax.activation:activation:jar:sources",
"javax.annotation:jsr250-api",
@@ -2774,7 +2836,11 @@
"org.commonmark:commonmark-ext-gfm-strikethrough:jar:sources",
"org.commonmark:commonmark-ext-gfm-tables",
"org.commonmark:commonmark-ext-gfm-tables:jar:sources",
+ "org.commonmark:commonmark-ext-yaml-front-matter",
+ "org.commonmark:commonmark-ext-yaml-front-matter:jar:sources",
"org.commonmark:commonmark:jar:sources",
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet",
+ "org.eclipse.jetty.ee10:jetty-ee10-servlet:jar:sources",
"org.eclipse.jetty.ee8:jetty-ee8-nested",
"org.eclipse.jetty.ee8:jetty-ee8-nested:jar:sources",
"org.eclipse.jetty.ee8:jetty-ee8-security",
diff --git a/java/com/google/gitiles/BUILD b/java/com/google/gitiles/BUILD
index 84e8166..404f413 100644
--- a/java/com/google/gitiles/BUILD
+++ b/java/com/google/gitiles/BUILD
@@ -6,6 +6,7 @@
"//java/com/google/gitiles/blame/cache",
"//lib:autolink",
"//lib:cm-autolink",
+ "//lib:cm-yaml-front-matter",
"//lib:commonmark",
"//lib:commons-io",
"//lib:commons-lang3",
diff --git a/java/com/google/gitiles/doc/GitilesMarkdown.java b/java/com/google/gitiles/doc/GitilesMarkdown.java
index e094931..f4c7d6e 100644
--- a/java/com/google/gitiles/doc/GitilesMarkdown.java
+++ b/java/com/google/gitiles/doc/GitilesMarkdown.java
@@ -18,6 +18,7 @@
import java.util.List;
import org.commonmark.Extension;
import org.commonmark.ext.autolink.AutolinkExtension;
+import org.commonmark.ext.front.matter.YamlFrontMatterExtension;
import org.commonmark.ext.gfm.strikethrough.StrikethroughExtension;
import org.commonmark.ext.gfm.tables.TablesExtension;
import org.commonmark.node.Node;
@@ -38,6 +39,9 @@
if (cfg.blockNote) {
ext.add(BlockNoteExtension.create());
}
+ if (cfg.frontMatter) {
+ ext.add(YamlFrontMatterExtension.create());
+ }
if (cfg.safeHtml) {
ext.add(GitilesHtmlExtension.create());
}
diff --git a/java/com/google/gitiles/doc/MarkdownConfig.java b/java/com/google/gitiles/doc/MarkdownConfig.java
index 4758654..53d979a 100644
--- a/java/com/google/gitiles/doc/MarkdownConfig.java
+++ b/java/com/google/gitiles/doc/MarkdownConfig.java
@@ -44,6 +44,7 @@
final boolean autoLink;
final boolean blockNote;
+ final boolean frontMatter;
final boolean ghThematicBreak;
final boolean multiColumn;
final boolean namedAnchor;
@@ -65,6 +66,7 @@
boolean githubFlavor = cfg.getBoolean("markdown", "githubFlavor", true);
autoLink = cfg.getBoolean("markdown", "autolink", githubFlavor);
blockNote = cfg.getBoolean("markdown", "blocknote", false);
+ frontMatter = cfg.getBoolean("markdown", "frontmatter", false);
ghThematicBreak = cfg.getBoolean("markdown", "ghthematicbreak", githubFlavor);
multiColumn = cfg.getBoolean("markdown", "multicolumn", false);
namedAnchor = cfg.getBoolean("markdown", "namedanchor", false);
@@ -94,6 +96,7 @@
autoLink = on("autolink", p.autoLink, enable, disable);
blockNote = on("blocknote", p.blockNote, enable, disable);
+ frontMatter = on("frontmatter", p.frontMatter, enable, disable);
ghThematicBreak = on("ghthematicbreak", p.ghThematicBreak, enable, disable);
multiColumn = on("multicolumn", p.multiColumn, enable, disable);
namedAnchor = on("namedanchor", p.namedAnchor, enable, disable);
diff --git a/java/com/google/gitiles/doc/MarkdownToHtml.java b/java/com/google/gitiles/doc/MarkdownToHtml.java
index cb8afae..f0518e6 100644
--- a/java/com/google/gitiles/doc/MarkdownToHtml.java
+++ b/java/com/google/gitiles/doc/MarkdownToHtml.java
@@ -27,6 +27,7 @@
import java.util.Collections;
import java.util.List;
import javax.annotation.Nullable;
+import org.commonmark.ext.front.matter.YamlFrontMatterBlock;
import org.commonmark.ext.gfm.strikethrough.Strikethrough;
import org.commonmark.ext.gfm.tables.TableBlock;
import org.commonmark.ext.gfm.tables.TableBody;
@@ -576,6 +577,10 @@
visit((TableBlock) node);
} else if (node instanceof TocBlock) {
toc.format();
+ } else if (node instanceof YamlFrontMatterBlock) {
+ // YAML front matter is document metadata: omit the whole block. We
+ // intentionally do not recurse into it, so its YamlFrontMatterNode
+ // children are never visited and need no visit(CustomNode) handling.
} else {
throw new IllegalArgumentException("cannot render " + node.getClass());
}
diff --git a/javatests/com/google/gitiles/BUILD b/javatests/com/google/gitiles/BUILD
index 692537d..b2410ff 100644
--- a/javatests/com/google/gitiles/BUILD
+++ b/javatests/com/google/gitiles/BUILD
@@ -42,8 +42,10 @@
"//java/com/google/gitiles:servlet",
":testutil",
"//lib:servlet-api",
+ "//lib:commonmark",
"//lib:commons-codec",
"//lib:commons-io",
+ "//lib:html-types",
"//lib/truth",
"//lib:jgit-junit",
"//lib/junit",
diff --git a/javatests/com/google/gitiles/doc/GitilesMarkdownTest.java b/javatests/com/google/gitiles/doc/GitilesMarkdownTest.java
new file mode 100644
index 0000000..74e4c3e
--- /dev/null
+++ b/javatests/com/google/gitiles/doc/GitilesMarkdownTest.java
@@ -0,0 +1,98 @@
+// Copyright (C) 2026 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.gitiles.doc;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.common.html.types.SafeHtml;
+import com.google.gitiles.GitilesView;
+import org.commonmark.node.Node;
+import org.eclipse.jgit.lib.Config;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.JUnit4;
+
+/** Tests for YAML front matter handling in {@link GitilesMarkdown}. */
+@RunWith(JUnit4.class)
+public class GitilesMarkdownTest {
+ private static final String MARKDOWN =
+ "---\ntitle: Kittens\nlayout: post\n---\n\n# Heading\n\nBody text.\n";
+
+ @Test
+ public void frontMatterStrippedWhenEnabled() {
+ String html = render(MARKDOWN, /* frontMatter= */ true);
+ assertThat(html).doesNotContain("title: Kittens");
+ assertThat(html).doesNotContain("layout: post");
+ assertThat(html).contains("Heading</h1>");
+ assertThat(html).contains("Body text.");
+ }
+
+ @Test
+ public void frontMatterNodeChildrenAreStrippedWithoutError() {
+ // Front matter parses into a YamlFrontMatterBlock whose YamlFrontMatterNode
+ // (CustomNode) children hold the key/value data, including list values.
+ // visit(CustomBlock) omits the block without recursing, so those children
+ // are never visited: rendering neither throws (as it would for an unhandled
+ // CustomNode) nor leaks the metadata.
+ String markdown =
+ "---\n"
+ + "title: Kittens\n"
+ + "tags:\n"
+ + " - cats\n"
+ + " - fluffy\n"
+ + "---\n"
+ + "\n"
+ + "# Heading\n"
+ + "\n"
+ + "Body text.\n";
+ String html = render(markdown, /* frontMatter= */ true);
+ assertThat(html).doesNotContain("Kittens");
+ assertThat(html).doesNotContain("cats");
+ assertThat(html).doesNotContain("fluffy");
+ assertThat(html).contains("Heading</h1>");
+ assertThat(html).contains("Body text.");
+ }
+
+ @Test
+ public void frontMatterRenderedAsContentWhenDisabled() {
+ // Without the extension the delimiters and keys are parsed as ordinary
+ // Markdown, leaking the raw metadata into the output. This is the behavior
+ // markdown.frontmatter is meant to fix.
+ String html = render(MARKDOWN, /* frontMatter= */ false);
+ assertThat(html).contains("title: Kittens");
+ }
+
+ private static String render(String markdown, boolean frontMatter) {
+ Config cfg = new Config();
+ cfg.setBoolean("markdown", null, "frontmatter", frontMatter);
+ MarkdownConfig mc = new MarkdownConfig(cfg);
+ Node node = GitilesMarkdown.parse(mc, markdown);
+ GitilesView view =
+ GitilesView.revision()
+ .setHostName("127.0.0.1")
+ .setServletPath("/g")
+ .setRepositoryName("repo")
+ .setRevision("HEAD")
+ .build();
+ SafeHtml html =
+ MarkdownToHtml.builder()
+ .setGitilesView(view)
+ .setConfig(mc)
+ .setFilePath("index.md")
+ .build()
+ .toSoyHtml(node);
+ return html == null ? "" : html.getSafeHtmlString();
+ }
+}
diff --git a/lib/BUILD b/lib/BUILD
index a36e385..bd8b90a 100644
--- a/lib/BUILD
+++ b/lib/BUILD
@@ -17,6 +17,7 @@
("cm-autolink", "@external_deps//:org_commonmark_commonmark_ext_autolink"),
("gfm-strikethrough", "@external_deps//:org_commonmark_commonmark_ext_gfm_strikethrough"),
("gfm-tables", "@external_deps//:org_commonmark_commonmark_ext_gfm_tables"),
+ ("cm-yaml-front-matter", "@external_deps//:org_commonmark_commonmark_ext_yaml_front_matter"),
("html-types", "@external_deps//:com_google_common_html_types_types"),
("jsr305", "@external_deps//:com_google_code_findbugs_jsr305"),
("servlet-api", "@external_deps//:javax_servlet_javax_servlet_api"),
@@ -77,7 +78,7 @@
java_library(
name = "jgit-servlet",
visibility = ["//visibility:public"],
- exports = ["@jgit//org.eclipse.jgit.http.server:jgit-servlet"],
+ exports = ["@jgit//org.eclipse.jgit.http.server.ee8:jgit-servlet-ee8"],
runtime_deps = [":jgit"],
)
diff --git a/modules/jgit b/modules/jgit
index c89601f..200be16 160000
--- a/modules/jgit
+++ b/modules/jgit
@@ -1 +1 @@
-Subproject commit c89601f3395d6128a681d62b8986f42197c39a4f
+Subproject commit 200be16661d3b9983bd34affd8e424daf5ad23d7
diff --git a/tools/java_deps.MODULE.bazel b/tools/java_deps.MODULE.bazel
index 77836a2..5a5a4e9 100644
--- a/tools/java_deps.MODULE.bazel
+++ b/tools/java_deps.MODULE.bazel
@@ -101,6 +101,7 @@
"org.commonmark:commonmark-ext-autolink:" + COMMONMARK_VERSION,
"org.commonmark:commonmark-ext-gfm-strikethrough:" + COMMONMARK_VERSION,
"org.commonmark:commonmark-ext-gfm-tables:" + COMMONMARK_VERSION,
+ "org.commonmark:commonmark-ext-yaml-front-matter:" + COMMONMARK_VERSION,
"org.eclipse.jetty:jetty-http:" + JETTY_VERSION,
"org.eclipse.jetty:jetty-io:" + JETTY_VERSION,
"org.eclipse.jetty:jetty-security:" + JETTY_VERSION,