You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@maven.apache.org by mi...@apache.org on 2020/01/26 16:56:59 UTC

[maven-release] 01/01: [MRELEASE-549] Pin svn:externals when copying (tagging/branching)

This is an automated email from the ASF dual-hosted git repository.

michaelo pushed a commit to branch MRELEASE-549
in repository https://gitbox.apache.org/repos/asf/maven-release.git

commit 4126dc3499a02c6e99c8d09ac2ad1789f4602db0
Author: Thorsten Heit <th...@gmx.de>
AuthorDate: Fri Dec 20 12:27:02 2019 +0100

    [MRELEASE-549] Pin svn:externals when copying (tagging/branching)
    
    Add support for the "--pin-externals" option in SCM branch and tag
    operations. This feature was introducted in Subversion 1.9 and is
    available in Maven SCM since version 1.11.1.
    
    This closes #32
---
 .../shared/release/config/ReleaseDescriptor.java   | 134 +++++++++++----------
 .../config/PropertiesReleaseDescriptorStore.java   |   2 +
 .../release/config/ReleaseDescriptorBuilder.java   |   6 +
 .../maven/shared/release/config/ReleaseUtils.java  |   5 +
 .../maven/shared/release/phase/ScmBranchPhase.java |  11 +-
 .../maven/shared/release/phase/ScmTagPhase.java    |  14 ++-
 .../src/main/mdo/release-descriptor.mdo            |  14 +++
 .../phase/GenerateReleasePomsPhaseTest.java        |   2 +
 .../release/phase/IsScmBranchParametersEquals.java |   4 +-
 .../release/phase/IsScmTagParametersEquals.java    |   3 +-
 .../phase/ScmCommitDevelopmentPhaseTest.java       |   1 +
 .../phase/ScmCommitPreparationPhaseTest.java       |   1 +
 .../maven/plugins/release/BranchReleaseMojo.java   |  10 ++
 .../maven/plugins/release/PrepareReleaseMojo.java  |  10 ++
 14 files changed, 144 insertions(+), 73 deletions(-)

