blob: d5bd8fdb962b410d006d5d42ad445ca96c5865b5 [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;
30
31import org.eclipse.jgit.diff.DiffEntry;
32import org.eclipse.jgit.diff.DiffEntry.ChangeType;
Dave Borowitz9de65952012-08-13 16:09:45 -070033import org.eclipse.jgit.lib.Constants;
Dave Borowitz9de65952012-08-13 16:09:45 -070034import org.eclipse.jgit.lib.PersonIdent;
35import org.eclipse.jgit.lib.Ref;
Dave Borowitz9de65952012-08-13 16:09:45 -070036import org.eclipse.jgit.revwalk.RevCommit;
37import org.eclipse.jgit.revwalk.RevWalk;
Dave Borowitz9de65952012-08-13 16:09:45 -070038import org.eclipse.jgit.util.RelativeDateFormatter;
Dave Borowitz9de65952012-08-13 16:09:45 -070039
40import java.io.IOException;
Dave Borowitz9de65952012-08-13 16:09:45 -070041import java.util.List;
42import java.util.Map;
43import java.util.Set;
44
Dave Borowitz01551272014-03-16 13:43:16 -070045import javax.annotation.Nullable;
Dave Borowitz9de65952012-08-13 16:09:45 -070046import javax.servlet.http.HttpServletRequest;
47
48/** Soy data converter for git commits. */
Dave Borowitz6ed598f2014-04-17 10:09:50 -070049public class CommitSoyData {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020050 static final ImmutableSet<Field> DEFAULT_FIELDS =
51 Sets.immutableEnumSet(
52 Field.AUTHOR,
53 Field.COMMITTER,
54 Field.SHA,
55 Field.TREE,
56 Field.TREE_URL,
57 Field.PARENTS,
58 Field.MESSAGE,
59 Field.LOG_URL,
60 Field.ARCHIVE_URL,
61 Field.ARCHIVE_TYPE);
Dave Borowitz9de65952012-08-13 16:09:45 -070062
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020063 private static final ImmutableSet<Field> NESTED_FIELDS =
64 Sets.immutableEnumSet(Field.PARENT_BLAME_URL);
Dave Borowitzf03fcea2014-04-21 17:20:33 -070065
Dave Borowitz3b086a72013-07-02 15:03:03 -070066 private Linkifier linkifier;
Dave Borowitz3b086a72013-07-02 15:03:03 -070067 private RevWalk walk;
Gustaf Lundh06a98702015-08-17 17:29:11 +020068 private CommitData.Builder cdb;
Dave Borowitzc8a15682013-07-02 14:33:08 -070069 private ArchiveFormat archiveFormat;
Dave Borowitz9de65952012-08-13 16:09:45 -070070
Dave Borowitz01551272014-03-16 13:43:16 -070071 CommitSoyData setLinkifier(@Nullable Linkifier linkifier) {
72 this.linkifier = linkifier;
Dave Borowitz3b086a72013-07-02 15:03:03 -070073 return this;
Dave Borowitz9de65952012-08-13 16:09:45 -070074 }
75
Dave Borowitz01551272014-03-16 13:43:16 -070076 CommitSoyData setRevWalk(@Nullable RevWalk walk) {
77 this.walk = walk;
Dave Borowitz3b086a72013-07-02 15:03:03 -070078 return this;
Dave Borowitz9de65952012-08-13 16:09:45 -070079 }
80
Dave Borowitz01551272014-03-16 13:43:16 -070081 CommitSoyData setArchiveFormat(@Nullable ArchiveFormat archiveFormat) {
82 this.archiveFormat = archiveFormat;
Dave Borowitzc8a15682013-07-02 14:33:08 -070083 return this;
84 }
85
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020086 Map<String, Object> toSoyData(
87 HttpServletRequest req, RevCommit c, Set<Field> fs, DateFormatter df) throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -070088 GitilesView view = ViewFilter.getView(req);
Gustaf Lundh06a98702015-08-17 17:29:11 +020089 if (cdb == null) {
90 cdb = new CommitData.Builder();
91 }
92
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020093 CommitData cd = cdb.setRevWalk(walk).setArchiveFormat(archiveFormat).build(req, c, fs);
Dave Borowitz3b086a72013-07-02 15:03:03 -070094
Dave Borowitz01551272014-03-16 13:43:16 -070095 Map<String, Object> data = Maps.newHashMapWithExpectedSize(fs.size());
96 if (cd.author != null) {
97 data.put("author", toSoyData(cd.author, df));
Dave Borowitz9de65952012-08-13 16:09:45 -070098 }
Dave Borowitz01551272014-03-16 13:43:16 -070099 if (cd.committer != null) {
100 data.put("committer", toSoyData(cd.committer, df));
Dave Borowitz9de65952012-08-13 16:09:45 -0700101 }
Dave Borowitz01551272014-03-16 13:43:16 -0700102 if (cd.sha != null) {
103 data.put("sha", cd.sha.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700104 }
Dave Borowitz01551272014-03-16 13:43:16 -0700105 if (cd.abbrev != null) {
106 data.put("abbrevSha", cd.abbrev.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700107 }
Dave Borowitz01551272014-03-16 13:43:16 -0700108 if (cd.url != null) {
109 data.put("url", cd.url);
Dave Borowitz9de65952012-08-13 16:09:45 -0700110 }
Dave Borowitz01551272014-03-16 13:43:16 -0700111 if (cd.logUrl != null) {
112 data.put("logUrl", cd.logUrl);
Dave Borowitzc222cce2013-06-19 10:47:06 -0700113 }
Dave Borowitz01551272014-03-16 13:43:16 -0700114 if (cd.archiveUrl != null) {
115 data.put("archiveUrl", cd.archiveUrl);
Dave Borowitzc8a15682013-07-02 14:33:08 -0700116 }
Dave Borowitz01551272014-03-16 13:43:16 -0700117 if (cd.archiveType != null) {
118 data.put("archiveType", cd.archiveType.getShortName());
Dave Borowitz9de65952012-08-13 16:09:45 -0700119 }
Dave Borowitz01551272014-03-16 13:43:16 -0700120 if (cd.tree != null) {
121 data.put("tree", cd.tree.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700122 }
Dave Borowitz01551272014-03-16 13:43:16 -0700123 if (cd.treeUrl != null) {
124 data.put("treeUrl", cd.treeUrl);
Dave Borowitz9de65952012-08-13 16:09:45 -0700125 }
Dave Borowitz01551272014-03-16 13:43:16 -0700126 if (cd.parents != null) {
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700127 data.put("parents", toSoyData(view, fs, cd.parents));
Dave Borowitz9de65952012-08-13 16:09:45 -0700128 }
Dave Borowitz01551272014-03-16 13:43:16 -0700129 if (cd.shortMessage != null) {
130 data.put("shortMessage", cd.shortMessage);
Dave Borowitz9de65952012-08-13 16:09:45 -0700131 }
Dave Borowitz01551272014-03-16 13:43:16 -0700132 if (cd.branches != null) {
133 data.put("branches", toSoyData(view, cd.branches, Constants.R_HEADS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700134 }
Dave Borowitz01551272014-03-16 13:43:16 -0700135 if (cd.tags != null) {
136 data.put("tags", toSoyData(view, cd.tags, Constants.R_TAGS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700137 }
Dave Borowitz01551272014-03-16 13:43:16 -0700138 if (cd.message != null) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700139 if (linkifier != null) {
Dave Borowitz01551272014-03-16 13:43:16 -0700140 data.put("message", linkifier.linkify(req, cd.message));
Dave Borowitz9de65952012-08-13 16:09:45 -0700141 } else {
Dave Borowitz01551272014-03-16 13:43:16 -0700142 data.put("message", cd.message);
Dave Borowitz9de65952012-08-13 16:09:45 -0700143 }
144 }
Dave Borowitz01551272014-03-16 13:43:16 -0700145 if (cd.diffEntries != null) {
146 data.put("diffTree", toSoyData(view, cd.diffEntries));
Dave Borowitz9de65952012-08-13 16:09:45 -0700147 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200148 checkState(
149 Sets.difference(fs, NESTED_FIELDS).size() == data.size(),
150 "bad commit data fields: %s != %s",
151 fs,
152 data.keySet());
Michael Moss92bb12f2014-05-09 10:55:35 -0700153 return data;
Dave Borowitz9de65952012-08-13 16:09:45 -0700154 }
155
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200156 Map<String, Object> toSoyData(HttpServletRequest req, RevCommit commit, DateFormatter df)
157 throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -0700158 return toSoyData(req, commit, DEFAULT_FIELDS, df);
Dave Borowitz9de65952012-08-13 16:09:45 -0700159 }
160
161 // TODO(dborowitz): Extract this.
Dave Borowitz97a70312014-04-28 15:50:23 -0700162 public static Map<String, String> toSoyData(PersonIdent ident, DateFormatter df) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700163 return ImmutableMap.of(
164 "name", ident.getName(),
165 "email", ident.getEmailAddress(),
Dave Borowitz97a70312014-04-28 15:50:23 -0700166 "time", df.format(ident),
Dave Borowitz9de65952012-08-13 16:09:45 -0700167 // TODO(dborowitz): Switch from relative to absolute at some threshold.
168 "relativeTime", RelativeDateFormatter.format(ident.getWhen()));
169 }
170
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200171 private List<Map<String, String>> toSoyData(
172 GitilesView view, Set<Field> fs, List<RevCommit> parents) {
Dave Borowitz01551272014-03-16 13:43:16 -0700173 List<Map<String, String>> result = Lists.newArrayListWithCapacity(parents.size());
Dave Borowitz9de65952012-08-13 16:09:45 -0700174 int i = 1;
175 // TODO(dborowitz): Render something slightly different when we're actively
176 // viewing a diff against one of the parents.
177 for (RevCommit parent : parents) {
178 String name = parent.name();
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700179 // Clear path on parent diff view, since this parent may not have a diff
180 // for the path in question.
Dave Borowitzdd3c3d92013-03-11 16:38:41 -0700181 GitilesView.Builder diff = GitilesView.diff().copyFrom(view).setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700182 String parentName;
Dave Borowitz01551272014-03-16 13:43:16 -0700183 if (parents.size() == 1) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700184 parentName = view.getRevision().getName() + "^";
185 } else {
186 parentName = view.getRevision().getName() + "^" + (i++);
187 }
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700188 diff.setOldRevision(parentName, parent);
189
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700190 Map<String, String> e = Maps.newHashMapWithExpectedSize(4);
191 e.put("sha", name);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200192 e.put("url", GitilesView.revision().copyFrom(view).setRevision(parentName, parent).toUrl());
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700193 e.put("diffUrl", diff.toUrl());
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700194 if (fs.contains(Field.PARENT_BLAME_URL)) {
195 // Assumes caller has ensured path is a file.
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200196 e.put(
197 "blameUrl",
198 GitilesView.blame()
199 .copyFrom(view)
200 .setRevision(Revision.peeled(parentName, parent))
201 .toUrl());
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700202 }
203 result.add(e);
Dave Borowitz9de65952012-08-13 16:09:45 -0700204 }
205 return result;
206 }
207
Dave Borowitz01551272014-03-16 13:43:16 -0700208 private static Object toSoyData(GitilesView view, DiffList dl) {
209 if (dl.oldRevision == null) {
210 return NullData.INSTANCE;
211 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200212 GitilesView.Builder diffUrl =
213 GitilesView.diff()
214 .copyFrom(view)
215 .setOldRevision(dl.oldRevision)
216 .setRevision(dl.revision)
217 .setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700218
Dave Borowitz01551272014-03-16 13:43:16 -0700219 List<Object> result = Lists.newArrayListWithCapacity(dl.entries.size());
220 for (DiffEntry e : dl.entries) {
221 Map<String, Object> entry = Maps.newHashMapWithExpectedSize(5);
222 ChangeType type = e.getChangeType();
223 if (type != DELETE) {
224 entry.put("path", e.getNewPath());
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200225 entry.put(
226 "url",
227 GitilesView.path()
228 .copyFrom(view)
229 .setRevision(dl.revision)
230 .setPathPart(e.getNewPath())
231 .toUrl());
Dave Borowitz01551272014-03-16 13:43:16 -0700232 } else {
233 entry.put("path", e.getOldPath());
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200234 entry.put(
235 "url",
236 GitilesView.path()
237 .copyFrom(view)
238 .setRevision(dl.oldRevision)
239 .setPathPart(e.getOldPath())
240 .toUrl());
Dave Borowitz9de65952012-08-13 16:09:45 -0700241 }
Dave Borowitz01551272014-03-16 13:43:16 -0700242 entry.put("diffUrl", diffUrl.setAnchor("F" + result.size()).toUrl());
243 entry.put("changeType", e.getChangeType().toString());
244 if (type == COPY || type == RENAME) {
245 entry.put("oldPath", e.getOldPath());
246 }
247 result.add(entry);
Dave Borowitz9de65952012-08-13 16:09:45 -0700248 }
Dave Borowitz01551272014-03-16 13:43:16 -0700249 return result;
Dave Borowitz9de65952012-08-13 16:09:45 -0700250 }
251
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200252 private static List<Map<String, String>> toSoyData(
253 GitilesView view, List<Ref> refs, String prefix) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700254 List<Map<String, String>> result = Lists.newArrayListWithCapacity(refs.size());
255 for (Ref ref : refs) {
256 if (ref.getName().startsWith(prefix)) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200257 result.add(
258 ImmutableMap.of(
259 "name", ref.getName().substring(prefix.length()),
260 "url",
261 GitilesView.revision()
262 .copyFrom(view)
263 .setRevision(Revision.unpeeled(ref.getName(), ref.getObjectId()))
264 .toUrl()));
Dave Borowitz9de65952012-08-13 16:09:45 -0700265 }
266 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700267 return result;
268 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700269}