mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-04 20:45:52 +01:00
merge with 2.0.0-m3
This commit is contained in:
@@ -42,7 +42,10 @@ import com.google.common.collect.Multimap;
|
||||
import org.eclipse.jgit.api.FetchCommand;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.attributes.Attribute;
|
||||
import org.eclipse.jgit.attributes.Attributes;
|
||||
import org.eclipse.jgit.diff.DiffFormatter;
|
||||
import org.eclipse.jgit.lfs.LfsPointer;
|
||||
import org.eclipse.jgit.lib.AnyObjectId;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
@@ -55,6 +58,7 @@ import org.eclipse.jgit.transport.FetchResult;
|
||||
import org.eclipse.jgit.transport.RefSpec;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.util.FS;
|
||||
import org.eclipse.jgit.util.LfsFactory;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.ContextEntry;
|
||||
@@ -65,12 +69,13 @@ import sonia.scm.web.GitUserAgentProvider;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import static java.util.Optional.empty;
|
||||
import static java.util.Optional.of;
|
||||
import static java.util.Optional.ofNullable;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
|
||||
@@ -80,7 +85,7 @@ import static java.util.Optional.ofNullable;
|
||||
*/
|
||||
public final class GitUtil
|
||||
{
|
||||
|
||||
|
||||
private static final GitUserAgentProvider GIT_USER_AGENT_PROVIDER = new GitUserAgentProvider();
|
||||
|
||||
/** Field description */
|
||||
@@ -326,14 +331,14 @@ public final class GitUtil
|
||||
|
||||
return branch;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns {@code true} if the provided reference name is a branch name.
|
||||
*
|
||||
*
|
||||
* @param refName reference name
|
||||
*
|
||||
*
|
||||
* @return {@code true} if the name is a branch name
|
||||
*
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
public static boolean isBranch(String refName)
|
||||
@@ -612,11 +617,11 @@ public final class GitUtil
|
||||
|
||||
/**
|
||||
* Returns the name of the tag or {@code null} if the the ref is not a tag.
|
||||
*
|
||||
*
|
||||
* @param refName ref name
|
||||
*
|
||||
*
|
||||
* @return name of tag or {@link null}
|
||||
*
|
||||
*
|
||||
* @since 1.50
|
||||
*/
|
||||
public static String getTagName(String refName)
|
||||
@@ -689,7 +694,7 @@ public final class GitUtil
|
||||
{
|
||||
//J-
|
||||
return fs.resolve(dir, DIRECTORY_OBJETCS).exists()
|
||||
&& fs.resolve(dir, DIRECTORY_REFS).exists()
|
||||
&& fs.resolve(dir, DIRECTORY_REFS).exists()
|
||||
&&!fs.resolve(dir, DIRECTORY_DOTGIT).exists();
|
||||
//J+
|
||||
}
|
||||
@@ -723,13 +728,30 @@ public final class GitUtil
|
||||
/**
|
||||
* Computes the first common ancestor of two revisions, aka merge base.
|
||||
*/
|
||||
public static Optional<ObjectId> computeCommonAncestor(org.eclipse.jgit.lib.Repository repository, ObjectId revision1, ObjectId revision2) throws IOException {
|
||||
public static ObjectId computeCommonAncestor(org.eclipse.jgit.lib.Repository repository, ObjectId revision1, ObjectId revision2) throws IOException {
|
||||
try (RevWalk mergeBaseWalk = new RevWalk(repository)) {
|
||||
mergeBaseWalk.setRevFilter(RevFilter.MERGE_BASE);
|
||||
mergeBaseWalk.markStart(mergeBaseWalk.lookupCommit(revision1));
|
||||
mergeBaseWalk.markStart(mergeBaseWalk.parseCommit(revision2));
|
||||
RevCommit commonAncestor = mergeBaseWalk.next();
|
||||
return ofNullable(commonAncestor).map(RevCommit::getId);
|
||||
RevCommit ancestor = mergeBaseWalk.next();
|
||||
if (ancestor == null) {
|
||||
throw new NoCommonHistoryException();
|
||||
}
|
||||
return ancestor.getId();
|
||||
}
|
||||
}
|
||||
|
||||
public static Optional<LfsPointer> getLfsPointer(org.eclipse.jgit.lib.Repository repo, String path, RevCommit commit, TreeWalk treeWalk) throws IOException {
|
||||
Attributes attributes = LfsFactory.getAttributesForPath(repo, path, commit);
|
||||
|
||||
Attribute filter = attributes.get("filter");
|
||||
if (filter != null && "lfs".equals(filter.getValue())) {
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
try (InputStream is = repo.open(blobId, Constants.OBJ_BLOB).openStream()) {
|
||||
return of(LfsPointer.parseLfsPointer(is));
|
||||
}
|
||||
} else {
|
||||
return empty();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,15 +10,11 @@ import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.eclipse.jgit.treewalk.EmptyTreeIterator;
|
||||
import org.eclipse.jgit.treewalk.TreeWalk;
|
||||
import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import sonia.scm.BadRequestException;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Collections.emptyList;
|
||||
|
||||
final class Differ implements AutoCloseable {
|
||||
|
||||
@@ -59,9 +55,7 @@ final class Differ implements AutoCloseable {
|
||||
if (!Strings.isNullOrEmpty(request.getAncestorChangeset()))
|
||||
{
|
||||
ObjectId otherRevision = repository.resolve(request.getAncestorChangeset());
|
||||
ObjectId ancestorId =
|
||||
computeCommonAncestor(repository, revision, otherRevision)
|
||||
.orElseThrow(NoCommonHistoryException::new);
|
||||
ObjectId ancestorId = GitUtil.computeCommonAncestor(repository, revision, otherRevision);
|
||||
RevTree tree = walk.parseCommit(ancestorId).getTree();
|
||||
treeWalk.addTree(tree);
|
||||
}
|
||||
@@ -88,10 +82,6 @@ final class Differ implements AutoCloseable {
|
||||
return new Differ(commit, walk, treeWalk);
|
||||
}
|
||||
|
||||
private static Optional<ObjectId> computeCommonAncestor(org.eclipse.jgit.lib.Repository repository, ObjectId revision1, ObjectId revision2) throws IOException {
|
||||
return GitUtil.computeCommonAncestor(repository, revision1, revision2);
|
||||
}
|
||||
|
||||
private Diff diff() throws IOException {
|
||||
List<DiffEntry> entries = DiffEntry.scan(treeWalk);
|
||||
return new Diff(commit, entries);
|
||||
@@ -122,15 +112,4 @@ final class Differ implements AutoCloseable {
|
||||
}
|
||||
}
|
||||
|
||||
private static class NoCommonHistoryException extends BadRequestException {
|
||||
|
||||
private NoCommonHistoryException() {
|
||||
super(emptyList(), "no common history");
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCode() {
|
||||
return "4iRct4avG1";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,7 @@ package sonia.scm.repository.spi;
|
||||
import com.google.common.base.Strings;
|
||||
import com.google.common.collect.Lists;
|
||||
import com.google.common.collect.Maps;
|
||||
import org.eclipse.jgit.lfs.LfsPointer;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectLoader;
|
||||
@@ -57,13 +58,17 @@ import sonia.scm.repository.GitSubModuleParser;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.SubRepository;
|
||||
import sonia.scm.store.Blob;
|
||||
import sonia.scm.store.BlobStore;
|
||||
import sonia.scm.util.Util;
|
||||
import sonia.scm.web.lfs.LfsBlobStoreFactory;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
@@ -86,18 +91,20 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
*/
|
||||
private static final Logger logger =
|
||||
LoggerFactory.getLogger(GitBrowseCommand.class);
|
||||
private final LfsBlobStoreFactory lfsBlobStoreFactory;
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
/**
|
||||
* Constructs ...
|
||||
*
|
||||
* @param context
|
||||
* @param context
|
||||
* @param repository
|
||||
* @param lfsBlobStoreFactory
|
||||
*/
|
||||
public GitBrowseCommand(GitContext context, Repository repository)
|
||||
public GitBrowseCommand(GitContext context, Repository repository, LfsBlobStoreFactory lfsBlobStoreFactory)
|
||||
{
|
||||
super(context, repository);
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
@@ -167,7 +174,7 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
* @throws IOException
|
||||
*/
|
||||
private FileObject createFileObject(org.eclipse.jgit.lib.Repository repo,
|
||||
BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk)
|
||||
BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk)
|
||||
throws IOException {
|
||||
|
||||
FileObject file = new FileObject();
|
||||
@@ -195,7 +202,6 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
ObjectLoader loader = repo.open(treeWalk.getObjectId(0));
|
||||
|
||||
file.setDirectory(loader.getType() == Constants.OBJ_TREE);
|
||||
file.setLength(loader.getSize());
|
||||
|
||||
// don't show message and date for directories to improve performance
|
||||
if (!file.isDirectory() &&!request.isDisableLastCommit())
|
||||
@@ -203,6 +209,16 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
logger.trace("fetch last commit for {} at {}", path, revId.getName());
|
||||
RevCommit commit = getLatestCommit(repo, revId, path);
|
||||
|
||||
Optional<LfsPointer> lfsPointer = GitUtil.getLfsPointer(repo, path, commit, treeWalk);
|
||||
|
||||
if (lfsPointer.isPresent()) {
|
||||
BlobStore lfsBlobStore = lfsBlobStoreFactory.getLfsBlobStore(repository);
|
||||
Blob blob = lfsBlobStore.get(lfsPointer.get().getOid().getName());
|
||||
file.setLength(blob.getSize());
|
||||
} else {
|
||||
file.setLength(loader.getSize());
|
||||
}
|
||||
|
||||
if (commit != null)
|
||||
{
|
||||
file.setLastModified(GitUtil.getCommitTime(commit));
|
||||
@@ -232,7 +248,7 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
* @return
|
||||
*/
|
||||
private RevCommit getLatestCommit(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revId, String path)
|
||||
ObjectId revId, String path)
|
||||
{
|
||||
RevCommit result = null;
|
||||
RevWalk walk = null;
|
||||
@@ -339,7 +355,7 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
}
|
||||
|
||||
private FileObject findFirstMatch(org.eclipse.jgit.lib.Repository repo,
|
||||
BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) throws IOException {
|
||||
BrowseCommandRequest request, ObjectId revId, TreeWalk treeWalk) throws IOException {
|
||||
String[] pathElements = request.getPath().split("/");
|
||||
int currentDepth = 0;
|
||||
int limit = pathElements.length;
|
||||
@@ -364,7 +380,7 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
@SuppressWarnings("unchecked")
|
||||
private Map<String,
|
||||
SubRepository> getSubRepositories(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revision)
|
||||
ObjectId revision)
|
||||
throws IOException {
|
||||
if (logger.isDebugEnabled())
|
||||
{
|
||||
@@ -375,7 +391,7 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
Map<String, SubRepository> subRepositories;
|
||||
try ( ByteArrayOutputStream baos = new ByteArrayOutputStream() )
|
||||
{
|
||||
new GitCatCommand(context, repository).getContent(repo, revision,
|
||||
new GitCatCommand(context, repository, lfsBlobStoreFactory).getContent(repo, revision,
|
||||
PATH_MODULES, baos);
|
||||
subRepositories = GitSubModuleParser.parse(baos.toString());
|
||||
}
|
||||
@@ -389,7 +405,7 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
}
|
||||
|
||||
private SubRepository getSubRepository(org.eclipse.jgit.lib.Repository repo,
|
||||
ObjectId revId, String path)
|
||||
ObjectId revId, String path)
|
||||
throws IOException {
|
||||
Map<String, SubRepository> subRepositories = subrepositoryCache.get(revId);
|
||||
|
||||
@@ -410,7 +426,7 @@ public class GitBrowseCommand extends AbstractGitCommand
|
||||
}
|
||||
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
|
||||
/** sub repository cache */
|
||||
private final Map<ObjectId, Map<String, SubRepository>> subrepositoryCache = Maps.newHashMap();
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.eclipse.jgit.errors.MissingObjectException;
|
||||
import org.eclipse.jgit.lfs.LfsPointer;
|
||||
import org.eclipse.jgit.lib.Constants;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.ObjectLoader;
|
||||
@@ -45,13 +46,18 @@ import org.eclipse.jgit.treewalk.filter.PathFilter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.GitUtil;
|
||||
import sonia.scm.store.Blob;
|
||||
import sonia.scm.store.BlobStore;
|
||||
import sonia.scm.util.IOUtil;
|
||||
import sonia.scm.util.Util;
|
||||
import sonia.scm.web.lfs.LfsBlobStoreFactory;
|
||||
|
||||
import java.io.Closeable;
|
||||
import java.io.FilterInputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Optional;
|
||||
|
||||
import static sonia.scm.ContextEntry.ContextBuilder.entity;
|
||||
import static sonia.scm.NotFoundException.notFound;
|
||||
@@ -61,15 +67,18 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand {
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(GitCatCommand.class);
|
||||
|
||||
public GitCatCommand(GitContext context, sonia.scm.repository.Repository repository) {
|
||||
private final LfsBlobStoreFactory lfsBlobStoreFactory;
|
||||
|
||||
public GitCatCommand(GitContext context, sonia.scm.repository.Repository repository, LfsBlobStoreFactory lfsBlobStoreFactory) {
|
||||
super(context, repository);
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getCatResult(CatCommandRequest request, OutputStream output) throws IOException {
|
||||
logger.debug("try to read content for {}", request);
|
||||
try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(request)) {
|
||||
closableObjectLoaderContainer.objectLoader.copyTo(output);
|
||||
try (Loader closableObjectLoaderContainer = getLoader(request)) {
|
||||
closableObjectLoaderContainer.copyTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -80,18 +89,18 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand {
|
||||
}
|
||||
|
||||
void getContent(org.eclipse.jgit.lib.Repository repo, ObjectId revId, String path, OutputStream output) throws IOException {
|
||||
try (ClosableObjectLoaderContainer closableObjectLoaderContainer = getLoader(repo, revId, path)) {
|
||||
closableObjectLoaderContainer.objectLoader.copyTo(output);
|
||||
try (Loader closableObjectLoaderContainer = getLoader(repo, revId, path)) {
|
||||
closableObjectLoaderContainer.copyTo(output);
|
||||
}
|
||||
}
|
||||
|
||||
private ClosableObjectLoaderContainer getLoader(CatCommandRequest request) throws IOException {
|
||||
private Loader getLoader(CatCommandRequest request) throws IOException {
|
||||
org.eclipse.jgit.lib.Repository repo = open();
|
||||
ObjectId revId = getCommitOrDefault(repo, request.getRevision());
|
||||
return getLoader(repo, revId, request.getPath());
|
||||
}
|
||||
|
||||
private ClosableObjectLoaderContainer getLoader(Repository repo, ObjectId revId, String path) throws IOException {
|
||||
private Loader getLoader(Repository repo, ObjectId revId, String path) throws IOException {
|
||||
TreeWalk treeWalk = new TreeWalk(repo);
|
||||
treeWalk.setRecursive(Util.nonNull(path).contains("/"));
|
||||
|
||||
@@ -116,21 +125,67 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand {
|
||||
treeWalk.setFilter(PathFilter.create(path));
|
||||
|
||||
if (treeWalk.next() && treeWalk.getFileMode(0).getObjectType() == Constants.OBJ_BLOB) {
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
ObjectLoader loader = repo.open(blobId);
|
||||
|
||||
return new ClosableObjectLoaderContainer(loader, treeWalk, revWalk);
|
||||
Optional<LfsPointer> lfsPointer = GitUtil.getLfsPointer(repo, path, entry, treeWalk);
|
||||
if (lfsPointer.isPresent()) {
|
||||
return loadFromLfsStore(treeWalk, revWalk, lfsPointer.get());
|
||||
} else {
|
||||
return loadFromGit(repo, treeWalk, revWalk);
|
||||
}
|
||||
} else {
|
||||
throw notFound(entity("Path", path).in("Revision", revId.getName()).in(repository));
|
||||
}
|
||||
}
|
||||
|
||||
private static class ClosableObjectLoaderContainer implements Closeable {
|
||||
private Loader loadFromGit(Repository repo, TreeWalk treeWalk, RevWalk revWalk) throws IOException {
|
||||
ObjectId blobId = treeWalk.getObjectId(0);
|
||||
ObjectLoader loader = repo.open(blobId);
|
||||
|
||||
return new GitObjectLoaderWrapper(loader, treeWalk, revWalk);
|
||||
}
|
||||
|
||||
private Loader loadFromLfsStore(TreeWalk treeWalk, RevWalk revWalk, LfsPointer lfsPointer) throws IOException {
|
||||
BlobStore lfsBlobStore = lfsBlobStoreFactory.getLfsBlobStore(repository);
|
||||
Blob blob = lfsBlobStore.get(lfsPointer.getOid().getName());
|
||||
GitUtil.release(revWalk);
|
||||
GitUtil.release(treeWalk);
|
||||
return new BlobLoader(blob);
|
||||
}
|
||||
|
||||
private interface Loader extends Closeable {
|
||||
void copyTo(OutputStream output) throws IOException;
|
||||
|
||||
InputStream openStream() throws IOException;
|
||||
}
|
||||
|
||||
private static class BlobLoader implements Loader {
|
||||
private final InputStream inputStream;
|
||||
|
||||
private BlobLoader(Blob blob) throws IOException {
|
||||
this.inputStream = blob.getInputStream();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void copyTo(OutputStream output) throws IOException {
|
||||
IOUtil.copy(inputStream, output);
|
||||
}
|
||||
|
||||
@Override
|
||||
public InputStream openStream() {
|
||||
return inputStream;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void close() throws IOException {
|
||||
this.inputStream.close();
|
||||
}
|
||||
}
|
||||
|
||||
private static class GitObjectLoaderWrapper implements Loader {
|
||||
private final ObjectLoader objectLoader;
|
||||
private final TreeWalk treeWalk;
|
||||
private final RevWalk revWalk;
|
||||
|
||||
private ClosableObjectLoaderContainer(ObjectLoader objectLoader, TreeWalk treeWalk, RevWalk revWalk) {
|
||||
private GitObjectLoaderWrapper(ObjectLoader objectLoader, TreeWalk treeWalk, RevWalk revWalk) {
|
||||
this.objectLoader = objectLoader;
|
||||
this.treeWalk = treeWalk;
|
||||
this.revWalk = revWalk;
|
||||
@@ -141,14 +196,22 @@ public class GitCatCommand extends AbstractGitCommand implements CatCommand {
|
||||
GitUtil.release(revWalk);
|
||||
GitUtil.release(treeWalk);
|
||||
}
|
||||
|
||||
public void copyTo(OutputStream output) throws IOException {
|
||||
this.objectLoader.copyTo(output);
|
||||
}
|
||||
|
||||
public InputStream openStream() throws IOException {
|
||||
return objectLoader.openStream();
|
||||
}
|
||||
}
|
||||
|
||||
private static class InputStreamWrapper extends FilterInputStream {
|
||||
|
||||
private final ClosableObjectLoaderContainer container;
|
||||
private final Loader container;
|
||||
|
||||
private InputStreamWrapper(ClosableObjectLoaderContainer container) throws IOException {
|
||||
super(container.objectLoader.openStream());
|
||||
private InputStreamWrapper(Loader container) throws IOException {
|
||||
super(container.openStream());
|
||||
this.container = container;
|
||||
}
|
||||
|
||||
|
||||
@@ -31,15 +31,12 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import org.eclipse.jgit.diff.DiffEntry;
|
||||
import org.eclipse.jgit.diff.DiffFormatter;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.DiffCommandBuilder;
|
||||
|
||||
import java.io.BufferedOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -52,22 +49,25 @@ public class GitDiffCommand extends AbstractGitCommand implements DiffCommand {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getDiffResult(DiffCommandRequest request, OutputStream output) throws IOException {
|
||||
public DiffCommandBuilder.OutputStreamConsumer getDiffResult(DiffCommandRequest request) throws IOException {
|
||||
@SuppressWarnings("squid:S2095") // repository will be closed with the RepositoryService
|
||||
org.eclipse.jgit.lib.Repository repository = open();
|
||||
try (DiffFormatter formatter = new DiffFormatter(new BufferedOutputStream(output))) {
|
||||
formatter.setRepository(repository);
|
||||
|
||||
Differ.Diff diff = Differ.diff(repository, request);
|
||||
Differ.Diff diff = Differ.diff(repository, request);
|
||||
|
||||
for (DiffEntry e : diff.getEntries()) {
|
||||
if (!e.getOldId().equals(e.getNewId())) {
|
||||
formatter.format(e);
|
||||
return output -> {
|
||||
try (DiffFormatter formatter = new DiffFormatter(output)) {
|
||||
formatter.setRepository(repository);
|
||||
|
||||
for (DiffEntry e : diff.getEntries()) {
|
||||
if (!e.getOldId().equals(e.getNewId())) {
|
||||
formatter.format(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
formatter.flush();
|
||||
}
|
||||
formatter.flush();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -79,7 +79,9 @@ final class GitHunkParser {
|
||||
++oldLineCounter;
|
||||
break;
|
||||
default:
|
||||
throw new IllegalStateException("cannot handle diff line: " + line);
|
||||
if (!line.equals("\\ No newline at end of file")) {
|
||||
throw new IllegalStateException("cannot handle diff line: " + line);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -39,6 +39,7 @@ import sonia.scm.repository.Feature;
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.repository.api.Command;
|
||||
import sonia.scm.web.lfs.LfsBlobStoreFactory;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.EnumSet;
|
||||
@@ -76,9 +77,10 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
|
||||
//~--- constructors ---------------------------------------------------------
|
||||
|
||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider) {
|
||||
public GitRepositoryServiceProvider(GitRepositoryHandler handler, Repository repository, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory) {
|
||||
this.handler = handler;
|
||||
this.repository = repository;
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
this.context = new GitContext(handler.getDirectory(repository.getId()), repository, storeProvider);
|
||||
}
|
||||
|
||||
@@ -143,7 +145,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
@Override
|
||||
public BrowseCommand getBrowseCommand()
|
||||
{
|
||||
return new GitBrowseCommand(context, repository);
|
||||
return new GitBrowseCommand(context, repository, lfsBlobStoreFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -155,7 +157,7 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
@Override
|
||||
public CatCommand getCatCommand()
|
||||
{
|
||||
return new GitCatCommand(context, repository);
|
||||
return new GitCatCommand(context, repository, lfsBlobStoreFactory);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -281,11 +283,13 @@ public class GitRepositoryServiceProvider extends RepositoryServiceProvider
|
||||
//~--- fields ---------------------------------------------------------------
|
||||
|
||||
/** Field description */
|
||||
private GitContext context;
|
||||
private final GitContext context;
|
||||
|
||||
/** Field description */
|
||||
private GitRepositoryHandler handler;
|
||||
private final GitRepositoryHandler handler;
|
||||
|
||||
/** Field description */
|
||||
private Repository repository;
|
||||
private final Repository repository;
|
||||
|
||||
private final LfsBlobStoreFactory lfsBlobStoreFactory;
|
||||
}
|
||||
|
||||
@@ -39,6 +39,7 @@ import sonia.scm.api.v2.resources.GitRepositoryConfigStoreProvider;
|
||||
import sonia.scm.plugin.Extension;
|
||||
import sonia.scm.repository.GitRepositoryHandler;
|
||||
import sonia.scm.repository.Repository;
|
||||
import sonia.scm.web.lfs.LfsBlobStoreFactory;
|
||||
|
||||
/**
|
||||
*
|
||||
@@ -49,11 +50,13 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||
|
||||
private final GitRepositoryHandler handler;
|
||||
private final GitRepositoryConfigStoreProvider storeProvider;
|
||||
private final LfsBlobStoreFactory lfsBlobStoreFactory;
|
||||
|
||||
@Inject
|
||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider) {
|
||||
public GitRepositoryServiceResolver(GitRepositoryHandler handler, GitRepositoryConfigStoreProvider storeProvider, LfsBlobStoreFactory lfsBlobStoreFactory) {
|
||||
this.handler = handler;
|
||||
this.storeProvider = storeProvider;
|
||||
this.lfsBlobStoreFactory = lfsBlobStoreFactory;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -61,7 +64,7 @@ public class GitRepositoryServiceResolver implements RepositoryServiceResolver {
|
||||
GitRepositoryServiceProvider provider = null;
|
||||
|
||||
if (GitRepositoryHandler.TYPE_NAME.equalsIgnoreCase(repository.getType())) {
|
||||
provider = new GitRepositoryServiceProvider(handler, repository, storeProvider);
|
||||
provider = new GitRepositoryServiceProvider(handler, repository, storeProvider, lfsBlobStoreFactory);
|
||||
}
|
||||
|
||||
return provider;
|
||||
|
||||
@@ -171,6 +171,6 @@ public class GitBrowseCommandTest extends AbstractGitCommandTestBase {
|
||||
}
|
||||
|
||||
private GitBrowseCommand createCommand() {
|
||||
return new GitBrowseCommand(createContext(), repository);
|
||||
return new GitBrowseCommand(createContext(), repository, null);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -39,12 +39,18 @@ import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import sonia.scm.NotFoundException;
|
||||
import sonia.scm.repository.GitRepositoryConfig;
|
||||
import sonia.scm.store.Blob;
|
||||
import sonia.scm.store.BlobStore;
|
||||
import sonia.scm.web.lfs.LfsBlobStoreFactory;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
/**
|
||||
* Unit tests for {@link GitCatCommand}.
|
||||
@@ -136,7 +142,7 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase {
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
request.setPath("b.txt");
|
||||
|
||||
InputStream catResultStream = new GitCatCommand(createContext(), repository).getCatResultStream(request);
|
||||
InputStream catResultStream = new GitCatCommand(createContext(), repository, null).getCatResultStream(request);
|
||||
|
||||
assertEquals('b', catResultStream.read());
|
||||
assertEquals('\n', catResultStream.read());
|
||||
@@ -145,13 +151,38 @@ public class GitCatCommandTest extends AbstractGitCommandTestBase {
|
||||
catResultStream.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testLfsStream() throws IOException {
|
||||
LfsBlobStoreFactory lfsBlobStoreFactory = mock(LfsBlobStoreFactory.class);
|
||||
BlobStore blobStore = mock(BlobStore.class);
|
||||
Blob blob = mock(Blob.class);
|
||||
when(lfsBlobStoreFactory.getLfsBlobStore(repository)).thenReturn(blobStore);
|
||||
when(blobStore.get("d2252bd9fde1bb2ae7531b432c48262c3cbe4df4376008986980de40a7c9cf8b"))
|
||||
.thenReturn(blob);
|
||||
when(blob.getInputStream()).thenReturn(new ByteArrayInputStream(new byte[]{'i', 's'}));
|
||||
|
||||
CatCommandRequest request = new CatCommandRequest();
|
||||
request.setRevision("lfs-test");
|
||||
request.setPath("lfs-image.png");
|
||||
|
||||
InputStream catResultStream = new GitCatCommand(createContext(), repository, lfsBlobStoreFactory)
|
||||
.getCatResultStream(request);
|
||||
|
||||
assertEquals('i', catResultStream.read());
|
||||
assertEquals('s', catResultStream.read());
|
||||
|
||||
assertEquals(-1, catResultStream.read());
|
||||
|
||||
catResultStream.close();
|
||||
}
|
||||
|
||||
private String execute(CatCommandRequest request) throws IOException {
|
||||
String content = null;
|
||||
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||
|
||||
try
|
||||
{
|
||||
new GitCatCommand(createContext(), repository).getCatResult(request,
|
||||
new GitCatCommand(createContext(), repository, null).getCatResult(request,
|
||||
baos);
|
||||
}
|
||||
finally
|
||||
|
||||
@@ -44,7 +44,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest, output);
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertEquals(DIFF_FILE_A + DIFF_FILE_B, output.toString());
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
DiffCommandRequest diffCommandRequest = new DiffCommandRequest();
|
||||
diffCommandRequest.setRevision("test-branch");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest, output);
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertEquals(DIFF_FILE_A + DIFF_FILE_B, output.toString());
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
diffCommandRequest.setRevision("test-branch");
|
||||
diffCommandRequest.setPath("a.txt");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest, output);
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertEquals(DIFF_FILE_A, output.toString());
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
diffCommandRequest.setRevision("master");
|
||||
diffCommandRequest.setAncestorChangeset("test-branch");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest, output);
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertEquals(DIFF_FILE_A_MULTIPLE_REVISIONS + DIFF_FILE_F_MULTIPLE_REVISIONS, output.toString());
|
||||
}
|
||||
|
||||
@@ -88,7 +88,7 @@ public class GitDiffCommandTest extends AbstractGitCommandTestBase {
|
||||
diffCommandRequest.setAncestorChangeset("test-branch");
|
||||
diffCommandRequest.setPath("a.txt");
|
||||
ByteArrayOutputStream output = new ByteArrayOutputStream();
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest, output);
|
||||
gitDiffCommand.getDiffResult(diffCommandRequest).accept(output);
|
||||
assertEquals(DIFF_FILE_A_MULTIPLE_REVISIONS, output.toString());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -68,6 +68,17 @@ class GitHunkParserTest {
|
||||
" a\n" +
|
||||
"~illegal line\n";
|
||||
|
||||
private static final String NO_NEWLINE_DIFF = "diff --git a/.editorconfig b/.editorconfig\n" +
|
||||
"index ea2a3ba..2f02f32 100644\n" +
|
||||
"--- a/.editorconfig\n" +
|
||||
"+++ b/.editorconfig\n" +
|
||||
"@@ -10,3 +10,4 @@\n" +
|
||||
" indent_style = space\n" +
|
||||
" indent_size = 2\n" +
|
||||
" charset = utf-8\n" +
|
||||
"+added line\n" +
|
||||
"\\ No newline at end of file\n";
|
||||
|
||||
@Test
|
||||
void shouldParseHunks() {
|
||||
List<Hunk> hunks = new GitHunkParser().parse(DIFF_001);
|
||||
@@ -127,6 +138,27 @@ class GitHunkParserTest {
|
||||
assertThrows(IllegalStateException.class, () -> new GitHunkParser().parse(ILLEGAL_DIFF));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldIgnoreNoNewlineLine() {
|
||||
List<Hunk> hunks = new GitHunkParser().parse(NO_NEWLINE_DIFF);
|
||||
|
||||
Hunk hunk = hunks.get(0);
|
||||
|
||||
Iterator<DiffLine> lines = hunk.iterator();
|
||||
|
||||
DiffLine line1 = lines.next();
|
||||
assertThat(line1.getOldLineNumber()).hasValue(10);
|
||||
assertThat(line1.getNewLineNumber()).hasValue(10);
|
||||
assertThat(line1.getContent()).isEqualTo("indent_style = space");
|
||||
|
||||
lines.next();
|
||||
lines.next();
|
||||
DiffLine lastLine = lines.next();
|
||||
assertThat(lastLine.getOldLineNumber()).isEmpty();
|
||||
assertThat(lastLine.getNewLineNumber()).hasValue(13);
|
||||
assertThat(lastLine.getContent()).isEqualTo("added line");
|
||||
}
|
||||
|
||||
private void assertHunk(Hunk hunk, int oldStart, int oldLineCount, int newStart, int newLineCount) {
|
||||
assertThat(hunk.getOldStart()).isEqualTo(oldStart);
|
||||
assertThat(hunk.getOldLineCount()).isEqualTo(oldLineCount);
|
||||
|
||||
Binary file not shown.
Reference in New Issue
Block a user