Repository: rust-lang/cargo (rust)

Issue:
`cargo publish` doesn't allow `--workspace`, multiple `--package`, or `--exclude`
Was testing out #1169 in `cargo-release` where we exclusively use `--package` and found it didn't work.

Pull request description:
Support package selection options like `--exclude` in `cargo publish`
Fixes #14652.

Is there a way to make the help text depend on whether nightly/unstable features are enabled? I couldn't find one...


Patch:
diff --git a/src/bin/cargo/commands/publish.rs b/src/bin/cargo/commands/publish.rs
index df1c4654f..42b6377c7 100644
--- a/src/bin/cargo/commands/publish.rs
+++ b/src/bin/cargo/commands/publish.rs
@@ -18,7 +18,11 @@ pub fn cli() -> Command {
             "Allow dirty working directories to be packaged",
         ))
         .arg_silent_suggestion()
-        .arg_package("Package to publish")
+        .arg_package_spec_no_all(
+            "Package(s) to publish",
+            "Publish all packages in the workspace (unstable)",
+            "Don't publish specified packages (unstable)",
+        )
         .arg_features()
         .arg_parallel()
         .arg_target_triple("Build for the target triple")
@@ -41,6 +45,23 @@ pub fn exec(gctx: &mut GlobalContext, args: &ArgMatches) -> CliResult {
         .into());
     }
 
