Skip to content

Commit 0b90c7d

Browse files
Bryan C. Millsgopherbot
Bryan C. Mills
authored andcommitted
cmd/go: use a nil *Origin to represent "uncheckable"
Previously, we used the presence of individual origin fields to decide whether an Origin could be checked for staleness, with a nil Origin representing “use whatever you have”. However, that turns out to be fairly bug-prone: if we forget to populate an Origin somewhere, we end up with an incomplete check instead of a non-reusable origin (see #61415, #61423). As of CL 543155, the reusability check for a given query now depends on what is needed by the query more than what is populated in the origin. With that in place, we can simplify the handling of the Origin struct by using a nil pointer to represent inconsistent or unavailable origin data, and otherwise always reporting whatever origin information we have regardless of whether we expect it to be reused. Updates #61415. Updates #61423. Change-Id: I97c51063d6c2afa394a05bf304a80c72c08f82cf Cq-Include-Trybots: luci.golang.try:gotip-linux-amd64-longtest,gotip-windows-amd64-longtest Reviewed-on: https://go-review.googlesource.com/c/go/+/543216 Auto-Submit: Bryan Mills <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Michael Matloob <[email protected]>
1 parent bb8a96f commit 0b90c7d

File tree

6 files changed

+52
-54
lines changed

6 files changed

+52
-54
lines changed

src/cmd/go/internal/modfetch/codehost/codehost.go

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -122,21 +122,6 @@ type Origin struct {
122122
RepoSum string `json:",omitempty"`
123123
}
124124

125-
// Checkable reports whether the Origin contains anything that can be checked.
126-
// If not, the Origin is purely informational and should fail a CheckReuse call.
127-
func (o *Origin) Checkable() bool {
128-
return o != nil && (o.TagSum != "" || o.Ref != "" || o.Hash != "" || o.RepoSum != "")
129-
}
130-
131-
// ClearCheckable clears the Origin enough to make Checkable return false.
132-
func (o *Origin) ClearCheckable() {
133-
o.TagSum = ""
134-
o.TagPrefix = ""
135-
o.Ref = ""
136-
o.Hash = ""
137-
o.RepoSum = ""
138-
}
139-
140125
// A Tags describes the available tags in a code repository.
141126
type Tags struct {
142127
Origin *Origin

src/cmd/go/internal/modfetch/coderepo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -362,7 +362,7 @@ func (r *codeRepo) convert(ctx context.Context, info *codehost.RevInfo, statVers
362362
}
363363
tags, tagsErr := r.code.Tags(ctx, prefix)
364364
if tagsErr != nil {
365-
origin.ClearCheckable()
365+
revInfo.Origin = nil
366366
if err == nil {
367367
err = tagsErr
368368
}

src/cmd/go/internal/modload/build.go

Lines changed: 32 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -161,53 +161,53 @@ func addUpdate(ctx context.Context, m *modinfo.ModulePublic) {
161161
}
162162
}
163163

164-
// mergeOrigin merges two origins,
164+
// mergeOrigin returns the union of data from two origins,
165165
// returning either a new origin or one of its unmodified arguments.
166-
// If the two origins conflict, mergeOrigin returns a non-specific one
167-
// that will not pass CheckReuse.
168-
// If m1 or m2 is nil, the other is returned unmodified.
169-
// But if m1 or m2 is non-nil and uncheckable, the result is also uncheckable,
170-
// to preserve uncheckability.
166+
// If the two origins conflict including if either is nil,
167+
// mergeOrigin returns nil.
171168
func mergeOrigin(m1, m2 *codehost.Origin) *codehost.Origin {
172-
if m1 == nil {
173-
return m2
174-
}
175-
if m2 == nil {
176-
return m1
177-
}
178-
if !m1.Checkable() {
179-
return m1
180-
}
181-
if !m2.Checkable() {
182-
return m2
169+
if m1 == nil || m2 == nil {
170+
return nil
183171
}
184172

185-
merged := new(codehost.Origin)
186-
*merged = *m1 // Clone to avoid overwriting fields in cached results.
173+
if m2.VCS != m1.VCS ||
174+
m2.URL != m1.URL ||
175+
m2.Subdir != m1.Subdir {
176+
return nil
177+
}
187178

179+
merged := *m1
180+
if m2.Hash != "" {
181+
if m1.Hash != "" && m1.Hash != m2.Hash {
182+
return nil
183+
}
184+
merged.Hash = m2.Hash
185+
}
188186
if m2.TagSum != "" {
189187
if m1.TagSum != "" && (m1.TagSum != m2.TagSum || m1.TagPrefix != m2.TagPrefix) {
190-
merged.ClearCheckable()
191-
return merged
188+
return nil
192189
}
193190
merged.TagSum = m2.TagSum
194191
merged.TagPrefix = m2.TagPrefix
195192
}
196-
if m2.Hash != "" {
197-
if m1.Hash != "" && m1.Hash != m2.Hash {
198-
merged.ClearCheckable()
199-
return merged
200-
}
201-
merged.Hash = m2.Hash
202-
}
203193
if m2.Ref != "" {
204194
if m1.Ref != "" && m1.Ref != m2.Ref {
205-
merged.ClearCheckable()
206-
return merged
195+
return nil
207196
}
208197
merged.Ref = m2.Ref
209198
}
210-
return merged
199+
200+
switch {
201+
case merged == *m1:
202+
return m1
203+
case merged == *m2:
204+
return m2
205+
default:
206+
// Clone the result to avoid an alloc for merged
207+
// if the result is equal to one of the arguments.
208+
clone := merged
209+
return &clone
210+
}
211211
}
212212

213213
// addVersions fills in m.Versions with the list of known versions.
@@ -331,7 +331,7 @@ func moduleInfo(ctx context.Context, rs *Requirements, m module.Version, mode Li
331331
mod := module.Version{Path: m.Path, Version: m.Version}
332332

333333
if m.Version != "" {
334-
if old := reuse[mod]; old != nil && old.Origin.Checkable() {
334+
if old := reuse[mod]; old != nil {
335335
if err := checkReuse(ctx, mod, old.Origin); err == nil {
336336
*m = *old
337337
m.Query = ""

src/cmd/go/internal/modload/list.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,8 +57,7 @@ func ListModules(ctx context.Context, args []string, mode ListMode, reuseFile st
5757
}
5858
return nil, fmt.Errorf("parsing %s: %v", reuseFile, err)
5959
}
60-
if m.Origin == nil || !m.Origin.Checkable() {
61-
// Nothing to check to validate reuse.
60+
if m.Origin == nil {
6261
continue
6362
}
6463
m.Reuse = true

src/cmd/go/internal/modload/query.go

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -111,8 +111,8 @@ func checkReuse(ctx context.Context, m module.Version, old *codehost.Origin) err
111111
}
112112

113113
func checkReuseRepo(ctx context.Context, repo versionRepo, path, query string, origin *codehost.Origin) error {
114-
if !origin.Checkable() {
115-
return errors.New("Origin is not checkable")
114+
if origin == nil {
115+
return errors.New("nil Origin")
116116
}
117117

118118
// Ensure that the Origin actually includes enough fields to resolve the query.
@@ -138,6 +138,9 @@ func checkReuseRepo(ctx context.Context, repo versionRepo, path, query string, o
138138
// If the version did not successfully resolve, the origin may indicate
139139
// a TagSum and/or RepoSum instead of a Hash, in which case we still need
140140
// to check those to ensure that the error is still applicable.
141+
if origin.Hash == "" && origin.Ref == "" && origin.TagSum == "" {
142+
return errors.New("no Origin information to check")
143+
}
141144

142145
case IsRevisionQuery(path, query):
143146
// This query may refer to a branch, non-version tag, or commit ID.
@@ -225,7 +228,7 @@ func queryProxy(ctx context.Context, proxy, path, query, current string, allowed
225228
return nil, err
226229
}
227230

228-
if old := reuse[module.Version{Path: path, Version: query}]; old != nil && old.Origin.Checkable() {
231+
if old := reuse[module.Version{Path: path, Version: query}]; old != nil {
229232
if err := checkReuseRepo(ctx, repo, path, query, old.Origin); err == nil {
230233
info := &modfetch.RevInfo{
231234
Version: old.Version,

src/cmd/go/testdata/script/mod_list_issue61423.txt

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,10 +51,20 @@ env GOMODCACHE=$WORK/modcache2
5151
# it is only going to have Git origin information about the one
5252
# commit — not the other tags that would go into resolving
5353
# the underlying version list.
54+
# 'go list' should not emit the partial information,
55+
# since it isn't enough to reconstruct the result.
5456

5557
go list -m -json vcs-test.golang.org/git/issue61415.git@latest
5658
cp stdout proxy-latest.json
5759
stdout '"Version": "v0.0.0-20231114180000-08a4fa6bb9c0"'
60+
! stdout '"Origin":'
61+
62+
# However, if we list a specific, stable version, we should get
63+
# whatever origin metadata the proxy has for the version.
64+
65+
go list -m -json vcs-test.golang.org/git/[email protected]
66+
cp stdout proxy-version.json
67+
stdout '"Version": "v0.0.0-20231114180000-08a4fa6bb9c0"'
5868
stdout '"Origin":'
5969
stdout '"VCS": "git"'
6070
stdout '"Hash": "08a4fa6bb9c04ffba03b26ae427b0d6335d90a2a"'
@@ -63,7 +73,8 @@ stdout '"Hash": "08a4fa6bb9c04ffba03b26ae427b0d6335d90a2a"'
6373

6474
# The -reuse flag has no effect with a proxy, since the proxy can serve
6575
# metadata about a given module version cheaply anyway.
66-
go list -reuse=proxy-latest.json -m -json vcs-test.golang.org/git/issue61415.git@latest
76+
77+
go list -reuse=proxy-version.json -m -json vcs-test.golang.org/git/[email protected]
6778
stdout '"Version": "v0.0.0-20231114180000-08a4fa6bb9c0"'
6879
stdout '"Origin":'
6980
stdout '"VCS": "git"'

0 commit comments

Comments
 (0)