mirror of
https://github.com/scm-manager/scm-manager.git
synced 2025-11-05 04:55:50 +01:00
Merge branch 'develop' into feature/manage-tags
# Conflicts: # scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgRepositoryServiceProvider.java
This commit is contained in:
@@ -39,7 +39,7 @@ import static sonia.scm.repository.Branch.VALID_BRANCH_NAMES;
|
||||
@NoArgsConstructor
|
||||
@Getter
|
||||
@Setter
|
||||
public class GitConfigDto extends HalRepresentation {
|
||||
public class GitConfigDto extends HalRepresentation implements UpdateGitConfigDto {
|
||||
|
||||
private boolean disabled = false;
|
||||
|
||||
|
||||
@@ -27,7 +27,9 @@ package sonia.scm.api.v2.resources;
|
||||
import io.swagger.v3.oas.annotations.OpenAPIDefinition;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import sonia.scm.config.ConfigurationPermissions;
|
||||
@@ -116,7 +118,23 @@ public class GitConfigResource {
|
||||
@PUT
|
||||
@Path("")
|
||||
@Consumes(GitVndMediaType.GIT_CONFIG)
|
||||
@Operation(summary = "Modify git configuration", description = "Modifies the global git configuration.", tags = "Git", operationId = "git_put_config")
|
||||
@Operation(
|
||||
summary = "Modify git configuration",
|
||||
description = "Modifies the global git configuration.",
|
||||
tags = "Git",
|
||||
operationId = "git_put_config",
|
||||
requestBody = @RequestBody(
|
||||
content = @Content(
|
||||
mediaType = GitVndMediaType.GIT_CONFIG,
|
||||
schema = @Schema(implementation = UpdateGitConfigDto.class),
|
||||
examples = @ExampleObject(
|
||||
name = "Overwrites current configuration with this one.",
|
||||
value = "{\n \"disabled\":false,\n \"gcExpression\":null,\n \"nonFastForwardDisallowed\":false,\n \"defaultBranch\":\"main\"\n}",
|
||||
summary = "Simple update configuration"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ApiResponse(responseCode = "204", description = "update success")
|
||||
@ApiResponse(responseCode = "401", description = "not authenticated / invalid credentials")
|
||||
@ApiResponse(responseCode = "403", description = "not authorized, the current user does not have the \"configuration:write:git\" privilege")
|
||||
|
||||
@@ -21,7 +21,7 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import de.otto.edison.hal.HalRepresentation;
|
||||
@@ -36,7 +36,7 @@ import lombok.Setter;
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
@SuppressWarnings("squid:S2160") // there is no proper semantic for equals on this dto
|
||||
public class GitRepositoryConfigDto extends HalRepresentation {
|
||||
public class GitRepositoryConfigDto extends HalRepresentation implements UpdateGitRepositoryConfigDto {
|
||||
|
||||
private String defaultBranch;
|
||||
|
||||
|
||||
@@ -21,13 +21,16 @@
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.media.Content;
|
||||
import io.swagger.v3.oas.annotations.media.ExampleObject;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import io.swagger.v3.oas.annotations.parameters.RequestBody;
|
||||
import io.swagger.v3.oas.annotations.responses.ApiResponse;
|
||||
import lombok.Getter;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.GitRepositoryConfig;
|
||||
@@ -106,7 +109,22 @@ public class GitRepositoryConfigResource {
|
||||
@PUT
|
||||
@Path("/")
|
||||
@Consumes(GitVndMediaType.GIT_REPOSITORY_CONFIG)
|
||||
@Operation(summary = "Modifies git repository configuration", description = "Modifies the repository related git configuration.", tags = "Git")
|
||||
@Operation(
|
||||
summary = "Modifies git repository configuration",
|
||||
description = "Modifies the repository related git configuration.",
|
||||
tags = "Git",
|
||||
requestBody = @RequestBody(
|
||||
content = @Content(
|
||||
mediaType = GitVndMediaType.GIT_REPOSITORY_CONFIG,
|
||||
schema = @Schema(implementation = UpdateGitRepositoryConfigDto.class),
|
||||
examples = @ExampleObject(
|
||||
name = "Overwrites current configuration with this one.",
|
||||
value = "{\n \"defaultBranch\":\"main\"\n}",
|
||||
summary = "Simple update configuration"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
@ApiResponse(
|
||||
responseCode = "204",
|
||||
description = "update success"
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
import org.hibernate.validator.constraints.Length;
|
||||
|
||||
import javax.validation.constraints.NotEmpty;
|
||||
import javax.validation.constraints.Pattern;
|
||||
|
||||
import static sonia.scm.repository.Branch.VALID_BRANCH_NAMES;
|
||||
|
||||
interface UpdateGitConfigDto {
|
||||
|
||||
boolean isDisabled();
|
||||
|
||||
String getGcExpression();
|
||||
|
||||
boolean isNonFastForwardDisallowed();
|
||||
|
||||
@NotEmpty
|
||||
@Length(min = 1, max = 100)
|
||||
@Pattern(regexp = VALID_BRANCH_NAMES)
|
||||
String getDefaultBranch();
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* MIT License
|
||||
*
|
||||
* Copyright (c) 2020-present Cloudogu GmbH and Contributors
|
||||
*
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
*
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
*
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
package sonia.scm.api.v2.resources;
|
||||
|
||||
interface UpdateGitRepositoryConfigDto {
|
||||
String getDefaultBranch();
|
||||
}
|
||||
@@ -24,14 +24,14 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
//~--- non-JDK imports --------------------------------------------------------
|
||||
|
||||
import com.google.common.annotations.VisibleForTesting;
|
||||
import com.google.common.base.Strings;
|
||||
import org.checkerframework.checker.nullness.qual.Nullable;
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.eclipse.jgit.lib.Repository;
|
||||
import org.eclipse.jgit.revwalk.RevWalk;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import sonia.scm.repository.Branch;
|
||||
@@ -44,12 +44,9 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
//~--- JDK imports ------------------------------------------------------------
|
||||
import static sonia.scm.repository.GitUtil.getCommit;
|
||||
import static sonia.scm.repository.GitUtil.getCommitTime;
|
||||
|
||||
/**
|
||||
*
|
||||
* @author Sebastian Sdorra
|
||||
*/
|
||||
public class GitBranchesCommand extends AbstractGitCommand implements BranchesCommand {
|
||||
|
||||
private static final Logger LOG = LoggerFactory.getLogger(GitBranchesCommand.class);
|
||||
@@ -60,23 +57,22 @@ public class GitBranchesCommand extends AbstractGitCommand implements BranchesCo
|
||||
super(context);
|
||||
}
|
||||
|
||||
//~--- get methods ----------------------------------------------------------
|
||||
|
||||
@Override
|
||||
public List<Branch> getBranches() throws IOException {
|
||||
Git git = createGit();
|
||||
|
||||
String defaultBranchName = determineDefaultBranchName(git);
|
||||
|
||||
try {
|
||||
Repository repository = git.getRepository();
|
||||
try (RevWalk refWalk = new RevWalk(repository)) {
|
||||
return git
|
||||
.branchList()
|
||||
.call()
|
||||
.stream()
|
||||
.map(ref -> createBranchObject(defaultBranchName, ref))
|
||||
.map(ref -> createBranchObject(repository, refWalk, defaultBranchName, ref))
|
||||
.collect(Collectors.toList());
|
||||
} catch (GitAPIException ex) {
|
||||
throw new InternalRepositoryException(repository, "could not read branches", ex);
|
||||
throw new InternalRepositoryException(this.repository, "could not read branches", ex);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,21 +82,31 @@ public class GitBranchesCommand extends AbstractGitCommand implements BranchesCo
|
||||
}
|
||||
|
||||
@Nullable
|
||||
private Branch createBranchObject(String defaultBranchName, Ref ref) {
|
||||
private Branch createBranchObject(Repository repository, RevWalk refWalk, String defaultBranchName, Ref ref) {
|
||||
String branchName = GitUtil.getBranch(ref);
|
||||
|
||||
if (branchName == null) {
|
||||
LOG.warn("could not determine branch name for branch name {} at revision {}", ref.getName(), ref.getObjectId());
|
||||
return null;
|
||||
} else {
|
||||
Long lastCommitDate = getCommitDate(repository, refWalk, branchName, ref);
|
||||
if (branchName.equals(defaultBranchName)) {
|
||||
return Branch.defaultBranch(branchName, GitUtil.getId(ref.getObjectId()));
|
||||
return Branch.defaultBranch(branchName, GitUtil.getId(ref.getObjectId()), lastCommitDate);
|
||||
} else {
|
||||
return Branch.normalBranch(branchName, GitUtil.getId(ref.getObjectId()));
|
||||
return Branch.normalBranch(branchName, GitUtil.getId(ref.getObjectId()), lastCommitDate);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Long getCommitDate(Repository repository, RevWalk refWalk, String branchName, Ref ref) {
|
||||
try {
|
||||
return getCommitTime(getCommit(repository, refWalk, ref));
|
||||
} catch (IOException e) {
|
||||
LOG.info("failed to read commit date of branch {} with revision {}", branchName, ref.getName());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private String determineDefaultBranchName(Git git) {
|
||||
String defaultBranchName = context.getConfig().getDefaultBranch();
|
||||
if (Strings.isNullOrEmpty(defaultBranchName)) {
|
||||
|
||||
@@ -24,117 +24,26 @@
|
||||
|
||||
package sonia.scm.repository.spi;
|
||||
|
||||
import org.eclipse.jgit.api.Git;
|
||||
import org.eclipse.jgit.api.ListBranchCommand;
|
||||
import org.eclipse.jgit.api.errors.GitAPIException;
|
||||
import org.eclipse.jgit.lib.ObjectId;
|
||||
import org.eclipse.jgit.lib.Ref;
|
||||
import org.junit.jupiter.api.BeforeEach;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.extension.ExtendWith;
|
||||
import org.mockito.Mock;
|
||||
import org.mockito.junit.jupiter.MockitoExtension;
|
||||
import org.junit.Test;
|
||||
import sonia.scm.repository.Branch;
|
||||
import sonia.scm.repository.GitRepositoryConfig;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
import static java.util.Arrays.asList;
|
||||
import static java.util.Collections.emptyList;
|
||||
import static java.util.Optional.of;
|
||||
import static org.assertj.core.api.Assertions.assertThat;
|
||||
import static org.mockito.Mockito.lenient;
|
||||
import static org.mockito.Mockito.mock;
|
||||
import static org.mockito.Mockito.when;
|
||||
|
||||
@ExtendWith(MockitoExtension.class)
|
||||
class GitBranchesCommandTest {
|
||||
|
||||
@Mock
|
||||
GitContext context;
|
||||
@Mock
|
||||
Git git;
|
||||
@Mock
|
||||
ListBranchCommand listBranchCommand;
|
||||
@Mock
|
||||
GitRepositoryConfig gitRepositoryConfig;
|
||||
|
||||
GitBranchesCommand branchesCommand;
|
||||
private Ref master;
|
||||
|
||||
@BeforeEach
|
||||
void initContext() {
|
||||
when(context.getConfig()).thenReturn(gitRepositoryConfig);
|
||||
}
|
||||
|
||||
@BeforeEach
|
||||
void initCommand() {
|
||||
master = createRef("master", "0000");
|
||||
branchesCommand = new GitBranchesCommand(context) {
|
||||
@Override
|
||||
Git createGit() {
|
||||
return git;
|
||||
}
|
||||
|
||||
@Override
|
||||
Optional<Ref> getRepositoryHeadRef(Git git) {
|
||||
return of(master);
|
||||
}
|
||||
};
|
||||
when(git.branchList()).thenReturn(listBranchCommand);
|
||||
}
|
||||
public class GitBranchesCommandTest extends AbstractGitCommandTestBase {
|
||||
|
||||
@Test
|
||||
void shouldCreateEmptyListWithoutBranches() throws IOException, GitAPIException {
|
||||
when(listBranchCommand.call()).thenReturn(emptyList());
|
||||
public void shouldReadBranches() throws IOException {
|
||||
GitBranchesCommand branchesCommand = new GitBranchesCommand(createContext());
|
||||
|
||||
List<Branch> branches = branchesCommand.getBranches();
|
||||
|
||||
assertThat(branches).isEmpty();
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMapNormalBranch() throws IOException, GitAPIException {
|
||||
Ref branch = createRef("branch", "1337");
|
||||
when(listBranchCommand.call()).thenReturn(asList(branch));
|
||||
|
||||
List<Branch> branches = branchesCommand.getBranches();
|
||||
|
||||
assertThat(branches).containsExactly(Branch.normalBranch("branch", "1337"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkMasterBranchWithMasterFromConfig() throws IOException, GitAPIException {
|
||||
Ref branch = createRef("branch", "1337");
|
||||
when(listBranchCommand.call()).thenReturn(asList(branch));
|
||||
when(gitRepositoryConfig.getDefaultBranch()).thenReturn("branch");
|
||||
|
||||
List<Branch> branches = branchesCommand.getBranches();
|
||||
|
||||
assertThat(branches).containsExactlyInAnyOrder(Branch.defaultBranch("branch", "1337"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void shouldMarkMasterBranchWithMasterFromHead() throws IOException, GitAPIException {
|
||||
Ref branch = createRef("branch", "1337");
|
||||
when(listBranchCommand.call()).thenReturn(asList(branch, master));
|
||||
|
||||
List<Branch> branches = branchesCommand.getBranches();
|
||||
|
||||
assertThat(branches).containsExactlyInAnyOrder(
|
||||
Branch.normalBranch("branch", "1337"),
|
||||
Branch.defaultBranch("master", "0000")
|
||||
assertThat(branches).contains(
|
||||
Branch.defaultBranch("master", "fcd0ef1831e4002ac43ea539f4094334c79ea9ec", 1339428655000L),
|
||||
Branch.normalBranch("mergeable", "91b99de908fcd04772798a31c308a64aea1a5523", 1541586052000L),
|
||||
Branch.normalBranch("rename", "383b954b27e052db6880d57f1c860dc208795247", 1589203061000L)
|
||||
);
|
||||
}
|
||||
|
||||
private Ref createRef(String branchName, String revision) {
|
||||
Ref ref = mock(Ref.class);
|
||||
lenient().when(ref.getName()).thenReturn("refs/heads/" + branchName);
|
||||
ObjectId objectId = mock(ObjectId.class);
|
||||
lenient().when(objectId.name()).thenReturn(revision);
|
||||
lenient().when(ref.getObjectId()).thenReturn(objectId);
|
||||
return ref;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user