blob: e3e5e2b0b22d47ac0cc664f8bb193e776520c11d [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
Dave Borowitz9de65952012-08-13 16:09:45 -070017import static com.google.common.base.Preconditions.checkState;
Dave Borowitzd6184a62013-01-10 11:53:54 -080018import static org.eclipse.jgit.diff.DiffEntry.ChangeType.COPY;
19import static org.eclipse.jgit.diff.DiffEntry.ChangeType.DELETE;
20import static org.eclipse.jgit.diff.DiffEntry.ChangeType.RENAME;
21
Dave Borowitz9de65952012-08-13 16:09:45 -070022import com.google.common.collect.ImmutableMap;
23import com.google.common.collect.ImmutableSet;
24import com.google.common.collect.Lists;
25import com.google.common.collect.Maps;
Dave Borowitz75a78322014-03-16 12:13:08 -070026import com.google.common.collect.Sets;
Dave Borowitz01551272014-03-16 13:43:16 -070027import com.google.gitiles.CommitData.DiffList;
28import com.google.gitiles.CommitData.Field;
Dave Borowitz9de65952012-08-13 16:09:45 -070029import com.google.template.soy.data.restricted.NullData;
Dave Borowitz3b744b12016-08-19 16:11:10 -040030import java.io.IOException;
31import java.util.List;
32import java.util.Map;
33import java.util.Set;
34import javax.annotation.Nullable;
35import javax.servlet.http.HttpServletRequest;
Dave Borowitz9de65952012-08-13 16:09:45 -070036import org.eclipse.jgit.diff.DiffEntry;
37import org.eclipse.jgit.diff.DiffEntry.ChangeType;
Dave Borowitz9de65952012-08-13 16:09:45 -070038import org.eclipse.jgit.lib.Constants;
Dave Borowitz9de65952012-08-13 16:09:45 -070039import org.eclipse.jgit.lib.PersonIdent;
40import org.eclipse.jgit.lib.Ref;
Dave Borowitz9de65952012-08-13 16:09:45 -070041import org.eclipse.jgit.revwalk.RevCommit;
42import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitz9de65952012-08-13 16:09:45 -070043import org.eclipse.jgit.util.RelativeDateFormatter;
Dave Borowitz9de65952012-08-13 16:09:45 -070044
Dave Borowitz9de65952012-08-13 16:09:45 -070045/** Soy data converter for git commits. */
Dave Borowitz6ed598f2014-04-17 10:09:50 -070046public class CommitSoyData {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020047 static final ImmutableSet<Field> DEFAULT_FIELDS =
48 Sets.immutableEnumSet(
49 Field.AUTHOR,
50 Field.COMMITTER,
51 Field.SHA,
52 Field.TREE,
53 Field.TREE_URL,
54 Field.PARENTS,
55 Field.MESSAGE,
Pontus Jaenssonc144de92021-10-29 11:13:23 +020056 Field.NOTES, // Optional Field
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020057 Field.LOG_URL,
58 Field.ARCHIVE_URL,
59 Field.ARCHIVE_TYPE);
Dave Borowitz9de65952012-08-13 16:09:45 -070060
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020061 private static final ImmutableSet<Field> NESTED_FIELDS =
62 Sets.immutableEnumSet(Field.PARENT_BLAME_URL);
Dave Borowitzf03fcea2014-04-21 17:20:33 -070063
Dave Borowitz3b086a72013-07-02 15:03:03 -070064 private Linkifier linkifier;
Gustaf Lundh06a98702015-08-17 17:29:11 +020065 private CommitData.Builder cdb;
Dave Borowitzc8a15682013-07-02 14:33:08 -070066 private ArchiveFormat archiveFormat;
Dave Borowitz9de65952012-08-13 16:09:45 -070067
Dave Borowitz01551272014-03-16 13:43:16 -070068 CommitSoyData setLinkifier(@Nullable Linkifier linkifier) {
69 this.linkifier = linkifier;
Dave Borowitz3b086a72013-07-02 15:03:03 -070070 return this;
Dave Borowitz9de65952012-08-13 16:09:45 -070071 }
72
Dave Borowitz01551272014-03-16 13:43:16 -070073 CommitSoyData setArchiveFormat(@Nullable ArchiveFormat archiveFormat) {
74 this.archiveFormat = archiveFormat;
Dave Borowitzc8a15682013-07-02 14:33:08 -070075 return this;
76 }
77
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020078 Map<String, Object> toSoyData(
Jonathan Niederb49306a2019-03-07 14:10:57 -080079 HttpServletRequest req, RevWalk walk, RevCommit c, Set<Field> fs, DateFormatter df)
80 throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -070081 GitilesView view = ViewFilter.getView(req);
Gustaf Lundh06a98702015-08-17 17:29:11 +020082 if (cdb == null) {
83 cdb = new CommitData.Builder();
84 }
85
Jonathan Niederb49306a2019-03-07 14:10:57 -080086 CommitData cd = cdb.setArchiveFormat(archiveFormat).build(req, walk, c, fs);
Dave Borowitz3b086a72013-07-02 15:03:03 -070087
Dave Borowitz01551272014-03-16 13:43:16 -070088 Map<String, Object> data = Maps.newHashMapWithExpectedSize(fs.size());
89 if (cd.author != null) {
90 data.put("author", toSoyData(cd.author, df));
Dave Borowitz9de65952012-08-13 16:09:45 -070091 }
Dave Borowitz01551272014-03-16 13:43:16 -070092 if (cd.committer != null) {
93 data.put("committer", toSoyData(cd.committer, df));
Dave Borowitz9de65952012-08-13 16:09:45 -070094 }
Dave Borowitz01551272014-03-16 13:43:16 -070095 if (cd.sha != null) {
96 data.put("sha", cd.sha.name());
Dave Borowitz9de65952012-08-13 16:09:45 -070097 }
Dave Borowitz01551272014-03-16 13:43:16 -070098 if (cd.abbrev != null) {
99 data.put("abbrevSha", cd.abbrev.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700100 }
Dave Borowitz01551272014-03-16 13:43:16 -0700101 if (cd.url != null) {
102 data.put("url", cd.url);
Dave Borowitz9de65952012-08-13 16:09:45 -0700103 }
Dave Borowitz01551272014-03-16 13:43:16 -0700104 if (cd.logUrl != null) {
105 data.put("logUrl", cd.logUrl);
Dave Borowitzc222cce2013-06-19 10:47:06 -0700106 }
Dave Borowitz01551272014-03-16 13:43:16 -0700107 if (cd.archiveUrl != null) {
108 data.put("archiveUrl", cd.archiveUrl);
Dave Borowitzc8a15682013-07-02 14:33:08 -0700109 }
Dave Borowitz01551272014-03-16 13:43:16 -0700110 if (cd.archiveType != null) {
111 data.put("archiveType", cd.archiveType.getShortName());
Dave Borowitz9de65952012-08-13 16:09:45 -0700112 }
Dave Borowitz01551272014-03-16 13:43:16 -0700113 if (cd.tree != null) {
114 data.put("tree", cd.tree.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700115 }
Dave Borowitz01551272014-03-16 13:43:16 -0700116 if (cd.treeUrl != null) {
117 data.put("treeUrl", cd.treeUrl);
Dave Borowitz9de65952012-08-13 16:09:45 -0700118 }
Dave Borowitz01551272014-03-16 13:43:16 -0700119 if (cd.parents != null) {
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700120 data.put("parents", toSoyData(view, fs, cd.parents));
Dave Borowitz9de65952012-08-13 16:09:45 -0700121 }
Dave Borowitz01551272014-03-16 13:43:16 -0700122 if (cd.shortMessage != null) {
123 data.put("shortMessage", cd.shortMessage);
Dave Borowitz9de65952012-08-13 16:09:45 -0700124 }
Dave Borowitz01551272014-03-16 13:43:16 -0700125 if (cd.branches != null) {
126 data.put("branches", toSoyData(view, cd.branches, Constants.R_HEADS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700127 }
Dave Borowitz01551272014-03-16 13:43:16 -0700128 if (cd.tags != null) {
129 data.put("tags", toSoyData(view, cd.tags, Constants.R_TAGS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700130 }
Dave Borowitz01551272014-03-16 13:43:16 -0700131 if (cd.message != null) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700132 if (linkifier != null) {
Dave Borowitz01551272014-03-16 13:43:16 -0700133 data.put("message", linkifier.linkify(req, cd.message));
Dave Borowitz9de65952012-08-13 16:09:45 -0700134 } else {
Dave Borowitz01551272014-03-16 13:43:16 -0700135 data.put("message", cd.message);
Dave Borowitz9de65952012-08-13 16:09:45 -0700136 }
137 }
Dave Borowitz01551272014-03-16 13:43:16 -0700138 if (cd.diffEntries != null) {
139 data.put("diffTree", toSoyData(view, cd.diffEntries));
Dave Borowitz9de65952012-08-13 16:09:45 -0700140 }
Pontus Jaenssonc144de92021-10-29 11:13:23 +0200141
142 int diffSetSize = Sets.difference(fs, NESTED_FIELDS).size();
143
144 if (fs.contains(
145 Field.NOTES)) { // Since NOTES is optional this is required for checkState to pass
146 diffSetSize -= 1;
147 }
148
149 checkState(diffSetSize == data.size(), "bad commit data fields: %s != %s", fs, data.keySet());
150
151 if (cd.notes != null && !cd.notes.isEmpty()) {
152 data.put("notes", cd.notes);
153 }
154
Michael Moss92bb12f2014-05-09 10:55:35 -0700155 return data;
Dave Borowitz9de65952012-08-13 16:09:45 -0700156 }
157
Jonathan Niederb49306a2019-03-07 14:10:57 -0800158 Map<String, Object> toSoyData(
159 HttpServletRequest req, RevWalk walk, RevCommit commit, DateFormatter df) throws IOException {
160 return toSoyData(req, walk, commit, DEFAULT_FIELDS, df);
Dave Borowitz9de65952012-08-13 16:09:45 -0700161 }
162
163 // TODO(dborowitz): Extract this.
Dave Borowitz97a70312014-04-28 15:50:23 -0700164 public static Map<String, String> toSoyData(PersonIdent ident, DateFormatter df) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700165 return ImmutableMap.of(
166 "name", ident.getName(),
167 "email", ident.getEmailAddress(),
Dave Borowitz97a70312014-04-28 15:50:23 -0700168 "time", df.format(ident),
Dave Borowitz9de65952012-08-13 16:09:45 -0700169 // TODO(dborowitz): Switch from relative to absolute at some threshold.
170 "relativeTime", RelativeDateFormatter.format(ident.getWhen()));
171 }
172
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200173 private List<Map<String, String>> toSoyData(
174 GitilesView view, Set<Field> fs, List<RevCommit> parents) {
Dave Borowitz01551272014-03-16 13:43:16 -0700175 List<Map<String, String>> result = Lists.newArrayListWithCapacity(parents.size());
Dave Borowitz9de65952012-08-13 16:09:45 -0700176 int i = 1;
177 // TODO(dborowitz): Render something slightly different when we're actively
178 // viewing a diff against one of the parents.
179 for (RevCommit parent : parents) {
180 String name = parent.name();
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700181 // Clear path on parent diff view, since this parent may not have a diff
182 // for the path in question.
Dave Borowitzdd3c3d92013-03-11 16:38:41 -0700183 GitilesView.Builder diff = GitilesView.diff().copyFrom(view).setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700184 String parentName;
Dave Borowitz01551272014-03-16 13:43:16 -0700185 if (parents.size() == 1) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700186 parentName = view.getRevision().getName() + "^";
187 } else {
Matthias Sohn0f85ecb2023-09-30 22:56:36 +0200188 parentName = view.getRevision().getName() + "^" + i++;
Dave Borowitz9de65952012-08-13 16:09:45 -0700189 }
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700190 diff.setOldRevision(parentName, parent);
191
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700192 Map<String, String> e = Maps.newHashMapWithExpectedSize(4);
193 e.put("sha", name);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200194 e.put("url", GitilesView.revision().copyFrom(view).setRevision(parentName, parent).toUrl());
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700195 e.put("diffUrl", diff.toUrl());
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700196 if (fs.contains(Field.PARENT_BLAME_URL)) {
197 // Assumes caller has ensured path is a file.
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200198 e.put(
199 "blameUrl",
200 GitilesView.blame()
201 .copyFrom(view)
202 .setRevision(Revision.peeled(parentName, parent))
203 .toUrl());
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700204 }
205 result.add(e);
Dave Borowitz9de65952012-08-13 16:09:45 -0700206 }
207 return result;
208 }
209
Dave Borowitz01551272014-03-16 13:43:16 -0700210 private static Object toSoyData(GitilesView view, DiffList dl) {
211 if (dl.oldRevision == null) {
212 return NullData.INSTANCE;
213 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200214 GitilesView.Builder diffUrl =
215 GitilesView.diff()
216 .copyFrom(view)
217 .setOldRevision(dl.oldRevision)
218 .setRevision(dl.revision)
219 .setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700220
Dave Borowitz01551272014-03-16 13:43:16 -0700221 List<Object> result = Lists.newArrayListWithCapacity(dl.entries.size());
222 for (DiffEntry e : dl.entries) {
223 Map<String, Object> entry = Maps.newHashMapWithExpectedSize(5);
224 ChangeType type = e.getChangeType();
225 if (type != DELETE) {
226 entry.put("path", e.getNewPath());
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200227 entry.put(
228 "url",
229 GitilesView.path()
230 .copyFrom(view)
231 .setRevision(dl.revision)
232 .setPathPart(e.getNewPath())
233 .toUrl());
Dave Borowitz01551272014-03-16 13:43:16 -0700234 } else {
235 entry.put("path", e.getOldPath());
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200236 entry.put(
237 "url",
238 GitilesView.path()
239 .copyFrom(view)
240 .setRevision(dl.oldRevision)
241 .setPathPart(e.getOldPath())
242 .toUrl());
Dave Borowitz9de65952012-08-13 16:09:45 -0700243 }
Dave Borowitz01551272014-03-16 13:43:16 -0700244 entry.put("diffUrl", diffUrl.setAnchor("F" + result.size()).toUrl());
245 entry.put("changeType", e.getChangeType().toString());
246 if (type == COPY || type == RENAME) {
247 entry.put("oldPath", e.getOldPath());
248 }
249 result.add(entry);
Dave Borowitz9de65952012-08-13 16:09:45 -0700250 }
Dave Borowitz01551272014-03-16 13:43:16 -0700251 return result;
Dave Borowitz9de65952012-08-13 16:09:45 -0700252 }
253
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200254 private static List<Map<String, String>> toSoyData(
255 GitilesView view, List<Ref> refs, String prefix) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700256 List<Map<String, String>> result = Lists.newArrayListWithCapacity(refs.size());
257 for (Ref ref : refs) {
258 if (ref.getName().startsWith(prefix)) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200259 result.add(
260 ImmutableMap.of(
261 "name", ref.getName().substring(prefix.length()),
262 "url",
263 GitilesView.revision()
264 .copyFrom(view)
265 .setRevision(Revision.unpeeled(ref.getName(), ref.getObjectId()))
266 .toUrl()));
Dave Borowitz9de65952012-08-13 16:09:45 -0700267 }
268 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700269 return result;
270 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700271}