From 401c1a17d0ddf23549d8f1ce9f0bc644543b1c4c Mon Sep 17 00:00:00 2001
From: jerome.lacoste <jerome.lacoste@gmail.com>
Date: Tue, 27 Oct 2009 10:29:43 +0100
Subject: [PATCH] Add support for svn tag diffs. Redo the original MCHANGELOG-79 patch by forking the SvnInfoCommand class, as it didn't provide tag support, and the old patch was broken due to changes in plexus-utils command line. No unit tests...

---
 .../maven/plugin/changelog/ChangeLogReport.java    |  126 +++++++++++--
 .../changelog/SvnTrunkChangeLogCommand.java        |  138 +++++++++++++
 .../command/info/SvnInfoCommandExpanded.java       |  202 ++++++++++++++++++++
 3 files changed, 448 insertions(+), 18 deletions(-)
 create mode 100644 src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/changelog/SvnTrunkChangeLogCommand.java
 create mode 100644 src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/info/SvnInfoCommandExpanded.java

diff --git a/src/main/java/org/apache/maven/plugin/changelog/ChangeLogReport.java b/src/main/java/org/apache/maven/plugin/changelog/ChangeLogReport.java
index 319bd6d..932db0a 100644
--- a/src/main/java/org/apache/maven/plugin/changelog/ChangeLogReport.java
+++ b/src/main/java/org/apache/maven/plugin/changelog/ChangeLogReport.java
@@ -59,11 +59,19 @@ import org.apache.maven.scm.ScmResult;
 import org.apache.maven.scm.ScmRevision;
 import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
 import org.apache.maven.scm.command.changelog.ChangeLogSet;
+import org.apache.maven.scm.log.DefaultLog;
 import org.apache.maven.scm.manager.ScmManager;
 import org.apache.maven.scm.provider.ScmProvider;
 import org.apache.maven.scm.provider.ScmProviderRepository;
 import org.apache.maven.scm.provider.ScmProviderRepositoryWithHost;
+import org.apache.maven.scm.provider.svn.AbstractSvnScmProvider;
+import org.apache.maven.scm.provider.svn.SvnTagBranchUtils;
+import org.apache.maven.scm.provider.svn.command.info.SvnInfoItem;
+import org.apache.maven.scm.provider.svn.command.info.SvnInfoScmResult;
 import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
+import org.apache.maven.scm.provider.svn.svnexe.command.changelog.SvnTrunkChangeLogCommand;
+import org.apache.maven.scm.provider.svn.svnexe.command.info.SvnInfoCommand;
+import org.apache.maven.scm.provider.svn.svnexe.command.info.SvnInfoCommandExpanded;
 import org.apache.maven.scm.repository.ScmRepository;
 import org.apache.maven.settings.Server;
 import org.apache.maven.settings.Settings;
