fix show diff in git format for svn repo

This commit is contained in:
Mohamed Karray
2018-10-26 16:26:55 +02:00
parent c214c9e1e4
commit 0360694052
15 changed files with 4137 additions and 169 deletions

View File

@@ -0,0 +1,265 @@
package sonia.scm.it;
import org.apache.http.HttpStatus;
import org.assertj.core.util.Lists;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.TemporaryFolder;
import sonia.scm.it.utils.RepositoryUtil;
import sonia.scm.it.utils.ScmRequests;
import sonia.scm.it.utils.TestData;
import sonia.scm.repository.Changeset;
import sonia.scm.repository.client.api.RepositoryClient;
import java.io.File;
import java.io.IOException;
import java.net.URISyntaxException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.assertj.core.api.Assertions.assertThat;
import static sonia.scm.it.utils.RestUtil.ADMIN_PASSWORD;
import static sonia.scm.it.utils.RestUtil.ADMIN_USERNAME;
public class DiffITCase {
@Rule
public TemporaryFolder tempFolder = new TemporaryFolder();
private RepositoryClient svnRepositoryClient;
private RepositoryClient gitRepositoryClient;
private RepositoryClient hgRepositoryClient;
private ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> svnRepositoryResponse;
private ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> hgRepositoryResponse;
private ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> gitRepositoryResponse;
private File svnFolder;
private File gitFolder;
private File hgFolder;
@Before
public void init() throws IOException {
TestData.createDefault();
String namespace = ADMIN_USERNAME;
String repo = TestData.getDefaultRepoName("svn");
svnRepositoryResponse =
ScmRequests.start()
.requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD)
.requestRepository(namespace, repo)
.assertStatusCode(HttpStatus.SC_OK);
svnFolder = tempFolder.newFolder("svn");
svnRepositoryClient = RepositoryUtil.createRepositoryClient("svn", svnFolder);
repo = TestData.getDefaultRepoName("git");
gitRepositoryResponse =
ScmRequests.start()
.requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD)
.requestRepository(namespace, repo)
.assertStatusCode(HttpStatus.SC_OK);
gitFolder = tempFolder.newFolder("git");
gitRepositoryClient = RepositoryUtil.createRepositoryClient("git", gitFolder);
repo = TestData.getDefaultRepoName("hg");
hgRepositoryResponse =
ScmRequests.start()
.requestIndexResource(ADMIN_USERNAME, ADMIN_PASSWORD)
.requestRepository(namespace, repo)
.assertStatusCode(HttpStatus.SC_OK);
hgFolder = tempFolder.newFolder("hg");
hgRepositoryClient = RepositoryUtil.createRepositoryClient("hg", hgFolder);
}
@Test
public void shouldFindDiffsInGitFormat() throws IOException {
String svnDiff = getDiff(RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), gitRepositoryResponse);
String hgDiff = getDiff(RepositoryUtil.createAndCommitFile(hgRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), hgRepositoryResponse);
assertThat(Lists.newArrayList(svnDiff, gitDiff, hgDiff))
.allSatisfy(diff -> assertThat(diff)
.contains("diff --git "));
}
@Test
public void svnAddFileDiffShouldBeConvertedToGitDiff() throws IOException {
String svnDiff = getDiff(RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
@Test
public void svnDeleteFileDiffShouldBeConvertedToGitDiff() throws IOException {
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
String svnDiff = getDiff(RepositoryUtil.removeAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.removeAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
@Test
public void svnUpdateFileDiffShouldBeConvertedToGitDiff() throws IOException {
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "content of a");
String svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, "a.txt", "the updated content of a"), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, "a.txt", "the updated content of a"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
@Test
public void svnMultipleChangesDiffShouldBeConvertedToGitDiff() throws IOException {
String svnDiff = getDiff(applyMultipleChanges(svnRepositoryClient, "fileToBeDeleted.txt", "fileToBeUpdated.txt", "addedFile.txt"), svnRepositoryResponse);
String gitDiff = getDiff(applyMultipleChanges(gitRepositoryClient, "fileToBeDeleted.txt", "fileToBeUpdated.txt", "addedFile.txt"), gitRepositoryResponse);
String endOfDiffPart = "\\ No newline at end of file\n";
String[] gitDiffs = gitDiff.split(endOfDiffPart);
List<String> expected = Arrays.stream(gitDiffs)
.map(this::getGitDiffWithoutIndexLine)
.collect(Collectors.toList());
assertThat(svnDiff.split(endOfDiffPart))
.containsExactlyInAnyOrderElementsOf(expected);
}
@Test
public void svnMultipleSubFolderChangesDiffShouldBeConvertedToGitDiff() throws IOException {
String svnDiff = getDiff(applyMultipleChanges(svnRepositoryClient, "a/b/fileToBeDeleted.txt", "a/c/fileToBeUpdated.txt", "a/d/addedFile.txt"), svnRepositoryResponse);
String gitDiff = getDiff(applyMultipleChanges(gitRepositoryClient, "a/b/fileToBeDeleted.txt", "a/c/fileToBeUpdated.txt", "a/d/addedFile.txt"), gitRepositoryResponse);
String endOfDiffPart = "\\ No newline at end of file\n";
String[] gitDiffs = gitDiff.split(endOfDiffPart);
List<String> expected = Arrays.stream(gitDiffs)
.map(this::getGitDiffWithoutIndexLine)
.collect(Collectors.toList());
assertThat(svnDiff.split(endOfDiffPart))
.containsExactlyInAnyOrderElementsOf(expected);
}
@Test
public void svnLargeChangesDiffShouldBeConvertedToGitDiff() throws IOException, URISyntaxException {
String fileName = "SvnDiffGenerator_forTest.java";
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, "");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, "");
String fileContent = getFileContent("/diff/largefile/original/SvnDiffGenerator_forTest.java");
String svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, fileContent), svnRepositoryResponse);
String gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, fileContent), gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
fileContent = getFileContent("/diff/largefile/modified/v1/SvnDiffGenerator_forTest.java");
svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, fileContent), svnRepositoryResponse);
gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, fileContent), gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
fileContent = getFileContent("/diff/largefile/modified/v2/SvnDiffGenerator_forTest.java");
svnDiff = getDiff(RepositoryUtil.updateAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, fileContent), svnRepositoryResponse);
gitDiff = getDiff(RepositoryUtil.updateAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, fileContent), gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
}
/**
* FIXME: the binary Git Diff output is not GIT conform
*/
@Test
@Ignore
@SuppressWarnings("squid:S1607")
public void svnBinaryChangesDiffShouldBeConvertedToGitDiff() throws IOException, URISyntaxException {
String fileName = "binary";
File file = new File(svnRepositoryClient.getWorkingCopy(), fileName);
Files.copy(Paths.get(getClass().getResource("/diff/binaryfile/echo").toURI()), Paths.get(file.toURI()));
Changeset commit = RepositoryUtil.addFileAndCommit(svnRepositoryClient, fileName, ADMIN_USERNAME, "");
file = new File(gitRepositoryClient.getWorkingCopy(), fileName);
Files.copy(Paths.get(getClass().getResource("/diff/binaryfile/echo").toURI()), Paths.get(file.toURI()));
Changeset commit1 = RepositoryUtil.addFileAndCommit(gitRepositoryClient, fileName, ADMIN_USERNAME, "");
String svnDiff = getDiff(commit, svnRepositoryResponse);
String gitDiff = getDiff(commit1, gitRepositoryResponse);
assertThat(svnDiff)
.isEqualTo(getGitDiffWithoutIndexLine(gitDiff));
}
@Test
public void svnRenameChangesDiffShouldBeConvertedToGitDiff() throws IOException, URISyntaxException {
String fileName = "a.txt";
RepositoryUtil.createAndCommitFile(svnRepositoryClient, ADMIN_USERNAME, fileName, "content of a");
RepositoryUtil.createAndCommitFile(gitRepositoryClient, ADMIN_USERNAME, fileName, "content of a");
String newFileName = "renamed_a.txt";
File file = new File(svnRepositoryClient.getWorkingCopy(), fileName);
file.renameTo(new File(svnRepositoryClient.getWorkingCopy(), newFileName));
String svnDiff = getDiff(RepositoryUtil.addFileAndCommit(svnRepositoryClient, newFileName, ADMIN_USERNAME, "renamed file"), svnRepositoryResponse);
file = new File(gitRepositoryClient.getWorkingCopy(), fileName);
file.renameTo(new File(gitRepositoryClient.getWorkingCopy(), newFileName));
String gitDiff = getDiff(RepositoryUtil.addFileAndCommit(gitRepositoryClient, newFileName, ADMIN_USERNAME, "renamed file"), gitRepositoryResponse);
String expected = getGitDiffWithoutIndexLine(gitDiff);
assertThat(svnDiff)
.isEqualTo(expected);
}
public String getFileContent(String name) throws URISyntaxException, IOException {
Path path;
path = Paths.get(getClass().getResource(name).toURI());
Stream<String> lines = Files.lines(path);
String data = lines.collect(Collectors.joining("\n"));
lines.close();
return data;
}
/**
* The index line is not provided from the svn git formatter and it is not needed in the ui diff view
* for more details about the git diff format: https://git-scm.com/docs/git-diff
*
* @param gitDiff
* @return diff without the index line
*/
private String getGitDiffWithoutIndexLine(String gitDiff) {
return gitDiff.replaceAll(".*(index.*\n)", "");
}
private String getDiff(Changeset svnChangeset, ScmRequests.RepositoryResponse<ScmRequests.IndexResponse> svnRepositoryResponse) {
return svnRepositoryResponse.requestChangesets()
.requestDiffInGitFormat(svnChangeset.getId())
.getResponse()
.body()
.asString();
}
private Changeset applyMultipleChanges(RepositoryClient repositoryClient, String fileToBeDeleted, final String fileToBeUpdated, final String addedFile) throws IOException {
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, fileToBeDeleted, "file to be deleted");
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, fileToBeUpdated, "file to be updated");
Map<String, String> addedFiles = new HashMap<String, String>() {{
put(addedFile, "content");
}};
Map<String, String> modifiedFiles = new HashMap<String, String>() {{
put(fileToBeUpdated, "the updated content");
}};
ArrayList<String> removedFiles = Lists.newArrayList(fileToBeDeleted);
return RepositoryUtil.commitMultipleFileModifications(repositoryClient, ADMIN_USERNAME, addedFiles, modifiedFiles, removedFiles);
}
}

