blob: 203051b72488403351c88ee56833a21d4c637d15 [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;
Pontus Jaenssonc144de92021-10-29 11:13:23 +020044import org.eclipse.jgit.notes.NoteMap;
Dave Borowitz01551272014-03-16 13:43:16 -070045import org.eclipse.jgit.revwalk.RevCommit;
46import org.eclipse.jgit.revwalk.RevWalk;
47import org.eclipse.jgit.treewalk.AbstractTreeIterator;
48import org.eclipse.jgit.treewalk.CanonicalTreeParser;
49import org.eclipse.jgit.treewalk.EmptyTreeIterator;
50import org.eclipse.jgit.util.io.NullOutputStream;
51
Dave Borowitz01551272014-03-16 13:43:16 -070052/** Format-independent data about a single commit. */
53class CommitData {
David Pursehousee3d3ec82016-06-15 22:10:48 +090054 enum Field {
Dave Borowitz01551272014-03-16 13:43:16 -070055 ABBREV_SHA,
56 ARCHIVE_TYPE,
57 ARCHIVE_URL,
58 AUTHOR,
59 BRANCHES,
60 COMMITTER,
61 DIFF_TREE,
62 LOG_URL,
63 MESSAGE,
Pontus Jaenssonc144de92021-10-29 11:13:23 +020064 NOTES,
Dave Borowitz01551272014-03-16 13:43:16 -070065 PARENTS,
Dave Borowitzf03fcea2014-04-21 17:20:33 -070066 PARENT_BLAME_URL,
Dave Borowitz01551272014-03-16 13:43:16 -070067 SHA,
68 SHORT_MESSAGE,
69 TAGS,
70 TREE,
71 TREE_URL,
72 URL;
Dave Borowitz90d1db92014-03-16 14:16:15 -070073
74 static ImmutableSet<Field> setOf(Iterable<Field> base, Field... fields) {
75 return Sets.immutableEnumSet(Iterables.concat(base, Arrays.asList(fields)));
76 }
Dave Borowitz01551272014-03-16 13:43:16 -070077 }
78
79 static class DiffList {
Dave Borowitz2298cef2014-09-23 10:29:46 -070080 Revision revision;
Dave Borowitz01551272014-03-16 13:43:16 -070081 Revision oldRevision;
82 List<DiffEntry> entries;
83 }
84
85 static class Builder {
Dave Borowitz01551272014-03-16 13:43:16 -070086 private ArchiveFormat archiveFormat;
87 private Map<AnyObjectId, Set<Ref>> refsById;
Pontus Jaenssonc144de92021-10-29 11:13:23 +020088 private static final int MAX_NOTE_SIZE = 524288;
Dave Borowitz01551272014-03-16 13:43:16 -070089
Dave Borowitz01551272014-03-16 13:43:16 -070090 Builder setArchiveFormat(@Nullable ArchiveFormat archiveFormat) {
91 this.archiveFormat = archiveFormat;
92 return this;
93 }
94
Jonathan Niederb49306a2019-03-07 14:10:57 -080095 CommitData build(HttpServletRequest req, RevWalk walk, RevCommit c, Set<Field> fs)
96 throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -070097 checkFields(fs);
98 checkNotNull(req, "request");
Jonathan Niederb49306a2019-03-07 14:10:57 -080099 checkNotNull(walk, "walk");
Dave Borowitz01551272014-03-16 13:43:16 -0700100 Repository repo = ServletUtils.getRepository(req);
101 GitilesView view = ViewFilter.getView(req);
102
103 CommitData result = new CommitData();
104
105 if (fs.contains(Field.AUTHOR)) {
Jonathan Nieder4b6753d2019-03-07 14:25:13 -0800106 walk.parseBody(c);
Dave Borowitz01551272014-03-16 13:43:16 -0700107 result.author = c.getAuthorIdent();
108 }
109 if (fs.contains(Field.COMMITTER)) {
Jonathan Nieder4b6753d2019-03-07 14:25:13 -0800110 walk.parseBody(c);
Dave Borowitz01551272014-03-16 13:43:16 -0700111 result.committer = c.getCommitterIdent();
112 }
113 if (fs.contains(Field.SHA)) {
114 result.sha = c.copy();
115 }
116 if (fs.contains(Field.ABBREV_SHA)) {
Shawn Pearceb5ad0a02015-05-24 20:33:17 -0700117 try (ObjectReader reader = repo.getObjectDatabase().newReader()) {
Dave Borowitz01551272014-03-16 13:43:16 -0700118 result.abbrev = reader.abbreviate(c);
Dave Borowitz01551272014-03-16 13:43:16 -0700119 }
120 }
121 if (fs.contains(Field.URL)) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200122 result.url = GitilesView.revision().copyFrom(view).setRevision(c).toUrl();
Dave Borowitz01551272014-03-16 13:43:16 -0700123 }
124 if (fs.contains(Field.LOG_URL)) {
125 result.logUrl = urlFromView(view, c, GitilesView.log());
126 }
127 if (fs.contains(Field.ARCHIVE_URL)) {
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200128 result.archiveUrl =
129 urlFromView(
130 view, c, GitilesView.archive().setExtension(archiveFormat.getDefaultSuffix()));
Dave Borowitz01551272014-03-16 13:43:16 -0700131 }
132 if (fs.contains(Field.ARCHIVE_TYPE)) {
133 result.archiveType = archiveFormat;
134 }
135 if (fs.contains(Field.TREE)) {
136 result.tree = c.getTree().copy();
137 }
138 if (fs.contains(Field.TREE_URL)) {
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700139 // Tree always implies the root tree.
Dave Borowitz01551272014-03-16 13:43:16 -0700140 result.treeUrl = GitilesView.path().copyFrom(view).setPathPart("/").toUrl();
141 }
142 if (fs.contains(Field.PARENTS)) {
143 result.parents = Arrays.asList(c.getParents());
144 }
Dave Borowitz01551272014-03-16 13:43:16 -0700145 if (fs.contains(Field.BRANCHES)) {
146 result.branches = getRefsById(repo, c, Constants.R_HEADS);
147 }
148 if (fs.contains(Field.TAGS)) {
149 result.tags = getRefsById(repo, c, Constants.R_TAGS);
150 }
Pontus Jaenssonc144de92021-10-29 11:13:23 +0200151 if (fs.contains(Field.NOTES)) {
152 Ref notesRef = repo.getRefDatabase().exactRef(Constants.R_NOTES_COMMITS);
153 if (notesRef != null) {
154 try {
155 byte[] data =
156 NoteMap.read(walk.getObjectReader(), walk.parseCommit(notesRef.getObjectId()))
157 .getCachedBytes(c, MAX_NOTE_SIZE);
158 result.notes = new String(data, "utf-8");
159 } catch (Exception e) {
160 result.notes = "";
161 }
162 }
163 }
Dave Borowitz01551272014-03-16 13:43:16 -0700164 if (fs.contains(Field.MESSAGE)) {
Jonathan Nieder4b6753d2019-03-07 14:25:13 -0800165 walk.parseBody(c);
Dave Borowitz01551272014-03-16 13:43:16 -0700166 result.message = c.getFullMessage();
167 }
Shawn Pearce680ab122015-05-11 23:15:06 -0700168 if (fs.contains(Field.SHORT_MESSAGE)) {
Jonathan Nieder4b6753d2019-03-07 14:25:13 -0800169 walk.parseBody(c);
Shawn Pearce680ab122015-05-11 23:15:06 -0700170 String msg = c.getShortMessage();
171 if (msg.length() > 80) {
172 String ft = result.message;
173 if (ft == null) {
174 ft = c.getFullMessage();
175 }
176 int lf = ft.indexOf('\n');
177 if (lf > 0) {
178 msg = ft.substring(0, lf);
179 }
180 }
181 result.shortMessage = msg;
182 }
Dave Borowitz01551272014-03-16 13:43:16 -0700183 if (fs.contains(Field.DIFF_TREE)) {
Jonathan Niederb49306a2019-03-07 14:10:57 -0800184 result.diffEntries = computeDiffEntries(repo, view, walk, c);
Dave Borowitz01551272014-03-16 13:43:16 -0700185 }
Dave Borowitz01551272014-03-16 13:43:16 -0700186 return result;
187 }
188
189 private void checkFields(Set<Field> fs) {
Dave Borowitz01551272014-03-16 13:43:16 -0700190 if (fs.contains(Field.ARCHIVE_URL) || fs.contains(Field.ARCHIVE_TYPE)) {
191 checkState(archiveFormat != null, "archive format required");
192 }
193 }
194
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200195 private static String urlFromView(
196 GitilesView view, RevCommit commit, GitilesView.Builder builder) {
Dave Borowitz01551272014-03-16 13:43:16 -0700197 Revision rev = view.getRevision();
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200198 return builder
199 .copyFrom(view)
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700200 .setOldRevision(Revision.NULL)
Dave Borowitz01551272014-03-16 13:43:16 -0700201 .setRevision(rev.getId().equals(commit) ? rev.getName() : commit.name(), commit)
Dave Borowitz359ad6d2014-04-21 16:07:59 -0700202 .setPathPart(view.getPathPart())
Dave Borowitz01551272014-03-16 13:43:16 -0700203 .toUrl();
204 }
205
Jonathan Niederc383b262022-03-16 17:14:32 -0700206 private List<Ref> getRefsById(Repository repo, ObjectId id, String prefix) throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -0700207 if (refsById == null) {
208 refsById = repo.getAllRefsByPeeledObjectId();
209 }
Dave Borowitz3b4baab2016-10-04 09:44:10 -0400210 Set<Ref> refs = refsById.get(id);
211 if (refs == null) {
212 return ImmutableList.of();
213 }
214 return refs.stream()
David Pursehouse14293fe2016-10-04 15:55:22 +0900215 .filter(r -> r.getName().startsWith(prefix))
Dave Borowitz3b4baab2016-10-04 09:44:10 -0400216 .sorted(comparing(Ref::getName))
217 .collect(toList());
Dave Borowitz01551272014-03-16 13:43:16 -0700218 }
219
Jonathan Niederb49306a2019-03-07 14:10:57 -0800220 private AbstractTreeIterator getTreeIterator(RevWalk walk, RevCommit commit)
221 throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -0700222 CanonicalTreeParser p = new CanonicalTreeParser();
223 p.reset(walk.getObjectReader(), walk.parseTree(walk.parseCommit(commit).getTree()));
224 return p;
225 }
226
Jonathan Niederb49306a2019-03-07 14:10:57 -0800227 private DiffList computeDiffEntries(
228 Repository repo, GitilesView view, RevWalk walk, RevCommit commit) throws IOException {
Dave Borowitz01551272014-03-16 13:43:16 -0700229 DiffList result = new DiffList();
Han-Wen Nienhuysc0200f62016-05-02 17:34:51 +0200230 result.revision =
231 view.getRevision().matches(commit)
232 ? view.getRevision()
233 : Revision.peeled(commit.name(), commit);
Dave Borowitz2298cef2014-09-23 10:29:46 -0700234
Dave Borowitz01551272014-03-16 13:43:16 -0700235 AbstractTreeIterator oldTree;
236 switch (commit.getParentCount()) {
237 case 0:
238 result.oldRevision = Revision.NULL;
239 oldTree = new EmptyTreeIterator();
240 break;
241 case 1:
Dave Borowitze360d5c2015-09-16 16:53:30 -0400242 result.oldRevision =
243 Revision.peeled(result.revision.getName() + "^", commit.getParent(0));
Jonathan Niederb49306a2019-03-07 14:10:57 -0800244 oldTree = getTreeIterator(walk, commit.getParent(0));
Dave Borowitz01551272014-03-16 13:43:16 -0700245 break;
246 default:
247 // TODO(dborowitz): handle merges
248 return result;
249 }
Jonathan Niederb49306a2019-03-07 14:10:57 -0800250 AbstractTreeIterator newTree = getTreeIterator(walk, commit);
Dave Borowitz01551272014-03-16 13:43:16 -0700251
Shawn Pearceb5ad0a02015-05-24 20:33:17 -0700252 try (DiffFormatter diff = new DiffFormatter(NullOutputStream.INSTANCE)) {
Dave Borowitz01551272014-03-16 13:43:16 -0700253 diff.setRepository(repo);
254 diff.setDetectRenames(true);
255 result.entries = diff.scan(oldTree, newTree);
256 return result;
Dave Borowitz01551272014-03-16 13:43:16 -0700257 }
258 }
259 }
260
261 ObjectId sha;
262 PersonIdent author;
263 PersonIdent committer;
264 AbbreviatedObjectId abbrev;
265 ObjectId tree;
266 List<RevCommit> parents;
267 String shortMessage;
268 String message;
Pontus Jaenssonc144de92021-10-29 11:13:23 +0200269 String notes;
Dave Borowitz01551272014-03-16 13:43:16 -0700270
271 List<Ref> branches;
272 List<Ref> tags;
273 DiffList diffEntries;
274
275 String url;
276 String logUrl;
277 String treeUrl;
278 String archiveUrl;
279 ArchiveFormat archiveType;
280}