gnodet commented on PR #820:
URL: 
https://github.com/apache/maven-shade-plugin/pull/820#issuecomment-5075398245

   ## Root Cause Analysis: Why Maven 4 Prunes Differently Than Maven 3.10
   
   @cstamas @hboutemy — I dug into why Maven 3.10.0-RC1 (Resolver 2.0.20) 
retains the duplicate `c` node under `b:alt` while Maven 4.0.0-rc-5 (same 
Resolver 2.0.20) prunes it. The short answer: **it's the `DependencyManager`, 
not the `ConflictResolver`.**
   
   ### The Configuration Difference
   
   The pivotal config property is 
`maven.resolver.dependencyManagerTransitivity`:
   
   | Maven Version | Default Value | DependencyManager Used |
   |---|---|---|
   | **3.9.x** (Resolver 1.x) | n/a | `ClassicDependencyManager` |
   | **3.10.0-rc-1** (Resolver 2.0.20) | `false` | 
`ClassicDependencyManager(deriveUntil=2, applyFrom=2)` |
   | **4.0.0-rc-5** (Resolver 2.0.20) | `true` | 
`TransitiveDependencyManager(deriveUntil=MAX_VALUE, applyFrom=2)` |
   
   Maven 3.10's `DefaultRepositorySystemSessionFactory` defaults 
`dependencyManagerTransitivity` to `false`, keeping `ClassicDependencyManager`. 
Maven 4's version defaults it to `!mavenMaven3Personality` = `true`, switching 
to `TransitiveDependencyManager`.
   
   ### How This Causes the Bug
   
   In `BfDependencyCollector.doRecurse()`, the collector builds a **pool cache 
key** from `(artifact, repos, selector, manager, traverser, filter)`:
   
   ```java
   Object key = args.pool.toKey(artifact, childRepos, childSelector,
                                 childManager, childTraverser, childFilter);
   List<DependencyNode> children = args.pool.getChildren(key);
   if (children == null) {
       boolean skipResolution = args.skipper.skipResolution(child, 
parentContext.parents);
       // ... resolve and cache
   } else {
       child.setChildren(children);  // pool hit → skipper never consulted
   }
   ```
   
   **Maven 3.10 (`ClassicDependencyManager`):** At depth ≥ 2, 
`deriveChildManager()` returns `this` (same instance). So the pool key for `c` 
under `b` equals the pool key for `c` under `b:alt` → **cache hit**. The cached 
children are reused directly; the GACE skipper is **never consulted**. Both `c` 
nodes get populated subtrees, and `ConflictResolver` (even in default 
`Verbosity.NONE`) sees both and retains both.
   
   **Maven 4 (`TransitiveDependencyManager`):** At every depth, 
`deriveChildManager()` creates a **new manager instance** incorporating managed 
dependencies from the current node. The pool key for `c` under `b` ≠ pool key 
for `c` under `b:alt` → **cache miss**. The GACE skipper **is consulted** on 
the second occurrence and marks `c` under `b:alt` as `skippedAsDuplicate`. That 
`c` node ends up with **no children**, and `ConflictResolver` (in 
`Verbosity.NONE`) prunes it entirely as a conflict loser. The shade plugin's 
exclusion walk never sees `c` under `b:alt`, so the exclusion is lost.
   
   ### Conclusion
   
   This is a **deliberate Maven 4 design decision** — 
`TransitiveDependencyManager` is the correct default for Maven 4's dependency 
model (dependency management applies transitively at all depths). The different 
pool-key behavior is a side effect, not a bug. The shade plugin genuinely needs 
to see all transitive occurrences regardless of conflict resolution, and 
`CONFIG_PROP_VERBOSE = STANDARD` is the documented mechanism for that use case. 
This PR's approach is the correct fix on the plugin side.
   
   ---
   
   _This analysis was generated by an AI agent and may contain inaccuracies. 
Please verify before relying on it._


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to