blob: 37bb2b4c4a0d5483c9109ae6e6e5719492697a87 [file] [log] [blame]
Dave Borowitz9de65952012-08-13 16:09:45 -07001// Copyright 2012 Google Inc. All Rights Reserved.
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7// http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package com.google.gitiles;
16
17import static com.google.common.base.Preconditions.checkNotNull;
Dave Borowitz9de65952012-08-13 16:09:45 -070018import static org.eclipse.jgit.lib.Constants.OBJ_BLOB;
19import static org.eclipse.jgit.lib.Constants.OBJ_COMMIT;
20import static org.eclipse.jgit.lib.Constants.OBJ_TAG;
21import static org.eclipse.jgit.lib.Constants.OBJ_TREE;
22
23import com.google.common.collect.ImmutableMap;
Dave Borowitz01551272014-03-16 13:43:16 -070024import com.google.common.collect.ImmutableSet;
Dave Borowitz9de65952012-08-13 16:09:45 -070025import com.google.common.collect.Lists;
Robert Iannucci5d93b852014-09-27 19:48:44 -070026import com.google.common.io.BaseEncoding;
Dave Borowitz01551272014-03-16 13:43:16 -070027import com.google.gitiles.CommitData.Field;
Dave Borowitz90d1db92014-03-16 14:16:15 -070028import com.google.gitiles.CommitJsonData.Commit;
Dave Borowitz2b2f34b2014-04-29 16:47:20 -070029import com.google.gitiles.DateFormatter.Format;
Masaya Suzuki5cecb862019-03-25 17:35:44 -070030import com.google.gitiles.GitilesRequestFailureException.FailureReason;
Dave Borowitz3b744b12016-08-19 16:11:10 -040031import java.io.IOException;
32import java.io.OutputStream;
33import java.io.Writer;
34import java.util.List;
35import java.util.Map;
36import javax.servlet.http.HttpServletRequest;
37import javax.servlet.http.HttpServletResponse;
Dave Borowitz9de65952012-08-13 16:09:45 -070038import org.eclipse.jgit.errors.IncorrectObjectTypeException;
39import org.eclipse.jgit.errors.MissingObjectException;
40import org.eclipse.jgit.http.server.ServletUtils;
Shawn Pearce73e34532015-02-12 16:27:54 -080041import org.eclipse.jgit.lib.Config;
Dave Borowitz9de65952012-08-13 16:09:45 -070042import org.eclipse.jgit.lib.Constants;
43import org.eclipse.jgit.lib.ObjectId;
Robert Iannucci5d93b852014-09-27 19:48:44 -070044import org.eclipse.jgit.lib.ObjectLoader;
Dave Borowitzfe8fdab2014-11-04 16:19:33 -080045import org.eclipse.jgit.lib.ObjectReader;
Dave Borowitz9de65952012-08-13 16:09:45 -070046import org.eclipse.jgit.lib.Repository;
47import org.eclipse.jgit.revwalk.RevCommit;
48import org.eclipse.jgit.revwalk.RevObject;
49import org.eclipse.jgit.revwalk.RevTag;
Shawn Pearce73e34532015-02-12 16:27:54 -080050import org.eclipse.jgit.revwalk.RevTree;
Dave Borowitz9de65952012-08-13 16:09:45 -070051import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitz9de65952012-08-13 16:09:45 -070052
Dave Borowitz9de65952012-08-13 16:09:45 -070053/** Serves an HTML page with detailed information about a ref. */
54public class RevisionServlet extends BaseServlet {
Dave Borowitz90d1db92014-03-16 14:16:15 -070055 private static final ImmutableSet<Field> COMMIT_SOY_FIELDS =
56 Field.setOf(CommitSoyData.DEFAULT_FIELDS, Field.DIFF_TREE);
57 private static final ImmutableSet<Field> COMMIT_JSON_FIELDS =
58 Field.setOf(CommitJsonData.DEFAULT_FIELDS, Field.DIFF_TREE);
Dave Borowitz01551272014-03-16 13:43:16 -070059
Chad Horohoead23f142012-11-12 09:45:39 -080060 private static final long serialVersionUID = 1L;
Dave Borowitz9de65952012-08-13 16:09:45 -070061
62 private final Linkifier linkifier;
63
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020064 public RevisionServlet(
65 GitilesAccess.Factory accessFactory, Renderer renderer, Linkifier linkifier) {
Dave Borowitz8d6d6872014-03-16 15:18:14 -070066 super(renderer, accessFactory);
Dave Borowitz9de65952012-08-13 16:09:45 -070067 this.linkifier = checkNotNull(linkifier, "linkifier");
68 }
69
70 @Override
Dave Borowitz90d1db92014-03-16 14:16:15 -070071 protected void doGetHtml(HttpServletRequest req, HttpServletResponse res) throws IOException {
Dave Borowitz9de65952012-08-13 16:09:45 -070072 GitilesView view = ViewFilter.getView(req);
73 Repository repo = ServletUtils.getRepository(req);
Dave Borowitz2b2f34b2014-04-29 16:47:20 -070074 GitilesAccess access = getAccess(req);
Shawn Pearce73e34532015-02-12 16:27:54 -080075 Config cfg = getAccess(req).getConfig();
Dave Borowitz9de65952012-08-13 16:09:45 -070076
Shawn Pearceb5ad0a02015-05-24 20:33:17 -070077 try (RevWalk walk = new RevWalk(repo)) {
Dave Borowitz2b2f34b2014-04-29 16:47:20 -070078 DateFormatter df = new DateFormatter(access, Format.DEFAULT);
Dave Borowitz558005d2012-12-20 15:48:08 -080079 List<RevObject> objects = listObjects(walk, view.getRevision());
Dave Borowitz9de65952012-08-13 16:09:45 -070080 List<Map<String, ?>> soyObjects = Lists.newArrayListWithCapacity(objects.size());
81 boolean hasBlob = false;
Shawn Pearce73e34532015-02-12 16:27:54 -080082 boolean hasReadme = false;
Dave Borowitz9de65952012-08-13 16:09:45 -070083
84 // TODO(sop): Allow caching commits by SHA-1 when no S cookie is sent.
85 for (RevObject obj : objects) {
86 try {
87 switch (obj.getType()) {
88 case OBJ_COMMIT:
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020089 soyObjects.add(
90 ImmutableMap.of(
91 "type",
92 Constants.TYPE_COMMIT,
93 "data",
94 new CommitSoyData()
95 .setLinkifier(linkifier)
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020096 .setArchiveFormat(getArchiveFormat(access))
Jonathan Niederb49306a2019-03-07 14:10:57 -080097 .toSoyData(req, walk, (RevCommit) obj, COMMIT_SOY_FIELDS, df)));
Dave Borowitz9de65952012-08-13 16:09:45 -070098 break;
99 case OBJ_TREE:
Shawn Pearce73e34532015-02-12 16:27:54 -0800100 Map<String, Object> tree =
Shawn Pearcec68ad0b2016-05-28 16:52:47 -0700101 new TreeSoyData(
102 walk.getObjectReader(), view, cfg, (RevTree) obj, req.getRequestURI())
103 .toSoyData(obj);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200104 soyObjects.add(ImmutableMap.of("type", Constants.TYPE_TREE, "data", tree));
Shawn Pearce73e34532015-02-12 16:27:54 -0800105 hasReadme = tree.containsKey("readmeHtml");
Dave Borowitz9de65952012-08-13 16:09:45 -0700106 break;
107 case OBJ_BLOB:
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200108 soyObjects.add(
109 ImmutableMap.of(
110 "type",
111 Constants.TYPE_BLOB,
112 "data",
113 new BlobSoyData(walk.getObjectReader(), view).toSoyData(obj)));
Dave Borowitz9de65952012-08-13 16:09:45 -0700114 hasBlob = true;
115 break;
116 case OBJ_TAG:
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200117 soyObjects.add(
118 ImmutableMap.of(
119 "type",
120 Constants.TYPE_TAG,
121 "data",
Jonathan Nieder9e3b1b72019-03-07 14:38:28 -0800122 new TagSoyData(linkifier, req).toSoyData(walk, (RevTag) obj, df)));
Dave Borowitz9de65952012-08-13 16:09:45 -0700123 break;
124 default:
Masaya Suzuki5cecb862019-03-25 17:35:44 -0700125 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_OBJECT_TYPE);
Dave Borowitz9de65952012-08-13 16:09:45 -0700126 }
127 } catch (MissingObjectException e) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -0700128 throw new GitilesRequestFailureException(FailureReason.OBJECT_NOT_FOUND, e);
Dave Borowitz9de65952012-08-13 16:09:45 -0700129 } catch (IncorrectObjectTypeException e) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -0700130 throw new GitilesRequestFailureException(FailureReason.INCORRECT_OBJECT_TYPE, e);
Dave Borowitz9de65952012-08-13 16:09:45 -0700131 }
132 }
133
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200134 renderHtml(
135 req,
136 res,
Sven Selbergd99004c2022-01-31 10:24:08 +0100137 "com.google.gitiles.templates.RevisionDetail.revisionDetail",
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200138 ImmutableMap.of(
139 "title", view.getRevision().getName(),
140 "objects", soyObjects,
141 "hasBlob", hasBlob,
142 "hasReadme", hasReadme));
Dave Borowitz9de65952012-08-13 16:09:45 -0700143 }
144 }
145
Dave Borowitz90d1db92014-03-16 14:16:15 -0700146 @Override
Robert Iannucci5d93b852014-09-27 19:48:44 -0700147 protected void doGetText(HttpServletRequest req, HttpServletResponse res) throws IOException {
148 GitilesView view = ViewFilter.getView(req);
149 Repository repo = ServletUtils.getRepository(req);
Shawn Pearceb5ad0a02015-05-24 20:33:17 -0700150 try (ObjectReader reader = repo.newObjectReader()) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200151 ObjectLoader loader = reader.open(view.getRevision().getId());
152 if (loader.getType() != OBJ_COMMIT) {
Masaya Suzuki5cecb862019-03-25 17:35:44 -0700153 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_OBJECT_TYPE);
David Pursehouseb9061262019-05-13 06:52:07 +0200154 }
155 PathServlet.setTypeHeader(res, loader.getType());
156 try (Writer writer = startRenderText(req, res);
157 OutputStream out = BaseEncoding.base64().encodingStream(writer)) {
158 loader.copyTo(out);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200159 }
Robert Iannucci5d93b852014-09-27 19:48:44 -0700160 }
161 }
162
163 @Override
Dave Borowitz90d1db92014-03-16 14:16:15 -0700164 protected void doGetJson(HttpServletRequest req, HttpServletResponse res) throws IOException {
165 GitilesView view = ViewFilter.getView(req);
166 Repository repo = ServletUtils.getRepository(req);
167
Shawn Pearceb5ad0a02015-05-24 20:33:17 -0700168 try (RevWalk walk = new RevWalk(repo)) {
Dave Borowitz2b2f34b2014-04-29 16:47:20 -0700169 DateFormatter df = new DateFormatter(getAccess(req), Format.DEFAULT);
Dave Borowitz90d1db92014-03-16 14:16:15 -0700170 RevObject obj = walk.parseAny(view.getRevision().getId());
Kevin Chowski67cd01f2023-07-12 12:24:40 -0600171 if (obj.getType() == OBJ_TAG) {
172 obj = ((RevTag) obj).getObject();
173 }
Dave Borowitz90d1db92014-03-16 14:16:15 -0700174 switch (obj.getType()) {
175 case OBJ_COMMIT:
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200176 renderJson(
177 req,
178 res,
Jonathan Niederb49306a2019-03-07 14:10:57 -0800179 new CommitJsonData().toJsonData(req, walk, (RevCommit) obj, COMMIT_JSON_FIELDS, df),
Dave Borowitz90d1db92014-03-16 14:16:15 -0700180 Commit.class);
181 break;
182 default:
183 // TODO(dborowitz): Support showing other types.
Masaya Suzuki5cecb862019-03-25 17:35:44 -0700184 throw new GitilesRequestFailureException(FailureReason.UNSUPPORTED_OBJECT_TYPE);
Dave Borowitz90d1db92014-03-16 14:16:15 -0700185 }
Dave Borowitz90d1db92014-03-16 14:16:15 -0700186 }
187 }
188
Dave Borowitz9de65952012-08-13 16:09:45 -0700189 // TODO(dborowitz): Extract this.
Dave Borowitz558005d2012-12-20 15:48:08 -0800190 static List<RevObject> listObjects(RevWalk walk, Revision rev)
Dave Borowitz9de65952012-08-13 16:09:45 -0700191 throws MissingObjectException, IOException {
192 List<RevObject> objects = Lists.newArrayListWithExpectedSize(1);
Dave Borowitz558005d2012-12-20 15:48:08 -0800193 ObjectId id = rev.getId();
194 RevObject cur;
Dave Borowitz9de65952012-08-13 16:09:45 -0700195 while (true) {
Dave Borowitz558005d2012-12-20 15:48:08 -0800196 cur = walk.parseAny(id);
Dave Borowitz9de65952012-08-13 16:09:45 -0700197 objects.add(cur);
Dave Borowitz558005d2012-12-20 15:48:08 -0800198 if (cur.getType() != Constants.OBJ_TAG) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700199 break;
200 }
Dave Borowitz558005d2012-12-20 15:48:08 -0800201 id = ((RevTag) cur).getObject();
202 }
203 if (cur.getType() == Constants.OBJ_COMMIT) {
204 objects.add(walk.parseTree(((RevCommit) cur).getTree()));
Dave Borowitz9de65952012-08-13 16:09:45 -0700205 }
206 return objects;
207 }
208}