+    let unstable = gctx.cli_unstable();
+    let enabled = unstable.package_workspace;
+    if args.get_flag("workspace") {
+        unstable.fail_if_stable_opt_custom_z("--workspace", 10948, "package-workspace", enabled)?;
+    }
+    if args._value_of("exclude").is_some() {
+        unstable.fail_if_stable_opt_custom_z("--exclude", 10948, "package-workspace", enabled)?;
+    }
+    if args._values_of("package").len() > 1 {
+        unstable.fail_if_stable_opt_custom_z(
+            "--package (multiple occurrences)",
+            10948,
+            "package-workspace",
+            enabled,
+        )?;
+    }
+
     ops::publish(
         &ws,
         &PublishOpts {
diff --git a/src/doc/man/cargo-publish.md b/src/doc/man/cargo-publish.md
index fa4c823ea..5c816b182 100644
--- a/src/doc/man/cargo-publish.md
+++ b/src/doc/man/cargo-publish.md
@@ -68,7 +68,64 @@ which defaults to `crates-io`.
 
 {{/options}}
 
-{{> section-options-package }}
+### Package Selection
+
+By default, when no package selection options are given, the packages selected
+depend on the selected manifest file (based on the current working directory if
+`--manifest-path` is not given). If the manifest is the root of a workspace then
+the workspaces default members are selected, otherwise only the package defined
+by the manifest will be selected.
+
+The default members of a workspace can be set explicitly with the
+`workspace.default-members` key in the root manifest. If this is not set, a
+virtual workspace will include all workspace members (equivalent to passing
+`--workspace`), and a non-virtual workspace will include only the root crate itself.
+
+Selecting more than one package is unstable and available only on the
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+and requires the `-Z package-workspace` flag to enable.
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+
+
+{{#options}}
+
+{{#option "`-p` _spec_..." "`--package` _spec_..."}}
+{{actionverb}} only the specified packages. See {{man "cargo-pkgid" 1}} for the
+SPEC format. This flag may be specified multiple times and supports common Unix
+glob patterns like `*`, `?` and `[]`. However, to avoid your shell accidentally 
+expanding glob patterns before Cargo handles them, you must use single quotes or
+double quotes around each pattern.
+{{/option}}
+
+Selecting more than one package with this option is unstable and available only
+on the
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+and requires the `-Z package-workspace` flag to enable.
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+
+{{#option "`--workspace`" }}
+{{actionverb}} all members in the workspace.
+
+This option is unstable and available only on the
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+and requires the `-Z package-workspace` flag to enable.
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+{{/option}}
+
+{{#option "`--exclude` _SPEC_..." }}
+Exclude the specified packages. Must be used in conjunction with the
+`--workspace` flag. This flag may be specified multiple times and supports
+common Unix glob patterns like `*`, `?` and `[]`. However, to avoid your shell
+accidentally expanding glob patterns before Cargo handles them, you must use
+single quotes or double quotes around each pattern.
+
+This option is unstable and available only on the
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+and requires the `-Z package-workspace` flag to enable.
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+{{/option}}
+
+{{/options}}
 
 ### Compilation Options
 
diff --git a/src/doc/man/generated_txt/cargo-publish.txt b/src/doc/man/generated_txt/cargo-publish.txt
index 6920f8dab..4b6d40aff 100644
--- a/src/doc/man/generated_txt/cargo-publish.txt
+++ b/src/doc/man/generated_txt/cargo-publish.txt
@@ -72,11 +72,65 @@ OPTIONS
            config key which defaults to crates-io.
 
    Package Selection
-       By default, the package in the current working directory is selected.
-       The -p flag can be used to choose a different package in a workspace.
+       By default, when no package selection options are given, the packages
+       selected depend on the selected manifest file (based on the current
+       working directory if --manifest-path is not given). If the manifest is
+       the root of a workspace then the workspaces default members are
+       selected, otherwise only the package defined by the manifest will be
+       selected.
 
-       -p spec, --package spec
-           The package to publish. See cargo-pkgid(1) for the SPEC format.
+       The default members of a workspace can be set explicitly with the
+       workspace.default-members key in the root manifest. If this is not set,
+       a virtual workspace will include all workspace members (equivalent to
+       passing --workspace), and a non-virtual workspace will include only the
+       root crate itself.
+
+       Selecting more than one package is unstable and available only on the
+       nightly channel
+       <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html> and
+       requires the -Z package-workspace flag to enable. See
+       <https://github.com/rust-lang/cargo/issues/10948> for more information.
+
+       -p spec…, --package spec…
+           Publish only the specified packages. See cargo-pkgid(1) for the SPEC
+           format. This flag may be specified multiple times and supports
+           common Unix glob patterns like *, ? and []. However, to avoid your
+           shell accidentally expanding glob patterns before Cargo handles
+           them, you must use single quotes or double quotes around each
+           pattern.
+
+Selecting more than one package with this option is unstable and available only
+
+on the
+
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+
+and requires the `-Z package-workspace` flag to enable.
+
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+
+       --workspace
+           Publish all members in the workspace.
+
+           This option is unstable and available only on the nightly channel
+           <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html> and
+           requires the -Z package-workspace flag to enable. See
+           <https://github.com/rust-lang/cargo/issues/10948> for more
+           information.
+
+       --exclude SPEC…
+           Exclude the specified packages. Must be used in conjunction with the
+           --workspace flag. This flag may be specified multiple times and
+           supports common Unix glob patterns like *, ? and []. However, to
+           avoid your shell accidentally expanding glob patterns before Cargo
+           handles them, you must use single quotes or double quotes around
+           each pattern.
+
+           This option is unstable and available only on the nightly channel
+           <https://doc.rust-lang.org/book/appendix-07-nightly-rust.html> and
+           requires the -Z package-workspace flag to enable. See
+           <https://github.com/rust-lang/cargo/issues/10948> for more
+           information.
 
    Compilation Options
        --target triple
diff --git a/src/doc/src/commands/cargo-publish.md b/src/doc/src/commands/cargo-publish.md
index d34ac72a6..d38547791 100644
--- a/src/doc/src/commands/cargo-publish.md
+++ b/src/doc/src/commands/cargo-publish.md
@@ -77,15 +77,58 @@ which defaults to <code>crates-io</code>.</dd>
 
 ### Package Selection
 
-By default, the package in the current working directory is selected. The `-p`
-flag can be used to choose a different package in a workspace.
+By default, when no package selection options are given, the packages selected
+depend on the selected manifest file (based on the current working directory if
+`--manifest-path` is not given). If the manifest is the root of a workspace then
+the workspaces default members are selected, otherwise only the package defined
+by the manifest will be selected.
+
+The default members of a workspace can be set explicitly with the
+`workspace.default-members` key in the root manifest. If this is not set, a
+virtual workspace will include all workspace members (equivalent to passing
+`--workspace`), and a non-virtual workspace will include only the root crate itself.
+
+Selecting more than one package is unstable and available only on the
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+and requires the `-Z package-workspace` flag to enable.
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+
 
 <dl>
 
-<dt class="option-term" id="option-cargo-publish--p"><a class="option-anchor" href="#option-cargo-publish--p"></a><code>-p</code> <em>spec</em></dt>
-<dt class="option-term" id="option-cargo-publish---package"><a class="option-anchor" href="#option-cargo-publish---package"></a><code>--package</code> <em>spec</em></dt>
-<dd class="option-desc">The package to publish. See <a href="cargo-pkgid.html">cargo-pkgid(1)</a> for the SPEC
-format.</dd>
+<dt class="option-term" id="option-cargo-publish--p"><a class="option-anchor" href="#option-cargo-publish--p"></a><code>-p</code> <em>spec</em>…</dt>
+<dt class="option-term" id="option-cargo-publish---package"><a class="option-anchor" href="#option-cargo-publish---package"></a><code>--package</code> <em>spec</em>…</dt>
+<dd class="option-desc">Publish only the specified packages. See <a href="cargo-pkgid.html">cargo-pkgid(1)</a> for the
+SPEC format. This flag may be specified multiple times and supports common Unix
+glob patterns like <code>*</code>, <code>?</code> and <code>[]</code>. However, to avoid your shell accidentally
+expanding glob patterns before Cargo handles them, you must use single quotes or
+double quotes around each pattern.</dd>
+
+
+Selecting more than one package with this option is unstable and available only
+on the
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+and requires the `-Z package-workspace` flag to enable.
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+
+<dt class="option-term" id="option-cargo-publish---workspace"><a class="option-anchor" href="#option-cargo-publish---workspace"></a><code>--workspace</code></dt>
+<dd class="option-desc">Publish all members in the workspace.</p>
+<p>This option is unstable and available only on the
+<a href="https://doc.rust-lang.org/book/appendix-07-nightly-rust.html">nightly channel</a>
+and requires the <code>-Z package-workspace</code> flag to enable.
+See <a href="https://github.com/rust-lang/cargo/issues/10948">https://github.com/rust-lang/cargo/issues/10948</a> for more information.</dd>
+
+
+<dt class="option-term" id="option-cargo-publish---exclude"><a class="option-anchor" href="#option-cargo-publish---exclude"></a><code>--exclude</code> <em>SPEC</em>…</dt>
+<dd class="option-desc">Exclude the specified packages. Must be used in conjunction with the
+<code>--workspace</code> flag. This flag may be specified multiple times and supports
+common Unix glob patterns like <code>*</code>, <code>?</code> and <code>[]</code>. However, to avoid your shell
+accidentally expanding glob patterns before Cargo handles them, you must use
+single quotes or double quotes around each pattern.</p>
+<p>This option is unstable and available only on the
+<a href="https://doc.rust-lang.org/book/appendix-07-nightly-rust.html">nightly channel</a>
+and requires the <code>-Z package-workspace</code> flag to enable.
+See <a href="https://github.com/rust-lang/cargo/issues/10948">https://github.com/rust-lang/cargo/issues/10948</a> for more information.</dd>
 
 
 </dl>
diff --git a/src/etc/man/cargo-publish.1 b/src/etc/man/cargo-publish.1
index c73dc28e8..8a33750d8 100644
--- a/src/etc/man/cargo-publish.1
+++ b/src/etc/man/cargo-publish.1
@@ -89,14 +89,59 @@ Otherwise it will use the default registry, which is defined by the
 which defaults to \fBcrates\-io\fR\&.
 .RE
 .SS "Package Selection"
-By default, the package in the current working directory is selected. The \fB\-p\fR
-flag can be used to choose a different package in a workspace.
-.sp
-\fB\-p\fR \fIspec\fR, 
-\fB\-\-package\fR \fIspec\fR
-.RS 4
-The package to publish. See \fBcargo\-pkgid\fR(1) for the SPEC
-format.
+By default, when no package selection options are given, the packages selected
+depend on the selected manifest file (based on the current working directory if
+\fB\-\-manifest\-path\fR is not given). If the manifest is the root of a workspace then
+the workspaces default members are selected, otherwise only the package defined
+by the manifest will be selected.
+.sp
+The default members of a workspace can be set explicitly with the
+\fBworkspace.default\-members\fR key in the root manifest. If this is not set, a
+virtual workspace will include all workspace members (equivalent to passing
+\fB\-\-workspace\fR), and a non\-virtual workspace will include only the root crate itself.
+.sp
+Selecting more than one package is unstable and available only on the
+\fInightly channel\fR <https://doc.rust\-lang.org/book/appendix\-07\-nightly\-rust.html>
+and requires the \fB\-Z package\-workspace\fR flag to enable.
+See <https://github.com/rust\-lang/cargo/issues/10948> for more information.
+.sp
+\fB\-p\fR \fIspec\fR\[u2026], 
+\fB\-\-package\fR \fIspec\fR\[u2026]
+.RS 4
+Publish only the specified packages. See \fBcargo\-pkgid\fR(1) for the
+SPEC format. This flag may be specified multiple times and supports common Unix
+glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell accidentally
+expanding glob patterns before Cargo handles them, you must use single quotes or
+double quotes around each pattern.
+.RE
+Selecting more than one package with this option is unstable and available only
+on the
+[nightly channel](https://doc.rust-lang.org/book/appendix-07-nightly-rust.html)
+and requires the `-Z package-workspace` flag to enable.
+See <https://github.com/rust-lang/cargo/issues/10948> for more information.
+.sp
+\fB\-\-workspace\fR
+.RS 4
+Publish all members in the workspace.
+.sp
+This option is unstable and available only on the
+\fInightly channel\fR <https://doc.rust\-lang.org/book/appendix\-07\-nightly\-rust.html>
+and requires the \fB\-Z package\-workspace\fR flag to enable.
+See <https://github.com/rust\-lang/cargo/issues/10948> for more information.
+.RE
+.sp
+\fB\-\-exclude\fR \fISPEC\fR\[u2026]
+.RS 4
+Exclude the specified packages. Must be used in conjunction with the
+\fB\-\-workspace\fR flag. This flag may be specified multiple times and supports
+common Unix glob patterns like \fB*\fR, \fB?\fR and \fB[]\fR\&. However, to avoid your shell
+accidentally expanding glob patterns before Cargo handles them, you must use
+single quotes or double quotes around each pattern.
+.sp
+This option is unstable and available only on the
+\fInightly channel\fR <https://doc.rust\-lang.org/book/appendix\-07\-nightly\-rust.html>
+and requires the \fB\-Z package\-workspace\fR flag to enable.
+See <https://github.com/rust\-lang/cargo/issues/10948> for more information.
 .RE
 .SS "Compilation Options"
 .sp


Test patch:
diff --git a/tests/testsuite/cargo_publish/help/stdout.term.svg b/tests/testsuite/cargo_publish/help/stdout.term.svg
index c678fe8f8..40981eb68 100644
--- a/tests/testsuite/cargo_publish/help/stdout.term.svg
+++ b/tests/testsuite/cargo_publish/help/stdout.term.svg
@@ -1,4 +1,4 @@
-<svg width="827px" height="776px" xmlns="http://www.w3.org/2000/svg">
+<svg width="827px" height="812px" xmlns="http://www.w3.org/2000/svg">
   <style>
     .fg { fill: #AAAAAA }
     .bg { background: #000000 }
@@ -59,49 +59,53 @@
 </tspan>
     <tspan x="10px" y="370px"><tspan class="fg-green bold">Package Selection:</tspan>
 </tspan>
-    <tspan x="10px" y="388px"><tspan>  </tspan><tspan class="fg-cyan bold">-p</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--package</tspan><tspan class="fg-cyan"> [</tspan><tspan class="fg-cyan">&lt;SPEC&gt;</tspan><tspan class="fg-cyan">]</tspan><tspan>  Package to publish</tspan>
+    <tspan x="10px" y="388px"><tspan>  </tspan><tspan class="fg-cyan bold">-p</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--package</tspan><tspan class="fg-cyan"> [</tspan><tspan class="fg-cyan">&lt;SPEC&gt;</tspan><tspan class="fg-cyan">]</tspan><tspan>  Package(s) to publish</tspan>
 </tspan>
-    <tspan x="10px" y="406px">
+    <tspan x="10px" y="406px"><tspan>      </tspan><tspan class="fg-cyan bold">--workspace</tspan><tspan>         Publish all packages in the workspace (unstable)</tspan>
 </tspan>
-    <tspan x="10px" y="424px"><tspan class="fg-green bold">Feature Selection:</tspan>
+    <tspan x="10px" y="424px"><tspan>      </tspan><tspan class="fg-cyan bold">--exclude</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;SPEC&gt;</tspan><tspan>    Don't publish specified packages (unstable)</tspan>
 </tspan>
-    <tspan x="10px" y="442px"><tspan>  </tspan><tspan class="fg-cyan bold">-F</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--features</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;FEATURES&gt;</tspan><tspan>  Space or comma separated list of features to activate</tspan>
+    <tspan x="10px" y="442px">
 </tspan>
-    <tspan x="10px" y="460px"><tspan>      </tspan><tspan class="fg-cyan bold">--all-features</tspan><tspan>         Activate all available features</tspan>
+    <tspan x="10px" y="460px"><tspan class="fg-green bold">Feature Selection:</tspan>
 </tspan>
-    <tspan x="10px" y="478px"><tspan>      </tspan><tspan class="fg-cyan bold">--no-default-features</tspan><tspan>  Do not activate the `default` feature</tspan>
+    <tspan x="10px" y="478px"><tspan>  </tspan><tspan class="fg-cyan bold">-F</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--features</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;FEATURES&gt;</tspan><tspan>  Space or comma separated list of features to activate</tspan>
 </tspan>
-    <tspan x="10px" y="496px">
+    <tspan x="10px" y="496px"><tspan>      </tspan><tspan class="fg-cyan bold">--all-features</tspan><tspan>         Activate all available features</tspan>
 </tspan>
-    <tspan x="10px" y="514px"><tspan class="fg-green bold">Compilation Options:</tspan>
+    <tspan x="10px" y="514px"><tspan>      </tspan><tspan class="fg-cyan bold">--no-default-features</tspan><tspan>  Do not activate the `default` feature</tspan>
 </tspan>
-    <tspan x="10px" y="532px"><tspan>  </tspan><tspan class="fg-cyan bold">-j</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--jobs</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;N&gt;</tspan><tspan>                Number of parallel jobs, defaults to # of CPUs.</tspan>
+    <tspan x="10px" y="532px">
 </tspan>
-    <tspan x="10px" y="550px"><tspan>      </tspan><tspan class="fg-cyan bold">--keep-going</tspan><tspan>              Do not abort the build as soon as there is an error</tspan>
+    <tspan x="10px" y="550px"><tspan class="fg-green bold">Compilation Options:</tspan>
 </tspan>
-    <tspan x="10px" y="568px"><tspan>      </tspan><tspan class="fg-cyan bold">--target</tspan><tspan class="fg-cyan"> [</tspan><tspan class="fg-cyan">&lt;TRIPLE&gt;</tspan><tspan class="fg-cyan">]</tspan><tspan>       Build for the target triple</tspan>
+    <tspan x="10px" y="568px"><tspan>  </tspan><tspan class="fg-cyan bold">-j</tspan><tspan>, </tspan><tspan class="fg-cyan bold">--jobs</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;N&gt;</tspan><tspan>                Number of parallel jobs, defaults to # of CPUs.</tspan>
 </tspan>
-    <tspan x="10px" y="586px"><tspan>      </tspan><tspan class="fg-cyan bold">--target-dir</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;DIRECTORY&gt;</tspan><tspan>  Directory for all generated artifacts</tspan>
+    <tspan x="10px" y="586px"><tspan>      </tspan><tspan class="fg-cyan bold">--keep-going</tspan><tspan>              Do not abort the build as soon as there is an error</tspan>
 </tspan>
-    <tspan x="10px" y="604px">
+    <tspan x="10px" y="604px"><tspan>      </tspan><tspan class="fg-cyan bold">--target</tspan><tspan class="fg-cyan"> [</tspan><tspan class="fg-cyan">&lt;TRIPLE&gt;</tspan><tspan class="fg-cyan">]</tspan><tspan>       Build for the target triple</tspan>
 </tspan>
-    <tspan x="10px" y="622px"><tspan class="fg-green bold">Manifest Options:</tspan>
+    <tspan x="10px" y="622px"><tspan>      </tspan><tspan class="fg-cyan bold">--target-dir</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;DIRECTORY&gt;</tspan><tspan>  Directory for all generated artifacts</tspan>
 </tspan>
-    <tspan x="10px" y="640px"><tspan>      </tspan><tspan class="fg-cyan bold">--manifest-path</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;PATH&gt;</tspan><tspan>  Path to Cargo.toml</tspan>
+    <tspan x="10px" y="640px">
 </tspan>
-    <tspan x="10px" y="658px"><tspan>      </tspan><tspan class="fg-cyan bold">--lockfile-path</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;PATH&gt;</tspan><tspan>  Path to Cargo.lock (unstable)</tspan>
+    <tspan x="10px" y="658px"><tspan class="fg-green bold">Manifest Options:</tspan>
 </tspan>
-    <tspan x="10px" y="676px"><tspan>      </tspan><tspan class="fg-cyan bold">--locked</tspan><tspan>                Assert that `Cargo.lock` will remain unchanged</tspan>
+    <tspan x="10px" y="676px"><tspan>      </tspan><tspan class="fg-cyan bold">--manifest-path</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;PATH&gt;</tspan><tspan>  Path to Cargo.toml</tspan>
 </tspan>
-    <tspan x="10px" y="694px"><tspan>      </tspan><tspan class="fg-cyan bold">--offline</tspan><tspan>               Run without accessing the network</tspan>
+    <tspan x="10px" y="694px"><tspan>      </tspan><tspan class="fg-cyan bold">--lockfile-path</tspan><tspan class="fg-cyan"> </tspan><tspan class="fg-cyan">&lt;PATH&gt;</tspan><tspan>  Path to Cargo.lock (unstable)</tspan>
 </tspan>
-    <tspan x="10px" y="712px"><tspan>      </tspan><tspan class="fg-cyan bold">--frozen</tspan><tspan>                Equivalent to specifying both --locked and --offline</tspan>
+    <tspan x="10px" y="712px"><tspan>      </tspan><tspan class="fg-cyan bold">--locked</tspan><tspan>                Assert that `Cargo.lock` will remain unchanged</tspan>
 </tspan>
-    <tspan x="10px" y="730px">
+    <tspan x="10px" y="730px"><tspan>      </tspan><tspan class="fg-cyan bold">--offline</tspan><tspan>               Run without accessing the network</tspan>
 </tspan>
-    <tspan x="10px" y="748px"><tspan>Run `</tspan><tspan class="fg-cyan bold">cargo help publish</tspan><tspan class="bold">` for more detailed information.</tspan>
+    <tspan x="10px" y="748px"><tspan>      </tspan><tspan class="fg-cyan bold">--frozen</tspan><tspan>                Equivalent to specifying both --locked and --offline</tspan>
 </tspan>
     <tspan x="10px" y="766px">
+</tspan>
+    <tspan x="10px" y="784px"><tspan>Run `</tspan><tspan class="fg-cyan bold">cargo help publish</tspan><tspan class="bold">` for more detailed information.</tspan>
+</tspan>
+    <tspan x="10px" y="802px">
 </tspan>
   </text>
 
diff --git a/tests/testsuite/publish.rs b/tests/testsuite/publish.rs
index 82b689e03..8753576b1 100644
--- a/tests/testsuite/publish.rs
+++ b/tests/testsuite/publish.rs
@@ -3364,6 +3364,120 @@ You may press ctrl-c to skip waiting; the crate should be available shortly.
         .run();
 }
 
+#[cargo_test]
+fn package_selection() {
+    let registry = registry::RegistryBuilder::new().http_api().build();
+    let p = project()
+        .file(
+            "Cargo.toml",
+            r#"
+                [workspace]
+                members = ["a", "b"]
+            "#,
+        )
+        .file("a/Cargo.toml", &basic_manifest("a", "0.1.0"))
+        .file("a/src/lib.rs", "#[test] fn a() {}")
+        .file("b/Cargo.toml", &basic_manifest("b", "0.1.0"))
+        .file("b/src/lib.rs", "#[test] fn b() {}")
+        .build();
+
+    p.cargo("publish --no-verify --dry-run -Zpackage-workspace --workspace")
+        .replace_crates_io(registry.index_url())
+        .masquerade_as_nightly_cargo(&["package-workspace"])
+        .with_stderr_data(str![[r#"
+[UPDATING] crates.io index
+[WARNING] manifest has no description, license, license-file, documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+[PACKAGING] a v0.1.0 ([ROOT]/foo/a)
+[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
+[WARNING] manifest has no description, license, license-file, documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+[PACKAGING] b v0.1.0 ([ROOT]/foo/b)
+[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
+[UPLOADING] a v0.1.0 ([ROOT]/foo/a)
+[WARNING] aborting upload due to dry run
+[UPLOADING] b v0.1.0 ([ROOT]/foo/b)
+[WARNING] aborting upload due to dry run
+
+"#]])
+        .with_stdout_data(str![[r#""#]])
+        .run();
+
+    p.cargo("publish --no-verify --dry-run -Zpackage-workspace --package a --package b")
+        .replace_crates_io(registry.index_url())
+        .masquerade_as_nightly_cargo(&["package-workspace"])
+        .with_stderr_data(str![[r#"
+[UPDATING] crates.io index
+[WARNING] manifest has no description, license, license-file, documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+[PACKAGING] a v0.1.0 ([ROOT]/foo/a)
+[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
+[WARNING] manifest has no description, license, license-file, documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+[PACKAGING] b v0.1.0 ([ROOT]/foo/b)
+[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
+[UPLOADING] a v0.1.0 ([ROOT]/foo/a)
+[WARNING] aborting upload due to dry run
+[UPLOADING] b v0.1.0 ([ROOT]/foo/b)
+[WARNING] aborting upload due to dry run
+
+"#]])
+        .with_stdout_data(str![[r#""#]])
+        .run();
+
+    p.cargo("publish --no-verify --dry-run -Zpackage-workspace --workspace --exclude b")
+        .replace_crates_io(registry.index_url())
+        .masquerade_as_nightly_cargo(&["package-workspace"])
+        .with_stderr_data(str![[r#"
+[UPDATING] crates.io index
+[WARNING] manifest has no description, license, license-file, documentation, homepage or repository.
+See https://doc.rust-lang.org/cargo/reference/manifest.html#package-metadata for more info.
+[PACKAGING] a v0.1.0 ([ROOT]/foo/a)
+[PACKAGED] 3 files, [FILE_SIZE]B ([FILE_SIZE]B compressed)
+[UPLOADING] a v0.1.0 ([ROOT]/foo/a)
+[WARNING] aborting upload due to dry run
+
+"#]])
+        .with_stdout_data(str![[r#""#]])
+        .run();
+
+    p.cargo("publish --no-verify --dry-run --package a --package b")
+        .replace_crates_io(registry.index_url())
+        .with_status(101)
+        .with_stderr_data(str![[r#"
+[ERROR] the `--package (multiple occurrences)` flag is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel
+See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels.
+See https://github.com/rust-lang/cargo/issues/10948 for more information about the `--package (multiple occurrences)` flag.
+
+"#]])
+        .with_stdout_data(str![[r#""#]])
+        .run();
+
+    p.cargo("publish --no-verify --dry-run --workspace")
+        .replace_crates_io(registry.index_url())
+        .with_status(101)
+        .with_stderr_data(str![[r#"
+[ERROR] the `--workspace` flag is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel
+See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels.
+See https://github.com/rust-lang/cargo/issues/10948 for more information about the `--workspace` flag.
+
+"#]])
+        .with_stdout_data(str![[r#""#]])
+        .run();
+
+    p.cargo("publish --no-verify --dry-run --exclude b")
+        .replace_crates_io(registry.index_url())
+        .with_status(101)
+        .with_stderr_data(str![[r#"
+[ERROR] the `--exclude` flag is unstable, and only available on the nightly channel of Cargo, but this is the `stable` channel
+See https://doc.rust-lang.org/book/appendix-07-nightly-rust.html for more information about Rust release channels.
+See https://github.com/rust-lang/cargo/issues/10948 for more information about the `--exclude` flag.
+
+"#]])
+        .with_stdout_data(str![[r#""#]])
+        .run();
+}
+
 #[cargo_test]
 fn wait_for_git_publish() {
     // Slow publish to an index with a git index.