Skip to content

Support unvendored repos: precise go.mod diffing on upstream single-root workspaces#5

Merged
bts-bastion merged 14 commits into
masterfrom
bts/go-unvendored
Jun 16, 2026
Merged

Support unvendored repos: precise go.mod diffing on upstream single-root workspaces#5
bts-bastion merged 14 commits into
masterfrom
bts/go-unvendored

Conversation

@bts-bastion

@bts-bastion bts-bastion commented Jun 16, 2026

Copy link
Copy Markdown

Rebase of the unvendored-repo support onto upstream's single-root go.work fix (PR digitalocean#73), plus two robustness fixes.

  • Don't-crash hardening: skip packages with load errors; tolerate PackageFromImport errors; filter non-local dependents.
  • BaseFileReader + SetBaseGoMod/SetBaseGoSum; precise go.mod/go.sum dependency diffing replaces mark-all.
  • Base-file paths resolved against the repo root (absolute-path contract).
  • Symlink normalization before path comparison (macOS /tmp, bind mounts).
  • Underlying toplevel() error preserved (%w).

Workspace opt-out is GOWORK=off (the fork's go.work commit is dropped). go test -race ./... and go vet ./... clean; no new dependencies beyond golang.org/x/mod promoted to direct.

These methods enable distinguishing local (main module) packages from
external dependencies and mapping changed dependency modules to the
local packages that import them. Foundation for unvendored repo support.

Co-authored by: Blue Thunder Somogyi
When packages.Load returns packages with errors (common for external
dependencies in unvendored repos), skip them when building the
forward/reverse dependency graphs. Module info is still recorded.

Co-authored by: Blue Thunder Somogyi
When a package in the dependency graph cannot be resolved (e.g., an
external dependency in an unvendored repo), skip it instead of
returning an error from ChangedPackages(). This prevents GTA from
crashing on unvendored repositories.

Co-authored by: Blue Thunder Somogyi
When traversing the dependency graph in markedPackages(), skip edges
to external (non-local) packages. Uses a type assertion so custom
Packager implementations retain the existing traversal behavior.
In GOPATH mode (no modules), all packages are treated as local.

Co-authored by: Blue Thunder Somogyi
New file gomod.go provides diffGoMod() and diffGoSum() for precise
dependency change detection. diffGoMod compares Require and Replace
directives using modfile.Parse with ParseLax fallback. diffGoSum does
line-level comparison to catch transitive dependency changes.

Co-authored by: Blue Thunder Somogyi
BaseFileReader enables reading file content at the git merge-base,
used to retrieve old go.mod/go.sum for dependency diff analysis.
SetBaseGoMod and SetBaseGoSum options provide the same capability
for file-differ mode (no git access).

Co-authored by: Blue Thunder Somogyi
When go.mod or go.sum changes in an unvendored repo, GTA now parses
the diff to identify exactly which dependency modules changed, then
marks only the local packages that import those modules. This replaces
the previous "mark all packages" approach which negated GTA's value.

Falls back to mark-all when base file content is unavailable (e.g.,
custom Differ without BaseFileReader, no SetBaseGoMod option).

go.work changes continue to use mark-all since workspace structural
changes warrant full re-evaluation.

Co-authored by: Blue Thunder Somogyi
…aces

Originally written against the fork's multi-root workspace implementation,
where it demonstrated a false negative: relativeModFilePath() computed
module-relative paths ("go.mod") while BaseFileReader.ReadBaseFile resolves
git-repo-root-relative paths, silently diffing the repo-root go.mod against
the module's new go.mod and marking nothing for a dependency bump.

On this branch (upstream single workspace root = directory containing
go.work) the computed path is correct and the test passes. It remains as a
regression guard for the path-resolution contract, which Task 6 hardens by
moving path resolution into the differ itself.
BaseFileReader.ReadBaseFile now takes an absolute path and the git differ
resolves it against the repo toplevel it already discovers during diff().
This removes relativeModFilePath(), whose g.roots-based computation was
only correct when the workspace/module root coincided with the git
toplevel, and rejects paths outside the repository.
Ported from the fork's go.work support work: verifies that in a Go
workspace, a change in one module marks dependent packages in sibling
modules, an isolated module reports only itself, and a go.work change
marks all workspace packages. These pass against upstream's single-root
workspace handling (PR digitalocean#73) without any multi-root machinery; gta relies
on packages.Load workspace awareness for cross-module resolution.
Wrap the toplevel() error with %w so an inconsistent-vendoring error
(from `go list -m`) surfaces to the caller instead of being replaced
with a generic "could not get top level directory" message.
packages.Load reports Module.Dir with symlinks resolved, but the git
differ root, GOWORK-derived roots, and caller-supplied differ paths may
be un-resolved. On systems where the repo path differs from its resolved
form (macOS /tmp -> /private/tmp, container bind mounts, automounts) the
equality/prefix comparisons in resolveLocal and isIgnoredByGo never
match, so a changed package silently resolves to nothing.

Add canonicalDir (EvalSymlinks with a deepest-existing-ancestor fallback
for deleted dirs) and normalize incoming paths in resolveLocal, the git
differ root, and roots before comparison.
Every git invocation in the differ now surfaces the exact command line
(cmd.String()) and git's full stderr in the returned error, instead of a
bare "exit status N". Adds a shared gitCmdError helper used by
execWithStderr and the previously-unwrapped `git diff` invocation in
diff() (which captured neither the command nor stderr). The underlying
error is preserved via %w so error-chain inspection still works.
Report full git command and stderr on git command failures
@bts-bastion bts-bastion merged commit 74dd492 into master Jun 16, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants