Build blame cache as own java_library This is a first step towards properly packaging of blame cache in Gerrit server which is needed for [1]. [1] currently (PS-44) depends on the whole gitiles-servlet and this leads to a: java.lang.NoClassDefFoundError: com/google/template/soy/SoyFileSet caused by packaging the gitiles-servlet in the Gerrit core but without all of its dependencies. [1] https://gerrit-review.googlesource.com/72798 Change-Id: I9326b30962c21082b3d152eb6397ddcf767d1ce3
diff --git a/gitiles-servlet/BUCK b/gitiles-servlet/BUCK index 8a0de1f..81c06e2 100644 --- a/gitiles-servlet/BUCK +++ b/gitiles-servlet/BUCK
@@ -1,6 +1,7 @@ SRCS = glob(['src/main/java/**/*.java']) RSRC = glob(['src/main/resources/**/*']) DEPS = [ + '//blame-cache:lib', '//lib:commons-lang', '//lib:grappa', '//lib:gson',
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCache.java b/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCache.java deleted file mode 100644 index d0c6fe5..0000000 --- a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCache.java +++ /dev/null
@@ -1,33 +0,0 @@ -// Copyright (C) 2014 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.blame; - -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.Repository; - -import java.io.IOException; -import java.util.List; - -public interface BlameCache { - /** @return the blame of a path at a given commit. */ - public List<Region> get(Repository repo, ObjectId commitId, String path) throws IOException; - - /** - * @return the last commit that modified a path, starting at the given - * commit. - */ - public ObjectId findLastCommit(Repository repo, ObjectId commitId, String path) - throws IOException; -}
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCacheImpl.java b/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCacheImpl.java deleted file mode 100644 index fc6ed6c..0000000 --- a/gitiles-servlet/src/main/java/com/google/gitiles/blame/BlameCacheImpl.java +++ /dev/null
@@ -1,218 +0,0 @@ -// Copyright (C) 2014 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.blame; - -import static com.google.common.base.Preconditions.checkState; -import static java.util.Objects.hash; - -import com.google.common.cache.Cache; -import com.google.common.cache.CacheBuilder; -import com.google.common.cache.Weigher; -import com.google.common.collect.ImmutableList; -import com.google.common.collect.Interner; -import com.google.common.collect.Interners; -import com.google.common.collect.Lists; -import com.google.common.collect.Maps; - -import org.eclipse.jgit.blame.BlameGenerator; -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.PersonIdent; -import org.eclipse.jgit.lib.Repository; -import org.eclipse.jgit.revwalk.RevWalk; -import org.eclipse.jgit.treewalk.filter.AndTreeFilter; -import org.eclipse.jgit.treewalk.filter.PathFilterGroup; -import org.eclipse.jgit.treewalk.filter.TreeFilter; -import org.eclipse.jgit.util.QuotedString; - -import java.io.IOException; -import java.util.Collections; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.Callable; -import java.util.concurrent.ExecutionException; - -/** Guava implementation of BlameCache, weighted by number of blame regions. */ -public class BlameCacheImpl implements BlameCache { - public static CacheBuilder<Key, List<Region>> defaultBuilder() { - return weigher(CacheBuilder.newBuilder()).maximumWeight(10 << 10); - } - - public static CacheBuilder<Key, List<Region>> weigher( - CacheBuilder<? super Key, ? super List<Region>> builder) { - return builder.weigher(new Weigher<Key, List<Region>>() { - @Override - public int weigh(Key key, List<Region> value) { - return value.size(); - } - }); - } - - public static class Key { - private final ObjectId commitId; - private final String path; - - public Key(ObjectId commitId, String path) { - this.commitId = commitId; - this.path = path; - } - - public ObjectId getCommitId() { - return commitId; - } - - public String getPath() { - return path; - } - - @Override - public boolean equals(Object o) { - if (o instanceof Key) { - Key k = (Key) o; - return Objects.equals(commitId, k.commitId) - && Objects.equals(path, k.path); - } - return false; - } - - @Override - public int hashCode() { - return hash(commitId, path); - } - - @Override - public String toString() { - return commitId.name() + ":" + QuotedString.GIT_PATH.quote(path); - } - } - - private final Cache<Key, List<Region>> cache; - - public BlameCacheImpl() { - this(defaultBuilder()); - } - - public Cache<Key, List<Region>> getCache() { - return cache; - } - - public Callable<List<Region>> newLoader(final Key key, final Repository repo) { - return new Callable<List<Region>>() { - @Override - public List<Region> call() throws IOException { - return loadBlame(key, repo); - } - }; - } - - public BlameCacheImpl(CacheBuilder<? super Key, ? super List<Region>> builder) { - this.cache = builder.build(); - } - - @Override - public List<Region> get(Repository repo, ObjectId commitId, String path) - throws IOException { - try { - Key key = new Key(commitId, path); - return cache.get(key, newLoader(key, repo)); - } catch (ExecutionException e) { - throw new IOException(e); - } - } - - @Override - public ObjectId findLastCommit(Repository repo, ObjectId commitId, String path) - throws IOException { - // Default implementation does no caching. - try (RevWalk rw = new RevWalk(repo)) { - rw.markStart(rw.parseCommit(commitId)); - rw.setRewriteParents(false); - // Don't use rename detection, even though BlameGenerator does. It is not - // possible for a commit to modify a path when not doing rename detection - // but to not modify the same path when taking renames into account. - rw.setTreeFilter(AndTreeFilter.create( - PathFilterGroup.createFromStrings(path), - TreeFilter.ANY_DIFF)); - return rw.next(); - } - } - - public static List<Region> loadBlame(Key key, Repository repo) throws IOException { - try (BlameGenerator gen = new BlameGenerator(repo, key.path)) { - gen.push(null, key.commitId); - return loadRegions(gen); - } - } - - private static class PooledCommit { - final ObjectId commit; - final PersonIdent author; - - private PooledCommit(ObjectId commit, PersonIdent author) { - this.commit = commit; - this.author = author; - } - } - - private static List<Region> loadRegions(BlameGenerator gen) throws IOException { - Map<ObjectId, PooledCommit> commits = Maps.newHashMap(); - Interner<String> strings = Interners.newStrongInterner(); - int lineCount = gen.getResultContents().size(); - - List<Region> regions = Lists.newArrayList(); - while (gen.next()) { - String path = gen.getSourcePath(); - PersonIdent author = gen.getSourceAuthor(); - ObjectId commit = gen.getSourceCommit(); - checkState(path != null && author != null && commit != null); - - PooledCommit pc = commits.get(commit); - if (pc == null) { - pc = new PooledCommit(commit.copy(), - new PersonIdent( - strings.intern(author.getName()), - strings.intern(author.getEmailAddress()), - author.getWhen(), - author.getTimeZone())); - commits.put(pc.commit, pc); - } - path = strings.intern(path); - commit = pc.commit; - author = pc.author; - regions.add(new Region(path, commit, author, gen.getResultStart(), gen.getResultEnd())); - } - Collections.sort(regions); - - // Fill in any gaps left by bugs in JGit, since rendering code assumes the - // full set of contiguous regions. - List<Region> result = Lists.newArrayListWithExpectedSize(regions.size()); - Region last = null; - for (Region r : regions) { - if (last != null) { - checkState(last.getEnd() <= r.getStart()); - if (last.getEnd() < r.getStart()) { - result.add(new Region(null, null, null, last.getEnd(), r.getStart())); - } - } - result.add(r); - last = r; - } - if (last != null && last.getEnd() != lineCount) { - result.add(new Region(null, null, null, last.getEnd(), lineCount)); - } - - return ImmutableList.copyOf(result); - } -}
diff --git a/gitiles-servlet/src/main/java/com/google/gitiles/blame/Region.java b/gitiles-servlet/src/main/java/com/google/gitiles/blame/Region.java deleted file mode 100644 index d6f928d..0000000 --- a/gitiles-servlet/src/main/java/com/google/gitiles/blame/Region.java +++ /dev/null
@@ -1,94 +0,0 @@ -// Copyright (C) 2014 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.blame; - -import static com.google.common.base.Preconditions.checkArgument; - -import org.eclipse.jgit.lib.ObjectId; -import org.eclipse.jgit.lib.PersonIdent; - -import java.io.Serializable; - -/** Region of the blame of a file. */ -public class Region implements Serializable, Comparable<Region> { - private static final long serialVersionUID = 1L; - - private final String sourcePath; - private final ObjectId sourceCommit; - private final PersonIdent sourceAuthor; - private final int count; - private transient int start; - - public Region(String path, ObjectId commit, PersonIdent author, int start, int end) { - checkArgument((path != null && commit != null && author != null) - || (path == null && commit == null && author == null), - "expected all null or none: %s, %s, %s", path, commit, author); - this.sourcePath = path; - this.sourceCommit = commit; - this.sourceAuthor = author; - this.start = start; - this.count = end - start; - } - - void setStart(int start) { - this.start = start; - } - - int getStart() { - return start; - } - - int getEnd() { - return start + count; - } - - public int getCount() { - return count; - } - - public String getSourcePath() { - return sourcePath; - } - - public ObjectId getSourceCommit() { - return sourceCommit; - } - - public PersonIdent getSourceAuthor() { - return sourceAuthor; - } - - @Override - public int compareTo(Region o) { - return start - o.start; - } - - @Override - public String toString() { - StringBuilder sb = new StringBuilder(); - if (sourceCommit != null) { - sb.append(sourceCommit.name(), 0, 7) - .append(' ') - .append(sourceAuthor.toExternalString()) - .append(" (").append(sourcePath).append(')'); - } else { - sb.append("<unblamed region>"); - } - sb.append(' ') - .append("start=").append(start) - .append(", count=").append(count); - return sb.toString(); - } -}