From 2508a1b9b71e96355381ec4f8d2ee123993606a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 5 Nov 2018 16:42:20 +0100 Subject: [PATCH 1/3] Implement log between two changesets for git --- .../scm/repository/api/LogCommandBuilder.java | 5 +++ .../scm/repository/spi/LogCommandRequest.java | 17 ++++++++-- .../scm/repository/spi/GitLogCommand.java | 31 ++++++++++++++++-- .../scm/repository/spi/GitLogCommandTest.java | 32 +++++++++++++++---- 4 files changed, 74 insertions(+), 11 deletions(-) diff --git a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java index 9c782a781b..3db8d6aae0 100644 --- a/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java +++ b/scm-core/src/main/java/sonia/scm/repository/api/LogCommandBuilder.java @@ -398,6 +398,11 @@ public final class LogCommandBuilder return this; } + public LogCommandBuilder setAncestorChangeset(String ancestorChangeset) { + request.setAncestorChangeset(ancestorChangeset); + return this; + } + //~--- inner classes -------------------------------------------------------- /** diff --git a/scm-core/src/main/java/sonia/scm/repository/spi/LogCommandRequest.java b/scm-core/src/main/java/sonia/scm/repository/spi/LogCommandRequest.java index 9356bb212a..92cd41662b 100644 --- a/scm-core/src/main/java/sonia/scm/repository/spi/LogCommandRequest.java +++ b/scm-core/src/main/java/sonia/scm/repository/spi/LogCommandRequest.java @@ -84,7 +84,8 @@ public final class LogCommandRequest implements Serializable, Resetable && Objects.equal(pagingStart, other.pagingStart) && Objects.equal(pagingLimit, other.pagingLimit) && Objects.equal(path, other.path) - && Objects.equal(branch, other.branch); + && Objects.equal(branch, other.branch) + && Objects.equal(ancestorChangeset, other.ancestorChangeset); //J+ } @@ -98,7 +99,7 @@ public final class LogCommandRequest implements Serializable, Resetable public int hashCode() { return Objects.hashCode(startChangeset, endChangeset, pagingStart, - pagingLimit, path, branch); + pagingLimit, path, branch, ancestorChangeset); } /** @@ -114,6 +115,7 @@ public final class LogCommandRequest implements Serializable, Resetable pagingLimit = 20; path = null; branch = null; + ancestorChangeset = null; } /** @@ -133,6 +135,7 @@ public final class LogCommandRequest implements Serializable, Resetable .add("pagingLimit", pagingLimit) .add("path", path) .add("branch", branch) + .add("ancestorChangeset", ancestorChangeset) .toString(); //J+ } @@ -205,6 +208,10 @@ public final class LogCommandRequest implements Serializable, Resetable this.startChangeset = startChangeset; } + public void setAncestorChangeset(String ancestorChangeset) { + this.ancestorChangeset = ancestorChangeset; + } + //~--- get methods ---------------------------------------------------------- /** @@ -284,6 +291,10 @@ public final class LogCommandRequest implements Serializable, Resetable return pagingLimit < 0; } + public String getAncestorChangeset() { + return ancestorChangeset; + } + //~--- fields --------------------------------------------------------------- /** Field description */ @@ -303,4 +314,6 @@ public final class LogCommandRequest implements Serializable, Resetable /** Field description */ private String startChangeset; + + private String ancestorChangeset; } diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java index 4e9261f517..7175df81f4 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitLogCommand.java @@ -43,6 +43,7 @@ import org.eclipse.jgit.lib.Ref; import org.eclipse.jgit.lib.Repository; import org.eclipse.jgit.revwalk.RevCommit; import org.eclipse.jgit.revwalk.RevWalk; +import org.eclipse.jgit.revwalk.filter.RevFilter; import org.eclipse.jgit.treewalk.filter.AndTreeFilter; import org.eclipse.jgit.treewalk.filter.PathFilter; import org.eclipse.jgit.treewalk.filter.TreeFilter; @@ -198,6 +199,14 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand endId = repository.resolve(request.getEndChangeset()); } + Ref branch = getBranchOrDefault(repository,request.getBranch()); + + ObjectId ancestorId = null; + + if (!Strings.isNullOrEmpty(request.getAncestorChangeset())) { + ancestorId = computeCommonAncestor(request, repository, startId, branch); + } + revWalk = new RevWalk(repository); converter = new GitChangesetConverter(repository, revWalk); @@ -208,8 +217,6 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand PathFilter.create(request.getPath()), TreeFilter.ANY_DIFF)); } - Ref branch = getBranchOrDefault(repository,request.getBranch()); - if (branch != null) { if (startId != null) { revWalk.markStart(revWalk.lookupCommit(startId)); @@ -217,11 +224,16 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand revWalk.markStart(revWalk.lookupCommit(branch.getObjectId())); } + Iterator iterator = revWalk.iterator(); while (iterator.hasNext()) { RevCommit commit = iterator.next(); + if (commit.getId().equals(ancestorId)) { + break; + } + if ((counter >= start) && ((limit < 0) || (counter < start + limit))) { changesetList.add(converter.createChangeset(commit)); @@ -229,7 +241,7 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand counter++; - if ((endId != null) && commit.getId().equals(endId)) { + if (commit.getId().equals(endId)) { break; } } @@ -263,4 +275,17 @@ public class GitLogCommand extends AbstractGitCommand implements LogCommand return changesets; } + + private ObjectId computeCommonAncestor(LogCommandRequest request, Repository repository, ObjectId startId, Ref branch) throws IOException { + try (RevWalk mergeBaseWalk = new RevWalk(repository)) { + mergeBaseWalk.setRevFilter(RevFilter.MERGE_BASE); + if (startId != null) { + mergeBaseWalk.markStart(mergeBaseWalk.lookupCommit(startId)); + } else { + mergeBaseWalk.markStart(mergeBaseWalk.lookupCommit(branch.getObjectId())); + } + mergeBaseWalk.markStart(mergeBaseWalk.parseCommit(repository.resolve(request.getAncestorChangeset()))); + return mergeBaseWalk.next().getId(); + } + } } diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java index 78db8ae686..4afaf09c67 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitLogCommandTest.java @@ -64,7 +64,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase * Tests log command with the usage of a default branch. */ @Test - public void testGetDefaultBranch() throws Exception { + public void testGetDefaultBranch() { // without default branch, the repository head should be used ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest()); @@ -92,7 +92,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase } @Test - public void testGetAll() throws Exception + public void testGetAll() { ChangesetPagingResult result = createCommand().getChangesets(new LogCommandRequest()); @@ -103,7 +103,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase } @Test - public void testGetAllByPath() throws Exception + public void testGetAllByPath() { LogCommandRequest request = new LogCommandRequest(); @@ -119,7 +119,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase } @Test - public void testGetAllWithLimit() throws Exception + public void testGetAllWithLimit() { LogCommandRequest request = new LogCommandRequest(); @@ -143,7 +143,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase } @Test - public void testGetAllWithPaging() throws Exception + public void testGetAllWithPaging() { LogCommandRequest request = new LogCommandRequest(); @@ -194,7 +194,7 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase } @Test - public void testGetRange() throws Exception + public void testGetRange() { LogCommandRequest request = new LogCommandRequest(); @@ -216,6 +216,26 @@ public class GitLogCommandTest extends AbstractGitCommandTestBase assertEquals("435df2f061add3589cb326cc64be9b9c3897ceca", c2.getId()); } + @Test + public void testGetAncestor() + { + LogCommandRequest request = new LogCommandRequest(); + + request.setBranch("test-branch"); + request.setAncestorChangeset("master"); + + ChangesetPagingResult result = createCommand().getChangesets(request); + + assertNotNull(result); + assertEquals(1, result.getTotal()); + assertEquals(1, result.getChangesets().size()); + + Changeset c = result.getChangesets().get(0); + + assertNotNull(c); + assertEquals("3f76a12f08a6ba0dc988c68b7f0b2cd190efc3c4", c.getId()); + } + @Test public void shouldFindDefaultBranchFromHEAD() throws Exception { setRepositoryHeadReference("ref: refs/heads/test-branch"); From 7ef9db0fe51fbbd0ca131757efdc02d299770db7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20Pfeuffer?= Date: Mon, 5 Nov 2018 18:09:18 +0100 Subject: [PATCH 2/3] Add changeset diff endpoint --- .../api/v2/resources/BranchRootResource.java | 45 +++++++++++++++++++ .../v2/resources/BranchToBranchDtoMapper.java | 1 + .../scm/api/v2/resources/ResourceLinks.java | 4 ++ 3 files changed, 50 insertions(+) diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java index 5f57d1bcc8..431e39e759 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchRootResource.java @@ -5,6 +5,7 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes; import com.webcohesion.enunciate.metadata.rs.TypeHint; import sonia.scm.NotFoundException; import sonia.scm.PageResult; +import sonia.scm.repository.Branch; import sonia.scm.repository.Branches; import sonia.scm.repository.Changeset; import sonia.scm.repository.ChangesetPagingResult; @@ -26,6 +27,7 @@ import javax.ws.rs.Produces; import javax.ws.rs.QueryParam; import javax.ws.rs.core.Response; import java.io.IOException; +import java.util.List; public class BranchRootResource { @@ -124,6 +126,49 @@ public class BranchRootResource { } } + @Path("{branch}/diffchangesets/{otherBranchName}") + @GET + @StatusCodes({ + @ResponseCode(code = 200, condition = "success"), + @ResponseCode(code = 401, condition = "not authenticated / invalid credentials"), + @ResponseCode(code = 403, condition = "not authorized, the current user has no privileges to read the changeset"), + @ResponseCode(code = 404, condition = "not found, no changesets available in the repository"), + @ResponseCode(code = 500, condition = "internal server error") + }) + @Produces(VndMediaType.CHANGESET_COLLECTION) + @TypeHint(CollectionDto.class) + public Response changesetDiff(@PathParam("namespace") String namespace, + @PathParam("name") String name, + @PathParam("branch") String branchName, + @PathParam("otherBranchName") String otherBranchName, + @DefaultValue("0") @QueryParam("page") int page, + @DefaultValue("10") @QueryParam("pageSize") int pageSize) throws Exception { + try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) { + List allBranches = repositoryService.getBranchesCommand().getBranches().getBranches(); + if (allBranches.stream().noneMatch(branch -> branchName.equals(branch.getName()))) { + throw new NotFoundException("branch", branchName); + } + if (allBranches.stream().noneMatch(branch -> otherBranchName.equals(branch.getName()))) { + throw new NotFoundException("branch", otherBranchName); + } + Repository repository = repositoryService.getRepository(); + RepositoryPermissions.read(repository).check(); + ChangesetPagingResult changesets = new PagedLogCommandBuilder(repositoryService) + .page(page) + .pageSize(pageSize) + .create() + .setBranch(branchName) + .setAncestorChangeset(otherBranchName) + .getChangesets(); + if (changesets != null && changesets.getChangesets() != null) { + PageResult pageResult = new PageResult<>(changesets.getChangesets(), changesets.getTotal()); + return Response.ok(branchChangesetCollectionToDtoMapper.map(page, pageSize, pageResult, repository, branchName)).build(); + } else { + return Response.ok().build(); + } + } + } + /** * Returns the branches for a repository. * diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java index 7ab3ef25a8..8167e28f9a 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/BranchToBranchDtoMapper.java @@ -28,6 +28,7 @@ public abstract class BranchToBranchDtoMapper { Links.Builder linksBuilder = linkingTo() .self(resourceLinks.branch().self(namespaceAndName, target.getName())) .single(linkBuilder("history", resourceLinks.branch().history(namespaceAndName, target.getName())).build()) + .single(linkBuilder("changesetDiff", resourceLinks.branch().changesetDiff(namespaceAndName, target.getName())).build()) .single(linkBuilder("changeset", resourceLinks.changeset().changeset(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()) .single(linkBuilder("source", resourceLinks.source().self(namespaceAndName.getNamespace(), namespaceAndName.getName(), target.getRevision())).build()); target.add(linksBuilder.build()); diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java index 1b397480a4..970166257a 100644 --- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java +++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/ResourceLinks.java @@ -322,6 +322,10 @@ class ResourceLinks { public String history(NamespaceAndName namespaceAndName, String branch) { return branchLinkBuilder.method("getRepositoryResource").parameters(namespaceAndName.getNamespace(), namespaceAndName.getName()).method("branches").parameters().method("history").parameters(branch).href(); } + + public String changesetDiff(NamespaceAndName namespaceAndName, String branch) { + return branchLinkBuilder.method("getRepositoryResource").parameters(namespaceAndName.getNamespace(), namespaceAndName.getName()).method("branches").parameters().method("changesetDiff").parameters(branch, "").href() + "{otherBranch}"; + } } public BranchCollectionLinks branchCollection() { From 291e6115f94cb56d8739514650584a33f2d82b28 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Wed, 7 Nov 2018 14:07:50 +0000 Subject: [PATCH 3/3] Close branch feature/log_two_revisions