cmd/go/internal/vgo: track directly-used vs indirectly-used modules

A cleanup pass in mvs.BuildList discards modules that are not reachable
in the requirement graph as satisfied for this build. For example, suppose:

	A -> B1, C1
	B1 -> D1
	B2 -> nothing
	C1 -> nothing
	D1 -> nothing
	D2 -> nothing

The effective build list is A, B1, C1, D1 (no cleanup possible).

Suppose that we update from B1 to B2. The effective build list
becomes A, B2, C1, D1, and since there is no path through those
module versions from A to D, the cleanup pass drops D.

This cleanup, which is not in,
aims to avoid user confusion by not listing irrelevant modules in
the output of commands like "vgo list -m all".

Unfortunately, the cleanup is not sound in general, because
there is no guarantee all of A's needs are listed as direct requirements.
For example, maybe A imports D. In that case, dropping D and then
building A will re-add the latest version of D (D2 instead of D1).
The most common time this happens is after an upgrade.

The fix is to make sure that go.mod does list all of the modules
required directly by A, and to make sure that the go.mod
minimizer (Algorithm R in the blog post) does not remove
direct requirements in the name of simplifying go.mod.

The way this is done is to annotate the requirements NOT used
directly by A with a known comment, "// indirect".

For example suppose A imports Then the go.mod
looks like it always has:

	module m

	require v1.5.2

But now suppose we upgrade our packages to their latest versions.
Then go.mod becomes:

	module m

	require (
 v0.3.0 // indirect
 v1.99.99 // indirect

The "// indirect" comments indicate that this requirement is used
only to upgrade something needed outside module m, not to satisfy
any packages in module m itself.

Vgo adds and removes these comments automatically.
If we add a direct import of to some package in m,
then the first time we build that package vgo strips the "// indirect"
on the requirement line. If we then remove that
package, the requirement remains listed as direct (the conservative
choice) until the next "vgo mod -sync", which considers all packages
in m and can mark the requirement indirect again.
Algorithm R is modified to be given a set of import paths that must
be preserved in the final output (all the ones not marked // indirect).

Maintenance of this extra information makes the cleanup pass safe.

Seeing all directly-imported modules in go.mod
and distinguishing between directly- and indirectly-imported modules in go.mod
are two of the most commonly-requested features,
so it's extra nice that the fix for the cleanup-induced bug
makes go.mod closer to what users expect.

Fixes golang/go#24042.
Fixes golang/go#25371.
Fixes golang/go#25969.

Change-Id: I4ed0729b867723fe90e836c2325f740b55b2b27b
Reviewed-by: Bryan C. Mills <>
14 files changed
tree: f7b3efe46acd3bd90a7644035d80948c4deb06eb
  7. codereview.cfg
  8. main.go
  9. vendor/

Versioned Go Prototype (vgo)

This repository holds a prototype of what the go command might look like with integrated support for package versioning.

See for documents about the design.


Use go get -u

You can also manually git clone the repository to $GOPATH/src/

Report Issues / Send Patches


This is still a very early prototype. You are likely to run into bugs. Please file bugs in the main Go issue tracker,, and put the prefix x/vgo: in the issue title.

Thank you.