diff --git a/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java b/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java
index ba117a1..a482727 100644
--- a/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java
+++ b/maven-release-api/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptor.java
@@ -24,14 +24,14 @@ import java.util.List;
 import org.apache.maven.model.Scm;
 
 /**
- * 
+ *
  * @author Robert Scholte
  */
 public interface ReleaseDescriptor
 {
     /**
      * Get if updateDependencies is false, dependencies version won't be updated to the next development version.
-     * 
+     *
      * @return boolean
      */
     boolean isUpdateDependencies();
@@ -39,56 +39,56 @@ public interface ReleaseDescriptor
     /**
      * Get whether to use the release profile that adds sources and javadocs to the released artifact, if appropriate.
      * If set to true, this will set the property "performRelease" to true.
-     * 
+     *
      * @return boolean
      */
     boolean isUseReleaseProfile();
 
     /**
      * Get whether to use the parent pom version for submodule versions.
-     * 
+     *
      * @return boolean
      */
     boolean isAutoVersionSubmodules();
 
     /**
      * Get whether a SNAPSHOT of the release plugin is allowed.
-     * 
+     *
      * @return boolean
      */
     boolean isSnapshotReleasePluginAllowed();
 
     /**
      * Get the commits must be done by modules or not. Set it to true in case of flat directory structure.
-     * 
+     *
      * @return boolean
      */
     boolean isCommitByProject();
 
     /**
      * Get whether to create a branch instead of do a release.
-     * 
+     *
      * @return boolean
      */
     boolean isBranchCreation();
 
     /**
      * Get whether to update branch POM versions.
-     * 
+     *
      * @return boolean
      */
     boolean isUpdateBranchVersions();
 
     /**
      * Get whether to update working copy POM versions.
-     * 
+     *
      * @return boolean
      */
     boolean isUpdateWorkingCopyVersions();
 
     /**
      * Get whether to suppress a commit of changes to the working copy before a tag or branch is created.
-     * 
+     *
      * @return boolean
      */
     boolean isSuppressCommitBeforeTagOrBranch();
@@ -96,14 +96,14 @@ public interface ReleaseDescriptor
     /**
      * Get should timestamped SNAPSHOT dependencies be allowed? Default is to fail when any SNAPSHOT dependency is
      * found.
-     * 
+     *
      * @return boolean
      */
     boolean isAllowTimestampedSnapshots();
 
     /**
      * Get whether to update branch versions to SNAPSHOT.
-     * 
+     *
      * @return boolean
      */
     boolean isUpdateVersionsToSnapshot();
@@ -111,7 +111,7 @@ public interface ReleaseDescriptor
     /**
      * Get nOTE : currently only implemented with svn scm. Enable a workaround to prevent issue due to svn client &gt;
      * 1.5.0 (https://issues.apache.org/jira/browse/SCM-406).
-     * 
+     *
      * @return boolean
      */
     boolean isRemoteTagging();
@@ -119,7 +119,7 @@ public interface ReleaseDescriptor
     /**
      * Get if the scm provider should use local checkouts via file://${basedir} instead of doing a clean checkout over
      * the network. This is very helpful for releasing large projects!
-     * 
+     *
      * @return boolean
      */
     boolean isLocalCheckout();
@@ -128,54 +128,54 @@ public interface ReleaseDescriptor
      * Get should distributed changes be pushed to the central repository? For many distributed SCMs like Git, a change
      * like a commit is only stored in your local copy of the repository. Pushing the change allows your to more easily
      * share it with other users.
-     * 
+     *
      * @return boolean
      */
     boolean isPushChanges();
 
     /**
      * Get default version to use for new working copy.
-     * 
+     *
      * Some SCMs may require a Work Item or a Task to allow the
      * changes to be pushed or delivered.
      * This field allows you to specify that Work Item
      * or Task. It is optional, and only relevant if pushChanges is true.
-     *  
+     *
      * @return String
      */
     String getWorkItem();
 
     /**
      * Get default version to use for new working copy.
-     * 
+     *
      * @return String
      */
     String getDefaultDevelopmentVersion();
 
     /**
      * Get relative path of the project returned by the checkout command.
-     * 
+     *
      * @return String
      */
     String getScmRelativePathProjectDirectory();
 
     /**
      * Get the directory where the tag will be checked out.
-     * 
+     *
      * @return String
      */
     String getCheckoutDirectory();
 
     /**
      * Get the goals to execute in perform phase for the release.
-     * 
+     *
      * @return String
      */
     String getPerformGoals();
 
     /**
      * Get default version to use for the tagged release or the new branch.
-     * 
+     *
      * @return String
      */
     String getDefaultReleaseVersion();
@@ -183,7 +183,7 @@ public interface ReleaseDescriptor
     /**
      * Get nOTE : currently only implemented with svn scm. It contains the revision of the committed released pom to
      * remotely tag the source code with this revision.
-     * 
+     *
      * @return String
      */
     String getScmReleasedPomRevision();
@@ -191,14 +191,14 @@ public interface ReleaseDescriptor
     /**
      * Get whether to add the model schema to the top of the rewritten POM if it wasn't there already. If
      * <code>false</code> then the root element will remain untouched.
-     * 
+     *
      * @return boolean
      */
     boolean isAddSchema();
 
     /**
      * Get whether to generate release POMs.
-     * 
+     *
      * @return boolean
      */
     boolean isGenerateReleasePoms();
@@ -206,7 +206,7 @@ public interface ReleaseDescriptor
     /**
      * Get whether the release process is interactive and the release manager should be prompted to confirm values, or
      * whether the defaults are used regardless.
-     * 
+     *
      * @return boolean
      */
     boolean isInteractive();
@@ -214,69 +214,69 @@ public interface ReleaseDescriptor
     /**
      * Get whether to use edit mode when making SCM modifications. This setting is disregarded if the SCM does not
      * support edit mode, or if edit mode is compulsory for the given SCM.
-     * 
+     *
      * @return boolean
      */
     boolean isScmUseEditMode();
 
     /**
-     * 
+     *
      * @return list of profiles to activate
      */
     List<String> getActivateProfiles();
 
     /**
      * Get the last completed phase.
-     * 
+     *
      * @return String
      */
     String getCompletedPhase();
 
     /**
      * Method getCheckModificationExcludes.
-     * 
+     *
      * @return List
      */
     List<String> getCheckModificationExcludes();
 
     /**
      * Get additional arguments to pass to any executed Maven process.
-     * 
+     *
      * @return String
      */
     String getAdditionalArguments();
 
     /**
      * Get the goals to execute in preparation for the release.
-     * 
+     *
      * @return String
      */
     String getPreparationGoals();
 
     /**
      * Get the goals to execute in on completion of preparation for the release.
-     * 
+     *
      * @return String
      */
     String getCompletionGoals();
 
     /**
      * Get the file name of the POM to pass to any executed Maven process.
-     * 
+     *
      * @return String
      */
     String getPomFileName();
 
     /**
      * Get the prefix of SCM modification messages.
-     * 
+     *
      * @return String
      */
     String getScmCommentPrefix();
 
     /**
      * Get the SCM commit comment when setting pom.xml to release.
-     * 
+     *
      * @return String
      * @since 3.0.0-M1
      */
@@ -284,7 +284,7 @@ public interface ReleaseDescriptor
 
     /**
      * Get the SCM commit comment when setting pom.xml back to development.
-     * 
+     *
      * @return String
      * @since 3.0.0-M1
      */
@@ -292,7 +292,7 @@ public interface ReleaseDescriptor
 
     /**
      * Get the SCM commit comment when branching.
-     * 
+     *
      * @return String
      * @since 3.0.0-M1
      */
@@ -300,7 +300,7 @@ public interface ReleaseDescriptor
 
     /**
      * Get the SCM commit comment when rolling back.
-     * 
+     *
      * @return String
      * @since 3.0.0-M1
      */
@@ -308,35 +308,35 @@ public interface ReleaseDescriptor
 
     /**
      * Get pass phrase for the private key.
-     * 
+     *
      * @return String
      */
     String getScmPrivateKeyPassPhrase();
 
     /**
      * Get the password for the user interacting with the scm.
-     * 
+     *
      * @return String
      */
     String getScmPassword();
 
     /**
      * Get private key for an SSH based SCM repository.
-     * 
+     *
      * @return String
      */
     String getScmPrivateKey();
 
     /**
      * Get tag or branch name: the identifier for the tag/branch. Example: maven-release-plugin-2.0.
-     * 
+     *
      * @return String
      */
     String getScmReleaseLabel();
 
     /**
      * Get where you are going to put your tagged sources Example https://svn.apache.org/repos/asf/maven/plugins/tags.
-     * 
+     *
      * @return String
      */
     String getScmTagBase();
@@ -344,14 +344,14 @@ public interface ReleaseDescriptor
     /**
      * Get where you are going to put your branched sources Example
      * https://svn.apache.org/repos/asf/maven/plugins/branches.
-     * 
+     *
      * @return String
      */
     String getScmBranchBase();
 
     /**
      * Get the id can be used to get the credentials by the server-id from the settings.xml.
-     * 
+     *
      * @return String
      */
     String getScmId();
@@ -359,28 +359,28 @@ public interface ReleaseDescriptor
     /**
      * Get this is a MavenSCM of where you're going to get the sources to make the release with. Example:
      * scm:svn:https://svn.apache.org/repos/asf/maven/plugins/trunk/maven-release-plugin.
-     * 
+     *
      * @return String
      */
     String getScmSourceUrl();
 
     /**
      * Get the user name to interact with the scm.
-     * 
+     *
      * @return String
      */
     String getScmUsername();
 
     /**
      * Get wait the specified number of seconds before creating a tag.
-     * 
+     *
      * @return int
      */
     int getWaitBeforeTagging();
 
     /**
      * Get the directory where the release is performed.
-     * 
+     *
      * @return String
      */
     String getWorkingDirectory();
@@ -388,28 +388,28 @@ public interface ReleaseDescriptor
     /**
      * Get specifies the format for generating a tag name. Property expansion is used with the optional prefix of
      * project, where properties are delimited with @{ and }.
-     * 
+     *
      * @return String
      */
     String getScmTagNameFormat();
 
     /**
      * Get the role-hint for the NamingPolicy implementation used to calculate the project branch and tag names.
-     * 
+     *
      * @return String
      */
     String getProjectNamingPolicyId();
 
     /**
      * Get the role-hint for the VersionPolicy implementation used to calculate the project versions.
-     * 
+     *
      * @return String
      */
-    String getProjectVersionPolicyId(); 
+    String getProjectVersionPolicyId();
 
     /**
      * Get the role-hint for the release Strategy implementation.
-     * 
+     *
      * @return String
      */
     String getReleaseStrategyId();
@@ -420,22 +420,22 @@ public interface ReleaseDescriptor
      * @param artifactKey the artifact key {@code String}
      */
     String getDependencyOriginalVersion( String artifactKey );
-    
+
     /**
      * @return {@code String} the release version for the resolved snapshot dependency.
      *
      * @param artifactKey the artifact key {@code String}
      */
-    String getDependencyReleaseVersion( String artifactKey ); 
+    String getDependencyReleaseVersion( String artifactKey );
 
     /**
      * @return {@code String} the release version for the resolved snapshot dependency.
      *
      * @param artifactKey the artifact key {@code String}
      */
-    String getDependencyDevelopmentVersion( String artifactKey ); 
+    String getDependencyDevelopmentVersion( String artifactKey );
+
 
-    
     String getProjectOriginalVersion( String projectKey );
 
     String getProjectDevelopmentVersion( String projectKey );
@@ -444,7 +444,7 @@ public interface ReleaseDescriptor
 
     /**
      * @return the original {@code Scm} information.
-     * 
+     *
      * @param projectKey the project key {@code String}
      */
     Scm getOriginalScmInfo( String projectKey );
@@ -452,8 +452,8 @@ public interface ReleaseDescriptor
     // Modifiable
     void addDependencyOriginalVersion( String versionlessKey, String string );
 
-    void addDependencyReleaseVersion( String versionlessKey, String version ); 
-    
+    void addDependencyReleaseVersion( String versionlessKey, String version );
+
     void addDependencyDevelopmentVersion( String versionlessKey, String version );
 
     void addReleaseVersion( String projectId, String nextVersion );
@@ -480,8 +480,16 @@ public interface ReleaseDescriptor
      * <li>"reports" or "3": resolve report dependencies</li>
      * <li>"extensions" or "4": resolve extension dependencies</li>
      * </ul>
-     * 
+     *
      * @return String
      */
     String getAutoResolveSnapshots();
+
+    /**
+     * Determines whether the {@code --pin-externals} option in {@code svn copy} command is enabled
+     * which is new in Subversion 1.9.
+     *
+     * @return boolean
+     */
+    boolean isPinExternals();
 }
diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java
index f1272de..9937104 100644
--- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java
+++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/PropertiesReleaseDescriptorStore.java
@@ -249,6 +249,8 @@ public class PropertiesReleaseDescriptorStore
 
         properties.setProperty( "remoteTagging", Boolean.toString( config.isRemoteTagging() ) );
 
+        properties.setProperty( "pinExternals", Boolean.toString( config.isPinExternals() ) );
+
         properties.setProperty( "pushChanges", Boolean.toString( config.isPushChanges() ) );
 
         if ( config.getWorkItem() != null )
diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java
index 629a897..7ff677a 100644
--- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java
+++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseDescriptorBuilder.java
@@ -446,6 +446,12 @@ public class ReleaseDescriptorBuilder
         return this;
     }
 
+    public ReleaseDescriptorBuilder setPinExternals( boolean pinExternals )
+    {
+        releaseDescriptor.setPinExternals( pinExternals );
+        return this;
+    }
+
     BuilderReleaseDescriptor build()
     {
         return releaseDescriptor;
diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java
index bcaf758..870f285 100644
--- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java
+++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/config/ReleaseUtils.java
@@ -161,6 +161,11 @@ public class ReleaseUtils
             String remoteTaggingStr = properties.getProperty( "remoteTagging" );
             builder.setRemoteTagging( Boolean.valueOf( remoteTaggingStr ) );
         }
