Skip to content

Commit 98ccf0f

Browse files
committed
fix: Use ops::update_lockfile for consistency with non-breaking update.
1 parent ef1656c commit 98ccf0f

File tree

3 files changed

+108
-33
lines changed

3 files changed

+108
-33
lines changed

src/bin/cargo/commands/update.rs

Lines changed: 26 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
use std::collections::HashMap;
2+
13
use crate::command_prelude::*;
24

35
use anyhow::anyhow;
46
use cargo::ops::{self, UpdateOptions};
57
use cargo::util::print_available_packages;
8+
use tracing::trace;
69

710
pub fn cli() -> Command {
811
subcommand("update")
@@ -92,28 +95,38 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
9295
let update_opts = UpdateOptions {
9396
recursive: args.flag("recursive"),
9497
precise: args.get_one::<String>("precise").map(String::as_str),
98+
breaking: args.flag("breaking"),
9599
to_update,
96100
dry_run: args.dry_run(),
97101
workspace: args.flag("workspace"),
98102
gctx,
99103
};
100104

101-
if args.flag("breaking") {
102-
gctx.cli_unstable()
103-
.fail_if_stable_opt("--breaking", 12425)?;
104-
105-
let upgrades = ops::upgrade_manifests(&mut ws, &update_opts.to_update)?;
106-
ops::resolve_ws(&ws, update_opts.dry_run)?;
107-
ops::write_manifest_upgrades(&ws, &upgrades, update_opts.dry_run)?;
105+
let breaking_update = update_opts.breaking; // or if doing a breaking precise update, coming in #14140.
108106

109-
if update_opts.dry_run {
110-
update_opts
111-
.gctx
112-
.shell()
113-
.warn("aborting update due to dry run")?;
107+
// We are using the term "upgrade" here, which is the typical case, but it
108+
// can also be a downgrade (in the case of a precise update). In general, it
109+
// is a change to a version req matching a precise version.
110+
let upgrades = if breaking_update {
111+
if update_opts.breaking {
112+
gctx.cli_unstable()
113+
.fail_if_stable_opt("--breaking", 12425)?;
114114
}
115+
116+
trace!("allowing breaking updates");
117+
ops::upgrade_manifests(&mut ws, &update_opts.to_update)?
115118
} else {
116-
ops::update_lockfile(&ws, &update_opts)?;
119+
HashMap::new()
120+
};
121+
122+
ops::update_lockfile(&ws, &update_opts, &upgrades)?;
123+
ops::write_manifest_upgrades(&ws, &upgrades, update_opts.dry_run)?;
124+
125+
if update_opts.dry_run {
126+
update_opts
127+
.gctx
128+
.shell()
129+
.warn("aborting update due to dry run")?;
117130
}
118131

119132
Ok(())

src/cargo/ops/cargo_update.rs

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use crate::ops;
99
use crate::sources::source::QueryKind;
1010
use crate::util::cache_lock::CacheLockMode;
1111
use crate::util::context::GlobalContext;
12+
use crate::util::interning::InternedString;
1213
use crate::util::toml_mut::dependency::{MaybeWorkspace, Source};
1314
use crate::util::toml_mut::manifest::LocalManifest;
1415
use crate::util::toml_mut::upgrade::upgrade_requirement;
@@ -20,12 +21,24 @@ use std::cmp::Ordering;
2021
use std::collections::{BTreeMap, HashMap, HashSet};
2122
use tracing::{debug, trace};
2223

23-
pub type UpgradeMap = HashMap<(String, SourceId), Version>;
24+
/// A map of all breaking upgrades which is filled in by
25+
/// upgrade_manifests/upgrade_dependency when going through workspace member
26+
/// manifests, and later used by write_manifest_upgrades in order to know which
27+
/// upgrades to write to manifest files on disk. Also used by update_lockfile to
28+
/// know which dependencies to keep unchanged if any have been upgraded (we will
29+
/// do either breaking or non-breaking updates, but not both).
30+
pub type UpgradeMap = HashMap<
31+
// The key is a tuple of the package name and the source id of the package.
32+
(InternedString, SourceId),
33+
// The value is the upgraded version of the package.
34+
Version,
35+
>;
2436

2537
pub struct UpdateOptions<'a> {
2638
pub gctx: &'a GlobalContext,
2739
pub to_update: Vec<String>,
2840
pub precise: Option<&'a str>,
41+
pub breaking: bool,
2942
pub recursive: bool,
3043
pub dry_run: bool,
3144
pub workspace: bool,
@@ -49,7 +62,11 @@ pub fn generate_lockfile(ws: &Workspace<'_>) -> CargoResult<()> {
4962
Ok(())
5063
}
5164

52-
pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoResult<()> {
65+
pub fn update_lockfile(
66+
ws: &Workspace<'_>,
67+
opts: &UpdateOptions<'_>,
68+
upgrades: &UpgradeMap,
69+
) -> CargoResult<()> {
5370
if opts.recursive && opts.precise.is_some() {
5471
anyhow::bail!("cannot specify both recursive and precise simultaneously")
5572
}
@@ -165,7 +182,13 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
165182
.filter(|s| !s.is_registry())
166183
.collect();
167184

168-
let keep = |p: &PackageId| !to_avoid_sources.contains(&p.source_id()) && !to_avoid.contains(p);
185+
let keep = |p: &PackageId| {
186+
(!to_avoid_sources.contains(&p.source_id()) && !to_avoid.contains(p))
187+
// In case of `--breaking` (or precise upgrades, coming in
188+
// #14140), we want to keep all packages unchanged that didn't
189+
// get upgraded.
190+
|| (opts.breaking && !upgrades.contains_key(&(p.name(), p.source_id())))
191+
};
169192

170193
let mut resolve = ops::resolve_with_previous(
171194
&mut registry,
@@ -185,11 +208,7 @@ pub fn update_lockfile(ws: &Workspace<'_>, opts: &UpdateOptions<'_>) -> CargoRes
185208
opts.precise.is_some(),
186209
&mut registry,
187210
)?;
188-
if opts.dry_run {
189-
opts.gctx
190-
.shell()
191-
.warn("not updating lockfile due to dry run")?;
192-
} else {
211+
if !opts.dry_run {
193212
ops::write_pkg_lockfile(ws, &mut resolve)?;
194213
}
195214
Ok(())
@@ -361,7 +380,7 @@ fn upgrade_dependency(
361380
.status_with_color("Upgrading", &upgrade_message, &style::GOOD)?;
362381
}
363382

364-
upgrades.insert((name.to_string(), dependency.source_id()), latest.clone());
383+
upgrades.insert((name, dependency.source_id()), latest.clone());
365384

366385
let req = OptVersionReq::Req(VersionReq::parse(&latest.to_string())?);
367386
let mut dep = dependency.clone();
@@ -433,7 +452,7 @@ pub fn write_manifest_upgrades(
433452
continue;
434453
};
435454

436-
let Some(latest) = upgrades.get(&(name.to_owned(), source_id)) else {
455+
let Some(latest) = upgrades.get(&(name.into(), source_id)) else {
437456
trace!("skipping dependency without an upgrade: {name}");
438457
continue;
439458
};

tests/testsuite/update.rs

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -925,7 +925,7 @@ fn dry_run_update() {
925925
[LOCKING] 1 package to latest compatible version
926926
[UPDATING] serde v0.1.0 -> v0.1.1
927927
[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
928-
[WARNING] not updating lockfile due to dry run
928+
[WARNING] aborting update due to dry run
929929
930930
"#]])
931931
.run();
@@ -1524,7 +1524,7 @@ fn report_behind() {
15241524
[LOCKING] 1 package to latest compatible version
15251525
[UPDATING] breaking v0.1.0 -> v0.1.1 (latest: v0.2.0)
15261526
[NOTE] pass `--verbose` to see 2 unchanged dependencies behind latest
1527-
[WARNING] not updating lockfile due to dry run
1527+
[WARNING] aborting update due to dry run
15281528
15291529
"#]])
15301530
.run();
@@ -1537,7 +1537,7 @@ fn report_behind() {
15371537
[UNCHANGED] pre v1.0.0-alpha.0 (latest: v1.0.0-alpha.1)
15381538
[UNCHANGED] two-ver v0.1.0 (latest: v0.2.0)
15391539
[NOTE] to see how you depend on a package, run `cargo tree --invert --package <dep>@<ver>`
1540-
[WARNING] not updating lockfile due to dry run
1540+
[WARNING] aborting update due to dry run
15411541
15421542
"#]])
15431543
.run();
@@ -1549,7 +1549,7 @@ fn report_behind() {
15491549
[UPDATING] `dummy-registry` index
15501550
[LOCKING] 0 packages to latest compatible versions
15511551
[NOTE] pass `--verbose` to see 3 unchanged dependencies behind latest
1552-
[WARNING] not updating lockfile due to dry run
1552+
[WARNING] aborting update due to dry run
15531553
15541554
"#]])
15551555
.run();
@@ -1562,7 +1562,7 @@ fn report_behind() {
15621562
[UNCHANGED] pre v1.0.0-alpha.0 (latest: v1.0.0-alpha.1)
15631563
[UNCHANGED] two-ver v0.1.0 (latest: v0.2.0)
15641564
[NOTE] to see how you depend on a package, run `cargo tree --invert --package <dep>@<ver>`
1565-
[WARNING] not updating lockfile due to dry run
1565+
[WARNING] aborting update due to dry run
15661566
15671567
"#]])
15681568
.run();
@@ -1912,10 +1912,13 @@ fn update_breaking() {
19121912
[UPDATING] multiple-registries v2.0.0 (registry `alternative`) -> v3.0.0
19131913
[UPDATING] multiple-registries v1.0.0 -> v2.0.0
19141914
[UPDATING] multiple-source-types v1.0.0 -> v2.0.0
1915+
[REMOVING] multiple-versions v1.0.0
1916+
[REMOVING] multiple-versions v2.0.0
19151917
[ADDING] multiple-versions v3.0.0
19161918
[UPDATING] platform-specific v1.0.0 -> v2.0.0
19171919
[UPDATING] shared v1.0.0 -> v2.0.0
19181920
[UPDATING] ws v1.0.0 -> v2.0.0
1921+
[NOTE] pass `--verbose` to see 4 unchanged dependencies behind latest
19191922
19201923
"#]])
19211924
.run();
@@ -2108,6 +2111,7 @@ fn update_breaking_specific_packages() {
21082111
[UPDATING] transitive-compatible v1.0.0 -> v1.0.1
21092112
[UPDATING] transitive-incompatible v1.0.0 -> v2.0.0
21102113
[UPDATING] ws v1.0.0 -> v2.0.0
2114+
[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
21112115
21122116
"#]])
21132117
.run();
@@ -2163,6 +2167,8 @@ fn update_breaking_specific_packages_that_wont_update() {
21632167
.masquerade_as_nightly_cargo(&["update-breaking"])
21642168
.with_stderr_data(str![[r#"
21652169
[UPDATING] `dummy-registry` index
2170+
[LOCKING] 0 packages to latest compatible versions
2171+
[NOTE] pass `--verbose` to see 5 unchanged dependencies behind latest
21662172
21672173
"#]])
21682174
.run();
@@ -2271,14 +2277,27 @@ fn update_breaking_spec_version() {
22712277
// Spec version not matching our current dependencies
22722278
p.cargo("update -Zunstable-options --breaking [email protected]")
22732279
.masquerade_as_nightly_cargo(&["update-breaking"])
2274-
.with_stderr_data(str![[r#""#]])
2280+
.with_status(101)
2281+
.with_stderr_data(str![[r#"
2282+
[ERROR] package ID specification `[email protected]` did not match any packages
2283+
Did you mean one of these?
2284+
2285+
2286+
2287+
"#]])
22752288
.run();
22762289

22772290
// Spec source not matching our current dependencies
22782291
p.cargo("update -Zunstable-options --breaking https://alternative.com#[email protected]")
22792292
.masquerade_as_nightly_cargo(&["update-breaking"])
2280-
.with_stderr_data(str![[r#""#]])
2281-
.run();
2293+
.with_status(101)
2294+
.with_stderr_data(str![[r#"
2295+
[ERROR] package ID specification `https://alternative.com/#[email protected]` did not match any packages
2296+
Did you mean one of these?
2297+
2298+
2299+
2300+
"#]]) .run();
22822301

22832302
// Accepted spec
22842303
p.cargo("update -Zunstable-options --breaking [email protected]")
@@ -2288,6 +2307,7 @@ fn update_breaking_spec_version() {
22882307
[UPGRADING] incompatible ^1.0 -> ^2.0
22892308
[LOCKING] 1 package to latest compatible version
22902309
[UPDATING] incompatible v1.0.0 -> v2.0.0
2310+
[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
22912311
22922312
"#]])
22932313
.run();
@@ -2301,6 +2321,7 @@ fn update_breaking_spec_version() {
23012321
[UPGRADING] incompatible ^2.0 -> ^3.0
23022322
[LOCKING] 1 package to latest compatible version
23032323
[UPDATING] incompatible v2.0.0 -> v3.0.0
2324+
[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
23042325
23052326
"#]])
23062327
.run();
@@ -2310,19 +2331,35 @@ fn update_breaking_spec_version() {
23102331
.masquerade_as_nightly_cargo(&["update-breaking"])
23112332
.with_stderr_data(str![[r#"
23122333
[UPDATING] `dummy-registry` index
2334+
[LOCKING] 0 packages to latest compatible versions
2335+
[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
23132336
23142337
"#]])
23152338
.run();
23162339

23172340
// Non-existing versions
23182341
p.cargo("update -Zunstable-options --breaking [email protected]")
23192342
.masquerade_as_nightly_cargo(&["update-breaking"])
2320-
.with_stderr_data(str![[r#""#]])
2343+
.with_status(101)
2344+
.with_stderr_data(str![[r#"
2345+
[ERROR] package ID specification `[email protected]` did not match any packages
2346+
Did you mean one of these?
2347+
2348+
2349+
2350+
"#]])
23212351
.run();
23222352

23232353
p.cargo("update -Zunstable-options --breaking [email protected]")
23242354
.masquerade_as_nightly_cargo(&["update-breaking"])
2325-
.with_stderr_data(str![[r#""#]])
2355+
.with_status(101)
2356+
.with_stderr_data(str![[r#"
2357+
[ERROR] package ID specification `[email protected]` did not match any packages
2358+
Did you mean one of these?
2359+
2360+
2361+
2362+
"#]])
23262363
.run();
23272364
}
23282365

@@ -2376,6 +2413,7 @@ fn update_breaking_spec_version_transitive() {
23762413
[UPGRADING] dep ^1.0 -> ^3.0
23772414
[LOCKING] 1 package to latest compatible version
23782415
[UPDATING] dep v1.0.0 -> v3.0.0
2416+
[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
23792417
23802418
"#]])
23812419
.run();
@@ -2385,6 +2423,8 @@ fn update_breaking_spec_version_transitive() {
23852423
.masquerade_as_nightly_cargo(&["update-breaking"])
23862424
.with_stderr_data(str![[r#"
23872425
[UPDATING] `dummy-registry` index
2426+
[LOCKING] 0 packages to latest compatible versions
2427+
[NOTE] pass `--verbose` to see 1 unchanged dependencies behind latest
23882428
23892429
"#]])
23902430
.run();
@@ -2453,6 +2493,8 @@ fn update_breaking_mixed_compatibility() {
24532493
[UPDATING] `dummy-registry` index
24542494
[UPGRADING] mixed-compatibility ^1.0 -> ^2.0
24552495
[LOCKING] 1 package to latest compatible version
2496+
[REMOVING] mixed-compatibility v1.0.0
2497+
[REMOVING] mixed-compatibility v2.0.0
24562498
[ADDING] mixed-compatibility v2.0.1
24572499
24582500
"#]])
@@ -2544,6 +2586,7 @@ fn update_breaking_mixed_pinning_renaming() {
25442586
[ADDING] mixed-pinned v2.0.0
25452587
[ADDING] mixed-ws-pinned v2.0.0
25462588
[ADDING] renamed-from v2.0.0
2589+
[NOTE] pass `--verbose` to see 3 unchanged dependencies behind latest
25472590
25482591
"#]])
25492592
.run();

0 commit comments

Comments
 (0)