| // Copyright 2012 Google Inc. All Rights Reserved. |
| // |
| // Licensed under the Apache License, Version 2.0 (the "License"); |
| // you may not use this file except in compliance with the License. |
| // You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, software |
| // distributed under the License is distributed on an "AS IS" BASIS, |
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| // See the License for the specific language governing permissions and |
| // limitations under the License. |
| |
| package com.google.gitiles; |
| |
| import static com.google.common.base.Preconditions.checkArgument; |
| import static com.google.common.base.Preconditions.checkNotNull; |
| import static javax.servlet.http.HttpServletResponse.SC_INTERNAL_SERVER_ERROR; |
| import static javax.servlet.http.HttpServletResponse.SC_NOT_FOUND; |
| |
| import com.google.common.base.Optional; |
| import com.google.common.base.Strings; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.ListMultimap; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Maps; |
| import com.google.gitiles.CommitSoyData.KeySet; |
| |
| import org.eclipse.jgit.errors.IncorrectObjectTypeException; |
| import org.eclipse.jgit.errors.MissingObjectException; |
| import org.eclipse.jgit.errors.RevWalkException; |
| import org.eclipse.jgit.http.server.ServletUtils; |
| import org.eclipse.jgit.lib.AbbreviatedObjectId; |
| import org.eclipse.jgit.lib.AnyObjectId; |
| import org.eclipse.jgit.lib.ObjectId; |
| import org.eclipse.jgit.lib.ObjectReader; |
| import org.eclipse.jgit.lib.Ref; |
| import org.eclipse.jgit.lib.Repository; |
| import org.eclipse.jgit.revwalk.FollowFilter; |
| import org.eclipse.jgit.revwalk.RevCommit; |
| import org.eclipse.jgit.revwalk.RevObject; |
| import org.eclipse.jgit.revwalk.RevTag; |
| import org.eclipse.jgit.revwalk.RevWalk; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| import java.io.IOException; |
| import java.util.Collection; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| |
| import javax.servlet.http.HttpServletRequest; |
| import javax.servlet.http.HttpServletResponse; |
| |
| /** Serves an HTML page with a shortlog for commits and paths. */ |
| public class LogServlet extends BaseServlet { |
| private static final long serialVersionUID = 1L; |
| private static final Logger log = LoggerFactory.getLogger(LogServlet.class); |
| |
| private static final String START_PARAM = "s"; |
| |
| private final Linkifier linkifier; |
| private final int limit; |
| |
| public LogServlet(Renderer renderer, Linkifier linkifier) { |
| this(renderer, linkifier, 100); |
| } |
| |
| private LogServlet(Renderer renderer, Linkifier linkifier, int limit) { |
| super(renderer); |
| this.linkifier = checkNotNull(linkifier, "linkifier"); |
| checkArgument(limit >= 0, "limit must be positive: %s", limit); |
| this.limit = limit; |
| } |
| |
| @Override |
| protected void doGet(HttpServletRequest req, HttpServletResponse res) throws IOException { |
| GitilesView view = ViewFilter.getView(req); |
| Repository repo = ServletUtils.getRepository(req); |
| RevWalk walk = null; |
| try { |
| try { |
| walk = newWalk(repo, view); |
| } catch (IncorrectObjectTypeException e) { |
| res.setStatus(SC_NOT_FOUND); |
| return; |
| } |
| |
| Optional<ObjectId> start = getStart(view.getParameters(), walk.getObjectReader()); |
| if (start == null) { |
| res.setStatus(SC_NOT_FOUND); |
| return; |
| } |
| |
| Map<String, Object> data = Maps.newHashMapWithExpectedSize(5); |
| |
| if (!view.getRevision().nameIsId()) { |
| List<Map<String, Object>> tags = Lists.newArrayListWithExpectedSize(1); |
| for (RevObject o : RevisionServlet.listObjects(walk, view.getRevision())) { |
| if (o instanceof RevTag) { |
| tags.add(new TagSoyData(linkifier, req).toSoyData((RevTag) o)); |
| } |
| } |
| if (!tags.isEmpty()) { |
| data.put("tags", tags); |
| } |
| } |
| |
| Paginator paginator = new Paginator(walk, limit, start.orNull()); |
| Map<AnyObjectId, Set<Ref>> refsById = repo.getAllRefsByPeeledObjectId(); |
| List<Map<String, Object>> entries = Lists.newArrayListWithCapacity(limit); |
| for (RevCommit c : paginator) { |
| entries.add(new CommitSoyData(null, req, repo, walk, view, refsById) |
| .toSoyData(c, KeySet.SHORTLOG)); |
| } |
| |
| String title = "Log - "; |
| if (view.getOldRevision() != Revision.NULL) { |
| title += view.getRevisionRange(); |
| } else { |
| title += view.getRevision().getName(); |
| } |
| |
| data.put("title", title); |
| data.put("entries", entries); |
| ObjectId next = paginator.getNextStart(); |
| if (next != null) { |
| data.put("nextUrl", copyAndCanonicalize(view) |
| .replaceParam(START_PARAM, next.name()) |
| .toUrl()); |
| } |
| ObjectId prev = paginator.getPreviousStart(); |
| if (prev != null) { |
| GitilesView.Builder prevView = copyAndCanonicalize(view); |
| if (!prevView.getRevision().getId().equals(prev)) { |
| prevView.replaceParam(START_PARAM, prev.name()); |
| } |
| data.put("previousUrl", prevView.toUrl()); |
| } |
| |
| render(req, res, "gitiles.logDetail", data); |
| } catch (RevWalkException e) { |
| log.warn("Error in rev walk", e); |
| res.setStatus(SC_INTERNAL_SERVER_ERROR); |
| return; |
| } finally { |
| if (walk != null) { |
| walk.release(); |
| } |
| } |
| } |
| |
| private static GitilesView.Builder copyAndCanonicalize(GitilesView view) { |
| // Canonicalize the view by using full SHAs. |
| GitilesView.Builder copy = GitilesView.log().copyFrom(view) |
| .setRevision(view.getRevision()); |
| if (view.getOldRevision() != Revision.NULL) { |
| copy.setOldRevision(view.getOldRevision()); |
| } |
| return copy; |
| } |
| |
| private static Optional<ObjectId> getStart(ListMultimap<String, String> params, |
| ObjectReader reader) throws IOException { |
| List<String> values = params.get(START_PARAM); |
| switch (values.size()) { |
| case 0: |
| return Optional.absent(); |
| case 1: |
| Collection<ObjectId> ids = reader.resolve(AbbreviatedObjectId.fromString(values.get(0))); |
| if (ids.size() != 1) { |
| return null; |
| } |
| return Optional.of(Iterables.getOnlyElement(ids)); |
| default: |
| return null; |
| } |
| } |
| |
| private static RevWalk newWalk(Repository repo, GitilesView view) |
| throws MissingObjectException, IncorrectObjectTypeException, IOException { |
| RevWalk walk = new RevWalk(repo); |
| walk.markStart(walk.parseCommit(view.getRevision().getId())); |
| if (view.getOldRevision() != Revision.NULL) { |
| walk.markUninteresting(walk.parseCommit(view.getOldRevision().getId())); |
| } |
| if (!Strings.isNullOrEmpty(view.getTreePath())) { |
| walk.setTreeFilter(FollowFilter.create(view.getTreePath())); |
| } |
| return walk; |
| } |
| } |