Merge changes I238c263b,Ifaa6867b,Id42dcb9e,Ie31b1db1,I3fca94f2, ... * changes: Update JGit to b1cc74b75 Update jetty to 9.4.53.v20231009 Update byte-buddy to 1.14.9 Update org.mockito:mockito-core to 5.6.0 Update com.ibm.icu:icu4j to 74.2 Update ch.qos.reload4j:reload4j to 1.2.25 Update com.google.code.gson:gson to 2.10.1 Update commons-codec:commons-codec to 1.16.0 Update com.googlecode.javaewah:JavaEWAH to 1.2.3 Update org.apache.commons:commons-compress to 1.25.0
diff --git a/.bazelrc b/.bazelrc index ac89666..7c7def3 100644 --- a/.bazelrc +++ b/.bazelrc
@@ -1,3 +1,7 @@ +# TODO(davido): Migrate all dependencies from WORKSPACE to MODULE.bazel +# https://issues.gerritcodereview.com/issues/303819949 +common --noenable_bzlmod + build --workspace_status_command="python3 ./tools/workspace_status.py" build --repository_cache=~/.gerritcodereview/bazel-cache/repository build --experimental_strict_action_env
diff --git a/.bazelversion b/.bazelversion index 91e4a9f..9fe9ff9 100644 --- a/.bazelversion +++ b/.bazelversion
@@ -1 +1 @@ -6.3.2 +7.0.1
diff --git a/WORKSPACE b/WORKSPACE index bdf86a3..2ff406b 100644 --- a/WORKSPACE +++ b/WORKSPACE
@@ -12,7 +12,7 @@ load("//tools:bazlets.bzl", "load_bazlets") load_bazlets( - commit = "e68cc7a45d9ee2b100024b9b12533b50a4598585", + commit = "50f43f450f2178425b26d5b2a2442ac3acd07f37", # local_path = "/home/<user>/projects/bazlets", ) @@ -140,8 +140,8 @@ maven_jar( name = "soy", - artifact = "com.google.template:soy:2023-12-13", - sha1 = "8b63495fba832cd93c8474a11812668876fee05c", + artifact = "com.google.template:soy:2024-01-30", + sha1 = "6e9ccb00926325c7a9293ed05a2eaf56ea15d60e", ) FLOGGER_VERS = "0.7.4"
diff --git a/java/com/google/gitiles/BlobSoyData.java b/java/com/google/gitiles/BlobSoyData.java index a6879ae..c90b434 100644 --- a/java/com/google/gitiles/BlobSoyData.java +++ b/java/com/google/gitiles/BlobSoyData.java
@@ -26,6 +26,7 @@ import com.google.template.soy.data.SoyListData; import com.google.template.soy.data.SoyMapData; import java.io.IOException; +import java.net.URI; import java.util.List; import java.util.Map; import javax.annotation.Nullable; @@ -79,6 +80,11 @@ public Map<String, Object> toSoyData(String path, ObjectId blobId) throws MissingObjectException, IOException { + return toSoyData(path, blobId, null); + } + + public Map<String, Object> toSoyData(String path, ObjectId blobId, @Nullable URI editUrl) + throws MissingObjectException, IOException { Map<String, Object> data = Maps.newHashMapWithExpectedSize(4); data.put("sha", ObjectId.toString(blobId)); @@ -119,6 +125,9 @@ data.put("fileUrl", GitilesView.path().copyFrom(view).toUrl()); data.put("logUrl", GitilesView.log().copyFrom(view).toUrl()); data.put("blameUrl", GitilesView.blame().copyFrom(view).toUrl()); + if (editUrl != null) { + data.put("editUrl", editUrl.toString()); + } if (imageBlob != null) { data.put("imgBlob", imageBlob); }
diff --git a/java/com/google/gitiles/GitilesView.java b/java/com/google/gitiles/GitilesView.java index 9bb43a9..f5be973 100644 --- a/java/com/google/gitiles/GitilesView.java +++ b/java/com/google/gitiles/GitilesView.java
@@ -32,6 +32,7 @@ import com.google.common.collect.LinkedListMultimap; import com.google.common.collect.ListMultimap; import com.google.common.collect.Multimaps; +import com.google.errorprone.annotations.CanIgnoreReturnValue; import java.io.UnsupportedEncodingException; import java.net.URLEncoder; import java.util.ArrayList; @@ -299,6 +300,12 @@ return this; } + @CanIgnoreReturnValue + public Builder removeParam(String key) { + params.removeAll(key); + return this; + } + public Builder putAllParams(Map<String, String[]> params) { for (Map.Entry<String, String[]> e : params.entrySet()) { this.params.putAll(e.getKey(), Arrays.asList(e.getValue()));
diff --git a/java/com/google/gitiles/LogSoyData.java b/java/com/google/gitiles/LogSoyData.java index 061ce23..4292bc9 100644 --- a/java/com/google/gitiles/LogSoyData.java +++ b/java/com/google/gitiles/LogSoyData.java
@@ -71,7 +71,10 @@ this.view = checkNotNull(ViewFilter.getView(req)); checkNotNull(pretty); Config config = access.getConfig(); - fields = config.getBoolean("logFormat", pretty, "verbose", false) ? VERBOSE_FIELDS : FIELDS; + fields = + config.getBoolean("logFormat", pretty, "verbose", false) || pretty.equals("fuller") + ? VERBOSE_FIELDS + : FIELDS; variant = firstNonNull(config.getString("logFormat", pretty, "variant"), pretty); } @@ -120,7 +123,9 @@ ObjectId prev = paginator.getPreviousStart(); if (prev != null) { GitilesView.Builder prevView = copyAndCanonicalizeView(revision); - if (!prevView.getRevision().getId().equals(prev)) { + if (prevView.getRevision().getId().equals(prev)) { + prevView.removeParam(LogServlet.START_PARAM); + } else { prevView.replaceParam(LogServlet.START_PARAM, prev.name()); } data.put("previousUrl", prevView.toUrl());
diff --git a/java/com/google/gitiles/PathServlet.java b/java/com/google/gitiles/PathServlet.java index 87d2680..fc0af05 100644 --- a/java/com/google/gitiles/PathServlet.java +++ b/java/com/google/gitiles/PathServlet.java
@@ -15,6 +15,7 @@ package com.google.gitiles; import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.gitiles.GitilesUrls.escapeName; import static com.google.gitiles.TreeSoyData.resolveTargetUrl; import static org.eclipse.jgit.lib.Constants.OBJ_BLOB; import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT; @@ -31,6 +32,8 @@ import java.io.IOException; import java.io.OutputStream; import java.io.Writer; +import java.net.URI; +import java.net.URISyntaxException; import java.util.List; import java.util.Map; import java.util.regex.Pattern; @@ -49,6 +52,8 @@ import org.eclipse.jgit.lib.ObjectId; import org.eclipse.jgit.lib.ObjectLoader; import org.eclipse.jgit.lib.ObjectReader; +import org.eclipse.jgit.lib.Ref; +import org.eclipse.jgit.lib.RefDatabase; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevObject; @@ -61,11 +66,14 @@ import org.eclipse.jgit.util.QuotedString; import org.eclipse.jgit.util.RawParseUtils; import org.eclipse.jgit.util.StringUtils; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; /** Serves an HTML page with detailed information about a path within a tree. */ // TODO(dborowitz): Handle non-UTF-8 names. public class PathServlet extends BaseServlet { private static final long serialVersionUID = 1L; + private static final Logger log = LoggerFactory.getLogger(PathServlet.class); static final String MODE_HEADER = "X-Gitiles-Path-Mode"; static final String TYPE_HEADER = "X-Gitiles-Object-Type"; @@ -532,10 +540,34 @@ return p.eof() ? p : null; } + private @Nullable URI createEditUrl(HttpServletRequest req, GitilesView view) + throws IOException { + String baseGerritUrl = this.urls.getBaseGerritUrl(req); + if (baseGerritUrl == null) { + return null; + } + String commitish = view.getRevision().getName(); + if (commitish == null || !commitish.startsWith("refs/heads/")) { + return null; + } + try { + URI editUrl = new URI(baseGerritUrl); + String path = + String.format("/admin/repos/edit/repo/%s/branch/%s/file/%s", + view.getRepositoryName(), + commitish, + view.getPathPart()); + return editUrl.resolve(escapeName(path)); + } catch (URISyntaxException use) { + log.warn("Malformed Edit URL", use); + } + return null; + } + private void showFile(HttpServletRequest req, HttpServletResponse res, WalkResult wr) throws IOException { GitilesView view = ViewFilter.getView(req); - Map<String, ?> data = new BlobSoyData(wr.getObjectReader(), view).toSoyData(wr.path, wr.id); + Map<String, ?> data = new BlobSoyData(wr.getObjectReader(), view).toSoyData(wr.path, wr.id, createEditUrl(req, view)); // TODO(sop): Allow caching files by SHA-1 when no S cookie is sent. renderHtml( req,
diff --git a/java/com/google/gitiles/VisibilityChecker.java b/java/com/google/gitiles/VisibilityChecker.java index fd81396..dedf547 100644 --- a/java/com/google/gitiles/VisibilityChecker.java +++ b/java/com/google/gitiles/VisibilityChecker.java
@@ -68,8 +68,8 @@ return false; } - return !walk.createReachabilityChecker() - .areAllReachable(ImmutableList.of(commit), startCommits) + return !walk.getObjectReader().createReachabilityChecker(walk) + .areAllReachable(ImmutableList.of(commit), startCommits.stream()) .isPresent(); }
diff --git a/java/com/google/gitiles/blame/cache/BlameCacheImpl.java b/java/com/google/gitiles/blame/cache/BlameCacheImpl.java index 2fa80f5..2d831a4 100644 --- a/java/com/google/gitiles/blame/cache/BlameCacheImpl.java +++ b/java/com/google/gitiles/blame/cache/BlameCacheImpl.java
@@ -163,7 +163,7 @@ } } - private static List<Region> loadRegions(BlameGenerator gen) throws IOException { + public static List<Region> loadRegions(BlameGenerator gen) throws IOException { Map<ObjectId, PooledCommit> commits = Maps.newHashMap(); Interner<String> strings = Interners.newStrongInterner(); int lineCount = gen.getResultContents().size();
diff --git a/java/com/google/gitiles/blame/cache/Region.java b/java/com/google/gitiles/blame/cache/Region.java index 17d280f..cd99043 100644 --- a/java/com/google/gitiles/blame/cache/Region.java +++ b/java/com/google/gitiles/blame/cache/Region.java
@@ -53,7 +53,7 @@ return start; } - int getEnd() { + public int getEnd() { return start + count; }
diff --git a/java/com/google/gitiles/dev/DevServer.java b/java/com/google/gitiles/dev/DevServer.java index 028edd6..675511b 100644 --- a/java/com/google/gitiles/dev/DevServer.java +++ b/java/com/google/gitiles/dev/DevServer.java
@@ -212,7 +212,7 @@ @Override public Object getUserKey() { - return null; + return ""; } @Override
diff --git a/javatests/com/google/gitiles/LogServletTest.java b/javatests/com/google/gitiles/LogServletTest.java index 4ef7a4d..d875e0e 100644 --- a/javatests/com/google/gitiles/LogServletTest.java +++ b/javatests/com/google/gitiles/LogServletTest.java
@@ -15,6 +15,7 @@ package com.google.gitiles; import static com.google.common.truth.Truth.assertThat; +import static javax.servlet.http.HttpServletResponse.SC_OK; import com.google.gitiles.CommitJsonData.Commit; import com.google.gitiles.CommitJsonData.Log; @@ -30,6 +31,10 @@ @RunWith(JUnit4.class) public class LogServletTest extends ServletTest { private static final TypeToken<Log> LOG = new TypeToken<Log>() {}; + private static final String MAIN = "main"; + private static final String AUTHOR_METADATA_ELEMENT = "<th class=\"Metadata-title\">author</th>"; + private static final String COMMITTER_METADATA_ELEMENT = + "<th class=\"Metadata-title\">committer</th>"; @Test public void basicLog() throws Exception { @@ -136,4 +141,125 @@ assertThat(jsonCommit.committer.time).isEqualTo(df.format(commit.getCommitterIdent())); assertThat(jsonCommit.message).isEqualTo(commit.getFullMessage()); } + + @Test + public void verifyPreviousButtonAction() throws Exception { + repo.branch(MAIN).commit().add("foo", "contents").create(); + RevCommit grandParent = repo.branch(MAIN).commit().add("foo", "contents").create(); + RevCommit parent = + repo.branch(MAIN).commit().parent(grandParent).add("foo", "contents").create(); + RevCommit main = repo.branch(MAIN).commit().parent(parent).create(); + + int numCommitsPerPage = 2; + String path = + "/repo/+log/" + grandParent.toObjectId().getName() + ".." + main.toObjectId().getName(); + FakeHttpServletResponse res = + buildResponse( + path, + "format=html" + "&n=" + numCommitsPerPage + "&s=" + parent.toObjectId().getName(), + SC_OK); + + assertThat(res.getActualBodyString()) + .contains( + "<a class=\"LogNav-prev\"" + + " href=\"/b/repo/+log/" + + grandParent.toObjectId().getName() + + ".." + + main.toObjectId().getName() + + "/?format=html" + + "&n=2" + + "\">"); + } + + @Test + public void verifyNextButtonAction() throws Exception { + repo.branch(MAIN).commit().add("foo", "contents").create(); + RevCommit grandParent = repo.branch(MAIN).commit().add("foo", "contents").create(); + RevCommit parent = + repo.branch(MAIN).commit().parent(grandParent).add("foo", "contents").create(); + RevCommit main = repo.branch(MAIN).commit().parent(parent).create(); + + int numCommitsPerPage = 1; + String path = + "/repo/+log/" + grandParent.toObjectId().getName() + ".." + main.toObjectId().getName(); + FakeHttpServletResponse res = + buildResponse(path, "format=html" + "&n=" + numCommitsPerPage, SC_OK); + + assertThat(res.getActualBodyString()) + .contains( + "<a class=\"LogNav-next\"" + + " href=\"/b/repo/+log/" + + grandParent.toObjectId().getName() + + ".." + + main.toObjectId().getName() + + "/?format=html" + + "&n=1" + + "&s=" + + parent.toObjectId().getName() + + "\">"); + } + + @Test + public void prettyDefaultUsesDefaultCssClass() throws Exception { + RevCommit parent = repo.branch(MAIN).commit().add("foo", "contents").create(); + RevCommit main = repo.branch(MAIN).commit().parent(parent).create(); + + String path = + "/repo/+log/" + parent.toObjectId().getName() + ".." + main.toObjectId().getName(); + FakeHttpServletResponse res = buildResponse(path, "format=html", SC_OK); + + assertThat(res.getActualBodyString()) + .contains("<li class=\"CommitLog-item CommitLog-item--default\">"); + assertThat(res.getActualBodyString()).doesNotContain(AUTHOR_METADATA_ELEMENT); + assertThat(res.getActualBodyString()).doesNotContain(COMMITTER_METADATA_ELEMENT); + } + + @Test + public void prettyExplicitlyDefaultUsesDefaultCssClass() throws Exception { + testPrettyHtmlOutput( + "default", /* shouldShowAuthor= */ false, /* shouldShowCommitter= */ false); + } + + @Test + public void prettyOnelineUsesOnelineCssClass() throws Exception { + testPrettyHtmlOutput( + "oneline", /* shouldShowAuthor= */ false, /* shouldShowCommitter= */ false); + } + + @Test + public void prettyCustomTypeUsesCustomCssClass() throws Exception { + testPrettyHtmlOutput( + "aCustomPrettyType", /* shouldShowAuthor= */ false, /* shouldShowCommitter= */ false); + } + + @Test + public void prettyFullerUsesFullerCssClass() throws Exception { + testPrettyHtmlOutput("fuller", /* shouldShowAuthor= */ true, /* shouldShowCommitter= */ true); + } + + private void testPrettyHtmlOutput( + String prettyType, boolean shouldShowAuthor, boolean shouldShowCommitter) throws Exception { + RevCommit parent = repo.branch(MAIN).commit().add("foo", "contents").create(); + RevCommit main = repo.branch(MAIN).commit().parent(parent).create(); + + String path = + "/repo/+log/" + parent.toObjectId().getName() + ".." + main.toObjectId().getName(); + FakeHttpServletResponse res = + buildResponse(path, "format=html" + "&pretty=" + prettyType, SC_OK); + + assertThat(res.getActualBodyString()) + .contains("<li class=\"CommitLog-item CommitLog-item--" + prettyType + "\">"); + + if (shouldShowAuthor) { + assertThat(res.getActualBodyString()).contains(AUTHOR_METADATA_ELEMENT); + } else { + assertThat(res.getActualBodyString()).doesNotContain(AUTHOR_METADATA_ELEMENT); + } + + if (shouldShowCommitter) { + assertThat(res.getActualBodyString()).contains(COMMITTER_METADATA_ELEMENT); + } else { + assertThat(res.getActualBodyString()).doesNotContain(COMMITTER_METADATA_ELEMENT); + } + } }
diff --git a/javatests/com/google/gitiles/PathServletTest.java b/javatests/com/google/gitiles/PathServletTest.java index 5002948..7531242 100644 --- a/javatests/com/google/gitiles/PathServletTest.java +++ b/javatests/com/google/gitiles/PathServletTest.java
@@ -103,6 +103,18 @@ } @Test + public void editUrl() throws Exception { + repo.branch("master").commit().add("editFoo", "Content").create(); + repo.reset("refs/heads/master"); + + Map<String, ?> data = buildData("/repo/+/refs/heads/master/editFoo"); + + String editUrl = (String) getBlobData(data).get("editUrl"); + String testUrl = "http://test-host-review/admin/repos/edit/repo/repo/branch/refs/heads/master/file/editFoo"; + assertThat(editUrl).isEqualTo(testUrl); + } + + @Test public void fileWithMaxLines() throws Exception { int MAX_LINE_COUNT = 50000; StringBuilder contentBuilder = new StringBuilder();
diff --git a/javatests/com/google/gitiles/doc/DocServletTest.java b/javatests/com/google/gitiles/doc/DocServletTest.java index 2a9c2c5..110f481 100644 --- a/javatests/com/google/gitiles/doc/DocServletTest.java +++ b/javatests/com/google/gitiles/doc/DocServletTest.java
@@ -158,7 +158,6 @@ repo.branch("master").commit().add("index.md", markdown).create(); String html = buildHtml("/repo/+/master/index.md"); - System.out.println(html); assertThat(html) .contains( "<ul><li><p>one</p><div class=\"aside\">remember this</div>"
diff --git a/resources/com/google/gitiles/templates/HostIndex.soy b/resources/com/google/gitiles/templates/HostIndex.soy index 846904a..353fd4f 100644 --- a/resources/com/google/gitiles/templates/HostIndex.soy +++ b/resources/com/google/gitiles/templates/HostIndex.soy
@@ -34,7 +34,7 @@ {/call} {if length($repositories)} - {if not $breadcrumbs} + {if !$breadcrumbs} <h1> {msg desc="Git repositories available on the host"} Git repositories on {$hostName}
diff --git a/resources/com/google/gitiles/templates/LogDetail.soy b/resources/com/google/gitiles/templates/LogDetail.soy index 108bbef..f231aa2 100644 --- a/resources/com/google/gitiles/templates/LogDetail.soy +++ b/resources/com/google/gitiles/templates/LogDetail.soy
@@ -285,7 +285,7 @@ rename or copy. */ {call logEntry variant="'full'" data="all" /} -{if $diffTree and length($diffTree)} +{if $diffTree && length($diffTree)} <ul class="DiffTree"> {for $entry in $diffTree} <li>
diff --git a/resources/com/google/gitiles/templates/ObjectDetail.soy b/resources/com/google/gitiles/templates/ObjectDetail.soy index 1dd3a40..f7c0b0c 100644 --- a/resources/com/google/gitiles/templates/ObjectDetail.soy +++ b/resources/com/google/gitiles/templates/ObjectDetail.soy
@@ -242,12 +242,14 @@ {@param? logUrl: ?} /** optional URL to a log for this file. */ {@param? blameUrl: ?} /** optional URL to a blame for this file. */ {@param? docUrl: ?} /** optional URL to view rendered file. */ + {@param? editUrl: ?} /** optional URL to create a change in Gerrit. */ <div class="u-sha1 u-monospace BlobSha1"> {msg desc="SHA-1 for the file's blob"}blob: {$sha}{/msg} {if $fileUrl}{sp}[<a href="{$fileUrl}">{msg desc="detail view of a file"}file{/msg}</a>]{/if} {if $logUrl}{sp}[<a href="{$logUrl}">{msg desc="history for a file"}log{/msg}</a>]{/if} {if $blameUrl}{sp}[<a href="{$blameUrl}">{msg desc="blame for a file"}blame{/msg}</a>]{/if} {if $docUrl}{sp}[<a href="{$docUrl}">{msg desc="view rendered file"}view{/msg}</a>]{/if} + {if $editUrl}{sp}[<a href="{$editUrl}">{msg desc="edit file in Gerrit"}edit{/msg}</a>]{/if} </div> {/template}
diff --git a/tools/run_dev.sh b/tools/run_dev.sh index 1f4ed5e..b045164 100755 --- a/tools/run_dev.sh +++ b/tools/run_dev.sh
@@ -35,7 +35,7 @@ ( cd "$ROOT" - bazel build java/com/google/gitiles/dev + "${BAZEL:-bazel}" build java/com/google/gitiles/dev ) set -x
diff --git a/version.bzl b/version.bzl index fef692b..eb86312 100644 --- a/version.bzl +++ b/version.bzl
@@ -3,4 +3,4 @@ # Used by :install and :deploy when talking to the destination repository. # Project uses semantic versioning described at: # https://semver.org -GITILES_VERSION = "1.3.0" +GITILES_VERSION = "1.4.0"