diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8004d390ec..dc85ccc883 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,4 @@
+
# Changelog
All notable changes to this project will be documented in this file.
@@ -6,6 +7,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## Unreleased
### Added
+- Add support for pr merge with prior rebase ([#1332](https://github.com/scm-manager/scm-manager/pull/1332))
- Tags overview for repository [#1331](https://github.com/scm-manager/scm-manager/pull/1331)
- Permissions can be specified for namespaces ([#1335](https://github.com/scm-manager/scm-manager/pull/1335))
- Show update info on admin information page ([#1342](https://github.com/scm-manager/scm-manager/pull/1342))
@@ -17,6 +19,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Overflow for too long branch names ([#1339](https://github.com/scm-manager/scm-manager/pull/1339))
- Set default branch in branch selector if nothing is selected ([#1338](https://github.com/scm-manager/scm-manager/pull/1338))
- Handling of branch with slashes in source view ([#1340](https://github.com/scm-manager/scm-manager/pull/1340))
+- Detect not existing paths correctly in Mercurial ([#1343](https://github.com/scm-manager/scm-manager/pull/1343))
## [2.5.0] - 2020-09-10
### Added
diff --git a/pom.xml b/pom.xml
index df3a55d56b..961aa2ce56 100644
--- a/pom.xml
+++ b/pom.xml
@@ -464,7 +464,7 @@
org.assertj
assertj-core
- 3.17.0
+ 3.17.1
test
@@ -903,7 +903,7 @@
- 3.5.7
+ 3.5.9
2.1
5.6.2
diff --git a/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java
index 98a9938346..a4083cef1d 100644
--- a/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java
+++ b/scm-core/src/main/java/sonia/scm/repository/api/MergeStrategy.java
@@ -21,11 +21,22 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.api;
public enum MergeStrategy {
- MERGE_COMMIT,
- FAST_FORWARD_IF_POSSIBLE,
- SQUASH
+ MERGE_COMMIT(true),
+ FAST_FORWARD_IF_POSSIBLE(true),
+ SQUASH(true),
+ REBASE(false);
+
+ private final boolean commitMessageAllowed;
+
+ MergeStrategy(boolean commitMessageAllowed) {
+ this.commitMessageAllowed = commitMessageAllowed;
+ }
+
+ public boolean isCommitMessageAllowed() {
+ return commitMessageAllowed;
+ }
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java
index bd4e6b26b5..38b6b6dee5 100644
--- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeCommand.java
@@ -60,7 +60,8 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
private static final Set STRATEGIES = ImmutableSet.of(
MergeStrategy.MERGE_COMMIT,
MergeStrategy.FAST_FORWARD_IF_POSSIBLE,
- MergeStrategy.SQUASH
+ MergeStrategy.SQUASH,
+ MergeStrategy.REBASE
);
@Inject
@@ -94,6 +95,9 @@ public class GitMergeCommand extends AbstractGitCommand implements MergeCommand
case MERGE_COMMIT:
return inClone(clone -> new GitMergeCommit(clone, request, context, repository), workingCopyFactory, request.getTargetBranch());
+ case REBASE:
+ return inClone(clone -> new GitMergeRebase(clone, request, context, repository), workingCopyFactory, request.getTargetBranch());
+
default:
throw new MergeStrategyNotSupportedException(repository, request.getMergeStrategy());
}
diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeRebase.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeRebase.java
new file mode 100644
index 0000000000..65cba4f7ba
--- /dev/null
+++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitMergeRebase.java
@@ -0,0 +1,96 @@
+/*
+ * 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.repository.spi;
+
+import org.eclipse.jgit.api.Git;
+import org.eclipse.jgit.api.MergeCommand;
+import org.eclipse.jgit.api.RebaseResult;
+import org.eclipse.jgit.api.errors.GitAPIException;
+import org.eclipse.jgit.lib.ObjectId;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+import sonia.scm.repository.InternalRepositoryException;
+import sonia.scm.repository.Repository;
+import sonia.scm.repository.api.MergeCommandResult;
+
+import java.io.IOException;
+import java.util.Collections;
+import java.util.Optional;
+
+public class GitMergeRebase extends GitMergeStrategy {
+
+ private static final Logger logger = LoggerFactory.getLogger(GitMergeRebase.class);
+
+ private final MergeCommandRequest request;
+
+ GitMergeRebase(Git clone, MergeCommandRequest request, GitContext context, Repository repository) {
+ super(clone, request, context, repository);
+ this.request = request;
+ }
+
+ @Override
+ MergeCommandResult run() throws IOException {
+ RebaseResult result;
+ String branchToMerge = request.getBranchToMerge();
+ String targetBranch = request.getTargetBranch();
+ try {
+ checkOutBranch(branchToMerge);
+ result =
+ getClone()
+ .rebase()
+ .setUpstream(targetBranch)
+ .call();
+ } catch (GitAPIException e) {
+ throw new InternalRepositoryException(getContext().getRepository(), "could not rebase branch " + branchToMerge + " onto " + targetBranch, e);
+ }
+
+ if (result.getStatus().isSuccessful()) {
+ return fastForwardTargetBranch(branchToMerge, targetBranch, result);
+ } else {
+ logger.info("could not rebase branch {} into {} with rebase status '{}' due to ...", branchToMerge, targetBranch, result.getStatus());
+ logger.info("... conflicts: {}", result.getConflicts());
+ logger.info("... failing paths: {}", result.getFailingPaths());
+ logger.info("... message: {}", result);
+ return MergeCommandResult.failure(branchToMerge, targetBranch, Optional.ofNullable(result.getConflicts()).orElse(Collections.singletonList("UNKNOWN")));
+ }
+ }
+
+ private MergeCommandResult fastForwardTargetBranch(String branchToMerge, String targetBranch, RebaseResult result) throws IOException {
+ try {
+ getClone().checkout().setName(targetBranch).call();
+ ObjectId sourceRevision = resolveRevision(branchToMerge);
+ getClone()
+ .merge()
+ .setFastForward(MergeCommand.FastForwardMode.FF_ONLY)
+ .include(branchToMerge, sourceRevision)
+ .call();
+ push();
+ return MergeCommandResult.success(getTargetRevision().name(), branchToMerge, sourceRevision.name());
+ } catch (GitAPIException e) {
+ return MergeCommandResult.failure(branchToMerge, targetBranch, result.getConflicts());
+ }
+
+ }
+}
diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java
index 716b172981..84c17a299a 100644
--- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java
+++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitMergeCommandTest.java
@@ -473,6 +473,47 @@ public class GitMergeCommandTest extends AbstractGitCommandTestBase {
}
+ @Test
+ public void shouldAllowMergeWithRebase() throws IOException, GitAPIException {
+ GitMergeCommand command = createCommand();
+ MergeCommandRequest request = new MergeCommandRequest();
+ request.setTargetBranch("master");
+ request.setBranchToMerge("mergeable");
+ request.setMergeStrategy(MergeStrategy.REBASE);
+ request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det"));
+
+ MergeCommandResult mergeCommandResult = command.merge(request);
+
+ assertThat(mergeCommandResult.isSuccess()).isTrue();
+
+ Repository repository = createContext().open();
+ Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call();
+ RevCommit mergeCommit = commits.iterator().next();
+ assertThat(mergeCommit.getParentCount()).isEqualTo(1);
+ assertThat(mergeCommit.getParent(0).name()).isEqualTo("fcd0ef1831e4002ac43ea539f4094334c79ea9ec");
+ assertThat(mergeCommit.getName()).isEqualTo(mergeCommandResult.getNewHeadRevision());
+ assertThat(mergeCommit.getName()).doesNotStartWith("91b99de908fcd04772798a31c308a64aea1a5523");
+ }
+
+ @Test
+ public void shouldRejectRebaseMergeIfBranchCannotBeRebased() throws IOException, GitAPIException {
+ GitMergeCommand command = createCommand();
+ MergeCommandRequest request = new MergeCommandRequest();
+ request.setTargetBranch("master");
+ request.setBranchToMerge("not-rebasable");
+ request.setMergeStrategy(MergeStrategy.REBASE);
+ request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det"));
+
+ MergeCommandResult mergeCommandResult = command.merge(request);
+
+ assertThat(mergeCommandResult.isSuccess()).isFalse();
+ Repository repository = createContext().open();
+ Iterable commits = new Git(repository).log().add(repository.resolve("master")).setMaxCount(1).call();
+ RevCommit headCommit = commits.iterator().next();
+ assertThat(headCommit.getName()).isEqualTo("fcd0ef1831e4002ac43ea539f4094334c79ea9ec");
+
+ }
+
private GitMergeCommand createCommand() {
return createCommand(git -> {
});
diff --git a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip
index 80483d04d1..4d23fa0284 100644
Binary files a/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip and b/scm-plugins/scm-git-plugin/src/test/resources/sonia/scm/repository/spi/scm-git-spi-test.zip differ
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java
index 6c83368cc4..8c217d6d4c 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/HgBrowseCommand.java
@@ -36,6 +36,9 @@ import sonia.scm.repository.spi.javahg.HgFileviewCommand;
import java.io.IOException;
+import static sonia.scm.ContextEntry.ContextBuilder.entity;
+import static sonia.scm.NotFoundException.notFound;
+
//~--- JDK imports ------------------------------------------------------------
/**
@@ -93,7 +96,8 @@ public class HgBrowseCommand extends AbstractCommand implements BrowseCommand
cmd.setLimit(request.getLimit());
cmd.setOffset(request.getOffset());
- FileObject file = cmd.execute();
+ FileObject file = cmd.execute()
+ .orElseThrow(() -> notFound(entity("File", request.getPath()).in("Revision", revision).in(getRepository())));
return new BrowserResult(c == null? "tip": c.getNode(), revision, file);
}
}
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java
index 776bfcaa7d..0ecd0a7d27 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommand.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.spi.javahg;
//~--- non-JDK imports --------------------------------------------------------
@@ -29,12 +29,12 @@ package sonia.scm.repository.spi.javahg;
import com.aragost.javahg.Repository;
import com.aragost.javahg.internals.AbstractCommand;
import com.aragost.javahg.internals.HgInputStream;
-
import sonia.scm.repository.FileObject;
//~--- JDK imports ------------------------------------------------------------
import java.io.IOException;
+import java.util.Optional;
/**
* Mercurial command to list files of a repository.
@@ -161,7 +161,7 @@ public class HgFileviewCommand extends AbstractCommand
*
* @throws IOException
*/
- public FileObject execute() throws IOException
+ public Optional execute() throws IOException
{
cmdAppend("-t");
diff --git a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java
index 80d6a69a5a..c9e315ab00 100644
--- a/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java
+++ b/scm-plugins/scm-hg-plugin/src/main/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReader.java
@@ -21,7 +21,7 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.spi.javahg;
import com.aragost.javahg.DateTime;
@@ -35,6 +35,10 @@ import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.LinkedList;
+import java.util.Optional;
+
+import static java.util.Optional.empty;
+import static java.util.Optional.of;
class HgFileviewCommandResultReader {
@@ -48,7 +52,7 @@ class HgFileviewCommandResultReader {
this.disableLastCommit = disableLastCommit;
}
- FileObject parseResult() throws IOException {
+ Optional parseResult() throws IOException {
Deque stack = new LinkedList<>();
FileObject last = null;
@@ -82,13 +86,17 @@ class HgFileviewCommandResultReader {
if (stack.isEmpty()) {
// if the stack is empty, the requested path is probably a file
- return last;
+ return of(last);
+ } else if (stack.size() == 1 && stack.getFirst().isDirectory() && stack.getFirst().getChildren().isEmpty()) {
+ // There are no empty directories in hg. When we get this,
+ // we just get the requested path as a directory, but it does not exist.
+ return empty();
} else {
// if the stack is not empty, the requested path is a directory
if (stream.read() == TRUNCATED_MARK) {
stack.getLast().setTruncated(true);
}
- return stack.getLast();
+ return of(stack.getLast());
}
}
diff --git a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java
index 2cb6a4af04..c268175108 100644
--- a/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java
+++ b/scm-plugins/scm-hg-plugin/src/test/java/sonia/scm/repository/spi/javahg/HgFileviewCommandResultReaderTest.java
@@ -21,11 +21,10 @@
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
-
+
package sonia.scm.repository.spi.javahg;
import com.aragost.javahg.internals.HgInputStream;
-import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.Test;
import sonia.scm.repository.FileObject;
@@ -34,6 +33,7 @@ import java.io.IOException;
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import java.util.Iterator;
+import java.util.Optional;
import java.util.OptionalLong;
import static java.nio.charset.StandardCharsets.UTF_8;
@@ -55,7 +55,7 @@ class HgFileviewCommandResultReaderTest {
.file("b.txt", 100, time2.toEpochMilli(), "file b\nwith some\nmore text")
.build();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.isDirectory()).isTrue();
assertThat(fileObject.getChildren())
@@ -84,7 +84,7 @@ class HgFileviewCommandResultReaderTest {
.file("a.txt")
.truncated();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.isTruncated()).isTrue();
}
@@ -96,7 +96,7 @@ class HgFileviewCommandResultReaderTest {
.file("dir/a.txt")
.build();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.isDirectory()).isTrue();
assertThat(fileObject.getName()).isEqualTo("dir");
@@ -117,7 +117,7 @@ class HgFileviewCommandResultReaderTest {
.file("d.txt")
.build();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.getChildren())
.extracting("name")
@@ -152,7 +152,7 @@ class HgFileviewCommandResultReaderTest {
.file("d.txt")
.build();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.getChildren())
.extracting("name")
@@ -179,7 +179,7 @@ class HgFileviewCommandResultReaderTest {
.file("d.txt")
.build();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.getChildren())
.extracting("name")
@@ -213,7 +213,7 @@ class HgFileviewCommandResultReaderTest {
.file("directory/b.txt")
.build();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.getChildren())
.extracting("name")
@@ -248,7 +248,7 @@ class HgFileviewCommandResultReaderTest {
.file("a.txt")
.build();
- FileObject fileObject = reader.parseResult();
+ FileObject fileObject = reader.parseResult().get();
assertThat(fileObject.getChildren())
.extracting("description")
@@ -258,6 +258,17 @@ class HgFileviewCommandResultReaderTest {
.containsOnly(OptionalLong.empty());
}
+ @Test
+ void shouldIgnoreSingleEmptyDir() throws IOException {
+ HgFileviewCommandResultReader reader = new MockInput()
+ .dir("empty")
+ .build();
+
+ Optional fileObject = reader.parseResult();
+
+ assertThat(fileObject).isEmpty();
+ }
+
private HgInputStream createInputStream(String input) {
return new HgInputStream(new ByteArrayInputStream(input.getBytes(UTF_8)), UTF_8.newDecoder());
}
diff --git a/scm-server/pom.xml b/scm-server/pom.xml
index 780d7aed4b..e012b5e7f0 100644
--- a/scm-server/pom.xml
+++ b/scm-server/pom.xml
@@ -69,7 +69,7 @@
- 1.2.2
+ 1.2.3
diff --git a/scm-ui/ui-webapp/public/locales/de/config.json b/scm-ui/ui-webapp/public/locales/de/config.json
index 8a872a5987..ad749c12c8 100644
--- a/scm-ui/ui-webapp/public/locales/de/config.json
+++ b/scm-ui/ui-webapp/public/locales/de/config.json
@@ -61,7 +61,7 @@
"realmDescriptionHelpText": "Beschreibung des Authentication Realm.",
"dateFormatHelpText": "Moments Datumsformat. Zulässige Formate sind in der MomentJS Dokumentation beschrieben.",
"pluginUrlHelpText": "Die URL der Plugin Center API. Beschreibung der Platzhalter: version = SCM-Manager Version; os = Betriebssystem; arch = Architektur",
- "releaseFeedUrlHelpText": "Die URL des RSS Release Feed des SCM-Manager. Darüber wird über die neue SCM-Manager Versionen informiert. Um diese Funktion zu deaktivieren lassen Sie dieses Feld leer.",
+ "releaseFeedUrlHelpText": "Die URL des RSS Release Feed des SCM-Manager. Darüber wird über die neue SCM-Manager Version informiert. Um diese Funktion zu deaktivieren lassen Sie dieses Feld leer.",
"enableForwardingHelpText": "mod_proxy Port Weiterleitung aktivieren.",
"disableGroupingGridHelpText": "Repository Gruppen deaktivieren. Nach einer Änderung an dieser Einstellung muss die Seite komplett neu geladen werden.",
"allowAnonymousAccessHelpText": "Anonyme Benutzer haben Zugriff auf freigegebene Repositories.",
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceToNamespaceDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceToNamespaceDtoMapper.java
index 70b85374ce..5067f95c0f 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceToNamespaceDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/NamespaceToNamespaceDtoMapper.java
@@ -47,7 +47,7 @@ class NamespaceToNamespaceDtoMapper {
.self(links.namespace().self(namespace))
.single(link("repositories", links.repositoryCollection().forNamespace(namespace)));
- if (NamespacePermissions.permissionRead().isPermitted() || NamespacePermissions.permissionWrite().isPermitted()) {
+ if (NamespacePermissions.permissionRead().isPermitted()) {
linkingTo
.single(link("permissions", links.namespacePermission().all(namespace)));
}
diff --git a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionCollectionToDtoMapper.java b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionCollectionToDtoMapper.java
index 6928dec872..db285c369a 100644
--- a/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionCollectionToDtoMapper.java
+++ b/scm-webapp/src/main/java/sonia/scm/api/v2/resources/RepositoryPermissionCollectionToDtoMapper.java
@@ -78,9 +78,7 @@ public class RepositoryPermissionCollectionToDtoMapper {
}
private Links createLinks(Namespace namespace) {
- if (!NamespacePermissions.permissionWrite().isPermitted()) {
- NamespacePermissions.permissionRead().check();
- }
+ NamespacePermissions.permissionRead().check();
Links.Builder linksBuilder = linkingTo()
.with(Links.linkingTo().self(resourceLinks.namespacePermission().all(namespace.getNamespace())).build());
if (NamespacePermissions.permissionWrite().isPermitted()) {
diff --git a/scm-webapp/src/main/java/sonia/scm/repository/DefaultNamespaceManager.java b/scm-webapp/src/main/java/sonia/scm/repository/DefaultNamespaceManager.java
index 6d6ef8963f..c291cfa97e 100644
--- a/scm-webapp/src/main/java/sonia/scm/repository/DefaultNamespaceManager.java
+++ b/scm-webapp/src/main/java/sonia/scm/repository/DefaultNamespaceManager.java
@@ -104,7 +104,7 @@ public class DefaultNamespaceManager implements NamespaceManager {
}
private Namespace createNamespaceForName(String namespace) {
- if (NamespacePermissions.permissionRead().isPermitted() || NamespacePermissions.permissionWrite().isPermitted()) {
+ if (NamespacePermissions.permissionRead().isPermitted()) {
return dao.get(namespace)
.map(Namespace::clone)
.orElse(new Namespace(namespace));
diff --git a/scm-webapp/src/main/resources/META-INF/scm/permissions.xml b/scm-webapp/src/main/resources/META-INF/scm/permissions.xml
index 0889341d25..76d1e7fa46 100644
--- a/scm-webapp/src/main/resources/META-INF/scm/permissions.xml
+++ b/scm-webapp/src/main/resources/META-INF/scm/permissions.xml
@@ -48,7 +48,7 @@
namespace:permissionRead
- namespace:permissionWrite
+ namespace:permissionRead,permissionWrite
user:*
diff --git a/scm-webapp/src/main/resources/locales/de/plugins.json b/scm-webapp/src/main/resources/locales/de/plugins.json
index f3372e2d85..cf2c29f21f 100644
--- a/scm-webapp/src/main/resources/locales/de/plugins.json
+++ b/scm-webapp/src/main/resources/locales/de/plugins.json
@@ -98,7 +98,7 @@
"displayName": "Berechtigungen auf Namespaces lesen",
"description": "Darf die Berechtigungen auf Namespace-Ebene sehen"
},
- "permissionWrite": {
+ "permissionRead,permissionWrite": {
"displayName": "Berechtigungen auf Namespaces modifizieren",
"description": "Darf die Berechtigungen auf Namespace-Ebene lesen und bearbeiten"
}
diff --git a/scm-webapp/src/main/resources/locales/en/plugins.json b/scm-webapp/src/main/resources/locales/en/plugins.json
index 3dfeef2300..4de2202976 100644
--- a/scm-webapp/src/main/resources/locales/en/plugins.json
+++ b/scm-webapp/src/main/resources/locales/en/plugins.json
@@ -98,7 +98,7 @@
"displayName": "Read permissions on namespaces",
"description": "May see the permissions set for namespaces"
},
- "permissionWrite": {
+ "permissionRead,permissionWrite": {
"displayName": "Modify permissions on namespaces",
"description": "May read and modify the permissions set for namespaces"
}
diff --git a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexDtoGeneratorTest.java b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexDtoGeneratorTest.java
index 97c9714513..94c78f798b 100644
--- a/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexDtoGeneratorTest.java
+++ b/scm-webapp/src/test/java/sonia/scm/api/v2/resources/IndexDtoGeneratorTest.java
@@ -132,6 +132,7 @@ class IndexDtoGeneratorTest {
when(resourceLinks.repositoryTypeCollection()).thenReturn(new ResourceLinks.RepositoryTypeCollectionLinks(scmPathInfo));
when(resourceLinks.repositoryRoleCollection()).thenReturn(new ResourceLinks.RepositoryRoleCollectionLinks(scmPathInfo));
when(resourceLinks.namespaceStrategies()).thenReturn(new ResourceLinks.NamespaceStrategiesLinks(scmPathInfo));
+ when(resourceLinks.namespaceCollection()).thenReturn(new ResourceLinks.NamespaceCollectionLinks(scmPathInfo));
when(resourceLinks.me()).thenReturn(new ResourceLinks.MeLinks(scmPathInfo, new ResourceLinks.UserLinks(scmPathInfo)));
}
}