Skip to content

Commit

Permalink
Merge pull request #80 from daniel-beck/JENKINS-18574
Browse files Browse the repository at this point in the history
[RFC] FIX JENKINS-18574/JENKINS-16045
  • Loading branch information
stephenc committed May 7, 2014
2 parents 6e4e391 + 8a1e46f commit 5a2683e
Show file tree
Hide file tree
Showing 5 changed files with 148 additions and 19 deletions.
12 changes: 11 additions & 1 deletion src/main/java/hudson/scm/DirAwareSVNXMLLogHandler.java
Expand Up @@ -37,13 +37,16 @@
public class DirAwareSVNXMLLogHandler extends SVNXMLLogHandler implements ISVNLogEntryHandler {

public static final String KIND_ATTR = "kind";
public static final String REL_PATH_ATTR = "localPath";

private boolean myIsOmitLogMessage;

private LinkedList<MergeFrame> myMergeStack;

private SVNLogFilter filter = new NullSVNLogFilter();

private SubversionChangeLogBuilder.PathContext context;

public DirAwareSVNXMLLogHandler(ContentHandler contentHandler, SVNLogFilter filter) {
super(contentHandler);
this.filter = filter;
Expand All @@ -56,7 +59,7 @@ public DirAwareSVNXMLLogHandler(ContentHandler contentHandler, ISVNDebugLog log)
public DirAwareSVNXMLLogHandler(ContentHandler contentHandler) {
super(contentHandler);
}

/**
* Sets whether log messages must be omitted or not.
*
Expand Down Expand Up @@ -105,6 +108,8 @@ protected void sendToHandler(SVNLogEntry logEntry) throws SAXException {
String key = paths.next();
SVNLogEntryPath path = (SVNLogEntryPath) logEntry.getChangedPaths().get(key);
addAttribute(ACTION_ATTR, path.getType() + "");
String relativeWorkspacePath = context.moduleWorkspacePath + path.getPath().substring(context.url.length() - context.repoUrl.length());
addAttribute(REL_PATH_ATTR, relativeWorkspacePath);
if (path.getCopyPath() != null) {
addAttribute(COPYFROM_PATH_ATTR, path.getCopyPath());
addAttribute(COPYFROM_REV_ATTR, path.getCopyRevision() + "");
Expand Down Expand Up @@ -147,6 +152,11 @@ protected void sendToHandler(SVNLogEntry logEntry) throws SAXException {
closeTag(LOGENTRY_TAG);
}
}

void setContext(SubversionChangeLogBuilder.PathContext context) {
this.context = context;
}

private class MergeFrame {
private long myNumberOfChildrenRemaining;
}
Expand Down
53 changes: 38 additions & 15 deletions src/main/java/hudson/scm/SubversionChangeLogBuilder.java
Expand Up @@ -25,14 +25,14 @@

import hudson.EnvVars;
import hudson.model.AbstractBuild;
import hudson.model.AbstractProject;
import hudson.model.BuildListener;
import hudson.model.Hudson;
import hudson.scm.SubversionSCM.ModuleLocation;
import hudson.FilePath;
import hudson.util.IOException2;
import hudson.remoting.VirtualChannel;
import hudson.FilePath.FileCallable;
import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.tmatesoft.svn.core.SVNException;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.ISVNLogEntryHandler;
Expand All @@ -43,7 +43,6 @@
import org.tmatesoft.svn.core.wc.SVNRevision;
import org.tmatesoft.svn.core.wc.SVNWCClient;
import org.tmatesoft.svn.core.wc.SVNInfo;
import org.tmatesoft.svn.core.wc.xml.SVNXMLLogHandler;
import org.xml.sax.helpers.LocatorImpl;

import javax.xml.transform.Result;
Expand All @@ -53,6 +52,7 @@
import java.io.IOException;
import java.io.PrintStream;
import java.io.File;
import java.io.Serializable;
import java.util.Map;
import java.util.Collection;

Expand Down Expand Up @@ -94,7 +94,7 @@ public SubversionChangeLogBuilder(AbstractBuild<?,?> build, EnvVars env, BuildLi
this.build = build;
this.env = env;
}

public boolean run(Collection<SubversionSCM.External> externals, Result changeLog) throws IOException, InterruptedException {
boolean changelogFileCreated = false;

Expand All @@ -113,7 +113,9 @@ public boolean run(Collection<SubversionSCM.External> externals, Result changeLo
final SVNClientManager manager = SubversionSCM.createClientManager(authProvider).getCore();
try {
SVNLogClient svnlc = manager.getLogClient();
changelogFileCreated |= buildModule(l.getURL(), svnlc, logHandler);
PathContext context = getUrlForPath(build.getWorkspace().child(l.getLocalDir()), authProvider);
context.moduleWorkspacePath = l.getLocalDir();
changelogFileCreated |= buildModule(context, svnlc, logHandler);
} finally {
manager.dispose();
}
Expand All @@ -125,8 +127,9 @@ public boolean run(Collection<SubversionSCM.External> externals, Result changeLo
try {
SVNLogClient svnlc = manager.getLogClient();
for(SubversionSCM.External ext : externals) {
changelogFileCreated |= buildModule(
getUrlForPath(build.getWorkspace().child(ext.path), authProvider), svnlc, logHandler);
PathContext context = getUrlForPath(build.getWorkspace().child(ext.path), authProvider);
context.moduleWorkspacePath = ext.path;
changelogFileCreated |= buildModule(context, svnlc, logHandler);
}
} finally {
manager.dispose();
Expand All @@ -139,11 +142,12 @@ public boolean run(Collection<SubversionSCM.External> externals, Result changeLo
return changelogFileCreated;
}

private String getUrlForPath(FilePath path, ISVNAuthenticationProvider authProvider) throws IOException, InterruptedException {
return path.act(new GetUrlForPath(authProvider));
private PathContext getUrlForPath(FilePath path, ISVNAuthenticationProvider authProvider) throws IOException, InterruptedException {
return path.act(new GetContextForPath(authProvider));
}

private boolean buildModule(String url, SVNLogClient svnlc, SVNXMLLogHandler logHandler) throws IOException2 {
private boolean buildModule(PathContext context, SVNLogClient svnlc, DirAwareSVNXMLLogHandler logHandler) throws IOException2 {
String url = context.url;
PrintStream logger = listener.getLogger();
Long prevRev = previousRevisions.get(url);
if(prevRev==null) {
Expand All @@ -166,7 +170,8 @@ private boolean buildModule(String url, SVNLogClient svnlc, SVNXMLLogHandler log
thisRev = new Long(prevRev.longValue());
prevRev = new Long(temp);
}


logHandler.setContext(context);
try {
if(debug)
listener.getLogger().printf("Computing changelog of %1s from %2s to %3s\n",
Expand Down Expand Up @@ -224,22 +229,24 @@ private static TransformerHandler createTransformerHandler() {
DUMMY_LOCATOR.setColumnNumber(-1);
}

private static class GetUrlForPath implements FileCallable<String> {
private static class GetContextForPath implements FileCallable<PathContext> {
private final ISVNAuthenticationProvider authProvider;

public GetUrlForPath(ISVNAuthenticationProvider authProvider) {
public GetContextForPath(ISVNAuthenticationProvider authProvider) {
this.authProvider = authProvider;
}

public String invoke(File p, VirtualChannel channel) throws IOException {
public PathContext invoke(File p, VirtualChannel channel) throws IOException {
final SvnClientManager manager = SubversionSCM.createClientManager(authProvider);
try {
final SVNWCClient svnwc = manager.getWCClient();

SVNInfo info;
try {
info = svnwc.doInfo(p, SVNRevision.WORKING);
return info.getURL().toDecodedString();
String url = info.getURL().toDecodedString();
String repoRoot = info.getRepositoryRootURL().toDecodedString();
return new PathContext(url, repoRoot, null);
} catch (SVNException e) {
e.printStackTrace();
return null;
Expand All @@ -251,4 +258,20 @@ public String invoke(File p, VirtualChannel channel) throws IOException {

private static final long serialVersionUID = 1L;
}

/**
* This class encapsulates context information for the paths in the change log.
*/
@Restricted(NoExternalUse.class)
public static class PathContext implements Serializable {
private PathContext(String url, String repoUrl, String moduleWorkspacePath) {
this.url = url;
this.moduleWorkspacePath = moduleWorkspacePath;
this.repoUrl = repoUrl;
}
public String url; // full URL to file
public String repoUrl; // full URL to module root
public String moduleWorkspacePath; // path to module root relative from workspace root
private static final long serialVersionUID = 1L;
}
}
25 changes: 24 additions & 1 deletion src/main/java/hudson/scm/SubversionChangeLogSet.java
Expand Up @@ -42,6 +42,8 @@
import java.util.Map;
import java.util.Set;

import org.kohsuke.accmod.Restricted;
import org.kohsuke.accmod.restrictions.NoExternalUse;
import org.kohsuke.stapler.export.Exported;
import org.kohsuke.stapler.export.ExportedBean;
import org.tmatesoft.svn.core.internal.util.SVNDate;
Expand Down Expand Up @@ -368,7 +370,16 @@ public int hashCode() {
public static class Path implements AffectedFile {
private LogEntry entry;
private char action;

/**
* full path to file within SVN repository, e.g. /trunk/project/foo/bar.txt
*/
private String value;

/**
* Path to file within workspace, e.g. stuff/foo/bar.txt
*/
private String localPath;
private String kind;

/**
Expand Down Expand Up @@ -399,9 +410,21 @@ public String getValue() {

/**
* Inherited from AffectedFile
*
* Since 2.TODO this no longer returns the path relative to repository root,
* but the path relative to the workspace root. Use getValue() instead.
*/
public String getPath() {
return getValue();
if (localPath == null) {
// compatibility to older versions that did not store this path
return value;
}
return localPath;
}

@Restricted(NoExternalUse.class)
public void setLocalPath(String path) {
this.localPath = path;
}

public void setValue(String value) {
Expand Down
58 changes: 56 additions & 2 deletions src/test/java/hudson/scm/SubversionChangeLogParserTest.java
Expand Up @@ -26,16 +26,46 @@ public class SubversionChangeLogParserTest extends AbstractSubversionTest {
@Test
@Bug(10324)
public void testPathsSortedAlphabetically() throws URISyntaxException, IOException, SAXException {
givenAChangelogFileWithUnsortedPaths();
givenAChangelogFileWithUnsortedPathsInLegacyFormat();
whenChangelogFileIsParsed();
thenAffectedPathsMustBeSortedAlphabetically();
}

@Test
@Bug(18574)
public void testPathsEqualToValues() throws URISyntaxException, IOException, SAXException {
givenAChangelogFileWithUnsortedPathsInLegacyFormat();
whenChangelogFileIsParsed();
thenPathsMustBeEqualToValues();
}

private void givenAChangelogFileWithUnsortedPaths() throws URISyntaxException {
@Test
@Bug(18574)
public void testValueIsRepoPath() throws URISyntaxException, IOException, SAXException {
givenAChangelogFileWithUnsortedPathsInLegacyFormat();
whenChangelogFileIsParsed();
thenValuesMustStartWithSlash();
}

@Test
@Bug(18574)
public void testNewChangelogFileForDifferentPathAndValue() throws URISyntaxException, IOException, SAXException {
givenAChangelogFileWithRelativePathAttributes();
whenChangelogFileIsParsed();
thenValuesMustStartWithSlash();
andPathsMustNotStartWithSlash();
}

private void givenAChangelogFileWithUnsortedPathsInLegacyFormat() throws URISyntaxException {
URL url = SubversionChangeLogParserTest.class.getResource("changelog_unsorted.xml");
this.changelogFile = new File(url.toURI().getSchemeSpecificPart());
}

private void givenAChangelogFileWithRelativePathAttributes() throws URISyntaxException {
URL url = SubversionChangeLogParserTest.class.getResource("changelog_relativepath.xml");
this.changelogFile = new File(url.toURI().getSchemeSpecificPart());
}

private void whenChangelogFileIsParsed() throws IOException, SAXException {
this.changeLogSet = new SubversionChangeLogParser(false).parse(null, this.changelogFile);
}
Expand All @@ -49,4 +79,28 @@ private void thenAffectedPathsMustBeSortedAlphabetically() {
}
}
}

private void thenPathsMustBeEqualToValues() {
for(LogEntry entry : changeLogSet.getLogs()) {
for(Path path : entry.getPaths()) {
Assert.assertEquals(path.getPath(), path.getValue());
}
}
}

private void thenValuesMustStartWithSlash() {
for(LogEntry entry : changeLogSet.getLogs()) {
for(Path path : entry.getPaths()) {
Assert.assertTrue(path.getValue().startsWith("/"));
}
}
}

private void andPathsMustNotStartWithSlash() {
for(LogEntry entry : changeLogSet.getLogs()) {
for(Path path : entry.getPaths()) {
Assert.assertFalse(path.getPath().startsWith("/"));
}
}
}
}
19 changes: 19 additions & 0 deletions src/test/resources/hudson/scm/changelog_relativepath.xml
@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="UTF-8"?>
<log>
<logentry revision="39">
<author>user</author>
<date>2014-05-03T12:45:33.664887Z</date>
<paths>
<path action="M" kind="file" localPath="foo/bar/wc1-pX/trunk/foo">/projectX/trunk/foo</path>
</paths>
<msg>changed foo</msg>
</logentry>
<logentry revision="39">
<author>user</author>
<date>2014-05-03T12:45:33.664887Z</date>
<paths>
<path action="M" kind="file" localPath="wc2-t/baz/foo">/projectX/trunk/foo</path>
</paths>
<msg>changed foo</msg>
</logentry>
</log>

0 comments on commit 5a2683e

Please sign in to comment.