@@ -214,6 +222,13 @@ public class ChangeLogReport
     private String tagBase;
 
     /**
+     * The url of trunk base directory (used by svn protocol).
+     *
+     * @parameter expression="${branchBase}"  default-value="trunk"
+     */
+    private String branchBase;
+
+    /**
      * The URL to view the scm. Basis for external links from the generated report.
      *
      * @parameter expression="${project.scm.url}"
@@ -481,40 +496,115 @@ public class ChangeLogReport
             }
             else if ( "tag".equals( type ) )
             {
-                if ( repository.getProvider().equals( "svn" ) )
-                {
-                    throw new MavenReportException( "The type '" + type + "' isn't supported for svn." );
-                }
-
                 Iterator tagsIter = tags.iterator();
 
                 String startTag = (String) tagsIter.next();
                 String endTag = null;
 
-                if ( tagsIter.hasNext() )
+
+                if ( repository.getProvider().equals( "svn" ) )
                 {
-                    while ( tagsIter.hasNext() )
+
+                    AbstractSvnScmProvider svnprovider = (AbstractSvnScmProvider)provider;
+
+                    SvnScmProviderRepository svnRepo = (SvnScmProviderRepository) repository.getProviderRepository();
+
+                    String tagDir = SvnTagBranchUtils.SVN_TAGS;
+                    String trunkDir = SvnTagBranchUtils.SVN_TRUNK;
+
+                    if ( !StringUtils.isEmpty( tagBase ))
                     {
-                        endTag = (String) tagsIter.next();
+                        svnRepo.setTagBase( tagBase );
+                        tagDir = tagBase;
+                    }
 
-                        result = provider.changeLog( repository, new ScmFileSet( basedir ), new ScmRevision( startTag ),
-                                                     new ScmRevision( endTag ) );
+                    getLog().info( "svnRepo: " + svnRepo );
+
+                    if ( !StringUtils.isEmpty( branchBase ))
+                    {
+                        trunkDir = branchBase;
+                    }
+
+                    String endRev = null;
+                    String startRev = null;
+
+                    SvnInfoCommandExpanded infoCommand = new SvnInfoCommandExpanded();
+                    SvnTrunkChangeLogCommand logCommand = new SvnTrunkChangeLogCommand();
+
+                    infoCommand.setLogger( new DefaultLog() );
+                    logCommand.setLogger( new DefaultLog() );
+
+                    if ( tagsIter.hasNext() )
+                    {
+                        while ( tagsIter.hasNext() )
+                        {
+                            endTag = (String) tagsIter.next();
+
+                            SvnInfoScmResult svnResultEnd = infoCommand.executeInfoTagCommand(svnRepo, new ScmFileSet(basedir), endTag, null, false, null);
+                            SvnInfoItem svninfo1 = (SvnInfoItem) svnResultEnd.getInfoItems().get(0);
+                            endRev = svninfo1.getLastChangedRevision();
+
+                            SvnInfoScmResult svnResultStart = infoCommand.executeInfoTagCommand(svnRepo, new ScmFileSet(basedir), startTag, null, false, null);
+                            SvnInfoItem svninfo2 = (SvnInfoItem) svnResultStart.getInfoItems().get(0);
+                            startRev = svninfo2.getLastChangedRevision();
+
+                            result = logCommand.executeChangeLogCommand(svnRepo, new ScmFileSet(basedir), null,null,null,null, new ScmRevision( startRev ), new ScmRevision( endRev ), trunkDir);
+
+                            checkResult( result );
+
+                            result.getChangeLog().setStartVersion(new ScmRevision(startTag));
+                            result.getChangeLog().setEndVersion(new ScmRevision(endTag));
+
+                            changeSets.add( result.getChangeLog() );
+
+                            startTag = endTag;
+                        }
+                    }
+                    else
+                    {
+                        SvnInfoScmResult svnResultStart = infoCommand.executeInfoCommand(svnRepo, new ScmFileSet(new File(svnRepo.getTagBase()), new File(tagDir + "/" + startTag)), null, false, null);
+                        SvnInfoItem svninfo2 = (SvnInfoItem) svnResultStart.getInfoItems().get(0);
+                        startRev = svninfo2.getLastChangedRevision();
+                        endRev = "HEAD";
+
+                        result = logCommand.executeChangeLogCommand(svnRepo, new ScmFileSet(basedir), null,null,null,null, new ScmRevision( startRev ), new ScmRevision( endRev ), trunkDir);
 
                         checkResult( result );
 
-                        changeSets.add( result.getChangeLog() );
+                        result.getChangeLog().setStartVersion(new ScmRevision(startTag));
+                        result.getChangeLog().setEndVersion(null);
 
-                        startTag = endTag;
+                        changeSets.add( result.getChangeLog() );
                     }
+
                 }
-                else
-                {
-                    result = provider.changeLog( repository, new ScmFileSet( basedir ), new ScmRevision( startTag ),
-                                                 new ScmRevision( endTag ) );
+                else {
 
-                    checkResult( result );
+                    if ( tagsIter.hasNext() )
+                    {
+                        while ( tagsIter.hasNext() )
+                        {
+                            endTag = (String) tagsIter.next();
 
-                    changeSets.add( result.getChangeLog() );
+                            result = provider.changeLog( repository, new ScmFileSet( basedir ), new ScmRevision( startTag ),
+                                                         new ScmRevision( endTag ) );
+
+                            checkResult( result );
+
+                            changeSets.add( result.getChangeLog() );
+
+                            startTag = endTag;
+                        }
+                    }
+                    else
+                    {
+                        result = provider.changeLog( repository, new ScmFileSet( basedir ), new ScmRevision( startTag ),
+                                                     new ScmRevision( endTag ) );
+
+                        checkResult( result );
+
+                        changeSets.add( result.getChangeLog() );
+                    }
                 }
             }
             else if ( "date".equals( type ) )
diff --git a/src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/changelog/SvnTrunkChangeLogCommand.java b/src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/changelog/SvnTrunkChangeLogCommand.java
new file mode 100644
index 0000000..7c49311
--- /dev/null
+++ b/src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/changelog/SvnTrunkChangeLogCommand.java
@@ -0,0 +1,138 @@
+package org.apache.maven.scm.provider.svn.svnexe.command.changelog;
+
+import java.io.File;
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import org.apache.maven.scm.ScmBranch;
+import org.apache.maven.scm.ScmException;
+import org.apache.maven.scm.ScmFileSet;
+import org.apache.maven.scm.ScmTag;
+import org.apache.maven.scm.ScmVersion;
+import org.apache.maven.scm.command.changelog.ChangeLogScmResult;
+import org.apache.maven.scm.command.changelog.ChangeLogSet;
+import org.apache.maven.scm.provider.ScmProviderRepository;
+import org.apache.maven.scm.provider.svn.SvnTagBranchUtils;
+import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
+import org.apache.maven.scm.provider.svn.svnexe.command.SvnCommandLineUtils;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.cli.CommandLineException;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.codehaus.plexus.util.cli.Commandline;
+
+public class SvnTrunkChangeLogCommand extends SvnChangeLogCommand {
+
+	private final static String DATE_FORMAT = "yyyy-MM-dd HH:mm:ss Z";
+
+	public ChangeLogScmResult executeChangeLogCommand(
+			ScmProviderRepository repo, ScmFileSet fileSet, Date startDate,
+			Date endDate, ScmBranch branch, String datePattern,
+			ScmVersion startVersion, ScmVersion endVersion, String trunkDir) throws ScmException {
+		Commandline cl = createCommandLine((SvnScmProviderRepository) repo,
+				fileSet.getBasedir(), branch, startDate, endDate, startVersion,
+				endVersion, trunkDir);
+
+		SvnChangeLogConsumer consumer = new SvnChangeLogConsumer(getLogger(),
+				datePattern);
+
+		CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
+
+		getLogger().info("Executing: " + SvnCommandLineUtils.cryptPassword(cl));
+		getLogger().info(
+				"Working directory: "
+						+ cl.getWorkingDirectory().getAbsolutePath());
+
+		int exitCode;
+
+		try {
+			exitCode = SvnCommandLineUtils.execute(cl, consumer, stderr,
+					getLogger());
+		} catch (CommandLineException ex) {
+			throw new ScmException("Error while executing svn command.", ex);
+		}
+
+		if (exitCode != 0) {
+			return new ChangeLogScmResult(cl.toString(),
+					"The svn command failed.", stderr.getOutput(), false);
+		}
+		ChangeLogSet changeLogSet = new ChangeLogSet(consumer
+				.getModifications(), startDate, endDate);
+		changeLogSet.setStartVersion(startVersion);
+		changeLogSet.setEndVersion(endVersion);
+
+		return new ChangeLogScmResult(cl.toString(), changeLogSet);
+	}
+
+	// ----------------------------------------------------------------------
+	// trunkに限定してログを出力するコマンドを生成
+	// ----------------------------------------------------------------------
+
+	private static Commandline createCommandLine(
+			SvnScmProviderRepository repository, File workingDirectory,
+			ScmBranch branch, Date startDate, Date endDate,
+			ScmVersion startVersion, ScmVersion endVersion, String trunkDir) {
+		SimpleDateFormat dateFormat = new SimpleDateFormat(DATE_FORMAT);
+
+		dateFormat.setTimeZone(TimeZone.getTimeZone("GMT"));
+
+		Commandline cl = SvnCommandLineUtils.getBaseSvnCommandLine(
+				workingDirectory, repository);
+
+		cl.createArgument().setValue("log");
+
+		cl.createArgument().setValue("-v");
+
+		if (startDate != null) {
+			cl.createArgument().setValue("-r");
+
+			if (endDate != null) {
+				cl.createArgument().setValue(
+						"{" + dateFormat.format(startDate) + "}" + ":" + "{"
+								+ dateFormat.format(endDate) + "}");
+			} else {
+				cl.createArgument().setValue(
+						"{" + dateFormat.format(startDate) + "}:HEAD");
+			}
+		}
+
+		if (startVersion != null) {
+			cl.createArgument().setValue("-r");
+
+			if (endVersion != null) {
+				if (startVersion.getName().equals(endVersion.getName())) {
+					cl.createArgument().setValue(startVersion.getName());
+				} else {
+					cl.createArgument()
+							.setValue(
+									startVersion.getName() + ":"
+											+ endVersion.getName());
+				}
+			} else {
+				cl.createArgument().setValue(startVersion.getName() + ":HEAD");
+			}
+		}
+
+		if (branch != null && StringUtils.isNotEmpty(branch.getName())) {
+			// By specifying a branch and this repository url below, subversion
+			// should show
+			// the changelog of that branch, but limit it to paths that also
+			// occur in this repository.
+			if (branch instanceof ScmTag) {
+				cl.createArgument().setValue(
+						SvnTagBranchUtils.resolveTagUrl(repository,
+								(ScmTag) branch));
+			} else {
+				cl.createArgument().setValue(
+						SvnTagBranchUtils.resolveBranchUrl(repository, branch));
+			}
+		}
+
+		String trunkPath = SvnTagBranchUtils.resolveUrl(repository.getUrl(),
+				null, null, new ScmBranch(trunkDir));
+
+		cl.createArgument().setValue(trunkPath);
+
+		return cl;
+	}
+}
diff --git a/src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/info/SvnInfoCommandExpanded.java b/src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/info/SvnInfoCommandExpanded.java
new file mode 100644
index 0000000..419f919
--- /dev/null
+++ b/src/main/java/org/apache/maven/scm/provider/svn/svnexe/command/info/SvnInfoCommandExpanded.java
@@ -0,0 +1,202 @@
+package org.apache.maven.scm.provider.svn.svnexe.command.info;
+
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+import org.apache.maven.scm.CommandParameters;
+import org.apache.maven.scm.ScmException;
+import org.apache.maven.scm.ScmFileSet;
+import org.apache.maven.scm.ScmResult;
+import org.apache.maven.scm.command.AbstractCommand;
+import org.apache.maven.scm.provider.ScmProviderRepository;
+import org.apache.maven.scm.provider.svn.command.SvnCommand;
+import org.apache.maven.scm.provider.svn.command.info.SvnInfoScmResult;
+import org.apache.maven.scm.provider.svn.repository.SvnScmProviderRepository;
+import org.apache.maven.scm.provider.svn.svnexe.command.SvnCommandLineUtils;
+import org.codehaus.plexus.util.StringUtils;
+import org.codehaus.plexus.util.cli.CommandLineException;
+import org.codehaus.plexus.util.cli.CommandLineUtils;
+import org.codehaus.plexus.util.cli.Commandline;
+import org.apache.maven.scm.ScmTag;
+import org.apache.maven.scm.provider.svn.SvnTagBranchUtils;
+import org.apache.maven.scm.provider.svn.SvnCommandUtils;
+
+import java.io.File;
+import java.util.Iterator;
+
+/**
+ * variation of SvnInfoCommand to work for branches. Taken from 1.2 release of maven-scm-providers 
+ *
+ * To be retrofited into original project
+ *
+ * @author <a href="mailto:kenney@apache.org">Kenney Westerhof</a>
+ * @version $Id: SvnInfoCommand.java 733015 2009-01-09 11:58:12Z dantran $
+ */
+public class SvnInfoCommandExpanded
+    extends AbstractCommand
+    implements SvnCommand
+{
+
+    /** {@inheritDoc} */
+    protected ScmResult executeCommand( ScmProviderRepository repository, ScmFileSet fileSet,
+                                        CommandParameters parameters )
+        throws ScmException
+    {
+        return executeInfoCommand( (SvnScmProviderRepository) repository, fileSet, parameters, false, null );
+    }
+
+    public SvnInfoScmResult executeInfoCommand( SvnScmProviderRepository repository, ScmFileSet fileSet,
+                                                CommandParameters parameters, boolean recursive, String revision )
+        throws ScmException
+    {
+        Commandline cl = createCommandLine( repository, fileSet, recursive, revision );
+
+        return executeInfoCommand( cl );
+    }
+
+    public SvnInfoScmResult executeInfoTagCommand( SvnScmProviderRepository repository, ScmFileSet fileSet,
+                                                   String tag, CommandParameters parameters, boolean recursive, String revision )
+        throws ScmException
+    {
+        Commandline cl = createTagCommandLine( repository, fileSet, tag, recursive, revision );
+
+        return executeInfoCommand( cl );
+    }
+
+    private SvnInfoScmResult executeInfoCommand( Commandline cl )
+        throws ScmException
+    {
+
+        SvnInfoConsumer consumer = new SvnInfoConsumer();
+
+        CommandLineUtils.StringStreamConsumer stderr = new CommandLineUtils.StringStreamConsumer();
+
+        if ( getLogger().isInfoEnabled() )
+        {
+            getLogger().info( "Executing: " + SvnCommandLineUtils.cryptPassword( cl ) );
+            getLogger().info( "Working directory: " + cl.getWorkingDirectory().getAbsolutePath() );
+        }
+
+        int exitCode;
+
+        try
+        {
+            exitCode = SvnCommandLineUtils.execute( cl, consumer, stderr, getLogger() );
+        }
+        catch ( CommandLineException ex )
+        {
+            throw new ScmException( "Error while executing command.", ex );
+        }
+
+        if ( exitCode != 0 )
+        {
+            return new SvnInfoScmResult( cl.toString(), "The svn command failed.", stderr.getOutput(), false );
+        }
+
+        return new SvnInfoScmResult( cl.toString(), consumer.getInfoItems() );
+    }
+
+    //set scope to protected to allow test to call it directly
+    protected static Commandline createCommandLine( SvnScmProviderRepository repository, ScmFileSet fileSet,
+                                                  boolean recursive, String revision )
+    {
+        Commandline cl = SvnCommandLineUtils.getBaseSvnCommandLine( fileSet.getBasedir(), repository );
+
+        cl.createArg().setValue( "info" );
+
+        if ( recursive )
+        {
+            cl.createArg().setValue( "--recursive" );
+        }
+
+        if ( StringUtils.isNotEmpty( revision ) )
+        {
+            cl.createArg().setValue( "-r" );
+
+            cl.createArg().setValue( revision );
+        }
+
+        Iterator it = fileSet.getFileList().iterator();
+
+        while ( it.hasNext() )
+        {
+            File file = (File) it.next();
+
+            if ( repository == null )
+            {
+                cl.createArg().setValue( file.getPath() );
+            }
+            else
+            {
+                cl.createArg().setValue( repository.getUrl() + "/" + file.getPath().replace( '\\', '/' ) );
+            }
+        }
+
+        return cl;
+    }
+
+    //set scope to protected to allow test to call it directly
+    protected static Commandline createTagCommandLine( SvnScmProviderRepository repository, ScmFileSet fileSet, String tag,
+                                                  boolean recursive, String revision )
+    {
+        Commandline cl = SvnCommandLineUtils.getBaseSvnCommandLine( fileSet.getBasedir(), repository );
+
+        cl.createArg().setValue( "info" );
+
+        if ( recursive )
+        {
+            cl.createArg().setValue( "--recursive" );
+        }
+
+        if ( StringUtils.isNotEmpty( revision ) )
+        {
+            cl.createArg().setValue( "-r" );
+
+            cl.createArg().setValue( revision );
+        }
+
+
+        Iterator it = fileSet.getFileList().iterator();
+
+        if ( ! it.hasNext() )
+        {
+             String tagUrl = SvnTagBranchUtils.resolveTagUrl( repository, new ScmTag( tag ) );
+             cl.createArg().setValue( SvnCommandUtils.fixUrl( tagUrl, repository.getUser() ) );
+        } else {
+            while ( it.hasNext() )
+            {
+                 File file = (File) it.next();
+
+                 if ( repository == null )
+                 {
+                     cl.createArg().setValue( file.getPath() );
+                 }
+                 else
+                 {
+                     // Note: this currently assumes you have the tag base checked out too
+                     String tagUrl = SvnTagBranchUtils.resolveTagUrl( repository, new ScmTag( tag ) ) + "/" + file.getPath().replace( '\\', '/' ) ;
+                     cl.createArg().setValue( SvnCommandUtils.fixUrl( tagUrl, repository.getUser() ) );
+                 }
+             }
+        }
+
+        return cl;
+    }
+
+}
-- 
1.6.0.4

