aboutsummaryrefslogtreecommitdiffhomepage
path: root/crates/mozart-registry
diff options
context:
space:
mode:
authornsfisis <nsfisis@gmail.com>2026-05-03 22:07:34 +0900
committernsfisis <nsfisis@gmail.com>2026-05-03 22:07:34 +0900
commit38706a6f0ceb773d473c4f5ddebf49e8e5ae46dc (patch)
treea38de4fbf27e6834eae3944bbd86ce53b13236cb /crates/mozart-registry
parent64f8bb0c1aa16d78c5edc3f3de5dd3ff6e5861de (diff)
downloadphp-mozart-38706a6f0ceb773d473c4f5ddebf49e8e5ae46dc.tar.gz
php-mozart-38706a6f0ceb773d473c4f5ddebf49e8e5ae46dc.tar.zst
php-mozart-38706a6f0ceb773d473c4f5ddebf49e8e5ae46dc.zip
fix(update): apply --minimal-changes via policy preferred versions
The previous implementation pinned every resolved package back to its locked version after the resolve, which discarded the new versions the solver had to pick when a root constraint moved off the lock (e.g. a require bumped from `1.*` to `2.*`). The lock effectively never moved, so transitive cascades from a forced root-level update were lost. Mirror Composer's `Installer::createPolicy(forUpdate=true, minimalUpdate=true)` instead: thread the lock's `name → normalized version` map through the policy as `preferred_versions`. The solver now picks the locked version as a tiebreaker when it still satisfies the active constraints, but moves freely when a constraint forces a different version. Drop the post-process hook entirely.
Diffstat (limited to 'crates/mozart-registry')
-rw-r--r--crates/mozart-registry/src/lockfile.rs1
-rw-r--r--crates/mozart-registry/src/resolver.rs22
2 files changed, 21 insertions, 2 deletions
diff --git a/crates/mozart-registry/src/lockfile.rs b/crates/mozart-registry/src/lockfile.rs
index 701a6f7..197335f 100644
--- a/crates/mozart-registry/src/lockfile.rs
+++ b/crates/mozart-registry/src/lockfile.rs
@@ -1680,6 +1680,7 @@ mod tests {
locked_packages: Vec::new(),
block_abandoned: false,
root_branch_alias: None,
+ preferred_versions: IndexMap::new(),
};
let resolved = resolve(&resolve_request)
diff --git a/crates/mozart-registry/src/resolver.rs b/crates/mozart-registry/src/resolver.rs
index 66e923d..33c3659 100644
--- a/crates/mozart-registry/src/resolver.rs
+++ b/crates/mozart-registry/src/resolver.rs
@@ -733,6 +733,11 @@ pub struct ResolveRequest {
/// alias's version for any link originally written as `self.version`.
/// `None` when the root carries no matching `branch-alias` entry.
pub root_branch_alias: Option<String>,
+ /// `name → normalized version` map fed to the policy's preferred-version
+ /// override. Used by `update --minimal-changes` so the solver only moves
+ /// a package when a constraint actually forces a different version.
+ /// Empty for a normal full update.
+ pub preferred_versions: IndexMap<String, String>,
}
/// Full data for a lock-pinned package, used in partial updates. Carried on
@@ -1337,8 +1342,20 @@ pub async fn resolve(request: &ResolveRequest) -> Result<Vec<ResolvedPackage>, R
return Err(ResolveError::NoSolution(report));
}
- // Create policy and solve
- let policy = DefaultPolicy::new(request.prefer_stable, request.prefer_lowest);
+ // Create policy and solve. When `preferred_versions` is non-empty (the
+ // `--minimal-changes` flow) feed it through the policy so the locked
+ // version wins over the regular highest/lowest pick whenever a candidate
+ // matches it. Mirrors Composer's
+ // `Installer::createPolicy` minimal-update branch.
+ let policy = if request.preferred_versions.is_empty() {
+ DefaultPolicy::new(request.prefer_stable, request.prefer_lowest)
+ } else {
+ DefaultPolicy::with_preferred(
+ request.prefer_stable,
+ request.prefer_lowest,
+ request.preferred_versions.clone(),
+ )
+ };
let fixed_set: IndexSet<u32> = fixed_ids.into_iter().collect();
let solver = Solver::new(rules, &pool, policy, fixed_set);
@@ -1786,6 +1803,7 @@ mod tests {
locked_packages: Vec::new(),
block_abandoned: false,
root_branch_alias: None,
+ preferred_versions: IndexMap::new(),
};
let result = resolve(&request).await;