View File

@@ -1,5 +1,6 @@
package sonia.scm.it;
import groovy.util.logging.Slf4j;
import io.restassured.response.ExtractableResponse;
import io.restassured.response.Response;
import org.apache.http.HttpStatus;
@@ -37,6 +38,7 @@ import static sonia.scm.it.utils.RestUtil.given;
import static sonia.scm.it.utils.ScmTypes.availableScmTypes;
@RunWith(Parameterized.class)
@Slf4j
public class RepositoryAccessITCase {
@Rule
@@ -175,6 +177,7 @@ public class RepositoryAccessITCase {
}
@Test
@SuppressWarnings("squid:S2925")
public void shouldReadContent() throws IOException, InterruptedException {
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "a.txt", "a");
@@ -262,40 +265,6 @@ public class RepositoryAccessITCase {
assertThat(changesets).size().isBetween(2, 3); // svn has an implicit root revision '0' that is extra to the two commits
}
@Test
public void shouldFindDiffs() throws IOException {
RepositoryClient repositoryClient = RepositoryUtil.createRepositoryClient(repositoryType, folder);
RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "a.txt", "a");
RepositoryUtil.createAndCommitFile(repositoryClient, "scmadmin", "b.txt", "b");
String changesetsUrl = given()
.when()
.get(TestData.getDefaultRepositoryUrl(repositoryType))
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.path("_links.changesets.href");
String diffUrl = given()
.when()
.get(changesetsUrl)
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.path("_embedded.changesets[0]._links.diff.href");
given()
.when()
.get(diffUrl)
.then()
.statusCode(HttpStatus.SC_OK)
.extract()
.body()
.asString()
.contains("diff");
}
@Test
@SuppressWarnings("unchecked")
@@ -393,12 +362,10 @@ public class RepositoryAccessITCase {
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "b.txt", "b");
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "c.txt", "c");
RepositoryUtil.createAndCommitFile(repositoryClient, ADMIN_USERNAME, "d.txt", "d");
Map<String, String> addedFiles = new HashMap<String, String>()
{{
Map<String, String> addedFiles = new HashMap<String, String>() {{
put("a.txt", "bla bla");
}};
Map<String, String> modifiedFiles = new HashMap<String, String>()
{{
Map<String, String> modifiedFiles = new HashMap<String, String>() {{
put("b.txt", "new content");
}};
ArrayList<String> removedFiles = Lists.newArrayList("c.txt", "d.txt");
@@ -414,7 +381,7 @@ public class RepositoryAccessITCase {
.assertAdded(a -> assertThat(a)
.hasSize(1)
.containsExactly("a.txt"))
.assertModified(m-> assertThat(m)
.assertModified(m -> assertThat(m)
.hasSize(1)
.containsExactly("b.txt"))
.assertRemoved(r -> assertThat(r)

View File

@@ -80,6 +80,11 @@ public class RepositoryUtil {
return file;
}
public static Changeset updateAndCommitFile(RepositoryClient repositoryClient, String username, String fileName, String content) throws IOException {
writeAndAddFile(repositoryClient, fileName, content);
return commit(repositoryClient, username, "updated " + fileName);
}
public static Changeset removeAndCommitFile(RepositoryClient repositoryClient, String username, String fileName) throws IOException {
deleteFileAndApplyRemoveCommand(repositoryClient, fileName);
return commit(repositoryClient, username, "removed " + fileName);
@@ -102,11 +107,21 @@ public class RepositoryUtil {
} else {
path = thisName;
}
repositoryClient.getAddCommand().add(path);
addFile(repositoryClient, path);
return path;
}
static Changeset commit(RepositoryClient repositoryClient, String username, String message) throws IOException {
public static Changeset addFileAndCommit(RepositoryClient repositoryClient, String path, String username, String message) throws IOException {
repositoryClient.getAddCommand().add(path);
return commit(repositoryClient, username, message);
}
public static void addFile(RepositoryClient repositoryClient, String path) throws IOException {
repositoryClient.getAddCommand().add(path);
}
public static Changeset commit(RepositoryClient repositoryClient, String username, String message) throws IOException {
LOG.info("user: {} try to commit with message: {}", username, message);
Changeset changeset = repositoryClient.getCommitCommand().commit(new Person(username, username + "@scm-manager.org"), message);
if (repositoryClient.isCommandSupported(ClientCommand.PUSH)) {

View File

@@ -220,8 +220,8 @@ public class ScmRequests {
return this;
}
public DiffResponse<ChangesetsResponse> requestDiff(String revision) {
return new DiffResponse<>(applyGETRequestFromLink(response, "_embedded.changesets.find{it.id=='" + revision + "'}._links.diff.href"), this);
public DiffResponse<ChangesetsResponse> requestDiffInGitFormat(String revision) {
return new DiffResponse<>(applyGETRequestFromLinkWithParams(response, "_embedded.changesets.find{it.id=='" + revision + "'}._links.diff.href", "?format=GIT"), this);
}
public ModificationsResponse<ChangesetsResponse> requestModifications(String revision) {
@@ -348,6 +348,10 @@ public class ScmRequests {
this.previousResponse = previousResponse;
}
public Response getResponse(){
return response;
}
public PREV returnToPrevious() {
return previousResponse;
}

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@@ -43,6 +43,26 @@ import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
/**
* This is a copy of the SvnDiffGenerator class from the patched SVNKit library used in SCM-Manager
* (a fork of SVNKit from TMate Software (http://svnkit.com/)).
*
* The original class can be found here: https://bitbucket.org/sdorra/svnkit/src/default/svnkit/src/main/java/org/tmatesoft/svn/core/internal/wc2/ng/SvnDiffGenerator.java
*
* The folowing modifications are applied when using the git format
* <ul>
* <il>
* remove the svn header
* </il>
* <il>
* use the git diff code 100644 on the new file and deleted file actions
* </il>
* <il>
* remove the labels in the added and deleted file headers eg. [+++ a/a.txt (revision 4)] will replaced with [+++ a/a.txt]
* </il>
* </ul>
*/
@SuppressWarnings("all")
public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
protected static final String WC_REVISION_LABEL = "(working copy)";
@@ -439,20 +459,21 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
}
if (!forcedBinaryDiff && (leftIsBinary || rightIsBinary)) {
boolean shouldStopDisplaying = displayHeader(outputStream, displayPath, rightFile == null, leftFile == null, operation);
if (useGitFormat) {
displayGitDiffHeader(outputStream, operation,
getRelativeToRootPath(target, originalTarget1),
getRelativeToRootPath(target, originalTarget2),
null);
boolean shouldStopDisplaying = false;
if (!useGitFormat) {
shouldStopDisplaying = displayHeader(outputStream, displayPath, rightFile == null, leftFile == null, operation);
} else {
String path1 = getRelativeToRootPath(target, originalTarget1);
String path2 = getRelativeToRootPath(target, originalTarget2);
displayGitDiffHeader(outputStream, operation,path1,path2,null);
}
visitedPaths.add(displayPath);
if (shouldStopDisplaying) {
return;
}
if (!useGitFormat){
displayBinary(mimeType1, mimeType2, outputStream, leftIsBinary, rightIsBinary);
}
return;
}
@@ -588,6 +609,7 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
final ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
try {
if (!useGitFormat) {
// display the svn header only if the git format is not required
displayHeader(byteArrayOutputStream, displayPath, deleted, added, operation);
} else {
displayGitDiffHeader(byteArrayOutputStream, operation,
@@ -888,6 +910,7 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
displayString(outputStream, " ");
displaySecondGitPath(outputStream, path2);
displayEOL(outputStream);
// 100644 is the mode code used from git for new and deleted file mode
displayString(outputStream, "new file mode 100644");
displayEOL(outputStream);
} catch (IOException e) {
@@ -902,6 +925,7 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
displayString(outputStream, " ");
displaySecondGitPath(outputStream, path2);
displayEOL(outputStream);
// 100644 is the mode code used from git for new and deleted file mode
displayString(outputStream, "deleted file mode 100644");
displayEOL(outputStream);
} catch (IOException e) {
@@ -958,11 +982,11 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
}
private void displayFirstGitPath(OutputStream outputStream, String path1) throws IOException {
displayGitPath(outputStream, path1, "a/", false);
displayGitPath(outputStream, path1, "a/");
}
private void displaySecondGitPath(OutputStream outputStream, String path2) throws IOException {
displayGitPath(outputStream, path2, "b/", false);
displayGitPath(outputStream, path2, "b/");
}
private void displayFirstGitLabelPath(OutputStream outputStream, String path1, String revision1, SvnDiffCallback.OperationKind operation) throws IOException {
@@ -971,7 +995,7 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
path1 = "/dev/null";
pathPrefix = "";
}
displayGitPath(outputStream, getLabel(path1, revision1), pathPrefix, true);
displayGitPath(outputStream, getLabel(path1, revision1), pathPrefix);
}
private void displaySecondGitLabelPath(OutputStream outputStream, String path2, String revision2, SvnDiffCallback.OperationKind operation) throws IOException {
@@ -980,16 +1004,12 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
path2 = "/dev/null";
pathPrefix = "";
}
displayGitPath(outputStream, getLabel(path2, revision2), pathPrefix, true);
displayGitPath(outputStream, getLabel(path2, revision2), pathPrefix);
}
private void displayGitPath(OutputStream outputStream, String path1, String pathPrefix, boolean label) throws IOException {
// if (!label && path1.length() == 0) {
// displayString(outputStream, ".");
// } else {
private void displayGitPath(OutputStream outputStream, String path1, String pathPrefix) throws IOException {
displayString(outputStream, pathPrefix);
displayString(outputStream, path1);
// }
}
private String getAdjustedPathWithLabel(String displayPath, String path, String revision, String commonAncestor) {
@@ -1013,6 +1033,7 @@ public class SCMSvnDiffGenerator implements ISvnDiffGenerator {
protected String getLabel(String path, String revToken) {
if (useGitFormat){
// the label in the git format contains only the path
return path;
}
revToken = revToken == null ? WC_REVISION_LABEL : revToken;

View File

@@ -1,10 +1,10 @@
/**
/*
* 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.
* 2. Redistributions in binary form must reproduce the above copyright notice,
@@ -13,7 +13,7 @@
* 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.
*
* <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,13 +24,11 @@
* 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
*
*/
package sonia.scm.repository.spi;
//~--- non-JDK imports --------------------------------------------------------
@@ -41,14 +39,12 @@ import org.slf4j.LoggerFactory;
import org.tmatesoft.svn.core.SVNDepth;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.wc.DefaultSVNDiffGenerator;
import org.tmatesoft.svn.core.wc.ISVNDiffGenerator;
import org.tmatesoft.svn.core.internal.wc2.ng.SvnNewDiffGenerator;
import org.tmatesoft.svn.core.wc.SVNClientManager;
import org.tmatesoft.svn.core.wc.SVNDiffClient;
import org.tmatesoft.svn.core.wc.SVNRevision;
import sonia.scm.repository.InternalRepositoryException;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.SvnUtil;
import sonia.scm.repository.api.DiffFormat;
import sonia.scm.util.Util;
@@ -61,8 +57,7 @@ import java.io.OutputStream;
*
* @author Sebastian Sdorra
*/
public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
{
public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand {
/**
* the logger for SvnDiffCommand
@@ -70,46 +65,26 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
private static final Logger logger =
LoggerFactory.getLogger(SvnDiffCommand.class);
public SvnDiffCommand(SvnContext context, Repository repository)
{
public SvnDiffCommand(SvnContext context, Repository repository) {
super(context, repository);
}
@Override
public void getDiffResult(DiffCommandRequest request, OutputStream output) throws RevisionNotFoundException {
if (logger.isDebugEnabled())
{
public void getDiffResult(DiffCommandRequest request, OutputStream output) {
logger.debug("create diff for {}", request);
}
Preconditions.checkNotNull(request, "request is required");
Preconditions.checkNotNull(output, "outputstream is required");
String path = request.getPath();
SVNClientManager clientManager = null;
try
{
try {
SVNURL svnurl = context.createUrl();
if (Util.isNotEmpty(path))
{
if (Util.isNotEmpty(path)) {
svnurl = svnurl.appendPath(path, true);
}
clientManager = SVNClientManager.newInstance();
SVNDiffClient diffClient = clientManager.getDiffClient();
ISVNDiffGenerator diffGenerator = diffClient.getDiffGenerator();
if (diffGenerator == null)
{
diffGenerator = new DefaultSVNDiffGenerator();
}
diffGenerator.setDiffAdded(true);
diffGenerator.setDiffDeleted(true);
diffClient.setDiffGenerator(diffGenerator);
diffClient.setDiffGenerator(new SvnNewDiffGenerator(new SCMSvnDiffGenerator()));
long currentRev = SvnUtil.getRevisionNumber(request.getRevision());
@@ -118,13 +93,9 @@ public class SvnDiffCommand extends AbstractSvnCommand implements DiffCommand
diffClient.doDiff(svnurl, SVNRevision.HEAD,
SVNRevision.create(currentRev - 1), SVNRevision.create(currentRev),
SVNDepth.INFINITY, false, output);
}
catch (SVNException ex)
{
} catch (SVNException ex) {
throw new InternalRepositoryException("could not create diff", ex);
}
finally
{
} finally {
SvnUtil.dispose(clientManager);
}
}

View File

@@ -12,7 +12,7 @@ import AvatarImage from "./AvatarImage";
import classNames from "classnames";
import ChangesetId from "./ChangesetId";
import type {Tag} from "@scm-manager/ui-types";
import DiffView from "../../containers/DiffView";
import ScmDiff from "../../containers/ScmDiff";
const styles = {
spacing: {
@@ -29,27 +29,28 @@ type Props = {
class ChangesetDetails extends React.Component<Props> {
render() {
const { changeset, repository, classes } = this.props;
const {changeset, repository, classes} = this.props;
const description = parseDescription(changeset.description);
const id = (
<ChangesetId repository={repository} changeset={changeset} link={false} />
<ChangesetId repository={repository} changeset={changeset} link={false}/>
);
const date = <DateFromNow date={changeset.date} />;
const date = <DateFromNow date={changeset.date}/>;
return (
<div>
<div className="content">
<h4>{description.title}</h4>
<article className="media">
<AvatarWrapper>
<p className={classNames("image", "is-64x64", classes.spacing)}>
<AvatarImage changeset={changeset} />
<AvatarImage changeset={changeset}/>
</p>
</AvatarWrapper>
<div className="media-content">
<p>
<ChangesetAuthor changeset={changeset} />
<ChangesetAuthor changeset={changeset}/>
</p>
<p>
<Interpolate
@@ -66,19 +67,21 @@ class ChangesetDetails extends React.Component<Props> {
return (
<span key={key}>
{item}
<br />
<br/>
</span>
);
})}
</p>
<DiffView changeset={changeset} sideBySide={false} />
</div>
<div>
<ScmDiff changeset={changeset} sideBySide={false}/>
</div>
</div>
);
}
getTags = () => {
const { changeset } = this.props;
const {changeset} = this.props;
return changeset._embedded.tags || [];
};
@@ -88,7 +91,7 @@ class ChangesetDetails extends React.Component<Props> {
return (
<div className="level-item">
{tags.map((tag: Tag) => {
return <ChangesetTag key={tag.name} tag={tag} />;
return <ChangesetTag key={tag.name} tag={tag}/>;
})}
</div>
);

View File

@@ -5,17 +5,18 @@ export type Description = {
};
export function parseDescription(description: string): Description {
const lineBreak = description.indexOf("\n");
let title;
let title = "";
let message = "";
if (description != null) {
const lineBreak = description.indexOf("\n");
if (lineBreak > 0) {
title = description.substring(0, lineBreak);
message = description.substring(lineBreak + 1);
} else {
title = description;
}
}
return {
title,

View File

@@ -23,7 +23,7 @@ class ScmDiff extends React.Component<Props, State> {
componentDidMount() {
const { changeset } = this.props;
const url = changeset._links.diff.href;
const url = changeset._links.diff.href+"?format=GIT";
apiClient
.get(url)
.then(response => response.text())
@@ -35,7 +35,7 @@ class ScmDiff extends React.Component<Props, State> {
const options = {
inputFormat: "diff",
outputFormat: this.props.sideBySide ? "side-by-side" : "line-by-line",
showFiles: true,
showFiles: false,
matching: "lines"
};

View File

@@ -5,16 +5,19 @@ import com.webcohesion.enunciate.metadata.rs.StatusCodes;
import sonia.scm.NotFoundException;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.RevisionNotFoundException;
import sonia.scm.repository.api.DiffFormat;
import sonia.scm.repository.api.RepositoryService;
import sonia.scm.repository.api.RepositoryServiceFactory;
import sonia.scm.util.HttpUtil;
import sonia.scm.web.VndMediaType;
import javax.inject.Inject;
import javax.ws.rs.DefaultValue;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import javax.ws.rs.PathParam;
import javax.ws.rs.Produces;
import javax.ws.rs.QueryParam;
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.core.StreamingOutput;
@@ -50,14 +53,15 @@ public class DiffRootResource {
@ResponseCode(code = 404, condition = "not found, no revision with the specified param for the repository available or repository not found"),
@ResponseCode(code = 500, condition = "internal server error")
})
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision){
public Response get(@PathParam("namespace") String namespace, @PathParam("name") String name, @PathParam("revision") String revision , @DefaultValue("NATIVE") @QueryParam("format") String format ){
HttpUtil.checkForCRLFInjection(revision);
DiffFormat diffFormat = DiffFormat.valueOf(format);
try (RepositoryService repositoryService = serviceFactory.create(new NamespaceAndName(namespace, name))) {
StreamingOutput responseEntry = output -> {
try {
repositoryService.getDiffCommand()
.setRevision(revision)
// .setFormat(DiffFormat.GIT) // TODO: Configure this at request time. Maybe as a query param?
.setFormat(diffFormat)
.retriveContent(output);
} catch (RevisionNotFoundException e) {
throw new WebApplicationException(Response.Status.NOT_FOUND);

View File

@@ -18,6 +18,7 @@ import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.junit.MockitoJUnitRunner;
import sonia.scm.api.rest.AuthorizationExceptionMapper;
import sonia.scm.api.rest.IllegalArgumentExceptionMapper;
import sonia.scm.repository.NamespaceAndName;
import sonia.scm.repository.Repository;
import sonia.scm.repository.RepositoryNotFoundException;
@@ -72,6 +73,7 @@ public class DiffResourceTest extends RepositoryTestBase {
dispatcher.getProviderFactory().registerProvider(NotFoundExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(AuthorizationExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(CRLFInjectionExceptionMapper.class);
dispatcher.getProviderFactory().registerProvider(IllegalArgumentExceptionMapper.class);
when(service.getDiffCommand()).thenReturn(diffCommandBuilder);
subjectThreadState.bind();
ThreadContext.bind(subject);
@@ -86,19 +88,17 @@ public class DiffResourceTest extends RepositoryTestBase {
@Test
public void shouldGetDiffs() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retriveContent(any())).thenReturn(diffCommandBuilder);
MockHttpRequest request = MockHttpRequest
.get(DIFF_URL + "revision")
.accept(VndMediaType.DIFF);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(200, response.getStatus());
log.info("Response :{}", response.getContentAsString());
assertThat(response.getStatus())
.isEqualTo(200);
assertThat(response.getContentAsString())
.isNotNull();
String expectedHeader = "Content-Disposition";
String expectedValue = "attachment; filename=\"repo-revision.diff\"; filename*=utf-8''repo-revision.diff";
assertThat(response.getOutputHeaders().containsKey(expectedHeader)).isTrue();
@@ -120,6 +120,7 @@ public class DiffResourceTest extends RepositoryTestBase {
@Test
public void shouldGet404OnMissingRevision() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retriveContent(any())).thenThrow(RevisionNotFoundException.class);
MockHttpRequest request = MockHttpRequest
@@ -133,6 +134,7 @@ public class DiffResourceTest extends RepositoryTestBase {
@Test
public void shouldGet400OnCrlfInjection() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retriveContent(any())).thenThrow(RevisionNotFoundException.class);
MockHttpRequest request = MockHttpRequest
@@ -143,6 +145,17 @@ public class DiffResourceTest extends RepositoryTestBase {
assertEquals(400, response.getStatus());
}
@Test
public void shouldGet400OnUnknownFormat() throws Exception {
when(diffCommandBuilder.setRevision(anyString())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.setFormat(any())).thenReturn(diffCommandBuilder);
when(diffCommandBuilder.retriveContent(any())).thenThrow(RevisionNotFoundException.class);
MockHttpRequest request = MockHttpRequest
.get(DIFF_URL + "revision?format=Unknown")
.accept(VndMediaType.DIFF);
MockHttpResponse response = new MockHttpResponse();
dispatcher.invoke(request, response);
assertEquals(400, response.getStatus());
}
}