merge 2.0.0-m3

This commit is contained in:
Eduard Heimbuch
2019-08-15 10:51:36 +02:00
172 changed files with 3320 additions and 1792 deletions

View File

@@ -0,0 +1,21 @@
package sonia.scm.repository;
import org.eclipse.jgit.lib.StoredConfig;
import java.io.IOException;
public class GitConfigHelper {
private static final String CONFIG_SECTION_SCMM = "scmm";
private static final String CONFIG_KEY_REPOSITORY_ID = "repositoryid";
public void createScmmConfig(Repository repository, org.eclipse.jgit.lib.Repository gitRepository) throws IOException {
StoredConfig config = gitRepository.getConfig();
config.setString(CONFIG_SECTION_SCMM, null, CONFIG_KEY_REPOSITORY_ID, repository.getId());
config.save();
}
public String getRepositoryId(StoredConfig gitConfig) {
return gitConfig.getString(CONFIG_SECTION_SCMM, null, CONFIG_KEY_REPOSITORY_ID);
}
}

View File

@@ -89,8 +89,6 @@ public class GitRepositoryHandler
GitRepositoryServiceProvider.COMMANDS);
private static final Object LOCK = new Object();
private static final String CONFIG_SECTION_SCMM = "scmm";
private static final String CONFIG_KEY_REPOSITORY_ID = "repositoryid";
private final Scheduler scheduler;
@@ -185,7 +183,7 @@ public class GitRepositoryHandler
}
public String getRepositoryId(StoredConfig gitConfig) {
return gitConfig.getString(GitRepositoryHandler.CONFIG_SECTION_SCMM, null, GitRepositoryHandler.CONFIG_KEY_REPOSITORY_ID);
return new GitConfigHelper().getRepositoryId(gitConfig);
}
//~--- methods --------------------------------------------------------------
@@ -194,9 +192,7 @@ public class GitRepositoryHandler
protected void create(Repository repository, File directory) throws IOException {
try (org.eclipse.jgit.lib.Repository gitRepository = build(directory)) {
gitRepository.create(true);
StoredConfig config = gitRepository.getConfig();
config.setString(CONFIG_SECTION_SCMM, null, CONFIG_KEY_REPOSITORY_ID, repository.getId());
config.save();
new GitConfigHelper().createScmmConfig(repository, gitRepository);
}
}

View File

@@ -43,6 +43,7 @@ import org.eclipse.jgit.api.FetchCommand;
import org.eclipse.jgit.api.Git;
import org.eclipse.jgit.api.errors.GitAPIException;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.AnyObjectId;
import org.eclipse.jgit.lib.Constants;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Ref;
@@ -441,7 +442,7 @@ public final class GitUtil
*
* @return
*/
public static String getId(ObjectId objectId)
public static String getId(AnyObjectId objectId)
{
String id = Util.EMPTY_STRING;

View File

@@ -0,0 +1,118 @@
package sonia.scm.repository.spi;
import com.google.common.base.Strings;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.lib.Repository;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
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.repository.GitUtil;
import sonia.scm.util.Util;
import java.io.IOException;
import java.util.List;
final class Differ implements AutoCloseable {
private final RevWalk walk;
private final TreeWalk treeWalk;
private final RevCommit commit;
private Differ(RevCommit commit, RevWalk walk, TreeWalk treeWalk) {
this.commit = commit;
this.walk = walk;
this.treeWalk = treeWalk;
}
static Diff diff(Repository repository, DiffCommandRequest request) throws IOException {
try (Differ differ = create(repository, request)) {
return differ.diff();
}
}
private static Differ create(Repository repository, DiffCommandRequest request) throws IOException {
RevWalk walk = new RevWalk(repository);
ObjectId revision = repository.resolve(request.getRevision());
RevCommit commit = walk.parseCommit(revision);
walk.markStart(commit);
commit = walk.next();
TreeWalk treeWalk = new TreeWalk(repository);
treeWalk.reset();
treeWalk.setRecursive(true);
if (Util.isNotEmpty(request.getPath()))
{
treeWalk.setFilter(PathFilter.create(request.getPath()));
}
if (!Strings.isNullOrEmpty(request.getAncestorChangeset()))
{
ObjectId otherRevision = repository.resolve(request.getAncestorChangeset());
ObjectId ancestorId = computeCommonAncestor(repository, revision, otherRevision);
RevTree tree = walk.parseCommit(ancestorId).getTree();
treeWalk.addTree(tree);
}
else if (commit.getParentCount() > 0)
{
RevTree tree = commit.getParent(0).getTree();
if (tree != null)
{
treeWalk.addTree(tree);
}
else
{
treeWalk.addTree(new EmptyTreeIterator());
}
}
else
{
treeWalk.addTree(new EmptyTreeIterator());
}
treeWalk.addTree(commit.getTree());
return new Differ(commit, walk, treeWalk);
}
private static 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);
}
@Override
public void close() {
GitUtil.release(walk);
GitUtil.release(treeWalk);
}
public static class Diff {
private final RevCommit commit;
private final List<DiffEntry> entries;
private Diff(RevCommit commit, List<DiffEntry> entries) {
this.commit = commit;
this.entries = entries;
}
public RevCommit getCommit() {
return commit;
}
public List<DiffEntry> getEntries() {
return entries;
}
}
}

