blob: 40c5f7456c26dc365250f2cabc0b957585688144 [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,
56 Field.LOG_URL,
57 Field.ARCHIVE_URL,
58 Field.ARCHIVE_TYPE);
Dave Borowitz9de65952012-08-13 16:09:45 -070059
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020060 private static final ImmutableSet<Field> NESTED_FIELDS =
61 Sets.immutableEnumSet(Field.PARENT_BLAME_URL);
Dave Borowitzf03fcea2014-04-21 17:20:33 -070062
Dave Borowitz3b086a72013-07-02 15:03:03 -070063 private Linkifier linkifier;
Dave Borowitz3b086a72013-07-02 15:03:03 -070064 private RevWalk walk;
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 setRevWalk(@Nullable RevWalk walk) {
74 this.walk = walk;
Dave Borowitz3b086a72013-07-02 15:03:03 -070075 return this;
Dave Borowitz9de65952012-08-13 16:09:45 -070076 }
77
Dave Borowitz01551272014-03-16 13:43:16 -070078 CommitSoyData setArchiveFormat(@Nullable ArchiveFormat archiveFormat) {
79 this.archiveFormat = archiveFormat;
Dave Borowitzc8a15682013-07-02 14:33:08 -070080 return this;
81 }
82
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020083 Map<String, Object> toSoyData(
84 HttpServletRequest req, RevCommit c, Set<Field> fs, DateFormatter df) throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -070085 GitilesView view = ViewFilter.getView(req);
Gustaf Lundh06a98702015-08-17 17:29:11 +020086 if (cdb == null) {
87 cdb = new CommitData.Builder();
88 }
89
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020090 CommitData cd = cdb.setRevWalk(walk).setArchiveFormat(archiveFormat).build(req, c, fs);
Dave Borowitz3b086a72013-07-02 15:03:03 -070091
Dave Borowitz01551272014-03-16 13:43:16 -070092 Map<String, Object> data = Maps.newHashMapWithExpectedSize(fs.size());
93 if (cd.author != null) {
94 data.put("author", toSoyData(cd.author, df));
Dave Borowitz9de65952012-08-13 16:09:45 -070095 }
Dave Borowitz01551272014-03-16 13:43:16 -070096 if (cd.committer != null) {
97 data.put("committer", toSoyData(cd.committer, df));
Dave Borowitz9de65952012-08-13 16:09:45 -070098 }
Dave Borowitz01551272014-03-16 13:43:16 -070099 if (cd.sha != null) {
100 data.put("sha", cd.sha.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700101 }
Dave Borowitz01551272014-03-16 13:43:16 -0700102 if (cd.abbrev != null) {
103 data.put("abbrevSha", cd.abbrev.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700104 }
Dave Borowitz01551272014-03-16 13:43:16 -0700105 if (cd.url != null) {
106 data.put("url", cd.url);
Dave Borowitz9de65952012-08-13 16:09:45 -0700107 }
Dave Borowitz01551272014-03-16 13:43:16 -0700108 if (cd.logUrl != null) {
109 data.put("logUrl", cd.logUrl);
Dave Borowitzc222cce2013-06-19 10:47:06 -0700110 }
Dave Borowitz01551272014-03-16 13:43:16 -0700111 if (cd.archiveUrl != null) {
112 data.put("archiveUrl", cd.archiveUrl);
Dave Borowitzc8a15682013-07-02 14:33:08 -0700113 }
Dave Borowitz01551272014-03-16 13:43:16 -0700114 if (cd.archiveType != null) {
115 data.put("archiveType", cd.archiveType.getShortName());
Dave Borowitz9de65952012-08-13 16:09:45 -0700116 }
Dave Borowitz01551272014-03-16 13:43:16 -0700117 if (cd.tree != null) {
118 data.put("tree", cd.tree.name());
Dave Borowitz9de65952012-08-13 16:09:45 -0700119 }
Dave Borowitz01551272014-03-16 13:43:16 -0700120 if (cd.treeUrl != null) {
121 data.put("treeUrl", cd.treeUrl);
Dave Borowitz9de65952012-08-13 16:09:45 -0700122 }
Dave Borowitz01551272014-03-16 13:43:16 -0700123 if (cd.parents != null) {
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700124 data.put("parents", toSoyData(view, fs, cd.parents));
Dave Borowitz9de65952012-08-13 16:09:45 -0700125 }
Dave Borowitz01551272014-03-16 13:43:16 -0700126 if (cd.shortMessage != null) {
127 data.put("shortMessage", cd.shortMessage);
Dave Borowitz9de65952012-08-13 16:09:45 -0700128 }
Dave Borowitz01551272014-03-16 13:43:16 -0700129 if (cd.branches != null) {
130 data.put("branches", toSoyData(view, cd.branches, Constants.R_HEADS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700131 }
Dave Borowitz01551272014-03-16 13:43:16 -0700132 if (cd.tags != null) {
133 data.put("tags", toSoyData(view, cd.tags, Constants.R_TAGS));
Dave Borowitz9de65952012-08-13 16:09:45 -0700134 }
Dave Borowitz01551272014-03-16 13:43:16 -0700135 if (cd.message != null) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700136 if (linkifier != null) {
Dave Borowitz01551272014-03-16 13:43:16 -0700137 data.put("message", linkifier.linkify(req, cd.message));
Dave Borowitz9de65952012-08-13 16:09:45 -0700138 } else {
Dave Borowitz01551272014-03-16 13:43:16 -0700139 data.put("message", cd.message);
Dave Borowitz9de65952012-08-13 16:09:45 -0700140 }
141 }
Dave Borowitz01551272014-03-16 13:43:16 -0700142 if (cd.diffEntries != null) {
143 data.put("diffTree", toSoyData(view, cd.diffEntries));
Dave Borowitz9de65952012-08-13 16:09:45 -0700144 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200145 checkState(
146 Sets.difference(fs, NESTED_FIELDS).size() == data.size(),
147 "bad commit data fields: %s != %s",
148 fs,
149 data.keySet());
Michael Moss92bb12f2014-05-09 10:55:35 -0700150 return data;
Dave Borowitz9de65952012-08-13 16:09:45 -0700151 }
152
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200153 Map<String, Object> toSoyData(HttpServletRequest req, RevCommit commit, DateFormatter df)
154 throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -0700155 return toSoyData(req, commit, DEFAULT_FIELDS, df);
Dave Borowitz9de65952012-08-13 16:09:45 -0700156 }
157
158 // TODO(dborowitz): Extract this.
Dave Borowitz97a70312014-04-28 15:50:23 -0700159 public static Map<String, String> toSoyData(PersonIdent ident, DateFormatter df) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700160 return ImmutableMap.of(
161 "name", ident.getName(),
162 "email", ident.getEmailAddress(),
Dave Borowitz97a70312014-04-28 15:50:23 -0700163 "time", df.format(ident),
Dave Borowitz9de65952012-08-13 16:09:45 -0700164 // TODO(dborowitz): Switch from relative to absolute at some threshold.
165 "relativeTime", RelativeDateFormatter.format(ident.getWhen()));
166 }
167
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200168 private List<Map<String, String>> toSoyData(
169 GitilesView view, Set<Field> fs, List<RevCommit> parents) {
Dave Borowitz01551272014-03-16 13:43:16 -0700170 List<Map<String, String>> result = Lists.newArrayListWithCapacity(parents.size());
Dave Borowitz9de65952012-08-13 16:09:45 -0700171 int i = 1;
172 // TODO(dborowitz): Render something slightly different when we're actively
173 // viewing a diff against one of the parents.
174 for (RevCommit parent : parents) {
175 String name = parent.name();
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700176 // Clear path on parent diff view, since this parent may not have a diff
177 // for the path in question.
Dave Borowitzdd3c3d92013-03-11 16:38:41 -0700178 GitilesView.Builder diff = GitilesView.diff().copyFrom(view).setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700179 String parentName;
Dave Borowitz01551272014-03-16 13:43:16 -0700180 if (parents.size() == 1) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700181 parentName = view.getRevision().getName() + "^";
182 } else {
183 parentName = view.getRevision().getName() + "^" + (i++);
184 }
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700185 diff.setOldRevision(parentName, parent);
186
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700187 Map<String, String> e = Maps.newHashMapWithExpectedSize(4);
188 e.put("sha", name);
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200189 e.put("url", GitilesView.revision().copyFrom(view).setRevision(parentName, parent).toUrl());
Dave Borowitz8d11fb72014-09-23 10:27:31 -0700190 e.put("diffUrl", diff.toUrl());
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700191 if (fs.contains(Field.PARENT_BLAME_URL)) {
192 // Assumes caller has ensured path is a file.
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200193 e.put(
194 "blameUrl",
195 GitilesView.blame()
196 .copyFrom(view)
197 .setRevision(Revision.peeled(parentName, parent))
198 .toUrl());
Dave Borowitzf03fcea2014-04-21 17:20:33 -0700199 }
200 result.add(e);
Dave Borowitz9de65952012-08-13 16:09:45 -0700201 }
202 return result;
203 }
204
Dave Borowitz01551272014-03-16 13:43:16 -0700205 private static Object toSoyData(GitilesView view, DiffList dl) {
206 if (dl.oldRevision == null) {
207 return NullData.INSTANCE;
208 }
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200209 GitilesView.Builder diffUrl =
210 GitilesView.diff()
211 .copyFrom(view)
212 .setOldRevision(dl.oldRevision)
213 .setRevision(dl.revision)
214 .setPathPart("");
Dave Borowitz9de65952012-08-13 16:09:45 -0700215
Dave Borowitz01551272014-03-16 13:43:16 -0700216 List<Object> result = Lists.newArrayListWithCapacity(dl.entries.size());
217 for (DiffEntry e : dl.entries) {
218 Map<String, Object> entry = Maps.newHashMapWithExpectedSize(5);
219 ChangeType type = e.getChangeType();
220 if (type != DELETE) {
221 entry.put("path", e.getNewPath());
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200222 entry.put(
223 "url",
224 GitilesView.path()
225 .copyFrom(view)
226 .setRevision(dl.revision)
227 .setPathPart(e.getNewPath())
228 .toUrl());
Dave Borowitz01551272014-03-16 13:43:16 -0700229 } else {
230 entry.put("path", e.getOldPath());
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200231 entry.put(
232 "url",
233 GitilesView.path()
234 .copyFrom(view)
235 .setRevision(dl.oldRevision)
236 .setPathPart(e.getOldPath())
237 .toUrl());
Dave Borowitz9de65952012-08-13 16:09:45 -0700238 }
Dave Borowitz01551272014-03-16 13:43:16 -0700239 entry.put("diffUrl", diffUrl.setAnchor("F" + result.size()).toUrl());
240 entry.put("changeType", e.getChangeType().toString());
241 if (type == COPY || type == RENAME) {
242 entry.put("oldPath", e.getOldPath());
243 }
244 result.add(entry);
Dave Borowitz9de65952012-08-13 16:09:45 -0700245 }
Dave Borowitz01551272014-03-16 13:43:16 -0700246 return result;
Dave Borowitz9de65952012-08-13 16:09:45 -0700247 }
248
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200249 private static List<Map<String, String>> toSoyData(
250 GitilesView view, List<Ref> refs, String prefix) {
Dave Borowitz9de65952012-08-13 16:09:45 -0700251 List<Map<String, String>> result = Lists.newArrayListWithCapacity(refs.size());
252 for (Ref ref : refs) {
253 if (ref.getName().startsWith(prefix)) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200254 result.add(
255 ImmutableMap.of(
256 "name", ref.getName().substring(prefix.length()),
257 "url",
258 GitilesView.revision()
259 .copyFrom(view)
260 .setRevision(Revision.unpeeled(ref.getName(), ref.getObjectId()))
261 .toUrl()));
Dave Borowitz9de65952012-08-13 16:09:45 -0700262 }
263 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700264 return result;
265 }
Dave Borowitz9de65952012-08-13 16:09:45 -0700266}