+        if ( properties.containsKey( "pinExternals" ) )
+        {
+            String pinExternals = properties.getProperty( "pinExternals" );
+            builder.setPinExternals( Boolean.valueOf( pinExternals ) );
+        }
         if ( properties.containsKey( "pushChanges" ) )
         {
             String pushChanges = properties.getProperty( "pushChanges" );
diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmBranchPhase.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmBranchPhase.java
index d1d85b6..576f511 100644
--- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmBranchPhase.java
+++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmBranchPhase.java
@@ -108,6 +108,7 @@ public class ScmBranchPhase
             scmBranchParameters.setMessage( releaseDescriptor.getScmCommentPrefix() + "copy for branch " + branchName );
             scmBranchParameters.setRemoteBranching( releaseDescriptor.isRemoteTagging() );
             scmBranchParameters.setScmRevision( releaseDescriptor.getScmReleasedPomRevision() );
+            scmBranchParameters.setPinExternals( releaseDescriptor.isPinExternals() );
 
             result = provider.branch( repository, fileSet, branchName, scmBranchParameters );
         }
@@ -137,12 +138,16 @@ public class ScmBranchPhase
         ReleaseDescriptor basedirAlignedReleaseDescriptor =
             ReleaseUtil.createBasedirAlignedReleaseDescriptor( releaseDescriptor, reactorProjects );
 