View File

@@ -0,0 +1,20 @@
package sonia.scm.repository.spi;
public class FileRange {
private final int start;
private final int lineCount;
public FileRange(int start, int lineCount) {
this.start = start;
this.lineCount = lineCount;
}
public int getStart() {
return start;
}
public int getLineCount() {
return lineCount;
}
}

View File

@@ -1,19 +1,19 @@
/**
* Copyright (c) 2010, Sebastian Sdorra
* All rights reserved.
*
* <p>
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* <p>
* 1. Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
* this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
* 3. Neither the name of SCM-Manager; nor the names of its
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
*
* contributors may be used to endorse or promote products derived from this
* software without specific prior written permission.
* <p>
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
@@ -24,9 +24,8 @@
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* <p>
* http://bitbucket.org/sdorra/scm-manager
*
*/
@@ -34,148 +33,41 @@ package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
import com.google.common.base.Strings;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import org.eclipse.jgit.lib.ObjectId;
import org.eclipse.jgit.revwalk.RevCommit;
import org.eclipse.jgit.revwalk.RevTree;
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 org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sonia.scm.repository.GitUtil;
import sonia.scm.repository.Repository;
import sonia.scm.util.Util;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
/**
*
* @author Sebastian Sdorra
*/
public class GitDiffCommand extends AbstractGitCommand implements DiffCommand
{
public class GitDiffCommand extends AbstractGitCommand implements DiffCommand {
/**
* the logger for GitDiffCommand
*/
private static final Logger logger =
LoggerFactory.getLogger(GitDiffCommand.class);
//~--- constructors ---------------------------------------------------------
/**
* Constructs ...
*
*
*
* @param context
* @param repository
*/
public GitDiffCommand(GitContext context, Repository repository)
{
GitDiffCommand(GitContext context, Repository repository) {
super(context, repository);
}
//~--- get methods ----------------------------------------------------------
/**
* Method description
*
*
* @param request
* @param output
*/
@Override
public void getDiffResult(DiffCommandRequest request, OutputStream output)
{
RevWalk walk = null;
TreeWalk treeWalk = null;
DiffFormatter formatter = null;
public void getDiffResult(DiffCommandRequest request, OutputStream output) 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);
try
{
org.eclipse.jgit.lib.Repository gr = open();
Differ.Diff diff = Differ.diff(repository, request);
walk = new RevWalk(gr);
ObjectId revision = gr.resolve(request.getRevision());
RevCommit commit = walk.parseCommit(revision);
walk.markStart(commit);
commit = walk.next();
treeWalk = new TreeWalk(gr);
treeWalk.reset();
treeWalk.setRecursive(true);
if (Util.isNotEmpty(request.getPath()))
{
treeWalk.setFilter(PathFilter.create(request.getPath()));
}
if (!Strings.isNullOrEmpty(request.getAncestorChangeset()))
{
ObjectId otherRevision = gr.resolve(request.getAncestorChangeset());
ObjectId ancestorId = computeCommonAncestor(gr, revision, otherRevision);
RevTree tree = walk.parseCommit(ancestorId).getTree();
treeWalk.addTree(tree);
}
else if (commit.getParentCount() > 0)
{
RevTree tree = commit.getParent(0).getTree();
if (tree != null)
{
treeWalk.addTree(tree);
}
else
{
treeWalk.addTree(new EmptyTreeIterator());
}
}
else
{
treeWalk.addTree(new EmptyTreeIterator());
}
treeWalk.addTree(commit.getTree());
formatter = new DiffFormatter(new BufferedOutputStream(output));
formatter.setRepository(gr);
List<DiffEntry> entries = DiffEntry.scan(treeWalk);
for (DiffEntry e : entries)
{
if (!e.getOldId().equals(e.getNewId()))
{
for (DiffEntry e : diff.getEntries()) {
if (!e.getOldId().equals(e.getNewId())) {
formatter.format(e);
}
}
formatter.flush();
}
catch (Exception ex)
{
// TODO throw exception
logger.error("could not create diff", ex);
}
finally
{
GitUtil.release(walk);
GitUtil.release(treeWalk);
GitUtil.release(formatter);
}
}
private ObjectId computeCommonAncestor(org.eclipse.jgit.lib.Repository repository, ObjectId revision1, ObjectId revision2) throws IOException {
return GitUtil.computeCommonAncestor(repository, revision1, revision2);
}
}

View File

@@ -0,0 +1,107 @@
package sonia.scm.repository.spi;
import org.eclipse.jgit.diff.DiffEntry;
import org.eclipse.jgit.diff.DiffFormatter;
import sonia.scm.repository.GitUtil;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Repository;
import sonia.scm.repository.api.DiffFile;
import sonia.scm.repository.api.DiffResult;
import sonia.scm.repository.api.Hunk;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.Iterator;
import java.util.stream.Collectors;
public class GitDiffResultCommand extends AbstractGitCommand implements DiffResultCommand {
GitDiffResultCommand(GitContext context, Repository repository) {
super(context, repository);
}
public DiffResult getDiffResult(DiffCommandRequest diffCommandRequest) throws IOException {
org.eclipse.jgit.lib.Repository repository = open();
return new GitDiffResult(repository, Differ.diff(repository, diffCommandRequest));
}
private class GitDiffResult implements DiffResult {
private final org.eclipse.jgit.lib.Repository repository;
private final Differ.Diff diff;
private GitDiffResult(org.eclipse.jgit.lib.Repository repository, Differ.Diff diff) {
this.repository = repository;
this.diff = diff;
}
@Override
public String getOldRevision() {
return GitUtil.getId(diff.getCommit().getParent(0).getId());
}
@Override
public String getNewRevision() {
return GitUtil.getId(diff.getCommit().getId());
}
@Override
public Iterator<DiffFile> iterator() {
return diff.getEntries()
.stream()
.map(diffEntry -> new GitDiffFile(repository, diffEntry))
.collect(Collectors.<DiffFile>toList())
.iterator();
}
}
private class GitDiffFile implements DiffFile {
private final org.eclipse.jgit.lib.Repository repository;
private final DiffEntry diffEntry;
private GitDiffFile(org.eclipse.jgit.lib.Repository repository, DiffEntry diffEntry) {
this.repository = repository;
this.diffEntry = diffEntry;
}
@Override
public String getOldRevision() {
return GitUtil.getId(diffEntry.getOldId().toObjectId());
}
@Override
public String getNewRevision() {
return GitUtil.getId(diffEntry.getNewId().toObjectId());
}
@Override
public String getOldPath() {
return diffEntry.getOldPath();
}
@Override
public String getNewPath() {
return diffEntry.getNewPath();
}
@Override
public Iterator<Hunk> iterator() {
String content = format(repository, diffEntry);
GitHunkParser parser = new GitHunkParser();
return parser.parse(content).iterator();
}
private String format(org.eclipse.jgit.lib.Repository repository, DiffEntry entry) {
try (ByteArrayOutputStream baos = new ByteArrayOutputStream(); DiffFormatter formatter = new DiffFormatter(baos)) {
formatter.setRepository(repository);
formatter.format(entry);
return baos.toString();
} catch (IOException ex) {
throw new InternalRepositoryException(GitDiffResultCommand.this.repository, "failed to format diff entry", ex);
}
}
}
}

View File

@@ -0,0 +1,48 @@
package sonia.scm.repository.spi;
import sonia.scm.repository.api.DiffLine;
import sonia.scm.repository.api.Hunk;
import java.util.Iterator;
import java.util.List;
public class GitHunk implements Hunk {
private final FileRange oldFileRange;
private final FileRange newFileRange;
private List<DiffLine> lines;
public GitHunk(FileRange oldFileRange, FileRange newFileRange) {
this.oldFileRange = oldFileRange;
this.newFileRange = newFileRange;
}
@Override
public int getOldStart() {
return oldFileRange.getStart();
}
@Override
public int getOldLineCount() {
return oldFileRange.getLineCount();
}
@Override
public int getNewStart() {
return newFileRange.getStart();
}
@Override
public int getNewLineCount() {
return newFileRange.getLineCount();
}
@Override
public Iterator<DiffLine> iterator() {
return lines.iterator();
}
void setLines(List<DiffLine> lines) {
this.lines = lines;
}
}

View File

@@ -0,0 +1,176 @@
package sonia.scm.repository.spi;
import sonia.scm.repository.api.DiffLine;
import sonia.scm.repository.api.Hunk;
import java.util.ArrayList;
import java.util.List;
import java.util.OptionalInt;
import java.util.Scanner;
import static java.util.OptionalInt.of;
final class GitHunkParser {
private static final int HEADER_PREFIX_LENGTH = "@@ -".length();
private static final int HEADER_SUFFIX_LENGTH = " @@".length();
private GitHunk currentGitHunk = null;
private List<DiffLine> collectedLines = null;
private int oldLineCounter = 0;
private int newLineCounter = 0;
GitHunkParser() {
}
public List<Hunk> parse(String content) {
List<Hunk> hunks = new ArrayList<>();
try (Scanner scanner = new Scanner(content)) {
while (scanner.hasNextLine()) {
String line = scanner.nextLine();
if (line.startsWith("@@")) {
parseHeader(hunks, line);
} else if (currentGitHunk != null) {
parseDiffLine(line);
}
}
}
if (currentGitHunk != null) {
currentGitHunk.setLines(collectedLines);
}
return hunks;
}
private void parseHeader(List<Hunk> hunks, String line) {
if (currentGitHunk != null) {
currentGitHunk.setLines(collectedLines);
}
String hunkHeader = line.substring(HEADER_PREFIX_LENGTH, line.length() - HEADER_SUFFIX_LENGTH);
String[] split = hunkHeader.split("\\s");
FileRange oldFileRange = createFileRange(split[0]);
// TODO merge contains two two block which starts with "-" e.g. -1,3 -2,4 +3,6
// check if it is relevant for our use case
FileRange newFileRange = createFileRange(split[1]);
currentGitHunk = new GitHunk(oldFileRange, newFileRange);
hunks.add(currentGitHunk);
collectedLines = new ArrayList<>();
oldLineCounter = currentGitHunk.getOldStart();
newLineCounter = currentGitHunk.getNewStart();
}
private void parseDiffLine(String line) {
String content = line.substring(1);
switch (line.charAt(0)) {
case ' ':
collectedLines.add(new UnchangedGitDiffLine(newLineCounter, oldLineCounter, content));
++newLineCounter;
++oldLineCounter;
break;
case '+':
collectedLines.add(new AddedGitDiffLine(newLineCounter, content));
++newLineCounter;
break;
case '-':
collectedLines.add(new RemovedGitDiffLine(oldLineCounter, content));
++oldLineCounter;
break;
default:
throw new IllegalStateException("cannot handle diff line: " + line);
}
}
private static class AddedGitDiffLine implements DiffLine {
private final int newLineNumber;
private final String content;
private AddedGitDiffLine(int newLineNumber, String content) {
this.newLineNumber = newLineNumber;
this.content = content;
}
@Override
public OptionalInt getOldLineNumber() {
return OptionalInt.empty();
}
@Override
public OptionalInt getNewLineNumber() {
return of(newLineNumber);
}
@Override
public String getContent() {
return content;
}
}
private static class RemovedGitDiffLine implements DiffLine {
private final int oldLineNumber;
private final String content;
private RemovedGitDiffLine(int oldLineNumber, String content) {
this.oldLineNumber = oldLineNumber;
this.content = content;
}
@Override
public OptionalInt getOldLineNumber() {
return of(oldLineNumber);
}
@Override
public OptionalInt getNewLineNumber() {
return OptionalInt.empty();
}
@Override
public String getContent() {
return content;
}
}
private static class UnchangedGitDiffLine implements DiffLine {
private final int newLineNumber;
private final int oldLineNumber;
private final String content;
private UnchangedGitDiffLine(int newLineNumber, int oldLineNumber, String content) {
this.newLineNumber = newLineNumber;
this.oldLineNumber = oldLineNumber;
this.content = content;
}
@Override
public OptionalInt getOldLineNumber() {
return of(oldLineNumber);
}
@Override
public OptionalInt getNewLineNumber() {
return of(newLineNumber);
}
@Override
public String getContent() {
return content;
}
}
private static FileRange createFileRange(String fileRangeString) {
int start;
int lineCount = 1;
int commaIndex = fileRangeString.indexOf(',');
if (commaIndex > 0) {
start = Integer.parseInt(fileRangeString.substring(0, commaIndex));
lineCount = Integer.parseInt(fileRangeString.substring(commaIndex + 1));
} else {
start = Integer.parseInt(fileRangeString);
}
return new FileRange(start, lineCount);
}
}

View File

@@ -0,0 +1,70 @@
package sonia.scm.repository.update;
import org.eclipse.jgit.storage.file.FileRepositoryBuilder;
import sonia.scm.migration.UpdateException;
import sonia.scm.migration.UpdateStep;
import sonia.scm.plugin.Extension;
import sonia.scm.repository.GitConfigHelper;
import sonia.scm.repository.GitRepositoryHandler;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryLocationResolver;
import sonia.scm.update.UpdateStepRepositoryMetadataAccess;
import sonia.scm.version.Version;
import javax.inject.Inject;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import static sonia.scm.version.Version.parse;
@Extension
public class GitV2UpdateStep implements UpdateStep {
private final RepositoryLocationResolver locationResolver;
private final UpdateStepRepositoryMetadataAccess<Path> repositoryMetadataAccess;
@Inject
public GitV2UpdateStep(RepositoryLocationResolver locationResolver, UpdateStepRepositoryMetadataAccess<Path> repositoryMetadataAccess) {
this.locationResolver = locationResolver;
this.repositoryMetadataAccess = repositoryMetadataAccess;
}
@Override
public void doUpdate() {
locationResolver.forClass(Path.class).forAllLocations(
(repositoryId, path) -> {
Repository repository = repositoryMetadataAccess.read(path);
if (isGitDirectory(repository)) {
try (org.eclipse.jgit.lib.Repository gitRepository = build(path.resolve("data").toFile())) {
new GitConfigHelper().createScmmConfig(repository, gitRepository);
} catch (IOException e) {
throw new UpdateException("could not update repository with id " + repositoryId + " in path " + path, e);
}
}
}
);
}
private org.eclipse.jgit.lib.Repository build(File directory) throws IOException {
return new FileRepositoryBuilder()
.setGitDir(directory)
.readEnvironment()
.findGitDir()
.build();
}
private boolean isGitDirectory(Repository repository) {
return GitRepositoryHandler.TYPE_NAME.equals(repository.getType());
}
@Override
public Version getTargetVersion() {
return parse("2.0.0");
}
@Override
public String getAffectedDataType() {
return "sonia.scm.plugin.git";
}
}

View File

@@ -0,0 +1,43 @@
package sonia.scm.web.lfs;
import sonia.scm.migration.UpdateStep;
import sonia.scm.plugin.Extension;
import sonia.scm.update.BlobDirectoryAccess;
import sonia.scm.version.Version;
import javax.inject.Inject;
import java.nio.file.Path;
@Extension
public class LfsV1UpdateStep implements UpdateStep {
private final BlobDirectoryAccess blobDirectoryAccess;
@Inject
public LfsV1UpdateStep(BlobDirectoryAccess blobDirectoryAccess) {
this.blobDirectoryAccess = blobDirectoryAccess;
}
@Override
public void doUpdate() throws Exception {
blobDirectoryAccess.forBlobDirectories(
f -> {
Path v1Directory = f.getFileName();
String v1DirectoryName = v1Directory.toString();
if (v1DirectoryName.endsWith("-git-lfs")) {
blobDirectoryAccess.moveToRepositoryBlobStore(f, v1DirectoryName, v1DirectoryName.substring(0, v1DirectoryName.length() - "-git-lfs".length()));
}
}
);
}
@Override
public Version getTargetVersion() {
return Version.parse("2.0.0");
}
@Override
public String getAffectedDataType() {
return "sonia.scm.git.lfs";
}
}