Support unvendored repos: precise go.mod diffing on upstream single-root workspaces#5
Merged
Conversation
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
jalaziz
approved these changes
Jun 16, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Rebase of the unvendored-repo support onto upstream's single-root
go.workfix (PR digitalocean#73), plus two robustness fixes.PackageFromImporterrors; filter non-local dependents.BaseFileReader+SetBaseGoMod/SetBaseGoSum; precise go.mod/go.sum dependency diffing replaces mark-all./tmp, bind mounts).toplevel()error preserved (%w).Workspace opt-out is
GOWORK=off(the fork'sgo.workcommit is dropped).go test -race ./...andgo vet ./...clean; no new dependencies beyondgolang.org/x/modpromoted to direct.