-        logInfo( result, "Full run would be branching " + basedirAlignedReleaseDescriptor.getWorkingDirectory() );
+        logInfo( result, "Full run would branch " + basedirAlignedReleaseDescriptor.getWorkingDirectory() );
         if ( releaseDescriptor.getScmBranchBase() != null )
         {
-            logInfo( result, "  To SCM URL: " + releaseDescriptor.getScmBranchBase() );
+            logInfo( result, "  to SCM URL " + releaseDescriptor.getScmBranchBase() );
+        }
+        logInfo( result, "  with label '" + releaseDescriptor.getScmReleaseLabel() + "'" );
+        if ( releaseDescriptor.isPinExternals() )
+        {
+            logInfo( result, "  and pinned externals" );
         }
-        logInfo( result, "  with label: '" + releaseDescriptor.getScmReleaseLabel() + "'" );
 
         result.setResultCode( ReleaseResult.SUCCESS );
 
diff --git a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmTagPhase.java b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmTagPhase.java
index 5f52d39..64f5511 100644
--- a/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmTagPhase.java
+++ b/maven-release-manager/src/main/java/org/apache/maven/shared/release/phase/ScmTagPhase.java
@@ -120,12 +120,15 @@ public class ScmTagPhase
                 new ScmTagParameters( releaseDescriptor.getScmCommentPrefix() + "copy for tag " + tagName );
             scmTagParameters.setRemoteTagging( releaseDescriptor.isRemoteTagging() );
             scmTagParameters.setScmRevision( releaseDescriptor.getScmReleasedPomRevision() );
+            scmTagParameters.setPinExternals( releaseDescriptor.isPinExternals() );
             if ( getLogger().isDebugEnabled() )
             {
                 getLogger().debug(
                     "ScmTagPhase :: scmTagParameters remotingTag " + releaseDescriptor.isRemoteTagging() );
                 getLogger().debug(
                     "ScmTagPhase :: scmTagParameters scmRevision " + releaseDescriptor.getScmReleasedPomRevision() );
+                getLogger().debug(
+                        "ScmTagPhase :: scmTagParameters pinExternals " + releaseDescriptor.isPinExternals() );
                 getLogger().debug( "ScmTagPhase :: fileSet  " + fileSet );
             }
             result = provider.tag( repository, fileSet, tagName, scmTagParameters );
@@ -160,13 +163,16 @@ public class ScmTagPhase
         if ( releaseDescriptor.isRemoteTagging() )
         {
             logInfo( result,
-                     "Full run would be tagging working copy " + basedirAlignedReleaseDescriptor.getWorkingDirectory()
-                         + " with label: '" + releaseDescriptor.getScmReleaseLabel() + "'" );
+                     "Full run would tag working copy '" + basedirAlignedReleaseDescriptor.getWorkingDirectory() + "'" );
         }
         else
         {
-            logInfo( result, "Full run would be tagging remotely " + basedirAlignedReleaseDescriptor.getScmSourceUrl()
-                + " with label: '" + releaseDescriptor.getScmReleaseLabel() + "'" );
+            logInfo( result, "Full run would tag remotely '" + basedirAlignedReleaseDescriptor.getScmSourceUrl() + "'" );
+        }
+        logInfo( result, "  with label '" + releaseDescriptor.getScmReleaseLabel() + "'" );
+        if ( releaseDescriptor.isPinExternals() )
+        {
+            logInfo( result, "  and pinned externals" );
         }
 
         result.setResultCode( ReleaseResult.SUCCESS );
