aboutsummaryrefslogtreecommitdiffstats
path: root/plugins
diff options
context:
space:
mode:
authorJulien HENRY <julien.henry@sonarsource.com>2014-11-06 09:42:22 +0100
committerJulien HENRY <julien.henry@sonarsource.com>2014-11-06 12:00:07 +0100
commit981c3bd653a209ae76f439a870e975b70082a3e4 (patch)
tree3d47e0c67a832777557626160dfb3eb9b1b9edb7 /plugins
parent308b05abd62d10769c377b338e1500fdd71630fd (diff)
downloadsonarqube-981c3bd653a209ae76f439a870e975b70082a3e4.tar.gz
sonarqube-981c3bd653a209ae76f439a870e975b70082a3e4.zip
SONAR-5843 Use the original SVN author on merges
Diffstat (limited to 'plugins')
-rw-r--r--plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java3
-rw-r--r--plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java53
-rw-r--r--plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnConfiguration.java17
-rw-r--r--plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java42
-rw-r--r--plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnPluginTest.java2
-rw-r--r--plugins/sonar-svn-plugin/src/test/resources/blame-with-merge-history.xml54
6 files changed, 157 insertions, 14 deletions
diff --git a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java
index 2e1038f162b..f088967589a 100644
--- a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java
+++ b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameCommand.java
@@ -128,6 +128,9 @@ public class SvnBlameCommand extends BlameCommand {
cl.setDirectory(baseDir);
cl.addArgument("blame");
cl.addArgument("--xml");
+ if (configuration.useMergeHistory()) {
+ cl.addArgument("--use-merge-history");
+ }
cl.addArgument("--non-interactive");
cl.addArgument("-x");
cl.addArgument("-w");
diff --git a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java
index 8224fab347d..3bf42c756b3 100644
--- a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java
+++ b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnBlameConsumer.java
@@ -68,6 +68,9 @@ public class SvnBlameConsumer implements StreamConsumer {
private static final Pattern DATE_PATTERN = Pattern.compile("<date>(.*)T(.*)\\.(.*)Z</date>");
+ private boolean insideCommitSection = false;
+ private boolean insideMergedSection = false;
+
private SimpleDateFormat dateFormat;
private List<BlameLine> lines = new ArrayList<BlameLine>();
@@ -82,31 +85,61 @@ public class SvnBlameConsumer implements StreamConsumer {
private int lineNumber = 0;
- private String revision;
-
+ private String committerRevision;
+ private String committer;
+ private Date committerDate;
+ private String authorRevision;
private String author;
+ private Date authorDate;
@Override
public void consumeLine(String line) {
Matcher matcher;
if ((matcher = LINE_PATTERN.matcher(line)).find()) {
- if (lineNumber != 0) {
- throw new IllegalStateException("Unable to blame file " + filename + ". No blame info at line " + lineNumber + ". Is file commited?");
- }
String lineNumberStr = matcher.group(1);
lineNumber = Integer.parseInt(lineNumberStr);
+ insideCommitSection = false;
+ insideMergedSection = false;
+ } else if (line.contains("<commit") && !insideMergedSection) {
+ insideCommitSection = true;
+ } else if (line.contains("<merged")) {
+ insideMergedSection = true;
+ insideCommitSection = false;
} else if ((matcher = REVISION_PATTERN.matcher(line)).find()) {
- revision = matcher.group(1);
+ if (insideCommitSection) {
+ committerRevision = matcher.group(1);
+ } else if (insideMergedSection) {
+ authorRevision = matcher.group(1);
+ }
} else if ((matcher = AUTHOR_PATTERN.matcher(line)).find()) {
- author = matcher.group(1);
+ if (insideCommitSection) {
+ committer = matcher.group(1);
+ } else if (insideMergedSection) {
+ author = matcher.group(1);
+ }
} else if ((matcher = DATE_PATTERN.matcher(line)).find()) {
String date = matcher.group(1);
String time = matcher.group(2);
Date dateTime = parseDateTime(date + " " + time);
- lines.add(new BlameLine().revision(revision).author(author).date(dateTime));
- lineNumber = 0;
- revision = null;
+ if (insideCommitSection) {
+ committerDate = dateTime;
+ } else if (insideMergedSection) {
+ authorDate = dateTime;
+ }
+ } else if (line.contains("</entry>")) {
+ if (authorRevision != null) {
+ lines.add(new BlameLine().revision(authorRevision).author(author).date(authorDate));
+ } else if (committerRevision != null) {
+ lines.add(new BlameLine().revision(committerRevision).author(committer).date(committerDate));
+ } else {
+ throw new IllegalStateException("Unable to blame file " + filename + ". No blame info at line " + lineNumber + ". Is file commited?");
+ }
+ insideCommitSection = false;
+ insideMergedSection = false;
author = null;
+ committer = null;
+ committerRevision = null;
+ authorRevision = null;
}
}
diff --git a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnConfiguration.java b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnConfiguration.java
index 16942c8a0f8..8c7db3efb6d 100644
--- a/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnConfiguration.java
+++ b/plugins/sonar-svn-plugin/src/main/java/org/sonar/plugins/scm/svn/SvnConfiguration.java
@@ -40,6 +40,7 @@ public class SvnConfiguration implements BatchComponent {
public static final String PASSWORD_PROP_KEY = "sonar.svn.password.secured";
public static final String CONFIG_DIR_PROP_KEY = "sonar.svn.config_dir";
public static final String TRUST_SERVER_PROP_KEY = "sonar.svn.trust_server_cert";
+ public static final String USE_MERGE_HISTORY_KEY = "sonar.svn.use_merge_history";
private final Settings settings;
public SvnConfiguration(Settings settings) {
@@ -84,6 +85,18 @@ public class SvnConfiguration implements BatchComponent {
.category(CoreProperties.CATEGORY_SCM)
.subCategory(CATEGORY_SVN)
.index(3)
+ .build(),
+ PropertyDefinition
+ .builder(USE_MERGE_HISTORY_KEY)
+ .name("Use merge history for blame")
+ .description(
+ "Use merge history (--use-merge-history) to get real author of a modification instead of commiter of the merge. May not be supported by your SVN server/client.")
+ .type(PropertyType.BOOLEAN)
+ .defaultValue("false")
+ .onQualifiers(Qualifiers.PROJECT)
+ .category(CoreProperties.CATEGORY_SCM)
+ .subCategory(CATEGORY_SVN)
+ .index(4)
.build());
}
@@ -106,4 +119,8 @@ public class SvnConfiguration implements BatchComponent {
return settings.getBoolean(TRUST_SERVER_PROP_KEY);
}
+ public boolean useMergeHistory() {
+ return settings.getBoolean(USE_MERGE_HISTORY_KEY);
+ }
+
}
diff --git a/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java b/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java
index 6bc94b51ddd..f306fe3fc60 100644
--- a/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java
+++ b/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnBlameCommandTest.java
@@ -141,6 +141,39 @@ public class SvnBlameCommandTest {
}
@Test
+ public void testParsingOfOutputWithMergeHistory() throws IOException {
+ File source = new File(baseDir, "src/foo.xoo");
+ FileUtils.write(source, "sample content");
+ DefaultInputFile inputFile = new DefaultInputFile("foo", "src/foo.xoo").setAbsolutePath(new File(baseDir, "src/foo.xoo").getAbsolutePath());
+ fs.add(inputFile);
+
+ BlameOutput result = mock(BlameOutput.class);
+ CommandExecutor commandExecutor = mock(CommandExecutor.class);
+
+ when(commandExecutor.execute(any(Command.class), any(StreamConsumer.class), any(StreamConsumer.class), anyLong())).thenAnswer(new Answer<Integer>() {
+
+ @Override
+ public Integer answer(InvocationOnMock invocation) throws Throwable {
+ StreamConsumer outConsumer = (StreamConsumer) invocation.getArguments()[1];
+ List<String> lines = IOUtils.readLines(getClass().getResourceAsStream("/blame-with-merge-history.xml"), "UTF-8");
+ for (String line : lines) {
+ outConsumer.consumeLine(line);
+ }
+ return 0;
+ }
+ });
+
+ when(input.filesToBlame()).thenReturn(Arrays.<InputFile>asList(inputFile));
+
+ new SvnBlameCommand(commandExecutor, mock(SvnConfiguration.class)).blame(input, result);
+ verify(result).blameResult(inputFile,
+ Arrays.asList(
+ new BlameLine().date(DateUtils.parseDateTime("2012-07-19T11:44:57+0200")).revision("9490").author("dgageot"),
+ new BlameLine().date(DateUtils.parseDateTime("2009-04-18T10:29:59+0000")).revision("9491").author("simon.brandhof"),
+ new BlameLine().date(DateUtils.parseDateTime("2009-08-31T22:32:17+0000")).revision("10558").author("david")));
+ }
+
+ @Test
public void shouldFailIfFileContainsLocalModification() throws IOException {
File source = new File(baseDir, "src/foo.xoo");
FileUtils.write(source, "sample content");
@@ -215,10 +248,13 @@ public class SvnBlameCommandTest {
settings.setProperty(SvnConfiguration.CONFIG_DIR_PROP_KEY, "/home/julien/.svn");
settings.setProperty(SvnConfiguration.TRUST_SERVER_PROP_KEY, "true");
+ settings.setProperty(SvnConfiguration.USE_MERGE_HISTORY_KEY, "true");
commandLine = svnBlameCommand.createCommandLine(baseDir, "src/main/java/Foo.java");
assertThat(commandLine.toCommandLine())
- .isEqualTo("svn blame --xml --non-interactive -x -w --config-dir /home/julien/.svn --username myUser --password myPass --trust-server-cert src/main/java/Foo.java");
- assertThat(commandLine.toString()).isEqualTo(
- "svn blame --xml --non-interactive -x -w --config-dir /home/julien/.svn --username ******** --password ******** --trust-server-cert src/main/java/Foo.java");
+ .isEqualTo(
+ "svn blame --xml --use-merge-history --non-interactive -x -w --config-dir /home/julien/.svn --username myUser --password myPass --trust-server-cert src/main/java/Foo.java");
+ assertThat(commandLine.toString())
+ .isEqualTo(
+ "svn blame --xml --use-merge-history --non-interactive -x -w --config-dir /home/julien/.svn --username ******** --password ******** --trust-server-cert src/main/java/Foo.java");
}
}
diff --git a/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnPluginTest.java b/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnPluginTest.java
index 7c8ea23a91a..745067f838e 100644
--- a/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnPluginTest.java
+++ b/plugins/sonar-svn-plugin/src/test/java/org/sonar/plugins/scm/svn/SvnPluginTest.java
@@ -27,6 +27,6 @@ public class SvnPluginTest {
@Test
public void getExtensions() {
- assertThat(new SvnPlugin().getExtensions()).hasSize(7);
+ assertThat(new SvnPlugin().getExtensions()).hasSize(8);
}
}
diff --git a/plugins/sonar-svn-plugin/src/test/resources/blame-with-merge-history.xml b/plugins/sonar-svn-plugin/src/test/resources/blame-with-merge-history.xml
new file mode 100644
index 00000000000..efe87073a7a
--- /dev/null
+++ b/plugins/sonar-svn-plugin/src/test/resources/blame-with-merge-history.xml
@@ -0,0 +1,54 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<blame>
+<target
+ path="pom.xml">
+<entry
+ line-number="1">
+<commit
+ revision="9491">
+<author>automatic-merge</author>
+<date>2009-04-18T10:29:59.077093Z</date>
+</commit>
+<merged
+ path="/dummy-svn/pom.xml">
+<commit
+ revision="9490">
+<author>dgageot</author>
+<date>2012-07-19T09:44:57.393222Z</date>
+</commit>
+</merged>
+</entry>
+<entry
+ line-number="2">
+<commit
+ revision="9491">
+<author>simon.brandhof</author>
+<date>2009-04-18T10:29:59.077093Z</date>
+</commit>
+<merged
+ path="/dummy-svn/pom.xml">
+<commit
+ revision="9491">
+<author>simon.brandhof</author>
+<date>2009-04-18T10:29:59.077093Z</date>
+</commit>
+</merged>
+</entry>
+<entry
+ line-number="3">
+<commit
+ revision="10558">
+<author>david</author>
+<date>2009-08-31T22:32:17.361675Z</date>
+</commit>
+<merged
+ path="/dummy-svn/pom.xml">
+<commit
+ revision="10558">
+<author>david</author>
+<date>2009-08-31T22:32:17.361675Z</date>
+</commit>
+</merged>
+</entry>
+</target>
+</blame>