diff --git a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java index d03af28e3a..0f7c9296a7 100644 --- a/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java +++ b/scm-plugins/scm-git-plugin/src/main/java/sonia/scm/repository/spi/GitModifyCommand.java @@ -77,7 +77,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman @Override public void create(String toBeCreated, File file, boolean overwrite) throws IOException { Path targetFile = new File(workDir, toBeCreated).toPath(); - Files.createDirectories(targetFile.getParent()); + createDirectories(targetFile); if (overwrite) { Files.move(file.toPath(), targetFile, REPLACE_EXISTING); } else { @@ -88,7 +88,7 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman } } try { - getClone().add().addFilepattern(toBeCreated).call(); + addFileToGit(toBeCreated); } catch (GitAPIException e) { throwInternalRepositoryException("could not add new file to index", e); } @@ -97,18 +97,22 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman @Override public void modify(String path, File file) throws IOException { Path targetFile = new File(workDir, path).toPath(); - Files.createDirectories(targetFile.getParent()); + createDirectories(targetFile); if (!targetFile.toFile().exists()) { throw notFound(createFileContext(path)); } Files.move(file.toPath(), targetFile, REPLACE_EXISTING); try { - getClone().add().addFilepattern(path).call(); + addFileToGit(path); } catch (GitAPIException e) { throwInternalRepositoryException("could not add new file to index", e); } } + private void addFileToGit(String toBeCreated) throws GitAPIException { + getClone().add().addFilepattern(removeStartingPathSeparators(toBeCreated)).call(); + } + @Override public void delete(String toBeDeleted) throws IOException { Path fileToBeDeleted = new File(workDir, toBeDeleted).toPath(); @@ -118,12 +122,27 @@ public class GitModifyCommand extends AbstractGitCommand implements ModifyComman throw notFound(createFileContext(toBeDeleted)); } try { - getClone().rm().addFilepattern(toBeDeleted).call(); + getClone().rm().addFilepattern(removeStartingPathSeparators(toBeDeleted)).call(); } catch (GitAPIException e) { throwInternalRepositoryException("could not remove file from index", e); } } + private String removeStartingPathSeparators(String path) { + while (path.startsWith(File.separator)) { + path = path.substring(1); + } + return path; + } + + private void createDirectories(Path targetFile) throws IOException { + try { + Files.createDirectories(targetFile.getParent()); + } catch (FileAlreadyExistsException e) { + throw alreadyExists(createFileContext(targetFile.toString())); + } + } + private ContextEntry.ContextBuilder createFileContext(String path) { ContextEntry.ContextBuilder contextBuilder = entity("file", path); if (!StringUtils.isEmpty(request.getBranch())) { diff --git a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java index 9916272596..4fbf7eaf02 100644 --- a/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java +++ b/scm-plugins/scm-git-plugin/src/test/java/sonia/scm/repository/spi/GitModifyCommandTest.java @@ -97,6 +97,24 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase { assertInTree(assertions); } + @Test + public void shouldCreateNewFileWhenPathStartsWithSlash() throws IOException, GitAPIException { + File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + + GitModifyCommand command = createCommand(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("test commit"); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("/new_file", newFile, false)); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + command.execute(request); + + TreeAssertions assertions = canonicalTreeParser -> assertThat(canonicalTreeParser.findFile("new_file")).isTrue(); + + assertInTree(assertions); + } + @Test(expected = AlreadyExistsException.class) public void shouldFailIfOverwritingExistingFileWithoutOverwriteFlag() throws IOException { File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); @@ -111,6 +129,20 @@ public class GitModifyCommandTest extends AbstractGitCommandTestBase { command.execute(request); } + @Test(expected = AlreadyExistsException.class) + public void shouldFailIfPathAlreadyExistsAsAFile() throws IOException { + File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); + + GitModifyCommand command = createCommand(); + + ModifyCommandRequest request = new ModifyCommandRequest(); + request.setCommitMessage("test commit"); + request.addRequest(new ModifyCommandRequest.CreateFileRequest("a.txt/newFile", newFile, false)); + request.setAuthor(new Person("Dirk Gently", "dirk@holistic.det")); + + command.execute(request); + } + @Test public void shouldOverwriteExistingFileIfOverwriteFlagSet() throws IOException, GitAPIException { File newFile = Files.write(temporaryFolder.newFile().toPath(), "new content".getBytes()).toFile(); diff --git a/scm-ui/public/locales/en/admin.json b/scm-ui/public/locales/en/admin.json index 7c9adff4f9..99febb68fc 100644 --- a/scm-ui/public/locales/en/admin.json +++ b/scm-ui/public/locales/en/admin.json @@ -70,13 +70,13 @@ "submit": "Save" }, "delete": { - "button": "Löschen", - "subtitle": "Berechtigungsrolle löschen", + "button": "Delete", + "subtitle": "Delete permission role", "confirmAlert": { - "title": "Berechtigungsrolle löschen", - "message": "Soll die Berechtigungsrolle wirklich gelöscht werden? Alle Nutzer dieser Rolle verlieren Ihre Berechtigungen.", - "submit": "Ja", - "cancel": "Nein" + "title": "Delete permission role", + "message": "Do you really want to delete this permission role? All users will lose their corresponding permissions.", + "submit": "Yes", + "cancel": "No" } } } diff --git a/scm-ui/src/repos/sources/components/FileTreeLeaf.js b/scm-ui/src/repos/sources/components/FileTreeLeaf.js index 2515891de0..2e1a81c0e9 100644 --- a/scm-ui/src/repos/sources/components/FileTreeLeaf.js +++ b/scm-ui/src/repos/sources/components/FileTreeLeaf.js @@ -92,7 +92,7 @@ class FileTreeLeaf extends React.Component { {file.description} {binder.hasExtension("repos.sources.tree.row.right") && ( - + {!file.directory && ( { const defaultBranches = branches.filter(b => b.defaultBranch); if (defaultBranches.length > 0) { - this.setState({ selectedBranch: defaultBranches[0] }); this.props.history.push( `${baseUrl}/${encodeURIComponent(defaultBranches[0].name)}/` ); + this.setState({ selectedBranch: defaultBranches[0] }); } } };