blob: 576de878a3f6a209ba0acbf5dacfa6b33acc40ae [file] [log] [blame]
Dave Borowitz01551272014-03-16 13:43:16 -07001// Copyright (C) 2014 The Android Open Source Project
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;
18import static com.google.common.base.Preconditions.checkState;
Dave Borowitz3b4baab2016-10-04 09:44:10 -040019import static java.util.Comparator.comparing;
20import static java.util.stream.Collectors.toList;
Dave Borowitz01551272014-03-16 13:43:16 -070021
Dave Borowitz3b4baab2016-10-04 09:44:10 -040022import com.google.common.collect.ImmutableList;
Dave Borowitz01551272014-03-16 13:43:16 -070023import com.google.common.collect.ImmutableSet;
Dave Borowitz90d1db92014-03-16 14:16:15 -070024import com.google.common.collect.Iterables;
Dave Borowitz90d1db92014-03-16 14:16:15 -070025import com.google.common.collect.Sets;
Dave Borowitz3b744b12016-08-19 16:11:10 -040026import java.io.IOException;
27import java.util.Arrays;
28import java.util.List;
29import java.util.Map;
30import java.util.Set;
31import javax.annotation.Nullable;
32import javax.servlet.http.HttpServletRequest;
Dave Borowitz01551272014-03-16 13:43:16 -070033import org.eclipse.jgit.diff.DiffEntry;
34import org.eclipse.jgit.diff.DiffFormatter;
35import org.eclipse.jgit.http.server.ServletUtils;
36import org.eclipse.jgit.lib.AbbreviatedObjectId;
37import org.eclipse.jgit.lib.AnyObjectId;
38import org.eclipse.jgit.lib.Constants;
39import org.eclipse.jgit.lib.ObjectId;
40import org.eclipse.jgit.lib.ObjectReader;
41import org.eclipse.jgit.lib.PersonIdent;
42import org.eclipse.jgit.lib.Ref;
43import org.eclipse.jgit.lib.Repository;
44import org.eclipse.jgit.revwalk.RevCommit;
45import org.eclipse.jgit.revwalk.RevWalk;
46import org.eclipse.jgit.treewalk.AbstractTreeIterator;
47import org.eclipse.jgit.treewalk.CanonicalTreeParser;
48import org.eclipse.jgit.treewalk.EmptyTreeIterator;
49import org.eclipse.jgit.util.io.NullOutputStream;
50
Dave Borowitz01551272014-03-16 13:43:16 -070051/** Format-independent data about a single commit. */
52class CommitData {
David Pursehousee3d3ec82016-06-15 22:10:48 +090053 enum Field {
Dave Borowitz01551272014-03-16 13:43:16 -070054 ABBREV_SHA,
55 ARCHIVE_TYPE,
56 ARCHIVE_URL,
57 AUTHOR,
58 BRANCHES,
59 COMMITTER,
60 DIFF_TREE,
61 LOG_URL,
62 MESSAGE,
63 PARENTS,
Dave Borowitzf03fcea2014-04-21 17:20:33 -070064 PARENT_BLAME_URL,
Dave Borowitz01551272014-03-16 13:43:16 -070065 SHA,
66 SHORT_MESSAGE,
67 TAGS,
68 TREE,
69 TREE_URL,
70 URL;
Dave Borowitz90d1db92014-03-16 14:16:15 -070071
72 static ImmutableSet<Field> setOf(Iterable<Field> base, Field... fields) {
73 return Sets.immutableEnumSet(Iterables.concat(base, Arrays.asList(fields)));
74 }
Dave Borowitz01551272014-03-16 13:43:16 -070075 }
76
77 static class DiffList {
Dave Borowitz2298cef2014-09-23 10:29:46 -070078 Revision revision;
Dave Borowitz01551272014-03-16 13:43:16 -070079 Revision oldRevision;
80 List<DiffEntry> entries;
81 }
82
83 static class Builder {
84 private RevWalk walk;
85 private ArchiveFormat archiveFormat;
86 private Map<AnyObjectId, Set<Ref>> refsById;
87
88 Builder setRevWalk(@Nullable RevWalk walk) {
89 this.walk = walk;
90 return this;
91 }
92
93 Builder setArchiveFormat(@Nullable ArchiveFormat archiveFormat) {
94 this.archiveFormat = archiveFormat;
95 return this;
96 }
97
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +020098 CommitData build(HttpServletRequest req, RevCommit c, Set<Field> fs) throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -070099 checkFields(fs);
100 checkNotNull(req, "request");
101 Repository repo = ServletUtils.getRepository(req);
102 GitilesView view = ViewFilter.getView(req);
103
104 CommitData result = new CommitData();
105
106 if (fs.contains(Field.AUTHOR)) {
107 result.author = c.getAuthorIdent();
108 }
109 if (fs.contains(Field.COMMITTER)) {
110 result.committer = c.getCommitterIdent();
111 }
112 if (fs.contains(Field.SHA)) {
113 result.sha = c.copy();
114 }
115 if (fs.contains(Field.ABBREV_SHA)) {
Shawn Pearceb5ad0a02015-05-24 20:33:17 -0700116 try (ObjectReader reader = repo.getObjectDatabase().newReader()) {
Dave Borowitz01551272014-03-16 13:43:16 -0700117 result.abbrev = reader.abbreviate(c);
Dave Borowitz01551272014-03-16 13:43:16 -0700118 }
119 }
120 if (fs.contains(Field.URL)) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200121 result.url = GitilesView.revision().copyFrom(view).setRevision(c).toUrl();
Dave Borowitz01551272014-03-16 13:43:16 -0700122 }
123 if (fs.contains(Field.LOG_URL)) {
124 result.logUrl = urlFromView(view, c, GitilesView.log());
125 }
126 if (fs.contains(Field.ARCHIVE_URL)) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200127 result.archiveUrl =
128 urlFromView(
129 view, c, GitilesView.archive().setExtension(archiveFormat.getDefaultSuffix()));
Dave Borowitz01551272014-03-16 13:43:16 -0700130 }
131 if (fs.contains(Field.ARCHIVE_TYPE)) {
132 result.archiveType = archiveFormat;
133 }
134 if (fs.contains(Field.TREE)) {
135 result.tree = c.getTree().copy();
136 }
137 if (fs.contains(Field.TREE_URL)) {
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700138 // Tree always implies the root tree.
Dave Borowitz01551272014-03-16 13:43:16 -0700139 result.treeUrl = GitilesView.path().copyFrom(view).setPathPart("/").toUrl();
140 }
141 if (fs.contains(Field.PARENTS)) {
142 result.parents = Arrays.asList(c.getParents());
143 }
Dave Borowitz01551272014-03-16 13:43:16 -0700144 if (fs.contains(Field.BRANCHES)) {
145 result.branches = getRefsById(repo, c, Constants.R_HEADS);
146 }
147 if (fs.contains(Field.TAGS)) {
148 result.tags = getRefsById(repo, c, Constants.R_TAGS);
149 }
150 if (fs.contains(Field.MESSAGE)) {
151 result.message = c.getFullMessage();
152 }
Shawn Pearce680ab122015-05-11 23:15:06 -0700153 if (fs.contains(Field.SHORT_MESSAGE)) {
154 String msg = c.getShortMessage();
155 if (msg.length() > 80) {
156 String ft = result.message;
157 if (ft == null) {
158 ft = c.getFullMessage();
159 }
160 int lf = ft.indexOf('\n');
161 if (lf > 0) {
162 msg = ft.substring(0, lf);
163 }
164 }
165 result.shortMessage = msg;
166 }
Dave Borowitz01551272014-03-16 13:43:16 -0700167 if (fs.contains(Field.DIFF_TREE)) {
168 result.diffEntries = computeDiffEntries(repo, view, c);
169 }
170
171 return result;
172 }
173
174 private void checkFields(Set<Field> fs) {
175 checkState(!fs.contains(Field.DIFF_TREE) || walk != null, "RevWalk required for diffTree");
176 if (fs.contains(Field.ARCHIVE_URL) || fs.contains(Field.ARCHIVE_TYPE)) {
177 checkState(archiveFormat != null, "archive format required");
178 }
179 }
180
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200181 private static String urlFromView(
182 GitilesView view, RevCommit commit, GitilesView.Builder builder) {
Dave Borowitz01551272014-03-16 13:43:16 -0700183 Revision rev = view.getRevision();
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200184 return builder
185 .copyFrom(view)
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700186 .setOldRevision(Revision.NULL)
Dave Borowitz01551272014-03-16 13:43:16 -0700187 .setRevision(rev.getId().equals(commit) ? rev.getName() : commit.name(), commit)
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700188 .setPathPart(view.getPathPart())
Dave Borowitz01551272014-03-16 13:43:16 -0700189 .toUrl();
190 }
191
David Pursehouse14293fe2016-10-04 15:55:22 +0900192 private List<Ref> getRefsById(Repository repo, ObjectId id, String prefix) {
Dave Borowitz01551272014-03-16 13:43:16 -0700193 if (refsById == null) {
194 refsById = repo.getAllRefsByPeeledObjectId();
195 }
Dave Borowitz3b4baab2016-10-04 09:44:10 -0400196 Set<Ref> refs = refsById.get(id);
197 if (refs == null) {
198 return ImmutableList.of();
199 }
200 return refs.stream()
David Pursehouse14293fe2016-10-04 15:55:22 +0900201 .filter(r -> r.getName().startsWith(prefix))
Dave Borowitz3b4baab2016-10-04 09:44:10 -0400202 .sorted(comparing(Ref::getName))
203 .collect(toList());
Dave Borowitz01551272014-03-16 13:43:16 -0700204 }
205
206 private AbstractTreeIterator getTreeIterator(RevCommit commit) throws IOException {
207 CanonicalTreeParser p = new CanonicalTreeParser();
208 p.reset(walk.getObjectReader(), walk.parseTree(walk.parseCommit(commit).getTree()));
209 return p;
210 }
211
212 private DiffList computeDiffEntries(Repository repo, GitilesView view, RevCommit commit)
213 throws IOException {
214 DiffList result = new DiffList();
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200215 result.revision =
216 view.getRevision().matches(commit)
217 ? view.getRevision()
218 : Revision.peeled(commit.name(), commit);
Dave Borowitz2298cef2014-09-23 10:29:46 -0700219
Dave Borowitz01551272014-03-16 13:43:16 -0700220 AbstractTreeIterator oldTree;
221 switch (commit.getParentCount()) {
222 case 0:
223 result.oldRevision = Revision.NULL;
224 oldTree = new EmptyTreeIterator();
225 break;
226 case 1:
Dave Borowitze360d5c2015-09-16 16:53:30 -0400227 result.oldRevision =
228 Revision.peeled(result.revision.getName() + "^", commit.getParent(0));
Dave Borowitz01551272014-03-16 13:43:16 -0700229 oldTree = getTreeIterator(commit.getParent(0));
230 break;
231 default:
232 // TODO(dborowitz): handle merges
233 return result;
234 }
235 AbstractTreeIterator newTree = getTreeIterator(commit);
236
Shawn Pearceb5ad0a02015-05-24 20:33:17 -0700237 try (DiffFormatter diff = new DiffFormatter(NullOutputStream.INSTANCE)) {
Dave Borowitz01551272014-03-16 13:43:16 -0700238 diff.setRepository(repo);
239 diff.setDetectRenames(true);
240 result.entries = diff.scan(oldTree, newTree);
241 return result;
Dave Borowitz01551272014-03-16 13:43:16 -0700242 }
243 }
244 }
245
246 ObjectId sha;
247 PersonIdent author;
248 PersonIdent committer;
249 AbbreviatedObjectId abbrev;
250 ObjectId tree;
251 List<RevCommit> parents;
252 String shortMessage;
253 String message;
254
255 List<Ref> branches;
256 List<Ref> tags;
257 DiffList diffEntries;
258
259 String url;
260 String logUrl;
261 String treeUrl;
262 String archiveUrl;
263 ArchiveFormat archiveType;
264}