Skip to content

A way for users to bulk upgrade across incompatible versions #12425

Open

Description

@epage

View all comments

Problem

A lot of incompatible upgrades have a small enough of breakages that a "upgrade all of my incompatible version requirements" would make it easier to go through this process.

This is currently exposed in the cargo upgrade command

Proposed Solution

Tasks

Deferred

  • Modifying version requirements for compatible upgrades

See #12425 (comment)

Previously, https://internals.rust-lang.org/t/feedback-on-cargo-upgrade-to-prepare-it-for-merging/17101/141

Unresolved questions

  • Flag name
    • --breaking to focus on semver (as this is limited to ^)
    • --incompatible applies to all version req operators and doesn't have a good short flag (-i is generally assumed to be "interactive")
    • Dart calls it --major-versions though we have the issue of "absolute major" (x.0.0) and "relative major (also 0.x.0, 0.0.x).
    • pnpm calls it --latest because of a "latest" tag
  • What unupgradeable cases should error vs warn vs ignore? update: silent failure on non-matching package specs with --breaking #16276 (comment)
    • Maybe also clarifying why something is unupgradeable

Notes

Related

Activity

  1. added
    C-feature-requestCategory: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`
    S-triageStatus: This issue is waiting on initial triage.
    on Aug 1, 2023
  2. epage commented on Aug 1, 2023

    @epage
    ContributorAuthor

    Below is some background on what we are using to help come up with a design

    Care Abouts

    Priorities

    • Don't break behavior on cargo update
    • Don't write out incompatible Cargo.lock and Cargo.toml
    • Focus is on end-users solving common problems and not on being a general programmatic CLI that is meant to cover every case
    • Be predictable and understandable
      • Can someone unfamiliar with Rust, reading a blog post, predict what different command invocations will do?
      • Preference for not having too similarly named commands
    • When higher priorities allow, avoid errors that make users go "if you know exactly what I was asking for then why didn't you just do it?"; those are a sign of issues with the UX.
    • Don't be hassle when dealing with intentionally held back dependencies

    Primary use cases:

    • Want to have simple workflow for "upgrade incompatible dependencies only".
    • Want to have simple workflow for bulk lock to latest compatible (already exists as cargo update)
    • (medium priority) Selective modify one dependency's version requirement to latest compatible, latest incompatible
    • (lower priority) Want to have simple-ish workflow for bulk upgrade to latest compatible
    • (lower priority) bulk upgrade all dependencies (could just be two command invocations)

    Secondary use cases are:

    • Selective modify version requirement to user-provided value
    • Upgrade explicitly pinned version requirements

    Some open questions we had

    • How do we tell when a renamed dependency like tokio_03 is pinned or not?
      • We could just assume all renamed are pinned
      • We could add a dependency field but I'm a bit leery of adding that kind of bookkeeping to the manifest
      • We could force users to --exclude these dependencies but that might be a bit of a pain to always remember to do
      • We could only skip renamed if multiple dependencies exist that point to the same package

    Context

    Currently, cargo update is focused solely on Cargo.lock editing

    • Spans entire dependency tree
    • Multiple versions of a package may exist, referenced by name@version
    • Deals with exact versions and not version ranges
    • Only affects you and not your dependents

    Version requirement editing is different in that

    • Workspace members only
    • May want differences between members
    • Supports alternative names for packages
    • Affects dependents

    And as a reminder of the CLI:

    cargo update -p foo  # select a non-ambiguous `foo` and update it
    cargo update -p foo@ver  # selects a specific `foo` to update
    cargo update -p foo --aggressive  # also update dependencies
    cargo update -p foo --precise <ver>  # select a specific version
    cargo update --locked  # fail if the lockfile will change

    Note: cargo add --locked will also fail if the manifest will change

    Some design tools we can consider include

    • Renaming a command, making the old name an alias
      • Even if there isn't a culture shift to use the new name, cargo <cmd> --help and cargo --list will point people to the new name
    • Versions without the build metadata field is a subset of version requirement syntax, we may be able to do some mixing of them
      • Precedence: using the same foo@ver syntax for versions and version requirements
    • Minimal-version resolution being the default mode would make Cargo.lock mostly align with Cargo.toml, making it easier to conflate the two commands (whether merging them or keeping separate but de-emphasizing update)

    Interjection

    Through this, I realized that the core of my concern with our previous attempts at a single command is that it feels like we are shoehorning new behaviors into cargo update rather than making the behavior cohesive.

    • If I see a cargo update --incompatible on a blog post, can I predict what will happen if you do cargo update? No, because --save is needed to match behavior
    • We were trying to make --package be both for package IDs and dependency names to make some of the cargo upgrade workflows work
    • We were trying to overload --precise to allow control over version requirements

    I also realized that my Windows Updates vs Windows Upgrades analogy for cargo update and cargo upgrade breaks down a little because cargo upgrade can do "upgrades" that are on the level of cargo update (say we call it cargo upgrade --compatible). The difference is in the target audience (yourself vs your dependents)

  3. epage commented on Aug 1, 2023

    @epage
    ContributorAuthor

    Proposal: cargo update only changes version requirements as a side effect

    The primary role of cargo update has been to update your active dependencies (ie Cargo.lock). We do not plan to change that role but give the user control to force it to update in situations that were previously unsupported, particularly updating the Cargo.toml if need be.

    Behavior:

    • By default, only "safe" updates are allowed (today's behavior)
    • cargo update --incompatible / cargo update -i will force (ie update version reqs) update unpinned, incompatible versions (no other dependencies)
      • Yes, this could potentially be called --breaking or something else. The name depends on what expresses the concept clearly especially in light of any pinning behavior we have
      • Keeping -i unused would keep the door for a --interactive to be added
    • cargo update -p foo --precise ver will force the update to happen, only erroring if we can't (don't own relevant version reqs, is pinned), even if its incompatible but unpinned. Version requirement is only changed on incompatible.
      • Maybe the error on pinned could be relaxed

    Somewhere between deferred and rejected (speaking for myself): Support in cargo update for writing to the manifest for non-breaking changes, like bulk compatible upgrades of version requirements (ie a -save flag) which was one of our lower priority workflows. A --save flag is more about updating versions for your dependents, which while important for having valid lower-bounds on version requirements, doesn't fit with the existing model of cargo update. Maybe in the future we can find a way to express this in cargo update that fits with how it works or maybe another command can take on this role. We just aren't wanting to distract our efforts for handling most of the use cases to handle this one. See #10498

    While this tells a cohesive story, a part of me is somewhat concerned that this goes beyond the name update.

    Potential related cargo update improvements

    Alternatives

    These are alternatives I had considered that help give an idea of what I mean by fitting into cargo.

    cargo update always modifies Cargo.toml

    • This would be a breaking change
    • This would get in the way of people intentionally keeping separate versions from version requirements

    We deprecate cargo update and a cargo upgrade always updates both files

    • This would get in the way of people intentionally keeping separate versions from version requirements

    We migrate to minimal-version resolution by default

    • cargo update becomes less useful and we move it out of the spot light.
    • A cargo upgrade is added that is focused on editing version requirements

    Separate commands for Cargo.lock (update) and Cargo.toml (upgrade)

    • Names don't clarify the role each fills
    • Much like Debian has apt dist-upgrade, maybe it could be cargo req-update?

    Misc Notes

    • I don't see us making a distinction between default operator and ^ as we document them as being the same thing and sometimes people use ^ just because
    • We are erring on the side of needing cargo update && cargo update --incompatible vs cargo update --incompatible doing both as there are people who do want them separated and running two commands, while annoying, allows us to cover more use cases.
  4. added a commit that references this issue on Aug 1, 2023
  5. epage commented on Aug 8, 2023

    @epage
    ContributorAuthor

    This proposal has been up here and on internals for a bit now without any major concerns raised.

    @rfcbot fcp merge

  6. rfcbot commented on Aug 8, 2023

    @rfcbot

    Team member @epage has proposed to merge this. The next step is review by the rest of the tagged team members:

    No concerns currently listed.

    Once a majority of reviewers approve (and at most 2 approvals are outstanding), this will enter its final comment period. If you spot a major issue that hasn't been raised at any point in this process, please speak up!

    See this document for info about what commands tagged team members can give me.

  7. djc commented on Aug 8, 2023

    @djc
    Contributor

    Having been fairly involved in this discussion via the internals thread, I'm happy to see this move forward in a direction that I wholeheartedly support. @epage thanks for pushing this forward!

  8. rfcbot commented on Aug 14, 2023

    @rfcbot

    🔔 This is now entering its final comment period, as per the review above. 🔔

  9. 78 remaining items

  10. TheLostLambda commented on Aug 8, 2024

    @TheLostLambda

    And just to have this all in the same place, from #14178 (comment):

    I am not sure about if 2.0.0-beta.1 to 3.0.0-beta.1 should works

    Perhaps that's better as 2.0.0-beta.1 to 3.0.0? That way you have to opt into each pre-release series, but you never "skip over" a chance to return to stable?

    So this upgrading between pre-releases only happens when all of the pre-releases have that same MAJOR, MINOR, and PATCH versions, and otherwise it looks for a newer stable version to upgrade to?

  11. ia0 commented on Aug 8, 2024

    @ia0

    Yes, I agree the logic should be:

    • Use the highest precedence release (i.e. not prerelease) that has higher precendence. If there are none, continue.
    • If the version is a prerelease, use the highest precedence prerelease that has higher precedence and is within the same release (i.e. same major, minor, and patch). If there are none, continue.
    • Keep the version as is.

    (Note that even though I'm writing that, that's not what I would like to use. I would like something like #14372. But it could be a separate flag.)

  12. epage commented on Aug 8, 2024

    @epage
    ContributorAuthor

    From what I understand of the current semantics, they are what they should be.

    --breaking is a "force" flag to update a dependency when a ^ operator would otherwise prevent the update.

    At this time, updating through pre-releases is not a breaking change, so --breaking does not apply (see #6016, #2222, #3263). If you work around that with =, then --breaking still does not apply.

    To update pre-releases that use ^, then we need the feature that is left out of this Issue, a way to bump the minimum of version reqs to what is in the lockfile (see #10498).

    `"^2.0.0-beta.1" when "^2.0.0-beta.2" is available

    cargo update --breaking should not change compatible version requirements

    "^2.0.0-beta.1" when `"^2.0.0" is available

    cargo update --breaking should not change compatible version requirements

    `"^2.0.0-beta.1" when "^3.0.0-beta.1"

    We need to clarify the semantics for how to handle this case

  13. SUPERCILEX commented on Oct 19, 2024

    @SUPERCILEX

    @VorpalBlade I just ran into the same issue as you, seems like updating non-breaking versions (#12425 (comment)) is now being tracked here: #10498.

  14. added
    S-needs-mentorStatus: Issue or feature is accepted, but needs a team member to commit to helping and reviewing.
    and removed
    S-acceptedStatus: Issue or feature is accepted, and has a team member available to help mentor or review
    on Jan 22, 2026
  15. chrisduerr commented on Sep 19, 2026

    @chrisduerr
    Contributor

    I'm surprised to see how little activity this issue gets, after just running into the removal of cargo update --breaking -Z unstable-options from nightly. To me this has been one of the biggest QoL improvements to cargo that I can remember, so I'm sad to see it go.

    I would like to make the argument against just delegating to cargo upgrade indefinitely, since there's numerous issues with that to me. Personally I'm not a big fan of installing cargo plugins, since it's much more difficult to have faith in their quality (though cargo-edit is well maintained) and they are difficult to update (unless you want to install a plugin to manage your plugins).

    I think one of the main problems is the experience for new users. Even in here, people mostly refer to the alternative as cargo upgrade, but finding that alone is difficult, since the plugin is actually called cargo-edit with another plugin existing named cargo-update. I'm honestly surprised there haven't been any typosquatting attacks on this (though maybe they've just been at a scale small enough for me not to notice). Being able to just do what you want 90% of the time (which is what --breaking did for me in nightly) right there in cargo would certainly be useful to someone starting out with cargo and just looking through all the options.

    Ultimately I'm not asking for any change here, I'd just like to emphasize that the previous attempt has provided real value to users. If there's better ideas, it's obviously best to get it right from the start, but it would be a shame to never see this land in cargo just because no proposal is perfect.

  16. VorpalBlade commented on Sep 19, 2026

    @VorpalBlade

    I'm surprised to see how little activity this issue gets, after just running into the removal of cargo update --breaking -Z unstable-options from nightly. To me this has been one of the biggest QoL improvements to cargo that I can remember, so I'm sad to see it go.

    It was unstable, I never knew (or at least remembered) it was there. I used cargo upgrade add-on all along. As most users probably did. So the nightly feature was badly publicized if they actually wanted testing of it.

  17. martinetd commented on Sep 19, 2026

    @martinetd

    FWIW I actually used cargo +nightly -Z unstable-options update --breaking and only bothered to install cargo-edit after this was yanked recently. Didn't run into any problem with the unstable option.

  18. Timmmm commented on Oct 16, 2026

    @Timmmm
    Contributor

    So what's the status of this? I was very patiently waiting for it to be stabilised and now it's been removed?

    I think it would help if the very top of the issue could get some kind of bold status box so we don't have to read through 130 comments to see what is going on. Something like this:

    ⚠️ CURRENT STATUS
    For a while this feature was available on nightly through the --breaking flag, however that was removed in August 2026 in order to simplify development of alternative options. We decided not to go with --breaking because ????. The new provision plan is ????.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

A-new-subcommandArea: new subcommandC-feature-requestCategory: proposal for a feature. Before PR, ping rust-lang/cargo if this is not `Feature accepted`Command-updateS-needs-mentorStatus: Issue or feature is accepted, but needs a team member to commit to helping and reviewing.T-cargoTeam: CargoZ-update-breakingNightly: update-breakingdisposition-mergeFCP with intent to mergefinished-final-comment-periodFCP complete

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions