@@ -147,7 +147,7 @@ public TreeVisitor<?, ExecutionContext> getVisitor() {
147147
148148 @ Override
149149 public Xml visitDocument (Xml .Document document , ExecutionContext ctx ) {
150- isNewDependencyPresent = checkIfNewDependencyPresents (newGroupId , newArtifactId , newVersion );
150+ isNewDependencyPresent = checkIfNewDependencyPresent (newGroupId , newArtifactId , newVersion );
151151 if (!oldGroupId .contains ("*" ) && !oldArtifactId .contains ("*" ) &&
152152 (changeManagedDependency == null || changeManagedDependency )) {
153153 doAfterVisit (new ChangeManagedDependencyGroupIdAndArtifactId (
@@ -170,16 +170,26 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
170170 }
171171 boolean isPluginDependency = isPluginDependencyTag (oldGroupId , oldArtifactId );
172172 boolean isAnnotationProcessorPath = isAnnotationProcessorPathTag (oldGroupId , oldArtifactId );
173+ boolean deferUpdate = false ;
173174 if (isOldDependencyTag || isPluginDependency || isAnnotationProcessorPath ) {
175+ ResolvedPom rp = getResolutionResult ().getPom ();
174176 String groupId = newGroupId ;
175177 if (groupId != null ) {
176- t = changeChildTagValue (t , "groupId" , groupId , ctx );
178+ Optional <Xml .Tag > groupIdTag = t .getChild ("groupId" );
179+ Optional <String > groupIdValue = t .getChildValue ("groupId" );
180+ if (groupIdTag .isPresent () && !groupId .equals (rp .getValue (groupIdValue .orElse (null )))) {
181+ t = changeChildTagValue (t , "groupId" , groupId , ctx );
182+ }
177183 } else {
178184 groupId = t .getChildValue ("groupId" ).orElseThrow (NoSuchElementException ::new );
179185 }
180186 String artifactId = newArtifactId ;
181187 if (artifactId != null ) {
182- t = changeChildTagValue (t , "artifactId" , artifactId , ctx );
188+ Optional <Xml .Tag > artifactIdTag = t .getChild ("artifactId" );
189+ Optional <String > artifactIdValue = t .getChildValue ("artifactId" );
190+ if (artifactIdTag .isPresent () && !artifactId .equals (rp .getValue (artifactIdValue .orElse (null )))) {
191+ t = changeChildTagValue (t , "artifactId" , artifactId , ctx );
192+ }
183193 } else {
184194 artifactId = t .getChildValue ("artifactId" ).orElseThrow (NoSuchElementException ::new );
185195 }
@@ -192,33 +202,37 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
192202 Scope scope = scopeTag .map (xml -> Scope .fromName (xml .getValue ().orElse ("compile" ))).orElse (Scope .Compile );
193203 Optional <Xml .Tag > versionTag = t .getChild ("version" );
194204
195- boolean configuredToOverrideManageVersion = overrideManagedVersion != null && overrideManagedVersion ; // False by default
205+ boolean configuredToOverrideManagedVersion = overrideManagedVersion != null && overrideManagedVersion ; // False by default
196206 boolean configuredToChangeManagedDependency = changeManagedDependency == null || changeManagedDependency ; // True by default
197207
198208 boolean versionTagPresent = versionTag .isPresent ();
199209 // dependencyManagement does not apply to plugin dependencies or annotation processor paths
200- boolean oldDependencyManaged = isOldDependencyTag && isDependencyManaged ( scope , oldGroupId , oldArtifactId );
210+ boolean oldDependencyDefinedManaged = isOldDependencyTag && canAffectManagedDependency ( getResolutionResult (), scope , oldGroupId , oldArtifactId );
201211 boolean newDependencyManaged = isOldDependencyTag && isDependencyManaged (scope , groupId , artifactId );
212+
202213 if (versionTagPresent ) {
203214 // If the previous dependency had a version but the new artifact is managed, removed the version tag.
204- if (!configuredToOverrideManageVersion && newDependencyManaged || (oldDependencyManaged && configuredToChangeManagedDependency )) {
215+ if (!configuredToOverrideManagedVersion && newDependencyManaged || (oldDependencyDefinedManaged && configuredToChangeManagedDependency )) {
205216 t = (Xml .Tag ) new RemoveContentVisitor <>(versionTag .get (), false , true ).visit (t , ctx );
206217 } else {
207218 // Otherwise, change the version to the new value.
208219 t = changeChildTagValue (t , "version" , resolvedNewVersion , ctx );
209220 }
210- } else if (configuredToOverrideManageVersion || !newDependencyManaged ) {
211- //If the version is not present, add the version if we are explicitly overriding a managed version or if no managed version exists.
221+ } else if (configuredToOverrideManagedVersion || ( !newDependencyManaged && !( oldDependencyDefinedManaged && configuredToChangeManagedDependency )) ) {
222+ // If the version is not present, add the version if we are explicitly overriding a managed version or if no managed version exists.
212223 Xml .Tag newVersionTag = Xml .Tag .build ("<version>" + resolvedNewVersion + "</version>" );
213224 //noinspection ConstantConditions
214225 t = (Xml .Tag ) new AddToTagVisitor <ExecutionContext >(t , newVersionTag , new MavenTagInsertionComparator (t .getChildren ())).visitNonNull (t , ctx , getCursor ().getParent ());
226+ } else if (!newDependencyManaged ) {
227+ // We leave it up to the managed dependency update to call `maybeUpdateModel()` instead to avoid interim dependency resolution failure
228+ deferUpdate = true ;
215229 }
216230 } catch (MavenDownloadingException e ) {
217231 return e .warn (tag );
218232 }
219233 }
220234
221- if (t != tag ) {
235+ if (t != tag && ! deferUpdate ) {
222236 maybeUpdateModel ();
223237 }
224238 }
@@ -227,7 +241,7 @@ public Xml visitTag(Xml.Tag tag, ExecutionContext ctx) {
227241 return t ;
228242 }
229243
230- private boolean checkIfNewDependencyPresents (@ Nullable String groupId , @ Nullable String artifactId , @ Nullable String version ) {
244+ private boolean checkIfNewDependencyPresent (@ Nullable String groupId , @ Nullable String artifactId , @ Nullable String version ) {
231245 if ((groupId == null ) || (artifactId == null )) {
232246 return false ;
233247 }
@@ -248,6 +262,23 @@ private boolean isDependencyManaged(Scope scope, String groupId, String artifact
248262 return false ;
249263 }
250264
265+ private boolean canAffectManagedDependency (MavenResolutionResult result , Scope scope , String groupId , String artifactId ) {
266+ // We're only going to be able to effect managed dependencies that are either direct or are brought in as direct via a local parent
267+ // `ChangeManagedDependencyGroupIdAndArtifactId` cannot manipulate BOM imported managed dependencies nor direct dependencies from remote parents
268+ Pom requestedPom = result .getPom ().getRequested ();
269+ for (ManagedDependency requestedManagedDependency : requestedPom .getDependencyManagement ()) {
270+ if (groupId .equals (requestedManagedDependency .getGroupId ()) && artifactId .equals (requestedManagedDependency .getArtifactId ())) {
271+ if (requestedManagedDependency instanceof ManagedDependency .Defined ) {
272+ return scope .isInClasspathOf (Scope .fromName (((ManagedDependency .Defined ) requestedManagedDependency ).getScope ()));
273+ }
274+ }
275+ }
276+ if (result .parentPomIsProjectPom () && result .getParent () != null ) {
277+ return canAffectManagedDependency (result .getParent (), scope , groupId , artifactId );
278+ }
279+ return false ;
280+ }
281+
251282 @ SuppressWarnings ("ConstantConditions" )
252283 private String resolveSemverVersion (ExecutionContext ctx , String groupId , String artifactId , @ Nullable String currentVersion ) throws MavenDownloadingException {
253284 if (versionComparator == null ) {
0 commit comments