diff --git a/maven-release-manager/src/main/mdo/release-descriptor.mdo b/maven-release-manager/src/main/mdo/release-descriptor.mdo
index 6bf44f2..ed0ea58 100644
--- a/maven-release-manager/src/main/mdo/release-descriptor.mdo
+++ b/maven-release-manager/src/main/mdo/release-descriptor.mdo
@@ -583,6 +583,18 @@
           </description>
         </field>
 
+        <field>
+          <name>pinExternals</name>
+          <version>3.0.0+</version>
+          <type>boolean</type>
+          <defaultValue>false</defaultValue>
+          <description>
+            Enable the "--pin-externals" option in svn copy commands which is new in Subversion 1.9.
+            NOTE : Currently only implemented with Subversion.
+            (https://issues.apache.org/jira/browse/SCM-805)
+          </description>
+        </field>
+
         <!-- Announcement Information
 
         Announcement related info, this can be a second part of the process.
@@ -805,6 +817,7 @@
              updateVersionsToSnapshot         != that.isUpdateVersionsToSnapshot()         ||
              allowTimestampedSnapshots        != that.isAllowTimestampedSnapshots()        ||
              remoteTagging                    != that.isRemoteTagging()                    ||
+             pinExternals                     != that.isPinExternals()                     ||
              localCheckout                    != that.isLocalCheckout()                    ||
              pushChanges                      != that.isPushChanges()
            )
@@ -1076,6 +1089,7 @@
         result = 29 * result + java.util.Objects.hashCode( scmReleasedPomRevision );
         result = 29 * result + java.util.Objects.hashCode( workItem );
         result = 29 * result + java.util.Objects.hashCode( autoResolveSnapshots );
+        result = 29 * result + java.util.Objects.hashCode( pinExternals );
 
         return result;
     }
diff --git a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/GenerateReleasePomsPhaseTest.java b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/GenerateReleasePomsPhaseTest.java
index c150589..ad381ee 100644
--- a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/GenerateReleasePomsPhaseTest.java
+++ b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/GenerateReleasePomsPhaseTest.java
@@ -117,6 +117,7 @@ public class GenerateReleasePomsPhaseTest
         builder.setGenerateReleasePoms( true );
         builder.setSuppressCommitBeforeTagOrBranch( true );
         builder.setRemoteTagging( false );
+        builder.setPinExternals( false );
         mapNextVersion( builder, "groupId:artifactId" );
 
         phase.execute( ReleaseUtils.buildReleaseDescriptor( builder ), new DefaultReleaseEnvironment(), reactorProjects );
@@ -135,6 +136,7 @@ public class GenerateReleasePomsPhaseTest
         builder.setGenerateReleasePoms( true );
         builder.setSuppressCommitBeforeTagOrBranch( true );
         builder.setRemoteTagging( true );
+        builder.setPinExternals( false );
         mapNextVersion( builder, "groupId:artifactId" );
 
         phase.execute( ReleaseUtils.buildReleaseDescriptor( builder ), new DefaultReleaseEnvironment(), reactorProjects );
diff --git a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmBranchParametersEquals.java b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmBranchParametersEquals.java
index 9fb572f..8431b85 100644
--- a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmBranchParametersEquals.java
+++ b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmBranchParametersEquals.java
@@ -40,7 +40,7 @@ public class IsScmBranchParametersEquals extends ArgumentMatcher<ScmBranchParame
     {
         ScmBranchParameters sbp = (ScmBranchParameters) argument;
         return sbp.getMessage().equals( this.scmBranchParameters.getMessage() )
-            //&& stp.isRemoteTagging() == this.scmBranchParameters.isRemoteTagging()
-            ;
+            && sbp.isRemoteBranching() == this.scmBranchParameters.isRemoteBranching()
+            && sbp.isPinExternals() == this.scmBranchParameters.isPinExternals();
     }
 }
diff --git a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmTagParametersEquals.java b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmTagParametersEquals.java
index 0c3dc75..6196256 100644
--- a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmTagParametersEquals.java
+++ b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/IsScmTagParametersEquals.java
@@ -39,6 +39,7 @@ public class IsScmTagParametersEquals extends ArgumentMatcher<ScmTagParameters>
     {
         ScmTagParameters stp = (ScmTagParameters) argument;
         return stp.getMessage().equals( this.scmTagParameters.getMessage() )
-            && stp.isRemoteTagging() == this.scmTagParameters.isRemoteTagging();
+            && stp.isRemoteTagging() == this.scmTagParameters.isRemoteTagging()
+            && stp.isPinExternals() == this.scmTagParameters.isPinExternals();
     }
 }
diff --git a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitDevelopmentPhaseTest.java b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitDevelopmentPhaseTest.java
index 50ba849..8378869 100644
--- a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitDevelopmentPhaseTest.java
+++ b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitDevelopmentPhaseTest.java
@@ -96,6 +96,7 @@ public class ScmCommitDevelopmentPhaseTest
         List<MavenProject> reactorProjects = createReactorProjects();
 
         builder.setRemoteTagging( false );
+        builder.setPinExternals( false );
         builder.setSuppressCommitBeforeTagOrBranch( true );
         builder.setUpdateWorkingCopyVersions( false );
 
diff --git a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitPreparationPhaseTest.java b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitPreparationPhaseTest.java
index 863cdbd..954086c 100644
--- a/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitPreparationPhaseTest.java
+++ b/maven-release-manager/src/test/java/org/apache/maven/shared/release/phase/ScmCommitPreparationPhaseTest.java
@@ -488,6 +488,7 @@ public class ScmCommitPreparationPhaseTest
         List<MavenProject> reactorProjects = createReactorProjects();
 
         builder.setRemoteTagging( true );
+        builder.setPinExternals( false );
         builder.setSuppressCommitBeforeTagOrBranch( true );
 
         ScmProvider scmProviderMock = mock( ScmProvider.class );
diff --git a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java
index 1cbd467..955b01a 100644
--- a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java
+++ b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/BranchReleaseMojo.java
@@ -230,6 +230,15 @@ public class BranchReleaseMojo
     @Parameter( defaultValue = "@{prefix} prepare branch @{releaseLabel}", property = "scmBranchCommitComment" )
     private String scmBranchCommitComment = "@{prefix} prepare branch @{releaseLabel}";
 
+    /**
+     * Currently only implemented with svn scm. Enable the {@code --pin-externals} option in
+     * {@code svn copy} command which is new in Subversion 1.9.
+     *
+     * @since 3.0.0
+     */
+    @Parameter( defaultValue = "false", property = "pinExternals" )
+    private boolean pinExternals;
+
     @Override
     public void execute()
         throws MojoExecutionException, MojoFailureException
@@ -254,6 +263,7 @@ public class BranchReleaseMojo
         config.setProjectVersionPolicyId( projectVersionPolicyId );
         config.setProjectNamingPolicyId( projectBranchNamingPolicyId );
         config.setScmBranchCommitComment( scmBranchCommitComment );
+        config.setPinExternals( pinExternals );
 
         if ( checkModificationExcludeList != null )
         {
diff --git a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java
index 7579c2f..7422354 100644
--- a/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java
+++ b/maven-release-plugin/src/main/java/org/apache/maven/plugins/release/PrepareReleaseMojo.java
@@ -299,6 +299,15 @@ public class PrepareReleaseMojo
     private String autoResolveSnapshots;
 
     /**
+     * Currently only implemented with svn scm. Enable the {@code --pin-externals} option in
+     * {@code svn copy} command which is new in Subversion 1.9.
+     *
+     * @since 3.0.0
+     */
+    @Parameter( defaultValue = "false", property = "pinExternals" )
+    private boolean pinExternals;
+
+    /**
      * {@inheritDoc}
      */
     @Override
@@ -343,6 +352,7 @@ public class PrepareReleaseMojo
         config.setScmDevelopmentCommitComment( scmDevelopmentCommitComment );
         config.setScmReleaseCommitComment( scmReleaseCommitComment );
         config.setAutoResolveSnapshots( autoResolveSnapshots );
+        config.setPinExternals( pinExternals );
 
         if ( checkModificationExcludeList != null )
         {